├── .gitignore ├── .gitmodules ├── .npmignore ├── .prettierignore ├── .prettierrc.js ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── README.zh-CN.md ├── docs-src ├── index.css ├── index.html ├── index.ts ├── public │ └── .nojekyll ├── tsconfig.json └── vite.config.ts ├── docs ├── .nojekyll ├── assets │ ├── index.1db60ffe.css │ └── index.ff870cb1.js └── index.html ├── package-lock.json ├── package.json ├── scripts └── build.mjs ├── src ├── RealmRecord.ts ├── ShadowRealm.ts ├── es-module │ ├── helpers.ts │ └── index.ts ├── helpers.ts ├── index.ts ├── polyfill.ts └── type.ts ├── test ├── cases │ ├── WrappedFunction │ │ ├── length-throws-typeerror.js │ │ ├── length.js │ │ ├── name-throws-typeerror.js │ │ ├── name.js │ │ └── throws-typeerror-on-revoked-proxy.js │ ├── constructor.js │ ├── descriptor.js │ ├── extensibility.js │ ├── global-object.js │ ├── instance-extensibility.js │ ├── instance.js │ ├── length.js │ ├── name.js │ ├── proto.js │ └── prototype │ │ ├── Symbol.toStringTag.js │ │ ├── evaluate │ │ ├── descriptor.js │ │ ├── errors-from-the-other-realm-is-wrapped-into-a-typeerror.js │ │ ├── globalthis-available-properties.js │ │ ├── globalthis-config-only-properties.js │ │ ├── globalthis-ordinary-object.js │ │ ├── length.js │ │ ├── name.js │ │ ├── nested-realms.js │ │ ├── no-conditional-strict-mode.js │ │ ├── not-constructor.js │ │ ├── proto.js │ │ ├── returns-primitive-values.js │ │ ├── returns-proxy-callable-object.js │ │ ├── returns-symbol-values.js │ │ ├── throws-error-from-ctor-realm.js │ │ ├── throws-syntaxerror-on-bad-syntax.js │ │ ├── throws-typeerror-if-evaluation-resolves-to-non-primitive.js │ │ ├── throws-typeerror-wrap-throwing.js │ │ ├── throws-when-argument-is-not-a-string.js │ │ ├── validates-realm-object.js │ │ ├── wrapped-function-arguments-are-wrapped-into-the-inner-realm-extended.js │ │ ├── wrapped-function-arguments-are-wrapped-into-the-inner-realm.js │ │ ├── wrapped-function-from-return-values-share-no-identity.js │ │ ├── wrapped-function-multiple-different-realms-nested.js │ │ ├── wrapped-function-multiple-different-realms.js │ │ ├── wrapped-function-observing-their-scopes.js │ │ ├── wrapped-function-proto-from-caller-realm.js │ │ ├── wrapped-function-proxied-observes-boundary.js │ │ ├── wrapped-function-throws-typeerror-from-caller-realm.js │ │ ├── wrapped-function-throws-typeerror-on-exceptional-exit.js │ │ ├── wrapped-function-throws-typeerror-on-non-primitive-arguments.js │ │ ├── wrapped-function-throws-typeerror-on-non-primitive-returns.js │ │ ├── wrapped-functions-accepts-callable-objects.js │ │ ├── wrapped-functions-can-resolve-callable-returns.js │ │ ├── wrapped-functions-new-wrapping-on-each-evaluation.js │ │ ├── wrapped-functions-share-no-properties-extended.js │ │ └── wrapped-functions-share-no-properties.js │ │ ├── importValue │ │ ├── descriptor.js │ │ ├── import-value.js │ │ ├── length.js │ │ ├── name.js │ │ ├── not-constructor.js │ │ ├── proto.js │ │ ├── specifier-tostring.js │ │ ├── throws-if-exportname-not-string.js │ │ ├── throws-if-import-value-does-not-exist.js │ │ ├── throws-typeerror-import-syntax-error.js │ │ ├── throws-typeerror-import-throws.js │ │ └── validates-realm-object.js │ │ └── proto.js ├── helpers.js └── transformer.js └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist 3 | .DS_Store -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "test/test262"] 2 | path = test/test262 3 | url = https://github.com/tc39/test262.git 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | docs/ 3 | docs-src/ 4 | node_modules/ 5 | scripts/ 6 | src/ 7 | test/ 8 | .gitignore 9 | .gitmodules 10 | .prettierignore 11 | .npmrc 12 | .prettierrc.js 13 | chromedriver.exe 14 | geckodriver.exe 15 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | src/es-module/helpers.ts -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | tabWidth: 4, 3 | semi: true, 4 | singleQuote: true, 5 | trailingComma: 'es5', 6 | }; -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "editor.codeActionsOnSave": { 4 | "source.fixAll.eslint": false, 5 | "source.fixAll.stylelint": false 6 | }, 7 | "editor.defaultFormatter": "esbenp.prettier-vscode", 8 | "editor.detectIndentation": false, 9 | "editor.guides.bracketPairs": true, 10 | "editor.tabSize": 4, 11 | "eslint.enable": false, 12 | "stylelint.enable": false, 13 | "prettier.enable": true, 14 | "[javascript]": { 15 | "editor.defaultFormatter": "esbenp.prettier-vscode" 16 | }, 17 | "[typescript]": { 18 | "editor.defaultFormatter": "esbenp.prettier-vscode" 19 | }, 20 | "[json]": { 21 | "editor.defaultFormatter": "esbenp.prettier-vscode" 22 | }, 23 | "[jsonc]": { 24 | "editor.defaultFormatter": "esbenp.prettier-vscode" 25 | }, 26 | "[css]": { 27 | "editor.defaultFormatter": "esbenp.prettier-vscode" 28 | }, 29 | "[scss]": { 30 | "editor.defaultFormatter": "esbenp.prettier-vscode" 31 | }, 32 | 33 | "[html]": { 34 | "editor.defaultFormatter": "esbenp.prettier-vscode" 35 | }, 36 | "[markdown]": { 37 | "editor.formatOnSave": false 38 | }, 39 | "files.exclude": { 40 | "**/CVS": true, 41 | "**/.DS_Store": true, 42 | "**/.ds": true, 43 | "**/.git": true, 44 | "**/.hg": true, 45 | "**/.svn": true, 46 | "**/.theia": true 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2021 Ambit Tsai 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ShadowRealm API Polyfill 2 | A implementation of the ShadowRealm API Proposal, a JavaScript sandbox, test with TC39 Test262 cases. 3 | 4 | [简体中文](https://gitee.com/ambit/shadowrealm-api) | English 5 | ```ts 6 | declare class ShadowRealm { 7 | constructor(); 8 | evaluate(sourceText: string): Primitive | Function; 9 | importValue(specifier: string, bindingName: string): Promise; 10 | } 11 | ``` 12 | Try it now 🎉 13 | 14 | ## Install 15 | ``` 16 | npm i -S shadowrealm-api 17 | ``` 18 | 19 | 20 | ## Usage 21 | ### Po**n**yfill: non-invasive 22 | ```javascript 23 | import ShadowRealm from 'shadowrealm-api' 24 | 25 | const realm = new ShadowRealm(); 26 | ``` 27 | 28 | ### Po**l**yfill: patch up the global object 29 | ```javascript 30 | import 'shadowrealm-api/dist/polyfill' 31 | 32 | const realm = new ShadowRealm(); 33 | ``` 34 | 35 | 36 | ## Debugging 37 | Print internal info for debugging 38 | ```js 39 | ShadowRealm.__debug = true; 40 | ``` 41 | 42 | 43 | ## Limitations 44 | 1. All code evaluated inside a ShadowRealm runs in **strict mode**; 45 | 2. The ESM statement must not contain redundant comments; 46 | ```js 47 | // ❌ 48 | import/* */defaultExport from "module-name"; 49 | export default/* */'xxx'; 50 | 51 | // ✅ 52 | import defaultExport from "module-name"; 53 | export default 'xxx'; 54 | ``` 55 | 3. Exporting variable declarations is not supported; 56 | ```js 57 | // ❌ 58 | export const obj = {...}, fn = () => {...}; 59 | 60 | // ✅ 61 | const obj = {...}, fn = () => {...}; 62 | export { obj, fn }; 63 | ``` 64 | 65 | 66 | ## Compatibility 67 | |IE|Edge|Firefox|Chrome|Safari|Opera| 68 | |:-:|:-:|:-:|:-:|:-:|:-:| 69 | ||14|29[1][2]|32[1][2]|8[2][3]|19[1][2]| 70 | |||41|49|10.1[3]|36| 71 | |||||14.1|| 72 | 73 | > Notes: 74 | > 1. Don't support destructuring assignment in ESM statement; 75 | > 1. Need `fetch` polyfill in top window; 76 | > 1. Need `URL` polyfill in top window; 77 | 78 | Use polyfills: 79 | ```js 80 | import "fetch polyfill"; 81 | import "URL polyfill"; 82 | import "shadowrealm-api/dist/polyfill"; 83 | // Your codes 84 | ``` 85 | 86 | 87 | ## Contact 88 | 1. WeChat: cai_fanwei 89 | 1. QQ Group: 663286147 90 | 1. E-mail: ambit_tsai@qq.com 91 | -------------------------------------------------------------------------------- /README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # ShadowRealm API Polyfill 2 | 依照 ShadowRealm API 提案实现的 JavaScript 沙箱,使用 TC39 Test262 的用例进行测试。 3 | 4 | 简体中文 | [English](https://github.com/ambit-tsai/shadowrealm-api) 5 | ```ts 6 | declare class ShadowRealm { 7 | constructor(); 8 | evaluate(sourceText: string): Primitive | Function; 9 | importValue(specifier: string, bindingName: string): Promise; 10 | } 11 | ``` 12 | 在线试用一下 🎉 13 | 14 | 15 | ## 安装 16 | ``` 17 | npm i -S shadowrealm-api 18 | ``` 19 | 20 | 21 | ## 使用 22 | ### Po**n**yfill: 无侵入性 23 | ```javascript 24 | import ShadowRealm from 'shadowrealm-api' 25 | 26 | const realm = new ShadowRealm(); 27 | ``` 28 | 29 | ### Po**l**yfill: 修补全局对象 30 | ```javascript 31 | import 'shadowrealm-api/dist/polyfill' 32 | 33 | const realm = new ShadowRealm(); 34 | ``` 35 | 36 | 37 | ## 调试 38 | 打印内部调试信息 39 | ```js 40 | ShadowRealm.__debug = true; 41 | ``` 42 | 43 | 44 | ## 限制 45 | 1. 在 ShadowRealm 中运行的所有代码都处于**严格模式**下; 46 | 2. ESM 语句不能含有冗余的注释; 47 | ```js 48 | // ❌ 49 | import/* */defaultExport from "module-name"; 50 | export default/* */'xxx'; 51 | 52 | // ✅ 53 | import defaultExport from "module-name"; 54 | export default 'xxx'; 55 | ``` 56 | 3. 不支持导出变量声明; 57 | ```js 58 | // ❌ 59 | export const obj = {...}, fn = () => {...}; 60 | 61 | // ✅ 62 | const obj = {...}, fn = () => {...}; 63 | export { obj, fn }; 64 | ``` 65 | 66 | 67 | ## 兼容性 68 | |IE|Edge|Firefox|Chrome|Safari|Opera| 69 | |:-:|:-:|:-:|:-:|:-:|:-:| 70 | ||14|29[1][2]|32[1][2]|8[2][3]|19[1][2]| 71 | |||41|49|10.1[3]|36| 72 | |||||14.1|| 73 | 74 | > Notes: 75 | > 1. ESM 语句不支持解构赋值; 76 | > 1. 顶层作用域需要`fetch`垫片; 77 | > 1. 顶层作用域需要`URL`垫片; 78 | 79 | 使用垫片: 80 | ```js 81 | import "fetch polyfill"; 82 | import "URL polyfill"; 83 | import "shadowrealm-api/dist/polyfill"; 84 | // 你的代码 85 | ``` 86 | 87 | 88 | ## 联系 89 | 1. 微信: cai_fanwei 90 | 1. QQ群: 663286147 91 | 1. 邮箱: ambit_tsai@qq.com 92 | -------------------------------------------------------------------------------- /docs-src/index.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | margin: 0; 4 | background-color: #f3f3f3; 5 | } 6 | 7 | .content { 8 | position: fixed; 9 | top: 50%; 10 | left: 50%; 11 | transform: translate(-50%, -50%); 12 | } 13 | 14 | .title { 15 | display: block; 16 | margin-top: 0; 17 | text-align: center; 18 | font-size: 24px; 19 | color: black; 20 | text-decoration: blink; 21 | } 22 | 23 | .code { 24 | display: inline-block; 25 | padding: 8px 16px; 26 | border-radius: 3px; 27 | background-color: #2b3a42; 28 | font-size: 13px; 29 | line-height: 19px; 30 | color: aqua; 31 | } 32 | -------------------------------------------------------------------------------- /docs-src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ShadowRealm Online 7 | 8 | 9 |
10 | ShadowRealm API 11 |

12 |         

Press F12 to open devtools for debugging

13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /docs-src/index.ts: -------------------------------------------------------------------------------- 1 | import './index.css'; 2 | import '../src/polyfill'; 3 | 4 | const code = ` 5 | class ShadowRealm { 6 | constructor(); 7 | evaluate(sourceText: string): Primitive | Function; 8 | importValue(specifier: string, bindingName: string): Promise; 9 | } 10 | 11 | `; 12 | 13 | setTimeout(() => { 14 | const el = document.querySelector('.code'); 15 | el.textContent = code; 16 | }); 17 | -------------------------------------------------------------------------------- /docs-src/public/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambit-tsai/shadowrealm-api/4aebc03fe1dd8a9d38cef9b281427fd40a3c930c/docs-src/public/.nojekyll -------------------------------------------------------------------------------- /docs-src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6", 4 | "moduleResolution": "node", 5 | }, 6 | } -------------------------------------------------------------------------------- /docs-src/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import { resolve } from 'path'; 3 | 4 | const root = resolve(__dirname); 5 | 6 | // https://vitejs.dev/config/ 7 | export default defineConfig({ 8 | root, 9 | base: '/shadowrealm-api/', 10 | build: { 11 | outDir: resolve(root, '../docs'), 12 | emptyOutDir: true, 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambit-tsai/shadowrealm-api/4aebc03fe1dd8a9d38cef9b281427fd40a3c930c/docs/.nojekyll -------------------------------------------------------------------------------- /docs/assets/index.1db60ffe.css: -------------------------------------------------------------------------------- 1 | html,body{height:100%;margin:0;background-color:#f3f3f3}.content{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%)}.title{display:block;margin-top:0;text-align:center;font-size:24px;color:#000;text-decoration:blink}.code{display:inline-block;padding:8px 16px;border-radius:3px;background-color:#2b3a42;font-size:13px;line-height:19px;color:#0ff} 2 | -------------------------------------------------------------------------------- /docs/assets/index.ff870cb1.js: -------------------------------------------------------------------------------- 1 | const ee=function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const n of document.querySelectorAll('link[rel="modulepreload"]'))o(n);new MutationObserver(n=>{for(const s of n)if(s.type==="childList")for(const u of s.addedNodes)u.tagName==="LINK"&&u.rel==="modulepreload"&&o(u)}).observe(document,{childList:!0,subtree:!0});function r(n){const s={};return n.integrity&&(s.integrity=n.integrity),n.referrerpolicy&&(s.referrerPolicy=n.referrerpolicy),n.crossorigin==="use-credentials"?s.credentials="include":n.crossorigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function o(n){if(n.ep)return;n.ep=!0;const s=r(n);fetch(n.href,s)}};ee();const M=window,k=Object.defineProperty,H={debug:!1},{log:q,warn:te}=console;function z(e){H.debug&&(q("[DEBUG]"),T(e)?te(e):q(e))}let W;if(M.Reflect)W=Reflect.apply;else{const e=Function.prototype.apply;W=function(t,r,o){return e.call(t,r,o)}}const re=String.prototype.replace;function ne(e,...t){return W(re,e,t)}function T(e){return e?typeof e=="object":!1}let{assign:$,keys:J}=Object;$||($=function(e){const t=arguments;for(let r=1,{length:o}=t;rt._.debug,set:n=>t._.debug=n}),o}function ue(e,t){const{Function:r,Promise:o,String:n,Symbol:s,TypeError:u}=e.intrinsics,{apply:d,define:h,dynamicImportPattern:y,dynamicImportReplacer:x,getWrappedValue:b,isObject:p,replace:E,wrapError:P}=t,{toString:v}=r,w=function(){if(!(this instanceof w))throw new u("Constructor requires a new operator");const a=t.createRealmRecord(e,t);h(this,"__realm",{value:a})},{prototype:I}=w,_=!0;h(I,"evaluate",{configurable:_,writable:_,value:l}),h(I,"importValue",{configurable:_,writable:_,value:f}),s&&s.toStringTag&&h(I,s.toStringTag,{configurable:_,value:"ShadowRealm"});function l(i){const a=p(this)?this.__realm:{};if(!a.intrinsics)throw new u("must be called on ShadowRealm object");if(typeof i!="string")throw new u("evaluate expects a string");i=E(i,y,x);let c;try{c=m(i,a)}catch(R){throw P(R,e,_)}return b(e,c,a,t)}function m(i,a){i=E(i,y,x),i=d(v,r(i),[]),i='"use strict";undefined;try'+E(i,/[^{]+/,"")+"catch(e){throw{_:e}}",t.log(i);const{globalObject:c}=a;return c.eval=a.intrinsics,d(a.evalInContext,c,[i])}function f(i,a){const c=p(this)?this.__realm:{};if(!c.intrinsics)throw new u("must be called on ShadowRealm object");if(i=n(i),typeof a!="string")throw new u("bindingName is not string");return new o((R,g)=>{c.globalObject.__import(i).then(C=>{if(!(a in C))throw new u('"'+i+'" has no export named "'+a+'"');const U=b(e,C[a],c,t);R(U)}).catch(C=>{const U=P(C,e);g(U)})})}return w}function fe(e,t){e.intrinsics.eval("("+pe.toString()+")")(e,t)}function pe(e,t){const{globalObject:r,intrinsics:o}=e,{apply:n,define:s,GLOBAL:u}=t,{Array:{prototype:{push:d}},Object:{create:h},Promise:y,Symbol:x}=o,{fetch:b,URL:p}=u,{all:E}=y,{then:P}=y.prototype;let v;const w={};s(r,"__import",{value:_}),s(r,"__from",{value:I}),s(r,"__export",{set:f=>t.assign(v,f)}),s(r,"__default",{set:f=>v.default=f});function I(f,i){const{href:a}=new p(f,i),c=w[a];if(c&&c.exports)return c.exports;throw new o.Error("Module does not exist ("+f+")")}function _(f,i=u.location.href){const{href:a}=new p(f,i);return new y((c,R)=>{const g=w[a];if(g&&g.exports)return c(g.exports);l(a).then(C=>{c(m(a,C))}).catch(R)})}function l(f){let i=w[f];if(i)return i.promise;const a=b(f,{credentials:"same-origin"}).then(c=>{if(c.status===200)return c.text();throw{_:new o.TypeError("Failed to fetch dynamically imported module: "+f)}}).then(c=>{const[R,g]=t.transformEsmSyntax(c),C=[];for(let O=0,{length:F}=g;O{for(let F=0,{length:A}=O;F{throw c._?c._:new o[c.name](c.message)});return i=w[f]={promise:a},a}function m(f,i){v=h(null),x&&x.toStringTag&&s(v,x.toStringTag,{value:"Module"}),r.eval(i);const a=w[f];return a.exports=v,v=void 0,a.exports}}function de(e,t){const{document:r}=e.intrinsics,o=r.createElement("iframe");return o.name="ShadowRealm",r.head.appendChild(o),o.contentWindow.eval("("+me.toString()+")")(t)}function me(e){const t=window,{Function:r,Object:o,Symbol:n}=t,{getOwnPropertyNames:s}=o,{apply:u,define:d,dynamicImportPattern:h,dynamicImportReplacer:y,replace:x}=e,b={},p={};let E;n&&n.unscopables&&d(p,n.unscopables,{value:o.seal(o.create(null))});for(const l of s(t)){b[l]=t[l];const m=e.globalReservedProps.indexOf(l)!==-1,f=o.getOwnPropertyDescriptor(t,l);l==="eval"?w():m&&d(p,l,f),f.configurable?delete t[l]:f.writable?t[l]=E:m||d(p,l,{value:E})}if(b.EventTarget)for(const l of s(b.EventTarget.prototype))l!=="constructor"&&d(t,l,{value:E});p.globalThis=p,p.Function=_();const P=r("with(this)return eval(arguments[0])"),v={intrinsics:b,globalObject:p,evalInContext:P};return e.defineShadowRealmCtor(v,e),e.addEsModuleHelpers(v,e),v;function w(){let l=!1;const m=I();d(p,"eval",{get(){return l?(l=!1,b.eval):m},set(f){l=f===b}})}function I(){return{eval(l){return l='"use strict";undefined;'+x(l,h,y),e.log(l),p.eval=b,u(P,p,[l])}}.eval}function _(){const{toString:l}=r,m=function(){const i=u(r,null,arguments);let a=u(l,i,[]);a=x(a,h,y),a='with(this)return function(){"use strict";return '+a+"}()",e.log(a);const c=r(a),R=u(c,p,[]);return function(){return u(R,this===t?void 0:this,arguments)}};return m.prototype=r.prototype,m.prototype.constructor=m,m}}const N=[],j=[],Q=/(^|[^.$])(\bimport\s*(\(|\/[/*]))/g,X="$1__$2",ge="\\bexport\\b(\\s+((var|let|const)\\s+([^;]+)|(async\\s+)?function(\\s+|\\s*\\*\\s*)([^\\s(]+)|class\\s+([^\\s{]+)|default\\s+((async\\s+)?function(\\s+|\\s*\\*\\s*)([^\\s(]+)|class\\s+([^\\s{]+)|)?)|\\s*(\\*\\s*(as\\s+(\\S+)\\s+)?from\\s*(\"[^\"]+\"|'[^']+'|`[^`]+`)|{([^}]+)}(\\s*from\\s*(\"[^\"]+\"|'[^']+'|`[^`]+`))?))|\\bimport\\b(\\s*(({([^}]+)}\\s*|\\*\\s*as\\s+(\\S+)\\s+)from\\s*)?|\\s+([^{*\"'`]+)(\\s+from|\\s*,\\s*({([^}]+)}\\s*|\\*\\s*as\\s+(\\S+)\\s+)from)\\s*)(\"[^\"]+\"|'[^']+'|`[^`]+`)",G=/([^\s,]+)\s+as\s+([^\s,]+)/g,he=[{p:RegExp(ge,"g"),r(e,t,r,o,n,s,u,d,h,y,x,b,p,E,P,v,w,I,_,l,m,f,i,a,c,R,g,C,U,O,F,A){if(n){for(const S of n.split(","))S.replace(/^\s*([^\s={}:]+)\s*($|=|})|[:{]\s*([^\s={}:]+)\s*($|=|})/,(L,V,Z,B)=>(j.push(V||B),L));return r}if(d)return"__export={"+d+":"+d+"};"+r;if(h)return j.push(h),r;if(p)return"__default="+p+";"+y;if(E)return j.push(E),r;if(r)return r.replace(/^default/,"__default=");if(I)return N.push(I),w?"__export={"+w+":__from("+I+",__meta.url)}":"__export=__from("+I+",__meta.url)";if(m){N.push(m);const S=[];return _.replace(/([^\s,]+)(\s+as\s+([^\s,]+))?/g,(L,V,Z,B)=>(S.push((B||V)+":m."+V),L)),";(function(){var m=__from("+m+",__meta.url);__export={"+S.join()+"}}())"}if(_){const S=_.replace(G,"$2:$1");return"__export={"+S+"}"}if(A){N.push(A);const S="=__from("+A+",__meta.url)";if(c){const L=c.replace(G,"$1:$2");return"const{"+L+"}"+S}if(R)return"var "+R+S;if(O){const L=O.replace(G,"$1:$2");return"const{"+L+"}"+S+","+g+S+".default"}return F?"var "+F+S+","+g+"="+F+".default":g?"var "+g+S+".default":A}}},{p:/\bimport\.meta\b/g,r:"__meta"},{p:Q,r:X}];function ye(e){for(const{p:r,r:o}of he)e=e.replace(r,o);if(j.length){for(let r=j.length-1;r>=0;--r)j[r]+=":"+j[r];e+=";__export={"+j.join()+"}",j.length=0}const t=[];for(let r=0,{length:o}=N;r; 6 | } 7 | 8 | `;setTimeout(()=>{const e=document.querySelector(".code");e.textContent=we}); 9 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ShadowRealm Online 7 | 8 | 9 | 10 | 11 |
12 | ShadowRealm API 13 |

14 |         

Press F12 to open devtools for debugging

15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shadowrealm-api", 3 | "version": "0.8.2", 4 | "description": "ShadowRealm polyfill for browser", 5 | "author": { 6 | "name": "Ambit Tsai", 7 | "email": "ambit_tsai@qq.com" 8 | }, 9 | "license": "Apache-2.0", 10 | "main": "dist/index.js", 11 | "scripts": { 12 | "start": "vite -c docs-src/vite.config.ts", 13 | "test": "test262-harness --hostType=chrome --hostPath=\"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe\" --prelude=test/helpers.js --prelude=dist/polyfill.umd.js --transformer=test/transformer.js --test262Dir=test/test262 test/cases/**/*.js", 14 | "test:firefox": "test262-harness --hostType=firefox --hostPath=\"C:\\Program Files\\Mozilla Firefox\\firefox.exe\" --prelude=test/helpers.js --prelude=dist/polyfill.umd.js --transformer=test/transformer.js --test262Dir=test/test262 test/cases/**/*.js", 15 | "build": "node --experimental-json-modules scripts/build.mjs", 16 | "build:docs": "vite build -c docs-src/vite.config.ts", 17 | "preview:docs": "vite preview -c docs-src/vite.config.ts" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/ambit-tsai/shadowrealm-api.git" 22 | }, 23 | "homepage": "https://github.com/ambit-tsai/shadowrealm-api#readme", 24 | "bugs": { 25 | "url": "https://github.com/ambit-tsai/shadowrealm-api/issues" 26 | }, 27 | "devDependencies": { 28 | "@rollup/plugin-typescript": "~8.3.0", 29 | "rollup": "~2.59.0", 30 | "rollup-plugin-terser": "~7.0.2", 31 | "test262-harness": "~8.0.0", 32 | "tslib": "~2.3.1", 33 | "typescript": "~4.5.2", 34 | "vite": "~2.7.2" 35 | }, 36 | "keywords": [ 37 | "shadowrealm", 38 | "shadowrealms", 39 | "realm", 40 | "realms", 41 | "sandbox", 42 | "polyfill", 43 | "ponyfill", 44 | "shim", 45 | "shims" 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /scripts/build.mjs: -------------------------------------------------------------------------------- 1 | import { rm, mkdir } from 'fs/promises'; 2 | import { rollup } from 'rollup'; 3 | import typescript from '@rollup/plugin-typescript'; 4 | import { terser } from 'rollup-plugin-terser'; 5 | import pkg from '../package.json'; 6 | 7 | const banner = ` 8 | /** 9 | * ${pkg.name}@${pkg.version} 10 | * ${pkg.description} 11 | * @author ${pkg.author.name} <${pkg.author.email}> 12 | * @license ${pkg.license} 13 | * @see {@link ${pkg.homepage}} 14 | */`; 15 | 16 | await rm('dist', { 17 | force: true, 18 | recursive: true, 19 | }); 20 | await mkdir('dist'); 21 | 22 | const terserPlugin = terser({ 23 | keep_fnames: /^ShadowRealm|evaluate|importValue|eval|Function$/, 24 | }); 25 | 26 | const bundle = await rollup({ 27 | input: ['src/index.ts', 'src/polyfill.ts'], 28 | plugins: [typescript(), terserPlugin], 29 | }); 30 | await bundle.write({ 31 | dir: 'dist', 32 | banner, 33 | format: 'esm', 34 | sourcemap: true, 35 | }); 36 | 37 | const polyfillBundle = await rollup({ 38 | input: 'src/polyfill.ts', 39 | plugins: [typescript(), terserPlugin], 40 | }); 41 | await polyfillBundle.write({ 42 | dir: 'dist', 43 | banner, 44 | format: 'umd', 45 | entryFileNames: '[name].umd.js', 46 | sourcemap: true, 47 | }); 48 | -------------------------------------------------------------------------------- /src/RealmRecord.ts: -------------------------------------------------------------------------------- 1 | import type { GlobalObject, RealmRecord } from './type'; 2 | import type { Utils } from '.'; 3 | 4 | export function createRealmRecord(parentRealmRec: RealmRecord, utils: Utils) { 5 | const { document } = parentRealmRec.intrinsics; 6 | const iframe = document.createElement('iframe'); 7 | iframe.name = 'ShadowRealm'; 8 | document.head.appendChild(iframe); 9 | const createInContext = (iframe.contentWindow as GlobalObject).eval( 10 | '(' + createRealmRecordInContext.toString() + ')' 11 | ); 12 | return createInContext(utils); 13 | } 14 | 15 | function createRealmRecordInContext(utils: Utils) { 16 | const win = window; 17 | const { Function: RawFunction, Object, Symbol } = win; 18 | const { getOwnPropertyNames } = Object; 19 | const { 20 | apply, 21 | define, 22 | dynamicImportPattern, 23 | dynamicImportReplacer, 24 | replace, 25 | } = utils; 26 | const intrinsics = {} as GlobalObject; 27 | const globalObject = {} as GlobalObject; 28 | let UNDEFINED: undefined; 29 | 30 | if (Symbol && Symbol.unscopables) { 31 | // Prevent escape from the `with` environment 32 | define(globalObject, Symbol.unscopables, { 33 | value: Object.seal(Object.create(null)), 34 | }); 35 | } 36 | 37 | // Handle window object 38 | for (const key of getOwnPropertyNames(win) as any[]) { 39 | intrinsics[key] = win[key]; 40 | const isReserved = utils.globalReservedProps.indexOf(key) !== -1; 41 | const descriptor = Object.getOwnPropertyDescriptor(win, key)!; 42 | if (key === 'eval') { 43 | defineSafeEval(); 44 | } else if (isReserved) { 45 | define(globalObject, key, descriptor); // copy to new global object 46 | } 47 | if (descriptor.configurable) { 48 | delete win[key]; 49 | } else if (descriptor.writable) { 50 | win[key] = UNDEFINED as any; 51 | } else if (!isReserved) { 52 | // Intercept properties that cannot be deleted 53 | define(globalObject, key, { value: UNDEFINED }); 54 | } 55 | } 56 | 57 | if (intrinsics.EventTarget) { 58 | // Intercept the props of EventTarget.prototype 59 | for (const key of getOwnPropertyNames( 60 | intrinsics.EventTarget.prototype 61 | )) { 62 | if (key !== 'constructor') { 63 | define(win, key, { value: UNDEFINED }); 64 | } 65 | } 66 | } 67 | 68 | globalObject.globalThis = globalObject; 69 | globalObject.Function = createSafeFunction(); 70 | 71 | const evalInContext = RawFunction('with(this)return eval(arguments[0])'); 72 | const realmRec = { intrinsics, globalObject, evalInContext } as RealmRecord; 73 | 74 | utils.defineShadowRealmCtor(realmRec, utils); 75 | utils.addEsModuleHelpers(realmRec, utils); 76 | 77 | return realmRec; 78 | 79 | function defineSafeEval() { 80 | let isInnerCall = false; 81 | const safeEval = createSafeEval(); 82 | define(globalObject, 'eval', { 83 | get() { 84 | if (isInnerCall) { 85 | isInnerCall = false; 86 | return intrinsics.eval; // used by safe eval 87 | } 88 | return safeEval; 89 | }, 90 | set(val) { 91 | isInnerCall = val === intrinsics; 92 | }, 93 | }); 94 | } 95 | 96 | function createSafeEval() { 97 | return { 98 | eval(x: string) { 99 | // `'use strict'` is used to enable strict mode 100 | // `undefined` is used to ensure that the return value remains unchanged 101 | x = 102 | '"use strict";undefined;' + 103 | replace(x, dynamicImportPattern, dynamicImportReplacer); 104 | utils.log(x); 105 | // @ts-ignore: `intrinsics` is the key to use raw `eval` 106 | globalObject.eval = intrinsics; 107 | return apply(evalInContext, globalObject, [x]); 108 | }, 109 | }.eval; // fix: TS1215: Invalid use of 'eval' 110 | } 111 | 112 | function createSafeFunction(): FunctionConstructor { 113 | const { toString } = RawFunction; 114 | const Ctor = function Function() { 115 | const rawFn = apply(RawFunction, null, arguments); 116 | let fnStr = apply(toString, rawFn, []); 117 | fnStr = replace(fnStr, dynamicImportPattern, dynamicImportReplacer); 118 | fnStr = 119 | 'with(this)return function(){"use strict";return ' + 120 | fnStr + 121 | '}()'; 122 | utils.log(fnStr); 123 | const wrapFn = RawFunction(fnStr); 124 | const safeFn: Function = apply(wrapFn, globalObject, []); 125 | return function (this: any) { 126 | const ctx = this === win ? undefined : this; 127 | return apply(safeFn, ctx, arguments); 128 | }; 129 | }; 130 | Ctor.prototype = RawFunction.prototype; 131 | Ctor.prototype.constructor = Ctor; 132 | return Ctor as any; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/ShadowRealm.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | BuiltinShadowRealm, 3 | RealmRecord, 4 | ShadowRealmConstructor, 5 | } from './type'; 6 | import type { Utils } from '.'; 7 | 8 | export function defineShadowRealmCtor( 9 | globalRealmRec: RealmRecord, 10 | utils: Utils 11 | ) { 12 | utils.define(globalRealmRec.globalObject, 'ShadowRealm', { 13 | configurable: true, 14 | writable: true, 15 | value: createShadowRealmCtor(globalRealmRec, utils), 16 | }); 17 | } 18 | 19 | export function createShadowRealmCtor( 20 | globalRealmRec: RealmRecord, 21 | utils: Utils 22 | ): ShadowRealmConstructor { 23 | const createInContext = globalRealmRec.intrinsics.eval( 24 | '(' + createShadowRealmCtorInContext.toString() + ')' 25 | ); 26 | const Ctor = createInContext(globalRealmRec, utils); 27 | utils.define(Ctor, '__debug', { 28 | get: () => utils._.debug, 29 | set: (val) => (utils._.debug = val), 30 | }); 31 | return Ctor; 32 | } 33 | 34 | function createShadowRealmCtorInContext( 35 | globalRealmRec: RealmRecord, 36 | utils: Utils 37 | ) { 38 | const { Function, Promise, String, Symbol, TypeError } = 39 | globalRealmRec.intrinsics; 40 | const { 41 | apply, 42 | define, 43 | dynamicImportPattern, 44 | dynamicImportReplacer, 45 | getWrappedValue, 46 | isObject, 47 | replace, 48 | wrapError, 49 | } = utils; 50 | const { toString } = Function; 51 | 52 | /** 53 | * ShadowRealm Class 54 | */ 55 | const Constructor = function ShadowRealm(this: BuiltinShadowRealm) { 56 | if (!(this instanceof Constructor)) { 57 | throw new TypeError('Constructor requires a new operator'); 58 | } 59 | const realmRec = utils.createRealmRecord(globalRealmRec, utils); 60 | define(this, '__realm', { value: realmRec }); 61 | }; 62 | 63 | const { prototype } = Constructor; 64 | const TRUE = true; 65 | 66 | define(prototype, 'evaluate', { 67 | configurable: TRUE, 68 | writable: TRUE, 69 | value: evaluate, 70 | }); 71 | define(prototype, 'importValue', { 72 | configurable: TRUE, 73 | writable: TRUE, 74 | value: importValue, 75 | }); 76 | if (Symbol && Symbol.toStringTag) { 77 | define(prototype, Symbol.toStringTag, { 78 | configurable: TRUE, 79 | value: 'ShadowRealm', 80 | }); 81 | } 82 | 83 | function evaluate(this: BuiltinShadowRealm, sourceText: string) { 84 | const realmRec = isObject(this) ? this.__realm : ({} as RealmRecord); 85 | if (!realmRec.intrinsics) { 86 | throw new TypeError('must be called on ShadowRealm object'); 87 | } 88 | if (typeof sourceText !== 'string') { 89 | throw new TypeError('evaluate expects a string'); 90 | } 91 | sourceText = replace( 92 | sourceText, 93 | dynamicImportPattern, 94 | dynamicImportReplacer 95 | ); 96 | let result: any; 97 | try { 98 | result = evalWithCatch(sourceText, realmRec); 99 | } catch (error) { 100 | throw wrapError(error, globalRealmRec, TRUE); 101 | } 102 | return getWrappedValue(globalRealmRec, result, realmRec, utils); 103 | } 104 | 105 | function evalWithCatch(x: string, realmRec: RealmRecord) { 106 | x = replace(x, dynamicImportPattern, dynamicImportReplacer); 107 | x = apply(toString, Function(x), []); 108 | x = 109 | '"use strict";undefined;try' + 110 | replace(x, /[^{]+/, '') + 111 | 'catch(e){throw{_:e}}'; 112 | utils.log(x); 113 | const { globalObject } = realmRec; 114 | // @ts-ignore 115 | globalObject.eval = realmRec.intrinsics; 116 | return apply(realmRec.evalInContext, globalObject, [x]); 117 | } 118 | 119 | function importValue( 120 | this: BuiltinShadowRealm, 121 | specifier: string, 122 | bindingName: string 123 | ) { 124 | const realmRec = isObject(this) ? this.__realm : ({} as RealmRecord); 125 | if (!realmRec.intrinsics) { 126 | throw new TypeError('must be called on ShadowRealm object'); 127 | } 128 | specifier = String(specifier); 129 | if (typeof bindingName !== 'string') { 130 | throw new TypeError('bindingName is not string'); 131 | } 132 | return new Promise((resolve, reject) => { 133 | realmRec.globalObject 134 | .__import(specifier) 135 | .then((exports) => { 136 | if (!(bindingName in exports)) { 137 | throw new TypeError( 138 | '"' + 139 | specifier + 140 | '" has no export named "' + 141 | bindingName + 142 | '"' 143 | ); 144 | } 145 | const wrappedValue = getWrappedValue( 146 | globalRealmRec, 147 | exports[bindingName], 148 | realmRec, 149 | utils 150 | ); 151 | resolve(wrappedValue); 152 | }) 153 | .catch((reason) => { 154 | const error = wrapError(reason, globalRealmRec); 155 | reject(error); 156 | }); 157 | }); 158 | } 159 | 160 | return Constructor; 161 | } 162 | -------------------------------------------------------------------------------- /src/es-module/helpers.ts: -------------------------------------------------------------------------------- 1 | const moduleSpecifiers: string[] = []; 2 | const exportedNames: string[] = []; 3 | 4 | 5 | /** 6 | * Syntax: 7 | * import("module-name"); => __import("module-name"); 8 | */ 9 | export const dynamicImportPattern = /(^|[^.$])(\bimport\s*(\(|\/[/*]))/g; 10 | export const dynamicImportReplacer = '$1__$2'; 11 | 12 | 13 | const exportAndImportPattern = '' + 14 | '\\bexport\\b(' + 15 | '\\s+(' + 16 | // export let name1, name2, …, nameN; // also var, const 17 | // export let name1 = …, name2 = …, …, nameN; // also var, const 18 | // export const { name1, name2: bar } = o; 19 | '(var|let|const)\\s+([^;]+)|' + 20 | // export function FunctionName(){...} 21 | '(async\\s+)?function(\\s+|\\s*\\*\\s*)([^\\s(]+)|' + 22 | // export class ClassName {...} 23 | 'class\\s+([^\\s{]+)|' + 24 | // export default expression; 25 | 'default\\s+(' + 26 | // export default function (…) { … } 27 | '(async\\s+)?function(\\s+|\\s*\\*\\s*)([^\\s(]+)|' + 28 | // export default class ClassName {...} 29 | 'class\\s+([^\\s{]+)|' + 30 | ')?' + 31 | ')|' + 32 | '\\s*(' + 33 | // export * from …; // does not set the default export 34 | '\\*\\s*(' + 35 | // export * as name1 from …; // Draft ECMAScript® 2O21 36 | 'as\\s+(\\S+)\\s+' + 37 | ')?from\\s*("[^"]+"|\'[^\']+\'|`[^`]+`)|' + 38 | // export { name1, variable2 as name2, …, nameN }; 39 | // export { name1, import2 as name2, …, nameN } from …; 40 | // export { default } from …; 41 | '{([^}]+)}(\\s*from\\s*("[^"]+"|\'[^\']+\'|`[^`]+`))?' + 42 | ')' + 43 | ')|' + 44 | '\\bimport\\b(' + 45 | // import "module-name"; 46 | '\\s*(' + 47 | '(' + 48 | // import { e1, e2, e3 as alias } from "module-name"; 49 | '{([^}]+)}\\s*|' + 50 | // import * as name from "module-name"; 51 | '\\*\\s*as\\s+(\\S+)\\s+' + 52 | ')from\\s*' + 53 | ')?|' + 54 | '\\s+([^{*"\'`]+)(' + 55 | // import defaultExport from "module-name"; 56 | '\\s+from|' + 57 | '\\s*,\\s*(' + 58 | // import defaultExport, { export [ , [...] ] } from "module-name"; 59 | '{([^}]+)}\\s*|' + 60 | // import defaultExport, * as name from "module-name"; 61 | '\\*\\s*as\\s+(\\S+)\\s+' + 62 | ')from' + 63 | ')\\s*' + 64 | ')("[^"]+"|\'[^\']+\'|`[^`]+`)'; 65 | 66 | 67 | const aliasPattern = /([^\s,]+)\s+as\s+([^\s,]+)/g; 68 | 69 | 70 | const patternAndReplacers: { p: RegExp, r: any }[] = [ 71 | { 72 | p: RegExp(exportAndImportPattern, 'g'), 73 | r( 74 | m: string, 75 | e1: string, e2: string, e3: string, e4: string, e5: string, 76 | e6: string, e7: string, e8: string, e9: string, e10: string, 77 | e11: string, e12: string, e13: string, e14: string, e15: string, 78 | e16: string, e17: string, e18: string, e19: string, e20: string, 79 | i1: string, i2: string, i3: string, i4: string, i5: string, 80 | i6: string, i7: string, i8: string, i9: string, i10: string, 81 | specifier: string, 82 | ) { 83 | // export let name1 = …, name2 = …, …, nameN; // also var, const 84 | if (e4) { 85 | for (const str of e4.split(',')) { 86 | str.replace( 87 | /^\s*([^\s={}:]+)\s*($|=|})|[:{]\s*([^\s={}:]+)\s*($|=|})/, 88 | (m: string, p1: string, p2: string, p3: string) => { 89 | exportedNames.push(p1 || p3); 90 | return m; 91 | } 92 | ); 93 | } 94 | return e2; 95 | } 96 | // export function FunctionName(){...} 97 | if (e7) { 98 | return '__export={' + e7 + ':' + e7 + '};' + e2; 99 | } 100 | // export class ClassName {...} 101 | if (e8) { 102 | exportedNames.push(e8); 103 | return e2; 104 | } 105 | // export default function (…) { … } 106 | if (e12) { 107 | return '__default=' + e12 + ';' + e9; 108 | } 109 | // export default class ClassName {...} 110 | if (e13) { 111 | exportedNames.push(e13); 112 | return e2; 113 | } 114 | // export default expression; 115 | if (e2) { 116 | return e2.replace(/^default/, '__default='); 117 | } 118 | if (e17) { 119 | moduleSpecifiers.push(e17); 120 | // export * as name1 from …; // Draft ECMAScript® 2O21 121 | if (e16) { 122 | return '__export={' + e16 + ':__from(' + e17 + ',__meta.url)}'; 123 | } 124 | // export * from …; // does not set the default export 125 | return '__export=__from(' + e17 + ',__meta.url)'; 126 | } 127 | // export { name1, import2 as name2, …, nameN } from …; 128 | if (e20) { 129 | moduleSpecifiers.push(e20); 130 | const exports: string[] = []; 131 | e18.replace(/([^\s,]+)(\s+as\s+([^\s,]+))?/g, (m: string, p1: string, p2:string, p3:string) => { 132 | exports.push((p3 || p1) + ':m.' + p1); 133 | return m; 134 | }); 135 | return ';(function(){' + 136 | 'var m=__from(' + e20 + ',__meta.url);' + 137 | '__export={' + exports.join() + '}' + 138 | '}())'; 139 | } 140 | // export { name1, variable2 as name2, …, nameN }; 141 | if (e18) { 142 | const params = e18.replace(aliasPattern, '$2:$1'); 143 | return '__export={' + params + '}'; 144 | } 145 | 146 | if (specifier) { 147 | moduleSpecifiers.push(specifier); 148 | const fromModule = '=__from(' + specifier + ',__meta.url)'; 149 | // import { e1, e2, e3 as alias } from "module-name"; 150 | if (i4) { 151 | const params = i4.replace(aliasPattern, '$1:$2'); 152 | return 'const{' + params + '}' + fromModule; 153 | } 154 | // import * as name from "module-name"; 155 | if (i5) { 156 | return 'var ' + i5 + fromModule; 157 | } 158 | // import defaultExport, { export [ , [...] ] } from "module-name"; 159 | if (i9) { 160 | const params = i9.replace(aliasPattern, '$1:$2'); 161 | return 'const{' + params + '}' + fromModule + ',' + i6 + fromModule + '.default'; 162 | } 163 | // import defaultExport, * as name from "module-name"; 164 | if (i10) { 165 | return 'var ' + i10 + fromModule + ',' + i6 + '=' + i10 + '.default'; 166 | } 167 | // import defaultExport from "module-name"; 168 | if (i6) { 169 | return 'var ' + i6 + fromModule + '.default'; 170 | } 171 | // import "module-name"; 172 | return specifier; 173 | } 174 | }, 175 | }, 176 | { 177 | /** 178 | * Syntax: 179 | * import.meta => __meta 180 | */ 181 | p: /\bimport\.meta\b/g, 182 | r: '__meta', 183 | }, 184 | { 185 | p: dynamicImportPattern, 186 | r: dynamicImportReplacer, 187 | }, 188 | ]; 189 | 190 | 191 | export function transformEsmSyntax(sourceText: string) { 192 | for (const { p, r } of patternAndReplacers) { 193 | sourceText = sourceText.replace(p, r); 194 | } 195 | if (exportedNames.length) { 196 | for (let i = exportedNames.length - 1; i >=0; --i) { 197 | exportedNames[i] += ':' + exportedNames[i]; 198 | } 199 | sourceText += ';__export={' + exportedNames.join() + '}'; 200 | exportedNames.length = 0; 201 | } 202 | const fromList: string[] = []; 203 | for (let i = 0, { length } = moduleSpecifiers; i < length; ++i) { 204 | const specifier = moduleSpecifiers[i]; 205 | fromList.push(specifier.substring(1, specifier.length - 1)); 206 | } 207 | moduleSpecifiers.length = 0; 208 | return [sourceText, fromList] as const; 209 | } 210 | -------------------------------------------------------------------------------- /src/es-module/index.ts: -------------------------------------------------------------------------------- 1 | import type { Module, RealmRecord } from '../type'; 2 | import type { Utils } from '..'; 3 | 4 | export function addEsModuleHelpers(realmRec: RealmRecord, utils: Utils) { 5 | const evalInContext = realmRec.intrinsics.eval( 6 | '(' + addEsModuleHelpersInContext.toString() + ')' 7 | ); 8 | evalInContext(realmRec, utils); 9 | } 10 | 11 | function addEsModuleHelpersInContext(realmRec: RealmRecord, utils: Utils) { 12 | const { globalObject, intrinsics } = realmRec; 13 | const { apply, define, GLOBAL } = utils; 14 | const { 15 | Array: { 16 | prototype: { push }, 17 | }, 18 | Object: { create }, 19 | Promise, 20 | Symbol, 21 | } = intrinsics; 22 | const { fetch, URL } = GLOBAL; 23 | const { all } = Promise; 24 | const { then } = Promise.prototype; 25 | 26 | let moduleExports: Record | undefined; 27 | const moduleCache: Record = {}; 28 | 29 | define(globalObject, '__import', { value: dynamicImport }); 30 | define(globalObject, '__from', { value: getModule }); 31 | define(globalObject, '__export', { 32 | set: (val) => utils.assign(moduleExports!, val), 33 | }); 34 | define(globalObject, '__default', { 35 | set: (val) => (moduleExports!.default = val), 36 | }); 37 | 38 | function getModule(specifier: string, base: string): object { 39 | const { href } = new URL(specifier, base); 40 | const module = moduleCache[href]; 41 | if (module && module.exports) { 42 | return module.exports; 43 | } 44 | throw new intrinsics.Error('Module does not exist (' + specifier + ')'); 45 | } 46 | 47 | function dynamicImport( 48 | specifier: string, 49 | base = GLOBAL.location.href 50 | ): Promise { 51 | const { href } = new URL(specifier, base); 52 | return new Promise((resolve, reject) => { 53 | const module = moduleCache[href]; 54 | if (module && module.exports) { 55 | return resolve(module.exports); 56 | } 57 | loadModule(href) 58 | .then((text) => { 59 | resolve(parseModule(href, text)); 60 | }) 61 | .catch(reject); 62 | }); 63 | } 64 | 65 | function loadModule(href: string): Promise { 66 | let module = moduleCache[href]; 67 | if (module) { 68 | return module.promise!; 69 | } 70 | const promise = fetch(href, { 71 | credentials: 'same-origin', 72 | }) 73 | .then((response) => { 74 | if (response.status === 200) { 75 | return response.text(); 76 | } 77 | throw { 78 | _: new intrinsics.TypeError( 79 | 'Failed to fetch dynamically imported module: ' + href 80 | ), 81 | }; 82 | }) 83 | .then( 84 | (sourceText) => { 85 | const [text, fromList] = 86 | utils.transformEsmSyntax(sourceText); 87 | const promiseList: Promise[] = []; 88 | for (let i = 0, { length } = fromList; i < length; ++i) { 89 | const { href: subHref } = new URL(fromList[i], href); 90 | fromList[i] = subHref; 91 | apply(push, promiseList, [loadModule(subHref)]); 92 | } 93 | const allPromise = apply(all, Promise, [promiseList]); 94 | return apply(then, allPromise, [ 95 | (results: any[]) => { 96 | for ( 97 | let i = 0, { length } = results; 98 | i < length; 99 | ++i 100 | ) { 101 | parseModule(fromList[i], results[i]); 102 | } 103 | return 'var __meta={url:"' + href + '"};' + text; 104 | }, 105 | ]); 106 | }, 107 | (reason) => { 108 | if (reason._) { 109 | throw reason._; 110 | } 111 | throw new intrinsics[reason.name as 'Error']( 112 | reason.message 113 | ); 114 | } 115 | ); 116 | module = moduleCache[href] = { promise } as Module; 117 | return promise; 118 | } 119 | 120 | function parseModule(href: string, text: string) { 121 | moduleExports = create(null); 122 | if (Symbol && Symbol.toStringTag) { 123 | define(moduleExports, Symbol.toStringTag, { 124 | value: 'Module', 125 | }); 126 | } 127 | globalObject.eval(text); 128 | const module = moduleCache[href]; 129 | module.exports = moduleExports!; 130 | moduleExports = undefined; 131 | return module.exports; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/helpers.ts: -------------------------------------------------------------------------------- 1 | import type { GlobalObject, RealmRecord } from './type'; 2 | import type { Utils } from '.'; 3 | 4 | export const GLOBAL: GlobalObject = window as any; 5 | export const define = Object.defineProperty; 6 | export const _ = { debug: false }; 7 | 8 | const { log: rawLog, warn } = console; 9 | 10 | export function log(msg: any) { 11 | if (_.debug) { 12 | rawLog('[DEBUG]'); 13 | if (isObject(msg)) { 14 | warn(msg); 15 | } else { 16 | rawLog(msg); 17 | } 18 | } 19 | } 20 | 21 | export let apply: typeof Reflect['apply']; 22 | if (GLOBAL.Reflect) { 23 | apply = Reflect.apply; 24 | } else { 25 | const applyOfFunction = Function.prototype.apply; 26 | apply = function (target: Function, ctx: any, args: ArrayLike) { 27 | return applyOfFunction.call(target, ctx, args); 28 | }; 29 | } 30 | 31 | const replaceOfString = String.prototype.replace; 32 | 33 | export function replace( 34 | str: string, 35 | ...args: [ 36 | string | RegExp, 37 | string | ((substring: string, ...args: any[]) => string) 38 | ] 39 | ) { 40 | return apply(replaceOfString, str, args); 41 | } 42 | 43 | export function isObject(val: any): val is Record { 44 | return val ? typeof val === 'object' : false; 45 | } 46 | 47 | export let { assign, keys } = Object; 48 | if (!assign) { 49 | assign = function (target: Record) { 50 | const args = arguments; 51 | for (let i = 1, { length } = args; i < length; ++i) { 52 | const source = args[i]; 53 | if (isObject(source)) { 54 | for (const key of keys(source)) { 55 | target[key] = source[key]; 56 | } 57 | } 58 | } 59 | return target; 60 | }; 61 | } 62 | 63 | export function wrapError( 64 | reason: any, 65 | { intrinsics }: RealmRecord, 66 | captured = false 67 | ): Error { 68 | log(reason); 69 | if (captured) { 70 | if (!reason._) { 71 | return new intrinsics[reason.name as 'SyntaxError'](reason.message); 72 | } 73 | reason = reason._; 74 | } 75 | const { TypeError } = intrinsics; 76 | const errType = 'Cross-Realm Error: '; 77 | if (isObject(reason)) { 78 | return new TypeError(errType + reason.name + ': ' + reason.message); 79 | } 80 | return new TypeError(errType + reason); 81 | } 82 | 83 | export function getWrappedValue( 84 | callerRealm: RealmRecord, 85 | value: T, 86 | valueRealm: RealmRecord, 87 | utils: Utils 88 | ): T { 89 | if (typeof value === 'function') { 90 | try { 91 | return createWrappedFunction(callerRealm, value, valueRealm, utils); 92 | } catch (error) { 93 | throw utils.wrapError(error, callerRealm); 94 | } 95 | } else if (isObject(value)) { 96 | throw new callerRealm.intrinsics.TypeError( 97 | 'need primitive or callable, got ' + value 98 | ); 99 | } 100 | return value; 101 | } 102 | 103 | function createWrappedFunction( 104 | callerRealm: RealmRecord, 105 | targetFunction: Function, 106 | targetRealm: RealmRecord, 107 | utils: Utils 108 | ) { 109 | let { length, name } = targetFunction; 110 | if (typeof length !== 'number' || length < 0) { 111 | length = 0; 112 | } 113 | if (typeof name !== 'string') { 114 | name = ''; 115 | } 116 | const wrapped = callerRealm.intrinsics.Function( 117 | 'params', 118 | 'return ' + wrappedFunctionInContext.toString() 119 | )(arguments); 120 | define(wrapped, 'length', { value: length }); 121 | define(wrapped, 'name', { value: name }); 122 | return wrapped; 123 | } 124 | 125 | /** 126 | * Isolated function 127 | */ 128 | function wrappedFunctionInContext() { 129 | // @ts-ignore: `params` is in parent scope 130 | const [callerRealm, targetFunction, targetRealm, utils] = params as [ 131 | RealmRecord, 132 | Function, 133 | RealmRecord, 134 | Utils 135 | ]; 136 | let result; 137 | try { 138 | const args = arguments; 139 | const wrappedArgs: any[] = []; 140 | for (let i = 0, { length } = args; i < length; ++i) { 141 | const wrappedValue = utils.getWrappedValue( 142 | targetRealm, 143 | args[i], 144 | callerRealm, 145 | utils 146 | ); 147 | wrappedArgs.push(wrappedValue); 148 | } 149 | result = utils.apply( 150 | targetFunction, 151 | targetRealm.globalObject, 152 | wrappedArgs 153 | ); 154 | } catch (error) { 155 | throw utils.wrapError(error, callerRealm); 156 | } 157 | return utils.getWrappedValue(callerRealm, result, targetRealm, utils); 158 | } 159 | 160 | export const globalReservedProps = [ 161 | // The global properties of ECMAScript 2022 162 | 'globalThis', 163 | 'Infinity', 164 | 'NaN', 165 | 'undefined', 166 | 'eval', 167 | 'isFinite', 168 | 'isNaN', 169 | 'parseFloat', 170 | 'parseInt', 171 | 'decodeURI', 172 | 'decodeURIComponent', 173 | 'encodeURI', 174 | 'encodeURIComponent', 175 | 'AggregateError', 176 | 'Array', 177 | 'ArrayBuffer', 178 | 'Atomics', 179 | 'BigInt', 180 | 'BigInt64Array', 181 | 'BigUint64Array', 182 | 'Boolean', 183 | 'DataView', 184 | 'Date', 185 | 'Error', 186 | 'EvalError', 187 | 'FinalizationRegistry', 188 | 'Float32Array', 189 | 'Float64Array', 190 | 'Function', 191 | 'Int8Array', 192 | 'Int16Array', 193 | 'Int32Array', 194 | 'Map', 195 | 'Number', 196 | 'Object', 197 | 'Promise', 198 | 'Proxy', 199 | 'RangeError', 200 | 'ReferenceError', 201 | 'RegExp', 202 | 'Set', 203 | 'SharedArrayBuffer', 204 | 'String', 205 | 'Symbol', 206 | 'SyntaxError', 207 | 'TypeError', 208 | 'Uint8Array', 209 | 'Uint8ClampedArray', 210 | 'Uint16Array', 211 | 'Uint32Array', 212 | 'URIError', 213 | 'WeakMap', 214 | 'WeakRef', 215 | 'WeakSet', 216 | 'Atomics', 217 | 'JSON', 218 | 'Math', 219 | 'Reflect', 220 | 221 | // Easy to debug 222 | 'console', 223 | ] as const; 224 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import * as helpers from './helpers'; 2 | import { addEsModuleHelpers } from './es-module'; 3 | import { createRealmRecord } from './RealmRecord'; 4 | import { createShadowRealmCtor, defineShadowRealmCtor } from './ShadowRealm'; 5 | import { 6 | dynamicImportPattern, 7 | dynamicImportReplacer, 8 | transformEsmSyntax, 9 | } from './es-module/helpers'; 10 | import type { RealmRecord } from './type'; 11 | 12 | export const utils = helpers.assign( 13 | { 14 | addEsModuleHelpers, 15 | createRealmRecord, 16 | createShadowRealmCtor, 17 | defineShadowRealmCtor, 18 | dynamicImportPattern, 19 | dynamicImportReplacer, 20 | transformEsmSyntax, 21 | }, 22 | helpers 23 | ); 24 | 25 | export type Utils = typeof utils; 26 | 27 | export default helpers.GLOBAL.ShadowRealm || 28 | createShadowRealmCtor({ intrinsics: helpers.GLOBAL } as RealmRecord, utils); 29 | -------------------------------------------------------------------------------- /src/polyfill.ts: -------------------------------------------------------------------------------- 1 | import { GLOBAL } from './helpers'; 2 | import { defineShadowRealmCtor } from './ShadowRealm'; 3 | import type { RealmRecord, ShadowRealmConstructor } from './type'; 4 | import { utils } from '.'; 5 | 6 | if (!GLOBAL.ShadowRealm) { 7 | defineShadowRealmCtor( 8 | { intrinsics: GLOBAL, globalObject: GLOBAL } as RealmRecord, 9 | utils 10 | ); 11 | } 12 | 13 | declare global { 14 | interface Window { 15 | ShadowRealm: ShadowRealmConstructor; 16 | } 17 | var ShadowRealm: ShadowRealmConstructor; 18 | } 19 | -------------------------------------------------------------------------------- /src/type.ts: -------------------------------------------------------------------------------- 1 | export type Intrinsics = Omit & { 2 | globalThis: GlobalObject; 3 | }; 4 | 5 | export type GlobalObject = Omit & { 6 | globalThis: GlobalObject; 7 | ShadowRealm: ShadowRealmConstructor; 8 | __import(specifier: string, base?: string): Promise>; 9 | }; 10 | 11 | export interface ShadowRealmConstructor { 12 | new (): ShadowRealm; 13 | __debug: boolean; 14 | } 15 | 16 | type Primitive = undefined | null | boolean | number | string | symbol | bigint; 17 | type Callable = Function; 18 | 19 | export interface ShadowRealm { 20 | evaluate(sourceText: string): Primitive | Callable; 21 | importValue( 22 | specifier: string, 23 | bindingName: string 24 | ): Promise; 25 | } 26 | 27 | export interface BuiltinShadowRealm extends ShadowRealm { 28 | readonly __realm: RealmRecord; 29 | } 30 | 31 | export interface RealmRecord { 32 | intrinsics: GlobalObject; 33 | globalObject: GlobalObject; 34 | evalInContext: (x: string) => any; 35 | } 36 | 37 | export interface Module { 38 | exports: object; 39 | promise?: Promise; 40 | } 41 | -------------------------------------------------------------------------------- /test/cases/WrappedFunction/length-throws-typeerror.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 Chengzhong Wu. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-wrappedfunctioncreate 5 | description: > 6 | WrappedFunctionCreate throws a TypeError from its caller realm. 7 | info: | 8 | WrappedFunctionCreate ( callerRealm: a Realm Record, Target: a function object, ) 9 | 10 | ... 11 | 7. Let result be CopyNameAndLength(wrapped, Target). 12 | 8. If result is an Abrupt Completion, throw a TypeError exception. 13 | ... 14 | 15 | features: [ShadowRealm] 16 | ---*/ 17 | 18 | assert.sameValue( 19 | typeof ShadowRealm.prototype.evaluate, 20 | 'function', 21 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 22 | ); 23 | 24 | const r = new ShadowRealm(); 25 | 26 | assert.throws(TypeError, () => r.evaluate(` 27 | function fn() {} 28 | Object.defineProperty(fn, 'length', { 29 | get: () => { 30 | throw new Error('blah'); 31 | }, 32 | enumerable: false, 33 | configurable: true, 34 | }); 35 | fn; 36 | `), 'expect a TypeError on length getter throwing'); 37 | -------------------------------------------------------------------------------- /test/cases/WrappedFunction/length.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 Chengzhong Wu. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-wrappedfunctioncreate 5 | description: > 6 | The value of WrappedFunction.name is copied from the target function 7 | info: | 8 | WrappedFunctionCreate ( callerRealm: a Realm Record, Target: a function object, ) 9 | 10 | ... 11 | 7. Let result be CopyNameAndLength(wrapped, Target). 12 | ... 13 | 14 | CopyNameAndLength ( F: a function object, Target: a function object, prefix: a String, optional argCount: a Number, ) 15 | 16 | 1. If argCount is undefined, then set argCount to 0. 17 | 2. Let L be 0. 18 | 3. Let targetHasLength be ? HasOwnProperty(Target, "length"). 19 | 4. If targetHasLength is true, then 20 | a. Let targetLen be ? Get(Target, "length"). 21 | b. If Type(targetLen) is Number, then 22 | i. If targetLen is +∞𝔽, set L to +∞. 23 | ii. Else if targetLen is -∞𝔽, set L to 0. 24 | iii. Else, 25 | 1. Let targetLenAsInt be ! ToIntegerOrInfinity(targetLen). 26 | 2. Assert: targetLenAsInt is finite. 27 | 3. Set L to max(targetLenAsInt - argCount, 0). 28 | 5. Perform ! SetFunctionLength(F, L). 29 | ... 30 | 31 | SetFunctionLength ( F, length ) 32 | 33 | ... 34 | 2. Return ! DefinePropertyOrThrow(F, "length", PropertyDescriptor { [[Value]]: 𝔽(length), [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }). 35 | 36 | includes: [propertyHelper.js] 37 | features: [ShadowRealm] 38 | ---*/ 39 | 40 | assert.sameValue( 41 | typeof ShadowRealm.prototype.evaluate, 42 | 'function', 43 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 44 | ); 45 | 46 | const r = new ShadowRealm(); 47 | 48 | 49 | let wrapped = r.evaluate(` 50 | function fn(foo, bar) {} 51 | fn; 52 | `); 53 | verifyProperty(wrapped, "length", { 54 | value: 2, 55 | enumerable: false, 56 | writable: false, 57 | configurable: true, 58 | }); 59 | 60 | 61 | wrapped = r.evaluate(` 62 | function fn() {} 63 | delete fn.length; 64 | fn; 65 | `); 66 | verifyProperty(wrapped, "length", { 67 | value: 0, 68 | enumerable: false, 69 | writable: false, 70 | configurable: true, 71 | }); 72 | 73 | 74 | wrapped = r.evaluate(` 75 | function fn() {} 76 | Object.defineProperty(fn, 'length', { 77 | get: () => Infinity, 78 | enumerable: false, 79 | configurable: true, 80 | }); 81 | fn; 82 | `); 83 | verifyProperty(wrapped, "length", { 84 | value: Infinity, 85 | enumerable: false, 86 | writable: false, 87 | configurable: true, 88 | }); 89 | 90 | 91 | wrapped = r.evaluate(` 92 | function fn() {} 93 | Object.defineProperty(fn, 'length', { 94 | get: () => -Infinity, 95 | enumerable: false, 96 | configurable: true, 97 | }); 98 | fn; 99 | `); 100 | verifyProperty(wrapped, "length", { 101 | value: 0, 102 | enumerable: false, 103 | writable: false, 104 | configurable: true, 105 | }); 106 | 107 | 108 | wrapped = r.evaluate(` 109 | function fn() {} 110 | Object.defineProperty(fn, 'length', { 111 | get: () => -1, 112 | enumerable: false, 113 | configurable: true, 114 | }); 115 | fn; 116 | `); 117 | verifyProperty(wrapped, "length", { 118 | value: 0, 119 | enumerable: false, 120 | writable: false, 121 | configurable: true, 122 | }); 123 | -------------------------------------------------------------------------------- /test/cases/WrappedFunction/name-throws-typeerror.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 Chengzhong Wu. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-wrappedfunctioncreate 5 | description: > 6 | WrappedFunctionCreate throws a TypeError from its caller realm. 7 | info: | 8 | WrappedFunctionCreate ( callerRealm: a Realm Record, Target: a function object, ) 9 | 10 | ... 11 | 7. Let result be CopyNameAndLength(wrapped, Target). 12 | 8. If result is an Abrupt Completion, throw a TypeError exception. 13 | ... 14 | 15 | features: [ShadowRealm] 16 | ---*/ 17 | 18 | assert.sameValue( 19 | typeof ShadowRealm.prototype.evaluate, 20 | 'function', 21 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 22 | ); 23 | 24 | const r = new ShadowRealm(); 25 | 26 | assert.throws(TypeError, () => r.evaluate(` 27 | function fn() {} 28 | Object.defineProperty(fn, 'name', { 29 | get: () => { 30 | throw new Error('blah'); 31 | }, 32 | enumerable: false, 33 | configurable: true, 34 | }); 35 | fn; 36 | `), 'expect a TypeError on name getter throwing'); 37 | -------------------------------------------------------------------------------- /test/cases/WrappedFunction/name.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 Chengzhong Wu. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-wrappedfunctioncreate 5 | description: > 6 | The value of WrappedFunction.name is copied from the target function 7 | info: | 8 | WrappedFunctionCreate ( callerRealm: a Realm Record, Target: a function object, ) 9 | 10 | ... 11 | 7. Let result be CopyNameAndLength(wrapped, Target). 12 | ... 13 | 14 | CopyNameAndLength ( F: a function object, Target: a function object, prefix: a String, optional argCount: a Number, ) 15 | 16 | ... 17 | 6. Let targetName be ? Get(Target, "name"). 18 | 7. If Type(targetName) is not String, set targetName to the empty String. 19 | 8. Perform ! SetFunctionName(F, targetName, prefix). 20 | 21 | SetFunctionName ( F, name [ , prefix ] ) 22 | 23 | ... 24 | 6. Return ! DefinePropertyOrThrow(F, "name", PropertyDescriptor { [[Value]]: name, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }). 25 | 26 | includes: [propertyHelper.js] 27 | features: [ShadowRealm] 28 | ---*/ 29 | 30 | assert.sameValue( 31 | typeof ShadowRealm.prototype.evaluate, 32 | 'function', 33 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 34 | ); 35 | 36 | const r = new ShadowRealm(); 37 | 38 | let wrapped = r.evaluate(` 39 | function fn() {} 40 | fn; 41 | `); 42 | verifyProperty(wrapped, "name", { 43 | value: "fn", 44 | enumerable: false, 45 | writable: false, 46 | configurable: true, 47 | }); 48 | 49 | // The name property is an accessor. 50 | wrapped = r.evaluate(` 51 | function fn() {} 52 | Object.defineProperty(fn, 'name', { 53 | get: () => "bar", 54 | enumerable: false, 55 | configurable: true, 56 | }); 57 | fn; 58 | `); 59 | verifyProperty(wrapped, "name", { 60 | value: "bar", 61 | enumerable: false, 62 | writable: false, 63 | configurable: true, 64 | }); 65 | 66 | // The value of fn.name is not a string. 67 | for (const name of [null, undefined, 0, '1n', false, NaN, Infinity, 'Symbol()', '[]', '{}']) { 68 | wrapped = r.evaluate(` 69 | function fn() {} 70 | Object.defineProperty(fn, 'name', { 71 | value: ${String(name)}, 72 | enumerable: false, 73 | configurable: true, 74 | }); 75 | fn; 76 | `); 77 | verifyProperty(wrapped, "name", { 78 | value: "", 79 | enumerable: false, 80 | writable: false, 81 | configurable: true, 82 | }); 83 | } 84 | -------------------------------------------------------------------------------- /test/cases/WrappedFunction/throws-typeerror-on-revoked-proxy.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 Chengzhong Wu. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-wrapped-function-exotic-objects-call-thisargument-argumentslist 5 | description: > 6 | WrappedFunctionCreate throws a TypeError the target is a revoked proxy. 7 | 8 | info: | 9 | WrappedFunctionCreate ( callerRealm: a Realm Record, Target: a function object, ) 10 | 1. Let target be F.[[WrappedTargetFunction]]. 11 | 2. Assert: IsCallable(target) is true. 12 | 3. Let callerRealm be F.[[Realm]]. 13 | 4. NOTE: Any exception objects produced after this point are associated with callerRealm. 14 | 5. Let targetRealm be ? GetFunctionRealm(target). 15 | ... 16 | 17 | GetFunctionRealm ( obj ) 18 | ... 19 | 3. If obj is a Proxy exotic object, then 20 | a. If obj.[[ProxyHandler]] is null, throw a TypeError exception. 21 | ... 22 | 23 | features: [ShadowRealm] 24 | ---*/ 25 | 26 | assert.sameValue( 27 | typeof ShadowRealm.prototype.evaluate, 28 | 'function', 29 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 30 | ); 31 | 32 | const r = new ShadowRealm(); 33 | 34 | const fn = r.evaluate(` 35 | globalThis.revocable = Proxy.revocable(() => {}, {}); 36 | 37 | globalThis.revocable.proxy; 38 | `); 39 | r.evaluate('revocable.revoke()'); 40 | assert.throws(TypeError, () => fn()); 41 | -------------------------------------------------------------------------------- /test/cases/constructor.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm-constructor 5 | description: > 6 | ShadowRealm is a constructor and has [[Construct]] internal method. 7 | includes: [isConstructor.js] 8 | features: [ShadowRealm, Reflect.construct] 9 | ---*/ 10 | assert.sameValue( 11 | typeof ShadowRealm, 12 | 'function', 13 | 'This test must fail if ShadowRealm is not a function' 14 | ); 15 | 16 | assert(isConstructor(ShadowRealm)); 17 | assert.sameValue(Object.getPrototypeOf(ShadowRealm), Function.prototype); 18 | new ShadowRealm(); 19 | -------------------------------------------------------------------------------- /test/cases/descriptor.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm-constructor 5 | description: > 6 | The ShadowRealm constructor is the initial value of the "ShadowRealm" property of the global object. 7 | includes: [propertyHelper.js] 8 | features: [ShadowRealm] 9 | ---*/ 10 | 11 | verifyProperty(this, "ShadowRealm", { 12 | enumerable: false, 13 | writable: true, 14 | configurable: true, 15 | }); 16 | -------------------------------------------------------------------------------- /test/cases/extensibility.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Leo Balter. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm-constructor 5 | description: > 6 | The ShadowRealm constructor is extensible 7 | info: | 8 | 17 ECMAScript Standard Built-in Objects 9 | 10 | Unless specified otherwise, the [[Extensible]] internal slot of a built-in 11 | object initially has the value true. 12 | features: [ShadowRealm] 13 | ---*/ 14 | 15 | assert.sameValue(Object.isExtensible(ShadowRealm), true); 16 | -------------------------------------------------------------------------------- /test/cases/global-object.js: -------------------------------------------------------------------------------- 1 | /*--- 2 | features: [ShadowRealm] 3 | ---*/ 4 | 5 | var realm = new ShadowRealm(); 6 | 7 | assert(realm.evaluate('this.window === undefined'), 'this.window'); 8 | assert( 9 | realm.evaluate('eval("this.window") === undefined'), 10 | 'eval("this.window")' 11 | ); 12 | assert( 13 | realm.evaluate('Function("return this && this.window")() === undefined'), 14 | 'Function return this.window' 15 | ); 16 | assert( 17 | realm.evaluate( 18 | 'Function.prototype.constructor("return this && this.window")() === undefined' 19 | ), 20 | 'Function.prototype.constructor return this.window' 21 | ); 22 | assert( 23 | realm.evaluate( 24 | '(function(){ return this && this.window }()) === undefined' 25 | ), 26 | 'function return this.window' 27 | ); 28 | 29 | assert(realm.evaluate('globalThis.window === undefined'), 'globalThis.window'); 30 | assert( 31 | realm.evaluate('eval("globalThis.window") === undefined'), 32 | 'eval("globalThis.window")' 33 | ); 34 | assert( 35 | realm.evaluate('Function("return globalThis.window")() === undefined'), 36 | 'Function return globalThis.window' 37 | ); 38 | assert( 39 | realm.evaluate( 40 | 'Function.prototype.constructor("return globalThis.window")() === undefined' 41 | ), 42 | 'Function.prototype.constructor return globalThis.window' 43 | ); 44 | assert( 45 | realm.evaluate('(function(){ return globalThis.window }()) === undefined'), 46 | 'function return globalThis.window' 47 | ); 48 | 49 | assert(realm.evaluate('typeof window === "undefined"'), 'window'); 50 | assert( 51 | realm.evaluate('eval("typeof window") === "undefined"'), 52 | 'window in eval' 53 | ); 54 | assert( 55 | realm.evaluate('Function("return typeof window")() === "undefined"'), 56 | 'window in Function' 57 | ); 58 | assert( 59 | realm.evaluate( 60 | 'Function.prototype.constructor("return typeof window")() === "undefined"' 61 | ), 62 | 'window in Function.prototype.constructor' 63 | ); 64 | assert( 65 | realm.evaluate('(function(){ return typeof window }()) === "undefined"'), 66 | 'window in a function' 67 | ); 68 | 69 | assert( 70 | realm.evaluate(` 71 | Object.defineProperty(Object.prototype, Symbol.unscopables, { 72 | value: { window: true }, 73 | }); 74 | typeof window === 'undefined'; 75 | `), 76 | 'set Symbol.unscopables for Object.prototype' 77 | ); 78 | assert( 79 | realm.evaluate(` 80 | try { 81 | Object.defineProperty(globalThis, Symbol.unscopables, { 82 | value: { window: true }, 83 | }); 84 | false; 85 | } catch (error) { 86 | typeof window === 'undefined'; 87 | } 88 | `), 89 | 'set Symbol.unscopables for globalThis' 90 | ); 91 | -------------------------------------------------------------------------------- /test/cases/instance-extensibility.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Leo Balter. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | 4 | /*--- 5 | esid: sec-shadowrealm 6 | description: > 7 | The new instance is extensible 8 | info: | 9 | ShadowRealm ( ) 10 | 11 | ... 12 | 2. Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%ShadowRealm.prototype%", 13 | « [[ShadowRealm]], [[ExecutionContext]] »). 14 | ... 15 | 13. Return O. 16 | 17 | OrdinaryCreateFromConstructor creates a new ordinary objects including the 18 | internal slots [[Prototype]] and [[Extensible]]. The latter will have its 19 | value set to true. 20 | includes: [propertyHelper.js] 21 | features: [ShadowRealm] 22 | ---*/ 23 | 24 | const realm = new ShadowRealm(); 25 | 26 | assert(Object.isExtensible(realm)); 27 | 28 | Object.defineProperty(realm, 'foo', { configurable: true }); 29 | assert(realm.hasOwnProperty('foo'), 'confirms extensibility adding a new property'); 30 | 31 | Object.defineProperty(realm, 'foo', { 32 | value: 'bar', 33 | writable: true, 34 | configurable: true, 35 | enumerable: false, 36 | }); 37 | 38 | verifyProperty(realm, 'foo', { 39 | value: 'bar', 40 | writable: true, 41 | configurable: true, 42 | enumerable: false, 43 | }); 44 | -------------------------------------------------------------------------------- /test/cases/instance.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm-constructor 5 | description: > 6 | new ShadowRealm() returns a shadow realm instance 7 | info: | 8 | ShadowRealm ( ) 9 | 10 | ... 11 | 2. Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%ShadowRealm.prototype%", 12 | « [[ShadowRealm]], [[ExecutionContext]] »). 13 | ... 14 | 13. Return O. 15 | features: [ShadowRealm] 16 | ---*/ 17 | assert.sameValue( 18 | typeof ShadowRealm, 19 | 'function', 20 | 'This test must fail if ShadowRealm is not a function' 21 | ); 22 | 23 | var realm = new ShadowRealm(); 24 | 25 | assert(realm instanceof ShadowRealm); 26 | assert.sameValue( 27 | Object.getPrototypeOf(realm), 28 | ShadowRealm.prototype, 29 | '[[Prototype]] is set to %ShadowRealm.prototype%' 30 | ); 31 | 32 | var otherRealm = new ShadowRealm(); 33 | assert.notSameValue(realm, otherRealm, 'each instance is different'); 34 | -------------------------------------------------------------------------------- /test/cases/length.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm-constructor 5 | description: > 6 | The value of ShadowRealm.length is 0 7 | info: | 8 | ShadowRealm ( ) 9 | 10 | Every built-in function object, including constructors, has a "length" property 11 | whose value is a non-negative integral Number. Unless otherwise specified, this value 12 | is equal to the number of required parameters shown in the subclause heading for the 13 | function description. Optional parameters and rest parameters are not included in 14 | the parameter count. 15 | 16 | Unless otherwise specified, the "length" property of a built-in function object has 17 | the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. 18 | 19 | includes: [propertyHelper.js] 20 | features: [ShadowRealm] 21 | ---*/ 22 | 23 | verifyProperty(ShadowRealm, "length", { 24 | value: 0, 25 | enumerable: false, 26 | writable: false, 27 | configurable: true, 28 | }); 29 | -------------------------------------------------------------------------------- /test/cases/name.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-ecmascript-standard-built-in-objects 5 | description: > 6 | The value of ShadowRealm.name is 'ShadowRealm' 7 | info: | 8 | Every built-in function object, including constructors, has a "name" property 9 | whose value is a String. Unless otherwise specified, this value is the name 10 | that is given to the function in this specification. 11 | 12 | Unless otherwise specified, the "name" property of a built-in function 13 | object has the attributes 14 | { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. 15 | 16 | includes: [propertyHelper.js] 17 | features: [ShadowRealm] 18 | ---*/ 19 | 20 | verifyProperty(ShadowRealm, "name", { 21 | value: "ShadowRealm", 22 | enumerable: false, 23 | writable: false, 24 | configurable: true, 25 | }); 26 | -------------------------------------------------------------------------------- /test/cases/proto.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-properties-of-the-shadowrealm-constructor 5 | description: > 6 | The [[Prototype]] of ShadowRealm is Function.Prototype. 7 | info: | 8 | Unless otherwise specified every built-in function and every built-in constructor 9 | has the Function prototype object, which is the initial value of the expression 10 | Function.prototype, as the value of its [[Prototype]] internal slot. 11 | 12 | features: [ShadowRealm] 13 | ---*/ 14 | 15 | assert.sameValue(Object.getPrototypeOf(ShadowRealm), Function.prototype); 16 | -------------------------------------------------------------------------------- /test/cases/prototype/Symbol.toStringTag.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Leo Balter. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype-@@tostringtag 5 | description: > 6 | `Symbol.toStringTag` property descriptor 7 | info: | 8 | The initial value of the @@toStringTag property is the String value 9 | "ShadowRealm". 10 | 11 | This property has the attributes { [[Writable]]: false, [[Enumerable]]: 12 | false, [[Configurable]]: true }. 13 | includes: [propertyHelper.js] 14 | features: [ShadowRealm, Symbol.toStringTag] 15 | ---*/ 16 | 17 | verifyProperty(ShadowRealm.prototype, Symbol.toStringTag, { 18 | value: 'ShadowRealm', 19 | enumerable: false, 20 | writable: false, 21 | configurable: true 22 | }); 23 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/descriptor.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | ShadowRealm.prototype.evaluate is an ECMAScript Standard built-in object function. 7 | includes: [propertyHelper.js] 8 | features: [ShadowRealm] 9 | ---*/ 10 | 11 | verifyProperty(ShadowRealm.prototype, "evaluate", { 12 | enumerable: false, 13 | writable: true, 14 | configurable: true, 15 | }); 16 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/errors-from-the-other-realm-is-wrapped-into-a-typeerror.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | ShadowRealm.prototype.evaluate wraps errors from other realm into TypeErrors 7 | features: [ShadowRealm] 8 | ---*/ 9 | 10 | assert.sameValue( 11 | typeof ShadowRealm.prototype.evaluate, 12 | 'function', 13 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 14 | ); 15 | 16 | const r = new ShadowRealm(); 17 | 18 | assert.throws(SyntaxError, () => r.evaluate('...'), 'SyntaxError exposed to Parent'); 19 | assert.throws(TypeError, () => r.evaluate('throw 42'), 'throw primitive => TypeError'); 20 | assert.throws(TypeError, () => r.evaluate('throw new ReferenceError("aaa")'), 'custom ctor => TypeError'); 21 | assert.throws(TypeError, () => r.evaluate('throw new TypeError("aaa")'), 'Child TypeError => Parent TypeError'); 22 | assert.throws(TypeError, () => r.evaluate('eval("...");'), 'syntaxerror parsing coming after runtime evaluation'); 23 | assert.throws(TypeError, () => r.evaluate(` 24 | 'use strict'; 25 | eval("var public = 1;"); 26 | `), 'strict-mode only syntaxerror parsing coming after runtime evaluation'); 27 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/globalthis-available-properties.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Leo Balter. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | The ShadowRealm global must include ECMAScript global properties 7 | info: | 8 | ShadowRealm ( ) 9 | 10 | ... 11 | 3. Let realmRec be CreateRealm(). 12 | 4. Set O.[[ShadowRealm]] to realmRec. 13 | ... 14 | 10. Perform ? SetRealmGlobalObject(realmRec, undefined, undefined). 15 | 11. Perform ? SetDefaultGlobalBindings(O.[[ShadowRealm]]). 16 | 12. Perform ? HostInitializeShadowRealm(O.[[ShadowRealm]]). 17 | 18 | SetDefaultGlobalBindings ( realmRec ) 19 | 20 | 1. Let global be realmRec.[[GlobalObject]]. 21 | 2. For each property of the Global Object specified in clause 19, do 22 | a. Let name be the String value of the property name. 23 | b. Let desc be the fully populated data Property Descriptor for the property, containing the specified attributes for the property. For properties listed in 19.2, 19.3, or 19.4 the value of the [[Value]] attribute is the corresponding intrinsic object from realmRec. 24 | c. Perform ? DefinePropertyOrThrow(global, name, desc). 25 | 3. Return global. 26 | features: [ShadowRealm] 27 | includes: [compareArray.js] 28 | ---*/ 29 | 30 | assert.sameValue( 31 | typeof ShadowRealm.prototype.evaluate, 32 | 'function', 33 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 34 | ); 35 | 36 | const r = new ShadowRealm(); 37 | 38 | const properties = [ 39 | 'globalThis', 40 | 'Infinity', 41 | 'NaN', 42 | 'undefined', 43 | 'eval', 44 | 'isFinite', 45 | 'isNaN', 46 | 'parseFloat', 47 | 'parseInt', 48 | 'decodeURI', 49 | 'decodeURIComponent', 50 | 'encodeURI', 51 | 'encodeURIComponent', 52 | 'AggregateError', 53 | 'Array', 54 | 'ArrayBuffer', 55 | 'BigInt', 56 | 'BigInt64Array', 57 | 'BigUint64Array', 58 | 'Boolean', 59 | 'DataView', 60 | 'Date', 61 | 'Error', 62 | 'EvalError', 63 | 'FinalizationRegistry', 64 | 'Float32Array', 65 | 'Float64Array', 66 | 'Function', 67 | 'Int8Array', 68 | 'Int16Array', 69 | 'Int32Array', 70 | 'Map', 71 | 'Number', 72 | 'Object', 73 | 'Promise', 74 | 'Proxy', 75 | 'RangeError', 76 | 'ReferenceError', 77 | 'RegExp', 78 | 'Set', 79 | // 'SharedArrayBuffer', 80 | 'String', 81 | 'Symbol', 82 | 'SyntaxError', 83 | 'TypeError', 84 | 'Uint8Array', 85 | 'Uint8ClampedArray', 86 | 'Uint16Array', 87 | 'Uint32Array', 88 | 'URIError', 89 | 'WeakMap', 90 | 'WeakRef', 91 | 'WeakSet', 92 | 'Atomics', 93 | 'JSON', 94 | 'Math', 95 | 'Reflect', 96 | // For polyfill 97 | 'eval', 98 | 'window', 99 | 'document', 100 | 'location', 101 | 'top', 102 | '__from', 103 | '__export', 104 | '__default', 105 | '__import', 106 | ]; 107 | 108 | const available = properties.filter((name) => { 109 | // This test is intentionally not using wrapped functions. 110 | // This test should not depend on wrapped functions. 111 | return r.evaluate( 112 | `Object.prototype.hasOwnProperty.call(globalThis, '${name}')` 113 | ); 114 | }); 115 | 116 | // This comparison is intentional to list difference in names if the the assertion fails 117 | assert.compareArray(properties, available); 118 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/globalthis-config-only-properties.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Leo Balter. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | Properties of globalThis must be configurable 7 | info: | 8 | ShadowRealm ( ) 9 | 10 | ... 11 | 3. Let realmRec be CreateRealm(). 12 | 4. Set O.[[ShadowRealm]] to realmRec. 13 | ... 14 | 10. Perform ? SetRealmGlobalObject(realmRec, undefined, undefined). 15 | 11. Perform ? SetDefaultGlobalBindings(O.[[ShadowRealm]]). 16 | 12. Perform ? HostInitializeShadowRealm(O.[[ShadowRealm]]). 17 | 18 | Runtime Semantics: HostInitializeShadowRealm ( realm ) 19 | 20 | HostInitializeShadowRealm is an implementation-defined abstract operation 21 | used to inform the host of any newly created realms from the ShadowRealm 22 | constructor. Its return value is not used, though it may throw an exception. 23 | The idea of this hook is to initialize host data structures related to the 24 | ShadowRealm, e.g., for module loading. 25 | 26 | The host may use this hook to add properties to the ShadowRealm's global 27 | object. Those properties must be configurable. 28 | features: [ShadowRealm] 29 | ---*/ 30 | 31 | assert.sameValue( 32 | typeof ShadowRealm.prototype.evaluate, 33 | 'function', 34 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 35 | ); 36 | 37 | const r = new ShadowRealm(); 38 | 39 | const anyMissed = r.evaluate(` 40 | // These names are the only exception as non configurable values. 41 | // Yet, they don't represent any object value. 42 | const esNonConfigValues = [ 43 | 'undefined', 44 | 'Infinity', 45 | 'NaN', 46 | // For polyfill 47 | 'eval', 48 | 'window', 49 | 'document', 50 | 'location', 51 | 'top', 52 | 'addEventListener', 53 | 'dispatchEvent', 54 | 'removeEventListener', 55 | '__from', 56 | '__export', 57 | '__default', 58 | '__import' 59 | ]; 60 | 61 | const entries = Object.entries(Object.getOwnPropertyDescriptors(globalThis)); 62 | 63 | const missed = entries 64 | .filter(entry => entry[1].configurable === false) 65 | .map(([name]) => name) 66 | .filter(name => !esNonConfigValues.includes(name)) 67 | .join(', '); 68 | 69 | missed; 70 | `); 71 | 72 | assert.sameValue(anyMissed, '', 'All globalThis properties must be configurable'); 73 | 74 | const result = r.evaluate(` 75 | const ObjectKeys = Object.keys; 76 | const hasOwn = Object.prototype.hasOwnProperty; 77 | const savedGlobal = globalThis; 78 | const names = Object.keys(Object.getOwnPropertyDescriptors(globalThis)); 79 | 80 | // These names are the only exception as non configurable values. 81 | // Yet, they don't represent any object value. 82 | const esNonConfigValues = [ 83 | 'undefined', 84 | 'Infinity', 85 | 'NaN', 86 | // For polyfill 87 | 'eval', 88 | 'window', 89 | 'document', 90 | 'location', 91 | 'top', 92 | 'addEventListener', 93 | 'dispatchEvent', 94 | 'removeEventListener', 95 | '__from', 96 | '__export', 97 | '__default', 98 | '__import' 99 | ]; 100 | 101 | // Delete every name except globalThis, for now 102 | const remainingNames = names.filter(name => { 103 | if (esNonConfigValues.includes(name)) { 104 | return false; 105 | } 106 | 107 | if (name !== 'globalThis') { 108 | delete globalThis[name]; 109 | return hasOwn.call(globalThis, name); 110 | } 111 | }); 112 | 113 | delete globalThis['globalThis']; 114 | 115 | if (hasOwn.call(savedGlobal, 'globalThis')) { 116 | remainingNames.push('globalThis'); 117 | } 118 | 119 | const failedDelete = remainingNames.join(', '); 120 | 121 | failedDelete; 122 | `); 123 | 124 | assert.sameValue(result, '', 'deleting any globalThis property must be effective'); 125 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/globalthis-ordinary-object.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Leo Balter. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | The globalThis must be an ordinary object from OrdinaryObjectCreate 7 | info: | 8 | ShadowRealm ( ) 9 | 10 | ... 11 | 3. Let realmRec be CreateRealm(). 12 | 4. Set O.[[ShadowRealm]] to realmRec. 13 | ... 14 | 10. Perform ? SetRealmGlobalObject(realmRec, undefined, undefined). 15 | 11. Perform ? SetDefaultGlobalBindings(O.[[ShadowRealm]]). 16 | 12. Perform ? HostInitializeShadowRealm(O.[[ShadowRealm]]). 17 | 18 | SetRealmGlobalObject ( realmRec, globalObj, thisValue ) 19 | 20 | 1. If globalObj is undefined, then 21 | a. Let intrinsics be realmRec.[[Intrinsics]]. 22 | b. Set globalObj to ! OrdinaryObjectCreate(intrinsics.[[%Object.prototype%]]). 23 | 2. Assert: Type(globalObj) is Object. 24 | 3. If thisValue is undefined, set thisValue to globalObj. 25 | ... 26 | 27 | OrdinaryObjectCreate ( proto [ , additionalInternalSlotsList ] ) 28 | 29 | 1. Let internalSlotsList be « [[Prototype]], [[Extensible]] ». 30 | 2. If additionalInternalSlotsList is present, append each of its elements to internalSlotsList. 31 | 3. Let O be ! MakeBasicObject(internalSlotsList). 32 | 4. Set O.[[Prototype]] to proto. 33 | 5. Return O. 34 | 35 | MakeBasicObject ( internalSlotsList ) 36 | 37 | ... 38 | 5. If internalSlotsList contains [[Extensible]], set obj.[[Extensible]] to true. 39 | features: [ShadowRealm] 40 | ---*/ 41 | 42 | assert.sameValue( 43 | typeof ShadowRealm.prototype.evaluate, 44 | 'function', 45 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 46 | ); 47 | 48 | const r = new ShadowRealm(); 49 | 50 | assert.sameValue( 51 | r.evaluate('Object.getPrototypeOf(globalThis) === Object.prototype'), 52 | true, 53 | 'The [[Prototype]] of globalThis is Object.prototype' 54 | ); 55 | 56 | assert.sameValue( 57 | r.evaluate('Object.isExtensible(globalThis)'), 58 | true, 59 | 'globalThis is extensible' 60 | ); 61 | 62 | assert.sameValue( 63 | r.evaluate('globalThis.constructor === Object'), 64 | true, 65 | 'globalThis.constructor is Object' 66 | ); 67 | 68 | assert.sameValue( 69 | r.evaluate(` 70 | let result; 71 | try { 72 | globalThis.__proto__ = {x: 2}; 73 | result = true; 74 | } catch (e) { 75 | result = false; 76 | } 77 | result; 78 | `), 79 | true, 80 | 'Can assign to globalThis.__proto__ directly' 81 | ); 82 | 83 | assert.sameValue( 84 | r.evaluate(` 85 | Reflect.set(globalThis, '__proto__', {x: 1}) && 86 | Reflect.setPrototypeOf(globalThis.__proto__, {x: 2}); 87 | `), 88 | true, 89 | 'Can set an ordinary globalThis.__proto__' 90 | ); 91 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/length.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | The value of ShadowRealm.prototype.evaluate.length is 1 7 | info: | 8 | ShadowRealm.prototype.evaluate ( sourceText ) 9 | 10 | Every built-in function object, including constructors, has a "length" property 11 | whose value is a non-negative integral Number. Unless otherwise specified, this value 12 | is equal to the number of required parameters shown in the subclause heading for the 13 | function description. Optional parameters and rest parameters are not included in 14 | the parameter count. 15 | 16 | Unless otherwise specified, the "length" property of a built-in function object has 17 | the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. 18 | 19 | includes: [propertyHelper.js] 20 | features: [ShadowRealm] 21 | ---*/ 22 | 23 | verifyProperty(ShadowRealm.prototype.evaluate, "length", { 24 | value: 1, 25 | enumerable: false, 26 | writable: false, 27 | configurable: true, 28 | }); 29 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/name.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | The value of ShadowRealm.prototype.evaluate.name is 'evaluate' 7 | info: | 8 | Every built-in function object, including constructors, has a "name" property 9 | whose value is a String. Unless otherwise specified, this value is the name 10 | that is given to the function in this specification. 11 | 12 | Unless otherwise specified, the "name" property of a built-in function 13 | object has the attributes 14 | { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. 15 | 16 | includes: [propertyHelper.js] 17 | features: [ShadowRealm] 18 | ---*/ 19 | 20 | verifyProperty(ShadowRealm.prototype.evaluate, "name", { 21 | value: "evaluate", 22 | enumerable: false, 23 | writable: false, 24 | configurable: true, 25 | }); 26 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/nested-realms.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Leo Balter. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | ShadowRealm can create a nested ShadowRealm 7 | features: [ShadowRealm] 8 | ---*/ 9 | 10 | assert.sameValue( 11 | typeof ShadowRealm.prototype.evaluate, 12 | 'function', 13 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 14 | ); 15 | 16 | globalThis.myValue = 'a'; 17 | const realm1 = new ShadowRealm(); 18 | 19 | realm1.evaluate('globalThis.myValue = "b";'); 20 | 21 | const realm2Evaluate = realm1.evaluate(` 22 | const realm2 = new ShadowRealm(); 23 | 24 | (str) => realm2.evaluate(str); 25 | `); 26 | 27 | realm2Evaluate('globalThis.myValue = "c";'); 28 | 29 | assert.sameValue(globalThis.myValue, 'a'); 30 | assert.sameValue(realm1.evaluate('globalThis.myValue'), 'b'); 31 | assert.sameValue(realm2Evaluate('globalThis.myValue'), 'c'); 32 | 33 | realm1.evaluate('globalThis.myValue = "d"'); 34 | 35 | assert.sameValue(globalThis.myValue, 'a', 'no side effects'); 36 | assert.sameValue(realm1.evaluate('globalThis.myValue'), 'd', 'no side effects'); 37 | assert.sameValue(realm2Evaluate('globalThis.myValue'), 'c', 'no side effects'); 38 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/no-conditional-strict-mode.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | The new realm has no conditional strict mode based on its outer realm 7 | info: | 8 | This test should always run with the outer realm in both strict and non 9 | strict mode to verify the realm code starts in non-strict mode. 10 | features: [ShadowRealm] 11 | ---*/ 12 | 13 | assert.sameValue( 14 | typeof ShadowRealm.prototype.evaluate, 15 | 'function', 16 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 17 | ); 18 | 19 | // const r = new ShadowRealm(); 20 | 21 | // const res = r.evaluate(` 22 | // function lol() { 23 | // arguments = 42; // This would be a SyntaxError if in strict mode 24 | 25 | // return arguments; 26 | // } 27 | // lol; 28 | // `); 29 | 30 | // assert.sameValue(res(), 42); 31 | 32 | // const res2 = r.evaluate('var public = 1; 42'); 33 | 34 | // assert.sameValue(res2, 42); 35 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/not-constructor.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | ShadowRealm.prototype.evaluate is not a constructor. 7 | includes: [isConstructor.js] 8 | features: [ShadowRealm, Reflect.construct] 9 | ---*/ 10 | 11 | assert.sameValue( 12 | typeof ShadowRealm.prototype.evaluate, 13 | 'function', 14 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 15 | ); 16 | 17 | // assert.sameValue( 18 | // isConstructor(ShadowRealm.prototype.evaluate), 19 | // false, 20 | // 'isConstructor(ShadowRealm.prototype.evaluate) must return false' 21 | // ); 22 | 23 | assert.throws( 24 | TypeError, 25 | () => { 26 | new ShadowRealm.prototype.evaluate(''); 27 | }, 28 | '`new ShadowRealm.prototype.evaluate("")` throws TypeError' 29 | ); 30 | 31 | const r = new ShadowRealm(); 32 | r.evaluate('globalThis.x = 0'); 33 | 34 | assert.throws( 35 | TypeError, 36 | () => { 37 | new r.evaluate('globalThis.x += 1;'); 38 | }, 39 | '`new r.evaluate("...")` throws TypeError' 40 | ); 41 | 42 | assert.sameValue( 43 | r.evaluate('globalThis.x'), 44 | 0, 45 | 'No code evaluated in the new expression' 46 | ); 47 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/proto.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | The [[Prototype]] of ShadowRealm.prototype.evaluate is Function.Prototype. 7 | 8 | Unless otherwise specified every built-in function and every built-in constructor 9 | has the Function prototype object, which is the initial value of the expression 10 | Function.prototype, as the value of its [[Prototype]] internal slot. 11 | 12 | features: [ShadowRealm] 13 | ---*/ 14 | 15 | assert.sameValue(Object.getPrototypeOf(ShadowRealm.prototype.evaluate), Function.prototype); 16 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/returns-primitive-values.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | ShadowRealm.prototype.evaluate returns primitive values 7 | features: [ShadowRealm] 8 | ---*/ 9 | 10 | assert.sameValue( 11 | typeof ShadowRealm.prototype.evaluate, 12 | 'function', 13 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 14 | ); 15 | 16 | const r = new ShadowRealm(); 17 | 18 | assert.sameValue(r.evaluate('1 + 1'), 2); 19 | assert.sameValue(r.evaluate('null'), null); 20 | assert.sameValue(r.evaluate(''), undefined, 'undefined from empty completion'); 21 | assert.sameValue(r.evaluate('undefined'), undefined); 22 | assert.sameValue(r.evaluate('true'), true); 23 | assert.sameValue(r.evaluate('false'), false); 24 | assert.sameValue(r.evaluate('function fn() {}'), undefined, 'fn declaration has empty completion'); 25 | assert.sameValue(r.evaluate('{}'), undefined, 'Block has empty completion'); 26 | assert.sameValue(r.evaluate('-0'), -0); 27 | assert.sameValue(r.evaluate('"str"'), 'str'); 28 | assert(Number.isNaN(r.evaluate('NaN'))); 29 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/returns-proxy-callable-object.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | ShadowRealm.prototype.evaluate wrapped proxy callable object. 7 | features: [ShadowRealm] 8 | ---*/ 9 | 10 | assert.sameValue( 11 | typeof ShadowRealm.prototype.evaluate, 12 | 'function', 13 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 14 | ); 15 | 16 | const r = new ShadowRealm(); 17 | 18 | const proxyCallable = r.evaluate(` 19 | function fn() { return 42; } 20 | new Proxy(fn, {}); 21 | `); 22 | 23 | assert.sameValue(typeof proxyCallable, 'function', 'wrapped proxy callable object is typeof function'); 24 | assert.sameValue(proxyCallable(), 42, 'wrappedpfn() returns 42'); 25 | assert.sameValue((new Proxy(proxyCallable, {}))(), 42, 'wrapped functions can be proxied'); 26 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/returns-symbol-values.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | ShadowRealm.prototype.evaluate returns symbol values 7 | features: [ShadowRealm] 8 | ---*/ 9 | 10 | assert.sameValue( 11 | typeof ShadowRealm.prototype.evaluate, 12 | 'function', 13 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 14 | ); 15 | 16 | const r = new ShadowRealm(); 17 | const s = r.evaluate('Symbol("foobar")'); 18 | 19 | assert.sameValue(typeof s, 'symbol'); 20 | assert.sameValue(s.constructor, Symbol, 'primitive does not expose other ShadowRealm constructor'); 21 | assert.sameValue(Object.getPrototypeOf(s), Symbol.prototype); 22 | assert.sameValue(Symbol.prototype.toString.call(s), 'Symbol(foobar)'); 23 | 24 | const shadowX = r.evaluate('Symbol.for("my symbol name")'); 25 | const myX = Symbol.for('my symbol name') 26 | 27 | assert.sameValue( 28 | shadowX, 29 | myX, 30 | 'The shadow realms observes the symbol global registry used in Symbol.for' 31 | ); 32 | 33 | assert.sameValue( 34 | Symbol.keyFor(shadowX), 35 | 'my symbol name', 36 | 'Symbol.keyFor observes the string key name of a symbol originally registered in the shadow realm' 37 | ); 38 | 39 | assert.sameValue( 40 | Symbol.keyFor(s), 41 | undefined, 42 | 'Symbol.keyFor cannot find a key for a regular symbol created in the shadow realm' 43 | ); 44 | 45 | const { get: description } = Object.getOwnPropertyDescriptor(Symbol.prototype, 'description'); 46 | 47 | assert.sameValue( 48 | description.call(s), 49 | 'foobar', 50 | 'get description for the symbol created in the shadow realm' 51 | ); 52 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/throws-error-from-ctor-realm.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Chengzhong Wu. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | ShadowRealm.prototype.evaluate throws a TypeError from ShadowRealm's creation realm. 7 | features: [ShadowRealm, cross-realm, Reflect] 8 | ---*/ 9 | 10 | assert.sameValue( 11 | typeof ShadowRealm.prototype.evaluate, 12 | 'function', 13 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 14 | ); 15 | 16 | var other = new ShadowRealm().__realm.globalObject; 17 | var OtherTypeError = other.TypeError; 18 | var OtherSyntaxError = other.SyntaxError; 19 | var OtherShadowRealm = other.ShadowRealm; 20 | 21 | var realm = Reflect.construct(OtherShadowRealm, []); 22 | 23 | assert.throws(OtherTypeError, () => realm.evaluate('globalThis'), 'throws a TypeError if return value can not be wrapped'); 24 | assert.throws(OtherTypeError, () => realm.evaluate('throw new Error()'), 'throws a TypeError if completion is abrupt'); 25 | 26 | assert.throws(OtherTypeError, () => realm.evaluate(1), 'throws a TypeError if sourceText is not a string'); 27 | assert.throws(OtherSyntaxError, () => realm.evaluate('...'), 'throws a SyntaxError if the sourceText is not valid'); 28 | 29 | const bogus = {}; 30 | assert.throws(OtherTypeError, function() { 31 | realm.evaluate.call(bogus, 'This is invalid code and should not be evaluated'); 32 | }, 'throws a TypeError if this is not a ShadowRealm object'); 33 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/throws-syntaxerror-on-bad-syntax.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | ShadowRealm.prototype.evaluate throws a SyntaxError if the syntax can't be parsed 7 | features: [ShadowRealm] 8 | ---*/ 9 | 10 | assert.sameValue( 11 | typeof ShadowRealm.prototype.evaluate, 12 | 'function', 13 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 14 | ); 15 | 16 | const r = new ShadowRealm(); 17 | 18 | assert.throws(SyntaxError, () => r.evaluate('...'), 'SyntaxError exposed to Parent'); 19 | assert.throws(SyntaxError, () => r.evaluate(` 20 | "use strict"; 21 | throw "do not evaluate"; 22 | function lol(){ 23 | arguments = 1; 24 | } 25 | `), 'Strict mode only SyntaxError, setting value to a fn arguments'); 26 | assert.throws(SyntaxError, () => r.evaluate(` 27 | "use strict"; 28 | throw "do not evaluate"; 29 | var public = 1; 30 | `), 'Strict mode only SyntaxError, var public'); 31 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/throws-typeerror-if-evaluation-resolves-to-non-primitive.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | ShadowRealm.prototype.evaluate throws a TypeError if evaluate resolves to non-primitive values 7 | features: [ShadowRealm] 8 | ---*/ 9 | 10 | assert.sameValue( 11 | typeof ShadowRealm.prototype.evaluate, 12 | 'function', 13 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 14 | ); 15 | 16 | const r = new ShadowRealm(); 17 | 18 | assert.throws(TypeError, () => r.evaluate('globalThis'), 'globalThis'); 19 | assert.throws(TypeError, () => r.evaluate('[]'), 'array literal'); 20 | assert.throws(TypeError, () => r.evaluate(` 21 | ({ 22 | [Symbol.toPrimitive]() { return 'string'; }, 23 | toString() { return 'str'; }, 24 | valueOf() { return 1; } 25 | }); 26 | `), 'object literal with immediate primitive coercion methods'); 27 | assert.throws(TypeError, () => r.evaluate('Object.create(null)'), 'ordinary object with null __proto__'); 28 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/throws-typeerror-wrap-throwing.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 Chengzhong Wu. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-wrappedfunctioncreate 5 | description: > 6 | WrappedFunctionCreate throws a TypeError if the accessing target's property may throw. 7 | 8 | info: | 9 | WrappedFunctionCreate ( callerRealm: a Realm Record, Target: a function object, ) 10 | ... 11 | 7. Let result be CopyNameAndLength(wrapped, Target). 12 | ... 13 | 14 | CopyNameAndLength ( F: a function object, Target: a function object, optional prefix: a String, optional argCount: a Number, ) 15 | ... 16 | 3. Let targetHasLength be ? HasOwnProperty(Target, "length"). 17 | 4. If targetHasLength is true, then 18 | a. Let targetLen be ? Get(Target, "length"). 19 | ... 20 | 6. Let targetName be ? Get(Target, "name"). 21 | 22 | features: [ShadowRealm] 23 | ---*/ 24 | 25 | assert.sameValue( 26 | typeof ShadowRealm.prototype.evaluate, 27 | 'function', 28 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 29 | ); 30 | 31 | const r = new ShadowRealm(); 32 | 33 | assert.throws( 34 | TypeError, 35 | () => 36 | r.evaluate(` 37 | const revocable = Proxy.revocable(() => {}, {}); 38 | revocable.revoke(); 39 | 40 | revocable.proxy; 41 | `), 42 | 'TypeError on wrapping a revoked callable proxy' 43 | ); 44 | 45 | assert.throws( 46 | TypeError, 47 | () => 48 | r.evaluate(` 49 | const fn = () => {}; 50 | Object.defineProperty(fn, 'name', { 51 | get() { 52 | throw new Error(); 53 | }, 54 | }); 55 | 56 | fn; 57 | `), 58 | 'TypeError on wrapping a fn with throwing name accessor' 59 | ); 60 | 61 | assert.throws( 62 | TypeError, 63 | () => 64 | r.evaluate(` 65 | const fn = () => {}; 66 | Object.defineProperty(fn, 'length', { 67 | get() { 68 | throw new Error(); 69 | }, 70 | }); 71 | 72 | fn; 73 | `), 74 | 'TypeError on wrapping a fn with throwing length accessor' 75 | ); 76 | 77 | // assert.throws(TypeError, () => r.evaluate(` 78 | // const proxy = new Proxy(() => {}, { 79 | // getOwnPropertyDescriptor(target, key) { 80 | // throw new Error(); 81 | // }, 82 | // }); 83 | 84 | // proxy; 85 | // `), 'TypeError on wrapping a callable proxy with throwing getOwnPropertyDescriptor trap'); 86 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/throws-when-argument-is-not-a-string.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | ShadowRealm.prototype.evaluate throws when argument is not a string. 7 | features: [ShadowRealm] 8 | ---*/ 9 | 10 | assert.sameValue( 11 | typeof ShadowRealm.prototype.evaluate, 12 | 'function', 13 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 14 | ); 15 | 16 | const r = new ShadowRealm(); 17 | 18 | assert.throws(TypeError, () => r.evaluate(['1+1'])); 19 | assert.throws(TypeError, () => r.evaluate({ [Symbol.toPrimitive]() { return '1+1'; }})); 20 | assert.throws(TypeError, () => r.evaluate(1)); 21 | assert.throws(TypeError, () => r.evaluate(null)); 22 | assert.throws(TypeError, () => r.evaluate(undefined)); 23 | assert.throws(TypeError, () => r.evaluate(true)); 24 | assert.throws(TypeError, () => r.evaluate(false)); 25 | assert.throws(TypeError, () => r.evaluate(new String('nope'))); 26 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/validates-realm-object.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | ShadowRealm.prototype.evaluate validates realm object. 7 | features: [ShadowRealm] 8 | ---*/ 9 | 10 | assert.sameValue( 11 | typeof ShadowRealm.prototype.evaluate, 12 | 'function', 13 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 14 | ); 15 | 16 | const r = new ShadowRealm(); 17 | const bogus = {}; 18 | 19 | assert.throws(TypeError, function() { 20 | r.evaluate.call(bogus, 'This is invalid code and should not be evaluated'); 21 | }, 'throws a TypeError if this is not a ShadowRealm object'); 22 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/wrapped-function-arguments-are-wrapped-into-the-inner-realm-extended.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | ShadowRealm.prototype.evaluate wrapped function arguments are wrapped into the inner realm, extended. 7 | features: [ShadowRealm] 8 | ---*/ 9 | 10 | assert.sameValue( 11 | typeof ShadowRealm.prototype.evaluate, 12 | 'function', 13 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 14 | ); 15 | 16 | const r = new ShadowRealm(); 17 | const blueFn = (x, y) => x + y; 18 | 19 | const redWrappedFn = r.evaluate(` 20 | function fn(wrapped1, wrapped2, wrapped3) { 21 | if (wrapped1.x) { 22 | return 1; 23 | } 24 | if (wrapped2.x) { 25 | return 2; 26 | } 27 | if (wrapped3.x) { 28 | // Not unwrapped 29 | return 3; 30 | } 31 | if (wrapped1 === wrapped2) { 32 | // Always a new wrapped function 33 | return 4; 34 | } 35 | 36 | // No unwrapping 37 | if (wrapped3 === fn) { 38 | return 5; 39 | }; 40 | 41 | return true; 42 | } 43 | fn.x = 'secret'; 44 | fn; 45 | `); 46 | assert.sameValue(redWrappedFn(blueFn, blueFn, redWrappedFn), true); 47 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/wrapped-function-arguments-are-wrapped-into-the-inner-realm.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | ShadowRealm.prototype.evaluate wrapped function arguments are wrapped into the inner realm 7 | features: [ShadowRealm] 8 | ---*/ 9 | 10 | assert.sameValue( 11 | typeof ShadowRealm.prototype.evaluate, 12 | 'function', 13 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 14 | ); 15 | 16 | const r = new ShadowRealm(); 17 | const blueFn = (x, y) => x + y; 18 | 19 | const redWrappedFn = r.evaluate(` 20 | 0, (blueWrappedFn, a, b, c) => { 21 | return blueWrappedFn(a, b) * c; 22 | } 23 | `); 24 | assert.sameValue(redWrappedFn(blueFn, 2, 3, 4), 20); 25 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/wrapped-function-from-return-values-share-no-identity.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | ShadowRealm.prototype.evaluate wrapped function from return values share no identity. 7 | features: [ShadowRealm] 8 | ---*/ 9 | 10 | assert.sameValue( 11 | typeof ShadowRealm.prototype.evaluate, 12 | 'function', 13 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 14 | ); 15 | 16 | const r = new ShadowRealm(); 17 | 18 | r.evaluate(` 19 | globalThis.fn = function fn() { return 42; } 20 | globalThis.arrow = x => x * 2; 21 | globalThis.pFn = new Proxy(fn, { 22 | apply() { 23 | pFn.used = 1; 24 | return 39; 25 | } 26 | }); 27 | globalThis.aFn = async function aFn() { 28 | return 1; 29 | } 30 | 31 | globalThis.genFn = function * genFn() { 32 | return 1; 33 | } 34 | 35 | fn.x = 'secrets'; 36 | arrow.x = 'secrets'; 37 | pFn.x = 'secrets'; 38 | aFn.x = 'secrets'; 39 | genFn.x = 'secrets'; 40 | `) 41 | 42 | const wrappedOrdinary = r.evaluate('() => fn')(); 43 | assert.sameValue(typeof wrappedOrdinary, 'function', 'ordinary function wrapped'); 44 | assert.sameValue(wrappedOrdinary(), 42, 'ordinary, return'); 45 | assert.sameValue(wrappedOrdinary.x, undefined, 'ordinary, no property shared'); 46 | assert.sameValue(Object.prototype.hasOwnProperty.call(wrappedOrdinary, 'x'), false, 'ordinary, no own property shared'); 47 | 48 | const wrappedArrow = r.evaluate('() => arrow')(); 49 | assert.sameValue(typeof wrappedArrow, 'function', 'arrow function wrapped'); 50 | assert.sameValue(wrappedArrow(7), 14, 'arrow function, return'); 51 | assert.sameValue(wrappedArrow.x, undefined, 'arrow function, no property'); 52 | assert.sameValue(Object.prototype.hasOwnProperty.call(wrappedArrow, 'x'), false, 'arrow function, no own property shared'); 53 | 54 | const wrappedProxied = r.evaluate('() => pFn')(); 55 | assert.sameValue(typeof wrappedProxied, 'function', 'proxied ordinary function wrapped'); 56 | assert.sameValue(r.evaluate('pFn.used'), undefined, 'pFn not called yet'); 57 | assert.sameValue(wrappedProxied(), 39, 'return of the proxied callable'); 58 | assert.sameValue(r.evaluate('pFn.used'), 1, 'pfn called'); 59 | assert.sameValue(wrappedProxied.x, undefined, 'proxy callable, no property'); 60 | assert.sameValue(Object.prototype.hasOwnProperty.call(wrappedProxied, 'x'), false, 'proxy callable, no own property shared'); 61 | 62 | const wrappedAsync = r.evaluate('() => aFn')(); 63 | assert.sameValue(typeof wrappedAsync, 'function', 'async function wrapped'); 64 | assert.throws(TypeError, () => wrappedAsync(), 'wrapped function cannot return non callable object'); 65 | assert.sameValue(wrappedAsync.x, undefined, 'async function, no property'); 66 | assert.sameValue(Object.prototype.hasOwnProperty.call(wrappedAsync, 'x'), false, 'async function, no own property shared'); 67 | 68 | const wrappedGenerator = r.evaluate('() => genFn')(); 69 | assert.sameValue(typeof wrappedGenerator, 'function', 'gen function wrapped'); 70 | assert.throws(TypeError, () => wrappedGenerator(), 'wrapped function cannot return non callable object'); 71 | assert.sameValue(wrappedGenerator.x, undefined, 'generator, no property'); 72 | assert.sameValue(Object.prototype.hasOwnProperty.call(wrappedGenerator, 'x'), false, 'generator, no own property shared'); 73 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/wrapped-function-multiple-different-realms-nested.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Leo Balter. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | ShadowRealm can wrap a function to multiple nested realms. 7 | features: [ShadowRealm] 8 | ---*/ 9 | assert.sameValue( 10 | typeof ShadowRealm.prototype.evaluate, 11 | 'function', 12 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 13 | ); 14 | 15 | globalThis.count = 0; 16 | const realm1 = new ShadowRealm(); 17 | 18 | const r1wrapped = realm1.evaluate(` 19 | globalThis.count = 0; 20 | () => globalThis.count += 1; 21 | `); 22 | 23 | const realm2Evaluate = realm1.evaluate(` 24 | const realm2 = new ShadowRealm(); 25 | 26 | (str) => realm2.evaluate(str); 27 | `); 28 | 29 | const r2wrapper = realm2Evaluate(` 30 | globalThis.wrapped = undefined; 31 | globalThis.count = 0; // Bait 32 | (fn) => globalThis.wrapped = fn; 33 | `); 34 | 35 | const rewrapped = r2wrapper(r1wrapped); 36 | 37 | assert.notSameValue(rewrapped, r1wrapped, 'rewrapped !== r1wrapped'); 38 | 39 | const r2wrapped = realm2Evaluate('globalThis.wrapped'); 40 | 41 | assert.notSameValue(r2wrapped, r1wrapped, 'r2wrapped !== r1wrapped'); 42 | assert.notSameValue(r2wrapped, rewrapped, 'r2wrapped !== rewrapped'); 43 | 44 | assert.sameValue(realm1.evaluate('globalThis.count'), 0, `getting wrapped function won't trigger a call`); 45 | 46 | assert.sameValue(r2wrapped(), 1, 'call from r2 wrapped (r2wrapped) cycles back to r1'); 47 | 48 | assert.sameValue(realm1.evaluate('globalThis.count'), 1, 'effects produced in a third realm, #1'); 49 | 50 | assert.sameValue(rewrapped(), 2, 'call from r2 wrapped (rewrapped) cycles back to r1'); 51 | 52 | assert.sameValue(realm1.evaluate('globalThis.count'), 2, 'effects produced in a third realm, #2'); 53 | 54 | assert.sameValue(realm2Evaluate('globalThis.count'), 0, 'no side effects produced in the wrong realm (realm2)'); 55 | assert.sameValue(globalThis.count, 0, 'no side effects produced in the wrong realm (main realm)'); 56 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/wrapped-function-multiple-different-realms.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Leo Balter. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | ShadowRealm can wrap a function to multiple realms. 7 | features: [ShadowRealm] 8 | ---*/ 9 | 10 | const realm1 = new ShadowRealm(); 11 | const realm2 = new ShadowRealm(); 12 | 13 | globalThis.count = 0; 14 | 15 | assert.notSameValue(realm1, realm2); 16 | 17 | const r1wrapped = realm1.evaluate(` 18 | globalThis.count = 0; 19 | () => globalThis.count += 1; 20 | `); 21 | 22 | const r2wrapper = realm2.evaluate(` 23 | globalThis.wrapped = undefined; 24 | globalThis.count = 0; // Bait 25 | (fn) => globalThis.wrapped = fn; 26 | `); 27 | 28 | const rewrapped = r2wrapper(r1wrapped); 29 | 30 | assert.notSameValue(rewrapped, r1wrapped, 'rewrapped !== r1wrapped'); 31 | 32 | const r2wrapped = realm2.evaluate('globalThis.wrapped'); 33 | 34 | assert.notSameValue(r2wrapped, r1wrapped, 'r2wrapped !== r1wrapped'); 35 | assert.notSameValue(r2wrapped, rewrapped, 'r2wrapped !== rewrapped'); 36 | 37 | assert.sameValue(realm1.evaluate('globalThis.count'), 0, `getting wrapped function won't trigger a call`); 38 | 39 | assert.sameValue(r2wrapped(), 1, 'call from r2 wrapped (r2wrapped) cycles back to r1'); 40 | 41 | assert.sameValue(realm1.evaluate('globalThis.count'), 1, 'effects produced in a third realm, #1'); 42 | 43 | assert.sameValue(rewrapped(), 2, 'call from r2 wrapped (rewrapped) cycles back to r1'); 44 | 45 | assert.sameValue(realm1.evaluate('globalThis.count'), 2, 'effects produced in a third realm, #2'); 46 | 47 | assert.sameValue(realm2.evaluate('globalThis.count'), 0, 'no side effects produced in the wrong realm (realm2)'); 48 | assert.sameValue(globalThis.count, 0, 'no side effects produced in the wrong realm (main realm)'); 49 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/wrapped-function-observing-their-scopes.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | ShadowRealm.prototype.evaluate wrapped function observing their scopes 7 | features: [ShadowRealm] 8 | ---*/ 9 | 10 | assert.sameValue( 11 | typeof ShadowRealm.prototype.evaluate, 12 | 'function', 13 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 14 | ); 15 | 16 | const r = new ShadowRealm(); 17 | let myValue; 18 | 19 | function blueFn(x) { 20 | myValue = x; 21 | return myValue; 22 | } 23 | 24 | // cb is a new function in the red ShadowRealm that chains the call to the blueFn 25 | const redFunction = r.evaluate(` 26 | var myValue = 'red'; 27 | 0, function(cb) { 28 | cb(42); 29 | return myValue; 30 | }; 31 | `); 32 | 33 | assert.sameValue(redFunction(blueFn), 'red'); 34 | assert.sameValue(myValue, 42); 35 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/wrapped-function-proto-from-caller-realm.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Chengzhong Wu. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | WrappedFunctionCreate should create a function derived from the caller realm 7 | info: | 8 | ShadowRealm.prototype.evaluate ( sourceText ) 9 | ... 10 | 4. Let callerRealm be the current Realm Record. 11 | 5. Let evalRealm be O.[[ShadowRealm]]. 12 | 6. Return ? PerformRealmEval(sourceText, callerRealm, evalRealm). 13 | 14 | PerformRealmEval ( sourceText, callerRealm, evalRealm ) 15 | ... 16 | 25. Return ? GetWrappedValue(callerRealm, result). 17 | 18 | GetWrappedValue ( callerRealm, value ) 19 | ... 20 | 2.b. Return ? WrappedFunctionCreate(callerRealm, value). 21 | 22 | WrappedFunctionCreate ( callerRealm, targetFunction ) 23 | ... 24 | 5. Set obj.[[Prototype]] to callerRealm.[[Intrinsics]].[[%Function.prototype%]]. 25 | features: [ShadowRealm, cross-realm, Reflect] 26 | ---*/ 27 | 28 | assert.sameValue( 29 | typeof ShadowRealm.prototype.evaluate, 30 | 'function', 31 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 32 | ); 33 | 34 | var other = new ShadowRealm().__realm.globalObject; 35 | var OtherShadowRealm = other.ShadowRealm; 36 | var OtherFunctionPrototype = other.Function.prototype; 37 | 38 | var yetAnother = new ShadowRealm().__realm.globalObject; 39 | var YetAnotherShadowRealm = yetAnother.ShadowRealm; 40 | var YetAnotherFunctionPrototype = yetAnother.Function.prototype; 41 | 42 | var realm = Reflect.construct(OtherShadowRealm, []); 43 | 44 | var checkArgWrapperFn = realm.evaluate('(x) => { return Object.getPrototypeOf(x) === Function.prototype }') 45 | assert.sameValue(checkArgWrapperFn(() => {}), true, 'callable arguments passed into WrappedFunction should be wrapped in target realm'); 46 | 47 | var fn = realm.evaluate('() => { return () => { return 1 } }'); 48 | assert.sameValue(Object.getPrototypeOf(fn), OtherFunctionPrototype, 'WrappedFunction should be derived from the caller realm'); 49 | assert.sameValue(Object.getPrototypeOf(fn()), OtherFunctionPrototype, 'callable results from WrappedFunction should be wrapped in caller realm'); 50 | 51 | var fn = YetAnotherShadowRealm.prototype.evaluate.call(realm, '() => { return () => { return 1 } }'); 52 | assert.sameValue(Object.getPrototypeOf(fn), YetAnotherFunctionPrototype, 'WrappedFunction should be derived from the caller realm'); 53 | assert.sameValue(Object.getPrototypeOf(fn()), YetAnotherFunctionPrototype, 'callable results from WrappedFunction should be wrapped in caller realm'); 54 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/wrapped-function-proxied-observes-boundary.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | Proxying a wrapped function and invoking it still performs boundary checks 7 | features: [ShadowRealm] 8 | ---*/ 9 | 10 | assert.sameValue( 11 | typeof ShadowRealm.prototype.evaluate, 12 | 'function', 13 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 14 | ); 15 | 16 | const r = new ShadowRealm(); 17 | 18 | const wrapped = r.evaluate(`() => { return 1; };`); 19 | 20 | const secretObj = {x: 2}; 21 | 22 | let received; 23 | 24 | const proxiedWrapped = new Proxy(wrapped, { 25 | apply(target, _, args) { 26 | assert.sameValue(target, wrapped); 27 | received = args; 28 | 29 | // Object can't be sent to the other Realm 30 | return target({x: 1}); 31 | } 32 | }); 33 | 34 | assert.throws( 35 | TypeError, 36 | () => proxiedWrapped(secretObj), 37 | 'Proxying a wrapped function and invoking it still performs boundary checks' 38 | ); 39 | 40 | assert.sameValue(received[0], secretObj, 'proxy still calls the handler trap'); 41 | assert.sameValue(received.length, 1); 42 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/wrapped-function-throws-typeerror-from-caller-realm.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Chengzhong Wu. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-wrapped-function-exotic-objects-call-thisargument-argumentslist 5 | description: > 6 | WrappedFunction throws a TypeError from its creation realm. 7 | features: [ShadowRealm, cross-realm, Reflect] 8 | ---*/ 9 | 10 | assert.sameValue( 11 | typeof ShadowRealm.prototype.evaluate, 12 | 'function', 13 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 14 | ); 15 | 16 | var other = new ShadowRealm().__realm.globalObject; 17 | var OtherTypeError = other.TypeError; 18 | var OtherShadowRealm = other.ShadowRealm; 19 | 20 | var yetAnother = new ShadowRealm().__realm.globalObject; 21 | var YetAnotherTypeError = yetAnother.TypeError; 22 | var YetAnotherShadowRealm = yetAnother.ShadowRealm; 23 | 24 | var realm = Reflect.construct(OtherShadowRealm, []); 25 | 26 | { 27 | let wrappedFunction = realm.evaluate('() => {}'); 28 | let wrappedFunction2 = realm.evaluate('() => globalThis'); 29 | 30 | assert.throws(OtherTypeError, () => wrappedFunction(1, globalThis), 'throws TypeError if arguments are not wrappable'); 31 | assert.throws(OtherTypeError, () => wrappedFunction2(), 'throws TypeError if return value is not wrappable'); 32 | } 33 | 34 | { 35 | let wrappedFunction = YetAnotherShadowRealm.prototype.evaluate.call(realm, '() => {}'); 36 | let wrappedFunction2 = YetAnotherShadowRealm.prototype.evaluate.call(realm, '() => globalThis'); 37 | assert.throws(YetAnotherTypeError, () => wrappedFunction(1, globalThis), 'throws TypeError if arguments are not wrappable'); 38 | assert.throws(YetAnotherTypeError, () => wrappedFunction2(), 'throws TypeError if return value is not wrappable'); 39 | } 40 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/wrapped-function-throws-typeerror-on-exceptional-exit.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Igalia SL. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-wrapped-function-exotic-objects-call-thisargument-argumentslist 5 | description: > 6 | WrappedFunction throws a TypeError if the wrapped function throws. 7 | features: [ShadowRealm] 8 | ---*/ 9 | 10 | assert.sameValue( 11 | typeof ShadowRealm.prototype.evaluate, 12 | 'function', 13 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 14 | ); 15 | 16 | const r = new ShadowRealm(); 17 | 18 | assert.sameValue( 19 | r.evaluate(`(f) => { 20 | try { 21 | f(); 22 | } catch (e) { 23 | if (e instanceof TypeError) { 24 | return 'ok'; 25 | } else { 26 | return e.toString(); 27 | } 28 | } 29 | return 'normal exit'; 30 | }`)(() => { throw Error("ahh"); }), 31 | 'ok', 32 | 'WrappedFunction throws TypeError (from the calling realm) if the wrapped callable throws any exception' 33 | ); 34 | 35 | assert.throws( 36 | TypeError, 37 | () => r.evaluate('() => { throw new Error("ahh"); }')(), 38 | 'WrappedFunction throws TypeError if the wrapped callable throws any exception' 39 | ); 40 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/wrapped-function-throws-typeerror-on-non-primitive-arguments.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Chengzhong Wu. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-wrapped-function-exotic-objects-call-thisargument-argumentslist 5 | description: > 6 | WrappedFunction throws a TypeError if any of the arguments are non-primitive 7 | features: [ShadowRealm] 8 | ---*/ 9 | 10 | assert.sameValue( 11 | typeof ShadowRealm.prototype.evaluate, 12 | 'function', 13 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 14 | ); 15 | 16 | const r = new ShadowRealm(); 17 | const wrappedFunction = r.evaluate('() => {}'); 18 | 19 | assert.sameValue( 20 | typeof wrappedFunction, 21 | 'function', 22 | 'This test must fail if wrappedFunction is not a function' 23 | ); 24 | 25 | assert.throws(TypeError, () => wrappedFunction(1, globalThis), 'globalThis'); 26 | assert.throws(TypeError, () => wrappedFunction(1, []), 'array literal'); 27 | assert.throws(TypeError, () => wrappedFunction(1, { 28 | [Symbol.toPrimitive]() { return 'string'; }, 29 | toString() { return 'str'; }, 30 | valueOf() { return 1; } 31 | }), 'object literal with immediate primitive coercion methods'); 32 | assert.throws(TypeError, () => wrappedFunction(1, Object.create(null)), 'ordinary object with null __proto__'); 33 | assert.throws(TypeError, () => wrappedFunction(1, new Proxy({}, { apply() {} })), 'non-callable proxy'); 34 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/wrapped-function-throws-typeerror-on-non-primitive-returns.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Chengzhong Wu. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-wrapped-function-exotic-objects-call-thisargument-argumentslist 5 | description: > 6 | WrappedFunction throws a TypeError if it returns non-primitive values 7 | features: [ShadowRealm] 8 | ---*/ 9 | 10 | assert.sameValue( 11 | typeof ShadowRealm.prototype.evaluate, 12 | 'function', 13 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 14 | ); 15 | 16 | const r = new ShadowRealm(); 17 | 18 | assert.throws(TypeError, r.evaluate('() => globalThis'), 'globalThis'); 19 | assert.throws(TypeError, r.evaluate('() => []'), 'array literal'); 20 | assert.throws(TypeError, r.evaluate(` 21 | () => ({ 22 | [Symbol.toPrimitive]() { return 'string'; }, 23 | toString() { return 'str'; }, 24 | valueOf() { return 1; } 25 | }); 26 | `), 'object literal with immediate primitive coercion methods'); 27 | assert.throws(TypeError, r.evaluate('() => Object.create(null)'), 'ordinary object with null __proto__'); 28 | assert.throws(TypeError, r.evaluate('() => new Proxy({}, { apply() {} })'), 'non-callable proxy'); 29 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/wrapped-functions-accepts-callable-objects.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | ShadowRealm.prototype.evaluate accepts callable objects 7 | features: [ShadowRealm] 8 | ---*/ 9 | 10 | assert.sameValue( 11 | typeof ShadowRealm.prototype.evaluate, 12 | 'function', 13 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 14 | ); 15 | 16 | const r = new ShadowRealm(); 17 | 18 | assert.sameValue(typeof r.evaluate('function fn() {} fn'), 'function', 'value from a fn declaration'); 19 | assert.sameValue(typeof r.evaluate('(function() {})'), 'function', 'function expression'); 20 | assert.sameValue(typeof r.evaluate('(async function() {})'), 'function', 'async function expression'); 21 | assert.sameValue(typeof r.evaluate('(function*() {})'), 'function', 'generator expression'); 22 | assert.sameValue(typeof r.evaluate('(async function*() {})'), 'function', 'async generator expression'); 23 | assert.sameValue(typeof r.evaluate('() => {}'), 'function', 'arrow function'); 24 | assert.sameValue(typeof r.evaluate('new Proxy(() => {}, { apply() {} })'), 'function', 'callable proxy'); 25 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/wrapped-functions-can-resolve-callable-returns.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | ShadowRealm.prototype.evaluate wrapped functions can resolve callable returns. 7 | features: [ShadowRealm] 8 | ---*/ 9 | 10 | assert.sameValue( 11 | typeof ShadowRealm.prototype.evaluate, 12 | 'function', 13 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 14 | ); 15 | 16 | const r = new ShadowRealm(); 17 | 18 | const wrapped = r.evaluate('x => y => x * y'); 19 | const nestedWrapped = wrapped(2); 20 | const otherNestedWrapped = wrapped(4); 21 | 22 | assert.sameValue(otherNestedWrapped(3), 12); 23 | assert.sameValue(nestedWrapped(3), 6); 24 | 25 | assert.notSameValue(nestedWrapped, otherNestedWrapped, 'new wrapping for each return'); 26 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/wrapped-functions-new-wrapping-on-each-evaluation.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | ShadowRealm.prototype.evaluate wrapped functions produce new wrapping on each evaluation. 7 | features: [ShadowRealm] 8 | ---*/ 9 | 10 | assert.sameValue( 11 | typeof ShadowRealm.prototype.evaluate, 12 | 'function', 13 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 14 | ); 15 | 16 | const r = new ShadowRealm(); 17 | 18 | r.evaluate(` 19 | globalThis.fn = function fn() { 20 | return 42; 21 | } 22 | `); 23 | 24 | const wrapped = r.evaluate('fn'); 25 | const otherWrapped = r.evaluate('fn'); 26 | 27 | assert.notSameValue(wrapped, otherWrapped); 28 | assert.sameValue(typeof wrapped, 'function'); 29 | assert.sameValue(typeof otherWrapped, 'function'); 30 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/wrapped-functions-share-no-properties-extended.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | ShadowRealm.prototype.evaluate wrapped functions share no properties, extended 7 | features: [ShadowRealm] 8 | ---*/ 9 | 10 | assert.sameValue( 11 | typeof ShadowRealm.prototype.evaluate, 12 | 'function', 13 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 14 | ); 15 | 16 | const r = new ShadowRealm(); 17 | 18 | r.evaluate(` 19 | globalThis.fn = function fn() { return 42; } 20 | globalThis.arrow = x => x * 2; 21 | globalThis.pFn = new Proxy(fn, { 22 | apply() { 23 | pFn.used = 1; 24 | return 39; 25 | } 26 | }); 27 | globalThis.aFn = async function aFn() { 28 | return 1; 29 | } 30 | 31 | globalThis.genFn = function * genFn() { 32 | return 1; 33 | } 34 | 35 | fn.x = 'secrets'; 36 | arrow.x = 'secrets'; 37 | pFn.x = 'secrets'; 38 | aFn.x = 'secrets'; 39 | genFn.x = 'secrets'; 40 | `); 41 | 42 | const wrappedOrdinary = r.evaluate('fn'); 43 | assert.sameValue(typeof wrappedOrdinary, 'function', 'ordinary function wrapped'); 44 | assert.sameValue(wrappedOrdinary(), 42, 'ordinary, return'); 45 | assert.sameValue(wrappedOrdinary.x, undefined, 'ordinary, no property shared'); 46 | 47 | const wrappedArrow = r.evaluate('arrow'); 48 | assert.sameValue(typeof wrappedArrow, 'function', 'arrow function wrapped'); 49 | assert.sameValue(wrappedArrow(7), 14, 'arrow function, return'); 50 | assert.sameValue(wrappedArrow.x, undefined, 'arrow function, no property'); 51 | 52 | const wrappedProxied = r.evaluate('pFn'); 53 | assert.sameValue(typeof wrappedProxied, 'function', 'proxied ordinary function wrapped'); 54 | assert.sameValue(r.evaluate('pFn.used'), undefined, 'pFn not called yet'); 55 | assert.sameValue(wrappedProxied(), 39, 'return of the proxied callable'); 56 | assert.sameValue(r.evaluate('pFn.used'), 1, 'pfn called'); 57 | assert.sameValue(wrappedProxied.x, undefined, 'proxy callable, no property'); 58 | 59 | const wrappedAsync = r.evaluate('aFn'); 60 | assert.sameValue(typeof wrappedAsync, 'function', 'async function wrapped'); 61 | assert.throws(TypeError, () => wrappedAsync(), 'wrapped function cannot return non callable object'); 62 | assert.sameValue(wrappedAsync.x, undefined, 'async fn, no property'); 63 | 64 | const wrappedGenerator = r.evaluate('genFn'); 65 | assert.sameValue(typeof wrappedGenerator, 'function', 'gen function wrapped'); 66 | assert.throws(TypeError, () => wrappedGenerator(), 'wrapped function cannot return non callable object'); 67 | assert.sameValue(wrappedGenerator.x, undefined, 'generator, no property'); 68 | -------------------------------------------------------------------------------- /test/cases/prototype/evaluate/wrapped-functions-share-no-properties.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.evaluate 5 | description: > 6 | ShadowRealm.prototype.evaluate wrapped functions share no properties 7 | features: [ShadowRealm] 8 | ---*/ 9 | 10 | assert.sameValue( 11 | typeof ShadowRealm.prototype.evaluate, 12 | 'function', 13 | 'This test must fail if ShadowRealm.prototype.evaluate is not a function' 14 | ); 15 | 16 | const r = new ShadowRealm(); 17 | 18 | const wrapped = r.evaluate(` 19 | function fn() { 20 | return fn.secret; 21 | } 22 | 23 | fn.secret = 'confidential'; 24 | fn; 25 | `); 26 | 27 | assert.sameValue(wrapped.secret, undefined); 28 | assert.sameValue(wrapped(), 'confidential'); 29 | -------------------------------------------------------------------------------- /test/cases/prototype/importValue/descriptor.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.importvalue 5 | description: > 6 | ShadowRealm.prototype.importValue is an ECMAScript Standard built-in object function. 7 | includes: [propertyHelper.js] 8 | features: [ShadowRealm] 9 | ---*/ 10 | 11 | verifyProperty(ShadowRealm.prototype, "importValue", { 12 | enumerable: false, 13 | writable: true, 14 | configurable: true, 15 | }); 16 | -------------------------------------------------------------------------------- /test/cases/prototype/importValue/import-value.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.importvalue 5 | description: > 6 | ShadowRealm.prototype.importValue can import a value. 7 | flags: [async, module] 8 | features: [ShadowRealm] 9 | ---*/ 10 | 11 | assert.sameValue( 12 | typeof ShadowRealm.prototype.importValue, 13 | 'function', 14 | 'This test must fail if ShadowRealm.prototype.importValue is not a function' 15 | ); 16 | 17 | const r = new ShadowRealm(); 18 | 19 | r.importValue('./import-value_FIXTURE.js', 'x').then(x => { 20 | 21 | assert.sameValue(x, 1); 22 | 23 | }).then($DONE, $DONE); 24 | -------------------------------------------------------------------------------- /test/cases/prototype/importValue/length.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.importvalue 5 | description: > 6 | The value of ShadowRealm.prototype.importValue.length is 2 7 | info: | 8 | Every built-in function object, including constructors, has a "length" property 9 | whose value is a non-negative integral Number. Unless otherwise specified, this value 10 | is equal to the number of required parameters shown in the subclause heading for the 11 | function description. Optional parameters and rest parameters are not included in 12 | the parameter count. 13 | 14 | Unless otherwise specified, the "length" property of a built-in function object has 15 | the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. 16 | 17 | includes: [propertyHelper.js] 18 | features: [ShadowRealm] 19 | ---*/ 20 | 21 | verifyProperty(ShadowRealm.prototype.importValue, "length", { 22 | value: 2, 23 | enumerable: false, 24 | writable: false, 25 | configurable: true, 26 | }); 27 | -------------------------------------------------------------------------------- /test/cases/prototype/importValue/name.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.importValue 5 | description: > 6 | The value of ShadowRealm.prototype.importValue.name is 'importValue' 7 | info: | 8 | ShadowRealm.prototype.importValue 9 | 10 | Every built-in function object, including constructors, has a "name" property 11 | whose value is a String. Unless otherwise specified, this value is the name 12 | that is given to the function in this specification. 13 | 14 | Unless otherwise specified, the "name" property of a built-in function 15 | object has the attributes 16 | { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. 17 | 18 | includes: [propertyHelper.js] 19 | features: [ShadowRealm] 20 | ---*/ 21 | 22 | verifyProperty(ShadowRealm.prototype.importValue, "name", { 23 | value: "importValue", 24 | enumerable: false, 25 | writable: false, 26 | configurable: true, 27 | }); 28 | -------------------------------------------------------------------------------- /test/cases/prototype/importValue/not-constructor.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.importvalue 5 | description: > 6 | ShadowRealm.prototype.importValue is not a constructor. 7 | includes: [isConstructor.js] 8 | features: [ShadowRealm, Reflect.construct] 9 | ---*/ 10 | 11 | assert.sameValue( 12 | typeof ShadowRealm.prototype.importValue, 13 | 'function', 14 | 'This test must fail if ShadowRealm.prototype.importValue is not a function' 15 | ); 16 | 17 | // assert.sameValue( 18 | // isConstructor(ShadowRealm.prototype.importValue), 19 | // false, 20 | // 'isConstructor(ShadowRealm.prototype.importValue) must return false' 21 | // ); 22 | 23 | assert.throws( 24 | TypeError, 25 | () => { 26 | new ShadowRealm.prototype.importValue('', 'name'); 27 | }, 28 | '`new ShadowRealm.prototype.importValue("")` throws TypeError' 29 | ); 30 | 31 | const r = new ShadowRealm(); 32 | 33 | assert.throws( 34 | TypeError, 35 | () => { 36 | new r.importValue('./import-value_FIXTURE.js', 'x'); 37 | }, 38 | '`new r.importValue("...")` throws TypeError' 39 | ); 40 | -------------------------------------------------------------------------------- /test/cases/prototype/importValue/proto.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.importvalue 5 | description: > 6 | The [[Prototype]] of ShadowRealm.prototype.importValue is AsyncFunction.Prototype. 7 | info: | 8 | Unless otherwise specified every built-in function and every built-in constructor 9 | has the Function prototype object, which is the initial value of the expression 10 | Function.prototype, as the value of its [[Prototype]] internal slot. 11 | 12 | features: [ShadowRealm] 13 | ---*/ 14 | 15 | assert.sameValue(Object.getPrototypeOf(ShadowRealm.prototype.importValue), Function.prototype); 16 | -------------------------------------------------------------------------------- /test/cases/prototype/importValue/specifier-tostring.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.importvalue 5 | description: > 6 | ShadowRealm.prototype.importValue coerces specifier to string. 7 | features: [ShadowRealm] 8 | ---*/ 9 | 10 | assert.sameValue( 11 | typeof ShadowRealm.prototype.importValue, 12 | 'function', 13 | 'This test must fail if ShadowRealm.prototype.importValue is not a function' 14 | ); 15 | 16 | const r = new ShadowRealm(); 17 | let count = 0; 18 | 19 | const specifier = Object.create(null); 20 | 21 | // A - valueOF 22 | 23 | specifier.valueOf = function() { 24 | count += 1; 25 | throw new Test262Error(); 26 | }; 27 | 28 | assert.throws(Test262Error, () => { 29 | r.importValue(specifier); 30 | }, 'ToString(specifier) returns abrupt from valueOf'); 31 | 32 | assert.sameValue(count, 1, 'ToString calls the valueOf method'); 33 | 34 | 35 | // B - toString 36 | 37 | count = 0; 38 | 39 | specifier.valueOf = function() { 40 | count += 1000; 41 | throw new Error('valueOf is not reached if toString is present'); 42 | }; 43 | 44 | specifier.toString = function() { 45 | count += 1; 46 | throw new Test262Error(); 47 | }; 48 | 49 | assert.throws(Test262Error, () => { 50 | r.importValue(specifier); 51 | }, 'ToString(specifier) returns abrupt from toString'); 52 | 53 | assert.sameValue(count, 1, 'ToString calls the toString method'); 54 | 55 | // C - @@toPrimitive 56 | 57 | count = 0; 58 | 59 | specifier[Symbol.toPrimitive] = function() { 60 | count += 1; 61 | throw new Test262Error(); 62 | }; 63 | 64 | specifier.toString = function() { 65 | count += 1000; 66 | throw new Error('toString is not reached if @@toPrimitive is present'); 67 | }; 68 | 69 | assert.throws(Test262Error, () => { 70 | r.importValue(specifier); 71 | }, 'ToString(specifier) returns abrupt from @@toPrimitive'); 72 | 73 | assert.sameValue(count, 1, 'ToString calls the @@toPrimitive method'); 74 | -------------------------------------------------------------------------------- /test/cases/prototype/importValue/throws-if-exportname-not-string.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.importvalue 5 | description: > 6 | ShadowRealm.prototype.importValue throws if exportName is not a string. 7 | features: [ShadowRealm] 8 | ---*/ 9 | 10 | assert.sameValue( 11 | typeof ShadowRealm.prototype.importValue, 12 | 'function', 13 | 'This test must fail if ShadowRealm.prototype.importValue is not a function' 14 | ); 15 | 16 | const r = new ShadowRealm(); 17 | let count = 0; 18 | 19 | const exportName = { 20 | toString() { 21 | count += 1; 22 | throw new Test262Error(); 23 | } 24 | }; 25 | 26 | assert.throws(TypeError, () => { 27 | r.importValue('', exportName); 28 | }); 29 | 30 | assert.sameValue(count, 0); 31 | -------------------------------------------------------------------------------- /test/cases/prototype/importValue/throws-if-import-value-does-not-exist.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-realmimportvalue 5 | description: > 6 | ShadowRealm.prototype.importValue rejects when export name does not exist 7 | info: | 8 | RealmImportValue ( specifierString, exportNameString, callerRealm, evalRealm, evalContext ) 9 | 10 | Assert: Type(specifierString) is String. 11 | Assert: Type(exportNameString) is String. 12 | Assert: callerRealm is a ShadowRealm Record. 13 | Assert: evalRealm is a ShadowRealm Record. 14 | Assert: evalContext is an execution context associated to a ShadowRealm instance's [[ExecutionContext]]. 15 | Let innerCapability be ! NewPromiseCapability(%Promise%). 16 | Let runningContext be the running execution context. 17 | If runningContext is not already suspended, suspend runningContext. 18 | Push evalContext onto the execution context stack; evalContext is now the running execution context. 19 | Perform ! HostImportModuleDynamically(null, specifierString, innerCapability). 20 | Suspend evalContext and remove it from the execution context stack. 21 | Resume the context that is now on the top of the execution context stack as the running 22 | execution context. 23 | Let steps be the steps of an ExportGetter function as described below. 24 | 25 | An ExportGetter function is an anonymous built-in function with a [[ExportNameString]] 26 | internal slot. When an ExportGetter function is called with argument exports, 27 | it performs the following steps: 28 | 29 | Assert: exports is a module namespace exotic object. 30 | Let f be the active function object. 31 | Let string be f.[[ExportNameString]]. 32 | Assert: Type(string) is String. 33 | Let hasOwn be ? HasOwnProperty(exports, string). 34 | If hasOwn is false, throw a TypeError exception. 35 | ... 36 | 37 | flags: [async, module] 38 | features: [ShadowRealm] 39 | ---*/ 40 | 41 | assert.sameValue( 42 | typeof ShadowRealm.prototype.importValue, 43 | 'function', 44 | 'This test must fail if ShadowRealm.prototype.importValue is not a function' 45 | ); 46 | 47 | const r = new ShadowRealm(); 48 | 49 | r.importValue('./import-value_FIXTURE.js', 'y') 50 | .then( 51 | () => { 52 | throw new Test262Error("Expected rejection"); 53 | }, 54 | err => { 55 | assert.sameValue(Object.getPrototypeOf(err), TypeError.prototype, 'should be rejected with TypeError'); 56 | } 57 | ) 58 | .then($DONE, $DONE); 59 | -------------------------------------------------------------------------------- /test/cases/prototype/importValue/throws-typeerror-import-syntax-error.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Chengzhong Wu. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-realmimportvalue 5 | description: > 6 | ShadowRealm.prototype.importValue rejects with TypeError when the imported script unable to be parsed. 7 | info: | 8 | RealmImportValue ( specifierString, exportNameString, callerRealm, evalRealm, evalContext ) 9 | 10 | ... 11 | 17. Return ! PerformPromiseThen(innerCapability.[[Promise]], onFulfilled, callerRealm.[[Intrinsics]].[[%ThrowTypeError%]], promiseCapability). 12 | 13 | flags: [async, module] 14 | features: [ShadowRealm] 15 | ---*/ 16 | 17 | assert.sameValue( 18 | typeof ShadowRealm.prototype.importValue, 19 | 'function', 20 | 'This test must fail if ShadowRealm.prototype.importValue is not a function' 21 | ); 22 | 23 | const r = new ShadowRealm(); 24 | 25 | r.importValue('./import-value_syntax_error_FIXTURE.js', 'y') 26 | .then( 27 | () => { 28 | throw new Test262Error("unreachable"); 29 | }, 30 | err => { 31 | assert.sameValue(Object.getPrototypeOf(err), TypeError.prototype, 'should be rejected with TypeError'); 32 | } 33 | ) 34 | .then($DONE, $DONE); 35 | -------------------------------------------------------------------------------- /test/cases/prototype/importValue/throws-typeerror-import-throws.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Chengzhong Wu. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-realmimportvalue 5 | description: > 6 | ShadowRealm.prototype.importValue rejects with TypeError when the imported script throws. 7 | info: | 8 | RealmImportValue ( specifierString, exportNameString, callerRealm, evalRealm, evalContext ) 9 | 10 | ... 11 | 17. Return ! PerformPromiseThen(innerCapability.[[Promise]], onFulfilled, callerRealm.[[Intrinsics]].[[%ThrowTypeError%]], promiseCapability). 12 | 13 | flags: [async, module] 14 | features: [ShadowRealm] 15 | ---*/ 16 | 17 | assert.sameValue( 18 | typeof ShadowRealm.prototype.importValue, 19 | 'function', 20 | 'This test must fail if ShadowRealm.prototype.importValue is not a function' 21 | ); 22 | 23 | const r = new ShadowRealm(); 24 | 25 | r.importValue('./import-value_throws_FIXTURE.js', 'y') 26 | .then( 27 | () => { 28 | throw new Test262Error("unreachable"); 29 | }, 30 | err => { 31 | assert.sameValue(Object.getPrototypeOf(err), TypeError.prototype, 'should be rejected with TypeError'); 32 | } 33 | ) 34 | .then($DONE, $DONE); 35 | -------------------------------------------------------------------------------- /test/cases/prototype/importValue/validates-realm-object.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-shadowrealm.prototype.importvalue 5 | description: > 6 | ShadowRealm.prototype.importValue validates realm object. 7 | features: [ShadowRealm] 8 | ---*/ 9 | 10 | assert.sameValue( 11 | typeof ShadowRealm.prototype.importValue, 12 | 'function', 13 | 'This test must fail if ShadowRealm.prototype.importValue is not a function' 14 | ); 15 | 16 | const r = new ShadowRealm(); 17 | const bogus = {}; 18 | 19 | assert.throws(TypeError, function() { 20 | r.importValue.call(bogus, "specifier", "name"); 21 | }, 'throws a TypeError if this is not a ShadowRealm object'); 22 | -------------------------------------------------------------------------------- /test/cases/prototype/proto.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Rick Waldron. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | /*--- 4 | esid: sec-properties-of-the-realm-prototype-object 5 | description: > 6 | The [[Prototype]] of ShadowRealm.prototype is Object.Prototype. 7 | info: | 8 | Unless otherwise specified every built-in prototype object has the Object prototype 9 | object, which is the initial value of the expression Object.prototype, as the value 10 | of its [[Prototype]] internal slot, except the Object prototype object itself. 11 | 12 | features: [ShadowRealm] 13 | ---*/ 14 | 15 | assert.sameValue(Object.getPrototypeOf(ShadowRealm.prototype), Object.prototype); 16 | -------------------------------------------------------------------------------- /test/helpers.js: -------------------------------------------------------------------------------- 1 | const fixtureMap = { 2 | '/import-value_FIXTURE.js': 'export var x = 1;', 3 | '/import-value_syntax_error_FIXTURE.js': 'This is an invalid JavaScript Module file.', 4 | '/import-value_throws_FIXTURE.js': `throw new Error('foobar');`, 5 | }; 6 | 7 | const rawFetch = window.fetch; 8 | 9 | window.fetch = function (url, opts) { 10 | if (typeof url === 'string') { 11 | for (const [file, code] of Object.entries(fixtureMap)) { 12 | if (url.endsWith(file)) { 13 | return rawFetch('data:text/javascript;base64,' + btoa(code), opts); 14 | } 15 | } 16 | } 17 | return rawFetch(url, opts); 18 | }; 19 | -------------------------------------------------------------------------------- /test/transformer.js: -------------------------------------------------------------------------------- 1 | module.exports = function (test262File) { 2 | return test262File.contents; 3 | }; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES5", 4 | "lib": ["DOM", "ESNext"], 5 | "strict": true, 6 | "moduleResolution": "node", 7 | "declaration": true, 8 | "declarationDir": "./dist" 9 | }, 10 | "include": ["src/**/*.ts"] 11 | } 12 | --------------------------------------------------------------------------------