Compare commits

..

203 Commits

Author SHA1 Message Date
89eea7d62b refactor: remove old useless exceptions 2024-01-06 19:51:48 +02:00
18d22a1282 Merge pull request #12 from TopchetoEU/TopchetoEU/cleanup
Major codebase cleanup
2024-01-06 18:28:11 +02:00
72a0d39d0b fix: make java 11 compatible 2024-01-06 18:27:36 +02:00
d8585a20bf refactor: don't require ctx in frame.push 2024-01-06 18:23:34 +02:00
e4c9a8756e fix: debugger hanging sometimes 2024-01-06 18:23:20 +02:00
c6e6425c7e fix: bring back ts minification 2024-01-06 17:53:42 +02:00
292ca64cb9 fix: wrong behavior in Number.toString (which somehow broke typescript) 2024-01-06 17:50:06 +02:00
4572db5c46 feat: some array tests 2024-01-06 17:49:36 +02:00
0251c4689d fix: use Values to access members in ObjectLib, instead of direct access 2024-01-06 17:49:27 +02:00
3173919b49 fix: implement proper parseInt logic 2024-01-06 17:48:35 +02:00
45f133c6b0 fix: use Values.call instead of direct calling 2024-01-06 17:48:10 +02:00
34276d720c fix: remove sparse call arguments 2024-01-06 17:47:38 +02:00
2c634778c3 fix: report proper function name in String.length errors 2024-01-06 17:47:07 +02:00
4aa757e625 fix: Function.bind now passess this argument, instead of the function itself 2024-01-06 17:46:39 +02:00
918f2623cd fix: small issue with sparse arrays 2024-01-06 17:46:13 +02:00
a321fc14bc fix: wrong signature of Map.forEach 2024-01-06 17:45:56 +02:00
07a6f18b16 refactor: some spring cleaning in array lib, fix small issue with join 2024-01-06 17:45:52 +02:00
5f4011aa0c refactor: move NO_RETURN to Values, remove some casters from Values 2024-01-06 17:45:44 +02:00
71f735b812 fix: some more libs fixes 2024-01-04 13:58:04 +02:00
e575b3287e fix: try-catch-finally fix #457846982 2024-01-04 13:57:41 +02:00
4fa5f5a815 feat: use new wrapper API in libs 2024-01-04 10:02:14 +02:00
a61c6a494e fix: some Argument and Engine API improvements, 2024-01-04 10:02:01 +02:00
978ee8db79 feat: make better native wrapper API 2023-12-28 16:55:57 +02:00
e372941e99 refactor: generalize Reading class 2023-12-27 20:18:41 +02:00
c36a0db860 refactor: remove more dead code 2023-12-27 20:18:23 +02:00
d6ee59363f refactor: remove unneeded event system 2023-12-27 20:10:11 +02:00
d5fd6e650e fix: clean up debugger API 2023-12-27 20:02:45 +02:00
c0b895e00a feat: greatly improve Context API 2023-12-27 14:22:18 +02:00
9ea5cd9277 refactor: remove old Data API 2023-12-27 14:21:52 +02:00
aaf9a6fa45 fix: pass environment to compiler via simple environment wrapper 2023-12-27 13:59:19 +02:00
579f09c837 fix: pass arguments to regex constructor in LOAD_REGEX 2023-12-27 13:28:17 +02:00
3343262e72 fix: main now uses new env API 2023-12-27 13:16:21 +02:00
153a1a9a49 feat: readd permissions API via new env API 2023-12-27 13:16:12 +02:00
bf38587271 feat: readd module API via env API 2023-12-27 13:15:46 +02:00
21534efd60 feat: readd FS API via new env API 2023-12-27 13:14:46 +02:00
802f2f3f52 fix: access env via context 2023-12-27 13:14:09 +02:00
38acc20a6f refactor: greatly improve Environment API 2023-12-26 17:12:20 +02:00
d7f6010319 fix: make code java 11 compatible 2023-12-26 14:22:35 +02:00
87f8975275 build: minify typescript code 2023-12-26 14:22:25 +02:00
09eb6507dc Merge pull request #11 from TopchetoEU/TopchetoEU/modules
Module support
2023-12-26 14:20:55 +02:00
2f58f6b245 feat: implement basic module system 2023-12-26 14:12:41 +02:00
4bc363485f fix: losts of FS API improvements 2023-12-26 14:02:32 +02:00
8e01db637b fix: improve path resolutions and FS API 2023-12-26 11:27:40 +02:00
1c64912786 feat: create memory-light directory list file 2023-12-26 11:27:09 +02:00
28265a8f44 fix: some bug fixes and improvements with File interface 2023-12-26 11:26:08 +02:00
e9e020512e fix: environment pushed when it shouldn't be 2023-12-24 15:17:38 +02:00
4b0bbf5190 fix: correctly convert virtual to real path 2023-12-24 14:40:27 +02:00
031f78ebf1 fix: don't include ts's code in repo anymore 2023-12-24 14:32:37 +02:00
562f1f9425 fix: remove major mistakes in README 2023-12-24 14:31:50 +02:00
82a09e8865 fix: some config files cleanup 2023-12-24 14:31:33 +02:00
90da2db1fb fix: error now is not null-prototyped 2023-12-24 14:30:48 +02:00
3d275c52c0 refactor: fix code to not result in compile warning 2023-12-24 14:30:31 +02:00
797585f539 feat: implement some stdlib functions 2023-12-24 14:28:12 +02:00
7a301eba8f fix: some stupid mistakes in FS 2023-12-24 14:27:27 +02:00
1b2068a274 fix: never-ending try-catch issues 2023-12-24 14:27:00 +02:00
078d7ed95f refactor: improve Engine API 2023-12-24 14:26:42 +02:00
93973c12b1 fix: change default return type to void for generators 2023-12-24 14:10:03 +02:00
cad4f34b51 fix: use correct env declarations in bootstrap.js 2023-12-24 14:08:20 +02:00
d3571d6ee2 fix: make .gitignore more restrictive 2023-12-22 10:46:30 +02:00
caf9131cde refactor: replace all CRLF with LF 2023-12-22 10:45:04 +02:00
8c6379eb24 Merge pull request #10 from TopchetoEU/TopchetoEU/mapping
Add support for source mappings
2023-12-18 22:42:38 +02:00
380a5c720a feat: make environment hidable from stack trace 2023-12-18 22:38:26 +02:00
76c3d377af feat: use mappings in stack traces 2023-12-18 22:30:14 +02:00
42f443572a fix: how the hell did i fix this (it was the cache all along) 2023-12-18 22:11:08 +02:00
773bc72f3e refactor: rename compileWithDebug to compile 2023-12-14 21:07:16 +02:00
0b5178e9fd fix: return minified typescript 2023-12-14 21:06:23 +02:00
8cffcff7db refactor: remove unused instructions 2023-12-14 17:02:24 +02:00
60bbaaccd4 fix: some issues with try-catch 2023-12-14 16:49:51 +02:00
60b1762462 refactor: remove printf 2023-12-14 16:49:35 +02:00
34434965d2 cant be fucked to split this one up 2023-12-14 12:39:01 +02:00
fe86123f0f refactor: improve function statement 2023-11-29 13:04:41 +02:00
d5e6edfa8b refactor: replace .locate with argument 2023-11-29 13:03:53 +02:00
73345062ca feat: implement new API with source maps 2023-11-28 21:40:37 +02:00
124341969c fix: simplify source map API 2023-11-28 21:39:25 +02:00
8defd93855 fix: use proper name for native constructors 2023-11-28 21:37:46 +02:00
6c57e0e9f2 fix: properly handle wrapper function 2023-11-28 21:37:13 +02:00
f1932914ee fix: move debugger assets to correct location 2023-11-27 20:28:02 +02:00
977701e601 feat: implement source maps 2023-11-26 16:50:07 +02:00
e8a7ac8da8 refactor: reorganize assets 2023-11-26 16:49:47 +02:00
6b1cb852c2 fix: arrays wrongly stringified in JSONLib 2023-11-26 13:51:56 +02:00
b59a003086 feat: implement VLQ parsing 2023-11-26 13:51:43 +02:00
1902e41f61 feat: make typescript output mappings 2023-11-26 11:52:47 +02:00
27162ef8ac feat: improve Bufffer API 2023-11-26 11:51:32 +02:00
4f22e76d2b fix: make code java 11 compatible 2023-11-25 20:22:16 +02:00
8924e7aadc build: fix build script to exit with code when error occurs 2023-11-25 20:16:06 +02:00
1d0e31a423 Merge pull request #9 from TopchetoEU/TopchetoEU/perms-and-fs
Permissions and filesystems
2023-11-25 20:10:59 +02:00
ab56908171 fix: faulty insufficient permissions error 2023-11-25 20:09:59 +02:00
eb14bb080c fix: debugger breakpoints not registered 2023-11-25 20:09:47 +02:00
f52f47cdb4 feat: properly implement filesystems 2023-11-25 19:36:18 +02:00
567eaa8514 fix: incorrect declarations 2023-11-25 19:13:20 +02:00
2cfdd8e335 feat: add utf8 encoding and decoding 2023-11-25 19:12:56 +02:00
4b1ec671e2 fix: micro tasks not handled properly 2023-11-25 18:58:35 +02:00
b127aadcf6 fix: promise was sending macro tasks instead of micro 2023-11-25 18:58:24 +02:00
b6eaff65ca style: remove unnececeary import 2023-11-25 18:48:46 +02:00
443dc0ffa1 feat: add await function to promise 2023-11-25 18:47:51 +02:00
e107dd3507 fix: promise doesn't use microtasks in some cases 2023-11-25 18:47:36 +02:00
6af3c70fce feat: add filesystem libs 2023-11-25 18:47:01 +02:00
8b743f49d1 feat: add toString, equals and hashCode overrides to wrappers and objects 2023-11-25 18:46:13 +02:00
e1ce384815 feat: add parse function to Filename 2023-11-25 18:44:27 +02:00
86d205e521 fix: parsing files won't modify last instruction to RET, instead will just append it 2023-11-25 18:44:11 +02:00
f0ad936e5b refactor: use new iterable names 2023-11-25 18:43:43 +02:00
4a1473c5be fix: sorting with no comparator now works 2023-11-25 18:42:54 +02:00
4111dbf5c4 fix: functions now print their name when stringified 2023-11-25 18:41:47 +02:00
1666682dc2 refactor: get rid of data parent hierarchy 2023-11-25 18:41:18 +02:00
f2b33d0233 feat: add support for async iterators
fix: comparasions had small behavioural issue
2023-11-25 18:40:49 +02:00
f5a0b6eaf7 fix: some improvements of compiled bytecode 2023-11-25 18:38:43 +02:00
829bea755d fix: CodeFrame didnt't handle out of bounds jumps well 2023-11-25 18:38:16 +02:00
4b0dcffd13 feat: add fs declarations in lib.d.ts 2023-11-25 18:37:40 +02:00
987f8b8f00 fix: handle native errors properly-ish 2023-11-25 14:18:46 +02:00
55e3d46bc2 feat: implement memory and root fs 2023-11-25 14:18:04 +02:00
3e25068219 feat: implement bulk of fs 2023-11-25 14:17:37 +02:00
7ecb8bfabb feat: implement permissions 2023-11-25 14:16:02 +02:00
488deea164 fix: improve performance of typescript by caching separate declarations 2023-11-14 09:24:39 +02:00
ed08041335 fix: internal error when trying to use key of "undefined" 2023-11-14 09:24:00 +02:00
0a4149ba81 fix: remove double space in "Uncaught ..." 2023-11-14 09:23:15 +02:00
30f5d619c3 fix: errors now have the right prototype and name 2023-11-14 09:22:56 +02:00
e7dbe91374 refactor: clean up protocol.json 2023-11-13 20:06:07 +02:00
455f5a613e feat: implement simple permission matching 2023-11-13 19:09:33 +02:00
1eeac3ae97 fix: replace templates in Metadata class with placeholder data 2023-11-13 19:08:56 +02:00
1acd78e119 refactor: clean up Main class 2023-11-13 18:56:59 +02:00
df9932874d feat: remove unnececeary @NativeInit directives 2023-11-06 14:03:15 +02:00
b47d1a7576 refactor: remove StackData and Data usage 2023-11-06 13:53:36 +02:00
fdfa8d7713 refactor: some minor fixes, rewrite README example 2023-11-06 10:55:38 +02:00
f5d1287948 fix: some annoying bugs, as well as splice 2023-11-06 00:55:30 +02:00
15f4278cb1 fix: build with java 17 2023-11-05 21:00:39 +02:00
df8465cb49 Merge pull request #8 from TopchetoEU/TopchetoEU/tests
Integrate typescript
2023-11-05 20:32:42 +02:00
e3104c223c fix: remove some unnececeary logs 2023-11-05 20:29:21 +02:00
1d0bae3de8 feat: include typescript code in source code 2023-11-05 20:27:23 +02:00
b66acd3089 fix: several more fixes 2023-11-05 19:44:44 +02:00
e326847287 feat: send value stack to debug client 2023-11-05 19:44:35 +02:00
26591d6631 fix: lazy operators incorrectly pop values from stack 2023-11-05 19:44:08 +02:00
af31b1ab79 fix: several small fixes 2023-11-05 19:43:53 +02:00
f885d4349f refactor: fix some bad code >:( 2023-11-05 19:43:28 +02:00
d57044acb7 fix: several bug fixes to help with typescript support 2023-11-05 12:44:29 +02:00
7df4e3b03f fix: oops 2023-11-04 11:43:57 +02:00
ed1009ab69 refactor: some code restructuring in the debugging 2023-11-04 11:42:06 +02:00
f856cdf37e fix: messages larger than 64KB are now fragmented properly 2023-11-04 11:41:31 +02:00
4f82574b8c fix: various small behavioural issues
fix: pesky try-catch logic
2023-11-04 11:40:50 +02:00
0ae24148d8 feat: write some tests 2023-11-04 11:38:48 +02:00
ac128d17f4 feat: implement Array.reduce
fix: native functions are now named
2023-11-04 11:38:29 +02:00
6508f15bb0 refactor: remove typescript source code from repo (for now) 2023-11-04 11:37:13 +02:00
69f93b4f87 refactor: make filenames more consistent 2023-11-04 11:36:36 +02:00
b675411925 fix: a lot of minor bugs 2023-10-29 23:47:48 +02:00
d1e93c2088 Merge branch 'master' of https://github.com/TopchetoEU/java-jscript 2023-10-29 12:33:18 +02:00
942db54546 feat: improve vscode debugging compatibility 2023-10-29 12:30:43 +02:00
d20df66982 feat: improve vscode debugging compatibility 2023-10-29 12:25:33 +02:00
16a9e5d761 Merge pull request #7 from TopchetoEU/TopcehtoEU/debugging
Debugging support
2023-10-28 17:11:55 +03:00
dc9d84a370 chore: nothing of use 2023-10-28 17:10:27 +03:00
edb71daef4 feat: fully implement local and capture scope object wrappers
feat: implement object sending and receiving
2023-10-28 16:58:33 +03:00
4b84309df6 feat: complete steptrough and breakpoints in debugger 2023-10-27 15:12:14 +03:00
d2d9fa9738 fix: exit now works 2023-10-07 14:08:47 +03:00
a4e5f7f471 refactor: clean up useless catch blocks 2023-10-07 13:55:44 +03:00
cc044374ba refactor: replace InterruptedException with unchecked alternative 2023-10-07 13:42:55 +03:00
517e3e6657 refactor: clean up context and stack data 2023-10-07 13:07:07 +03:00
926b9c17d8 feat: readd JSON to lib 2023-10-06 18:42:35 +03:00
fc705e7383 feat: readd date internals 2023-10-06 12:03:33 +03:00
a17ec737b7 refactor: rename polyfills to libs 2023-10-06 11:57:29 +03:00
952a4d631d fix: keep order of object fields 2023-10-04 22:06:49 +03:00
005610ca40 Merge pull request #6 from TopchetoEU/TopchetoEU/function-optimizations
Cache function bodies
2023-10-04 21:38:29 +03:00
9743a6c078 fix: cache function bodies 2023-10-04 21:34:33 +03:00
21a6d20ac5 refactor: some code cleanup, emit better bytecode 2023-10-04 12:16:06 +03:00
4d1846f082 fix: reconfigure workflow and build.js 2023-10-04 09:00:44 +03:00
9547c86b32 Merge pull request #5 from TopchetoEU/TopchetoEU/corelib-reprogramming
Core library reprogramming
2023-10-04 08:50:26 +03:00
1cccfa90a8 refactor: remove pesky node files 2023-10-04 08:49:49 +03:00
604b752be6 feat: add global functions 2023-10-04 08:49:20 +03:00
6c7fe6deaf feat: add old data to both subcontexts 2023-10-04 08:30:33 +03:00
68f3b6d926 feat: fully remove typescript init code 2023-10-04 08:10:26 +03:00
63b04019cf feat: implement timers in java 2023-09-28 10:25:32 +03:00
9c65bacbac fix: type name can f itself 2023-09-28 09:38:51 +03:00
0dacaaeb4c a lot of fixes 2023-09-27 15:08:23 +03:00
c1b84689c4 fix: rewrite try-catch-finally logic 2023-09-27 10:27:19 +03:00
bd08902196 fix: revert back to old error protocol 2023-09-27 10:27:04 +03:00
22ec95a7b5 feat: implement errors 2023-09-26 22:54:12 +03:00
6c71911575 feat: implement symbols in java 2023-09-26 11:29:49 +03:00
e16c0fedb1 feat: implement map and set polyfills in java 2023-09-26 08:31:27 +03:00
cf36b7adc5 fix: some string polyfill fixes 2023-09-25 18:33:07 +03:00
ff9b57aeb9 fix: remove string from core.ts 2023-09-25 18:32:59 +03:00
47c62128ab feat: implement string polyfill in java 2023-09-25 18:18:36 +03:00
4aaf2f26db feat: add some standard functions to Number 2023-09-24 20:50:53 +03:00
f21cdc831c fix: primitive values will be native classes now 2023-09-24 14:15:12 +03:00
86b206051d feat: implement bool polyfills in java 2023-09-24 13:49:37 +03:00
f2cd50726d feat: implement array polyfills in java 2023-09-24 12:56:53 +03:00
d8071af480 refactor: remove dead .ts code 2023-09-21 10:50:48 +03:00
0b7442a3d8 fix: remove raw native funcs, use this arg passing instead 2023-09-21 10:50:03 +03:00
356a5a5b78 feat: a lot of typescript corelibs translated to java 2023-09-20 23:02:23 +03:00
da4b35f506 feat: Create filesystem interface and a physical filesystem 2023-09-18 10:31:50 +03:00
8c049ac08f fix: forgor to delete this 2023-09-09 19:02:55 +03:00
292d08d5a6 Merge pull request #4 from TopchetoEU/TopchetoEU/environments
Create environments
2023-09-09 18:55:48 +03:00
692fae4049 feat: some cleanup 2023-09-09 18:55:20 +03:00
3824d11c97 debugging again 2023-09-09 18:42:10 +03:00
7a226c49d3 some build script fixes 2023-09-09 18:37:24 +03:00
8f0c0226a8 oops 2023-09-09 18:17:10 +03:00
0343c97d5e fix script 2023-09-09 18:16:56 +03:00
fea17d944b fuck 2023-09-09 18:12:24 +03:00
8fb01e1091 fix: remove try_break and try_cont (dead code) 2023-09-09 18:12:20 +03:00
1ce5fc9d99 refactor: move parse to Contetx 2023-09-09 18:09:20 +03:00
0e04459fe7 feat: implement new system for context tracking 2023-09-09 17:32:46 +03:00
78b192babe fix: some build scripts 2023-09-08 02:36:09 +03:00
38656bd654 fix: small issues 2023-09-07 16:35:49 +03:00
29d3f378a5 major changes in preparations for environments 2023-09-04 14:30:57 +03:00
d1b37074a6 fix: resturcture build pipeline 2023-08-29 11:49:27 +03:00
a1e07a8046 fix: reprogram standard library for env api 2023-08-27 21:21:25 +03:00
54c6af52cf bruh 2023-08-26 12:19:26 +03:00
251 changed files with 17221 additions and 12904 deletions

10
.gitattributes vendored
View File

@@ -1,9 +1 @@
# * -text
# https://help.github.com/articles/dealing-with-line-endings/
#
# Linux start script should use lf
/gradlew text eol=lf
# These are Windows script files and should use crlf
*.bat text eol=crlf

View File

@@ -11,20 +11,20 @@ jobs:
runs-on: "ubuntu-latest" runs-on: "ubuntu-latest"
steps: steps:
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: 'adopt'
java-version: '11'
- name: Clone repository - name: Clone repository
uses: GuillaumeFalourd/clone-github-repo-action@main uses: GuillaumeFalourd/clone-github-repo-action@main
with: with:
branch: 'master' # fuck this political bullshitshit, took me an hour to fix this
owner: 'TopchetoEU' owner: 'TopchetoEU'
repository: 'java-jscript' repository: 'java-jscript'
- name: "Build" - name: "Build"
run: | run: |
cd java-jscript; cd java-jscript; node ./build.js release ${{ github.ref }}
ls;
mkdir -p dst/classes;
rsync -av --exclude='*.java' src/ dst/classes;
tsc --outDir dst/classes/me/topchetoeu/jscript/js --declarationDir dst/classes/me/topchetoeu/jscript/dts;
find src -name "*.java" | xargs javac -d dst/classes;
jar -c -f dst/jscript.jar -e me.topchetoeu.jscript.Main -C dst/classes .
- uses: "marvinpinto/action-automatic-releases@latest" - uses: "marvinpinto/action-automatic-releases@latest"
with: with:

26
.gitignore vendored
View File

@@ -1,8 +1,18 @@
.vscode *
.gradle
.ignore !/src
out !/src/**/*
build
bin /src/assets/js/ts.js
dst
/*.js !/tests
!/tests/**/*
!/.github
!/.github/**/*
!/.gitignore
!/.gitattributes
!/build.js
!/LICENSE
!/README.md

View File

View File

@@ -2,33 +2,28 @@
**NOTE: This had nothing to do with Microsoft's dialect of EcmaScript** **NOTE: This had nothing to do with Microsoft's dialect of EcmaScript**
**WARNING: Currently, this code is mostly undocumented. Proceed with caution and a psychiatrist.** **WARNING: Currently, this code is undocumented. Proceed with caution and a psychiatrist.**
JScript is an engine, capable of running EcmaScript 5, written entirely in Java. This engine has been developed with the goal of being easy to integrate with your preexisting codebase, **THE GOAL OF THIS ENGINE IS NOT PERFORMANCE**. My crude experiments show that this engine is 50x-100x slower than V8, which, although bad, is acceptable for most simple scripting purposes. JScript is an engine, capable of running EcmaScript 5, written entirely in Java. This engine has been developed with the goal of being easy to integrate with your preexisting codebase, **THE GOAL OF THIS ENGINE IS NOT PERFORMANCE**. My crude experiments show that this engine is 50x-100x slower than V8, which, although bad, is acceptable for most simple scripting purposes. Note that although the codebase has a Main class, this isn't meant to be a standalone program, but instead a library for running JavaScript code.
## Example ## Example
The following will create a REPL using the engine as a backend. Not that this won't properly log errors. I recommend checking out the implementation in `Main.main`: The following is going to execute a simple javascript statement:
```java ```java
var engine = new PolyfillEngine(new File(".")); var engine = new Engine(false);
var in = new BufferedReader(new InputStreamReader(System.in)); // Initialize a standard environment, with implementations of most basic standard libraries (Object, Array, Symbol, etc.)
engine.start(); var env = Internals.apply(new Environment());
while (true) { // Queue code to load internal libraries and start engine
try { var awaitable = engine.pushMsg(false, env, new Filename("tmp", "eval"), "10 + Math.sqrt(5 / 3)", null);
var raw = in.readLine(); // Run the engine on the same thread, until the event loop runs empty
engine.run(true);
var res = engine.pushMsg(false, engine.global(), Map.of(), "<stdio>", raw, null).await(); // Get our result
Values.printValue(engine.context(), res); System.out.println(awaitable.await());
System.out.println();
}
catch (EngineException e) {
try {
System.out.println("Uncaught " + e.toString(engine.context()));
}
catch (InterruptedException _e) { return; }
}
catch (IOException | InterruptedException e) { return; }
}
``` ```
## NOTE:
To setup the typescript bundle in your sources, run `node build.js init-ts`. This will download the latest version of typescript, minify it, and add it to your src/assets folder. If you are going to work with the `node build.js debug|release` command, this is not a necessary step.

173
build.js Normal file
View File

@@ -0,0 +1,173 @@
const { spawn } = require('child_process');
const fs = require('fs/promises');
const pt = require('path');
const { argv, exit } = require('process');
const { Readable } = require('stream');
async function* find(src, dst, wildcard) {
const stat = await fs.stat(src);
if (stat.isDirectory()) {
for (const el of await fs.readdir(src)) {
for await (const res of find(pt.join(src, el), dst ? pt.join(dst, el) : undefined, wildcard)) yield res;
}
}
else if (stat.isFile() && wildcard(src)) yield dst ? { src, dst } : src;
}
async function copy(src, dst, wildcard) {
const promises = [];
for await (const el of find(src, dst, wildcard)) {
promises.push((async () => {
await fs.mkdir(pt.dirname(el.dst), { recursive: true });
await fs.copyFile(el.src, el.dst);
})());
}
await Promise.all(promises);
}
function run(suppressOutput, cmd, ...args) {
return new Promise((res, rej) => {
const proc = spawn(cmd, args, { stdio: suppressOutput ? 'ignore' : 'inherit' });
proc.once('exit', code => {
if (code === 0) res(code);
else rej(new Error(`Process ${cmd} exited with code ${code}.`));
});
})
}
async function downloadTypescript(outFile) {
try {
// Import the required libraries, without the need of a package.json
console.log('Importing modules...');
await run(true, 'npm', 'i', 'tar', 'zlib', 'uglify-js');
await fs.mkdir(pt.dirname(outFile), { recursive: true });
await fs.mkdir('tmp', { recursive: true });
const tar = require('tar');
const zlib = require('zlib');
const { minify } = await import('uglify-js');
// Download the package.json file of typescript
const packageDesc = await (await fetch('https://registry.npmjs.org/typescript/latest')).json();
const url = packageDesc.dist.tarball;
console.log('Extracting typescript...');
await new Promise(async (res, rej) => Readable.fromWeb((await fetch(url)).body)
.pipe(zlib.createGunzip())
.pipe(tar.x({ cwd: 'tmp', filter: v => v === 'package/lib/typescript.js' }))
.on('end', res)
.on('error', rej)
);
console.log('Compiling typescript to ES5...');
const ts = require('./tmp/package/lib/typescript');
const program = ts.createProgram([ 'tmp/package/lib/typescript.js' ], {
outFile: "tmp/typescript-es5.js",
target: ts.ScriptTarget.ES5,
module: ts.ModuleKind.None,
downlevelIteration: true,
allowJs: true,
});
program.emit();
console.log('Minifying typescript...');
const minified = minify((await fs.readFile('tmp/typescript-es5.js')).toString());
// const minified = { code: (await fs.readFile('tmp/typescript-es5.js')).toString() };
if (minified.error) throw minified.error;
// Patch unsupported regex syntax
minified.code = minified.code.replaceAll('[-/\\\\^$*+?.()|[\\]{}]', '[-/\\\\^$*+?.()|\\[\\]{}]');
const stream = await fs.open(outFile, 'w');
// Write typescript's license
await stream.write(new TextEncoder().encode(`
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
The following is a minified version of the unmodified Typescript 5.2
***************************************************************************** */
`));
await stream.write(minified.code);
console.log('Typescript bundling done!');
}
finally {
// Clean up all stuff left from typescript bundling
await fs.rm('tmp', { recursive: true, force: true });
await fs.rm('package.json');
await fs.rm('package-lock.json');
await fs.rm('node_modules', { recursive: true });
}
}
async function compileJava(conf) {
try {
await fs.writeFile('Metadata.java', (await fs.readFile('src/me/topchetoeu/jscript/Metadata.java')).toString()
.replace('${VERSION}', conf.version)
.replace('${NAME}', conf.name)
.replace('${AUTHOR}', conf.author)
);
const args = ['--release', '11', ];
if (argv[2] === 'debug') args.push('-g');
args.push('-d', 'dst/classes', 'Metadata.java');
console.log('Compiling java project...');
for await (const path of find('src', undefined, v => v.endsWith('.java') && !v.endsWith('Metadata.java'))) args.push(path);
await run(false, conf.javahome + 'javac', ...args);
console.log('Compiled java project!');
}
finally {
await fs.rm('Metadata.java');
}
}
(async () => {
try {
if (argv[2] === 'init-ts') {
await downloadTypescript('src/assets/js/ts.js');
}
else {
const conf = {
name: "java-jscript",
author: "TopchetoEU",
javahome: "",
version: argv[3]
};
if (conf.version.startsWith('refs/tags/')) conf.version = conf.version.substring(10);
if (conf.version.startsWith('v')) conf.version = conf.version.substring(1);
try { await fs.rm('dst', { recursive: true }); } catch {}
await Promise.all([
downloadTypescript('dst/classes/assets/js/ts.js'),
copy('src', 'dst/classes', v => !v.endsWith('.java')),
compileJava(conf),
]);
await run(true, 'jar', '-c', '-f', 'dst/jscript.jar', '-e', 'me.topchetoeu.jscript.Main', '-C', 'dst/classes', '.');
console.log('Done!');
}
}
catch (e) {
if (argv[2] === 'debug') throw e;
console.log(e.toString());
exit(-1);
}
})();

View File

@@ -1,191 +0,0 @@
type PropertyDescriptor<T, ThisT> = {
value: any;
writable?: boolean;
enumerable?: boolean;
configurable?: boolean;
} | {
get?(this: ThisT): T;
set(this: ThisT, val: T): void;
enumerable?: boolean;
configurable?: boolean;
} | {
get(this: ThisT): T;
set?(this: ThisT, val: T): void;
enumerable?: boolean;
configurable?: boolean;
};
type Exclude<T, U> = T extends U ? never : T;
type Extract<T, U> = T extends U ? T : never;
type Record<KeyT extends string | number | symbol, ValT> = { [x in KeyT]: ValT }
interface IArguments {
[i: number]: any;
length: number;
}
interface MathObject {
readonly E: number;
readonly PI: number;
readonly SQRT2: number;
readonly SQRT1_2: number;
readonly LN2: number;
readonly LN10: number;
readonly LOG2E: number;
readonly LOG10E: number;
asin(x: number): number;
acos(x: number): number;
atan(x: number): number;
atan2(y: number, x: number): number;
asinh(x: number): number;
acosh(x: number): number;
atanh(x: number): number;
sin(x: number): number;
cos(x: number): number;
tan(x: number): number;
sinh(x: number): number;
cosh(x: number): number;
tanh(x: number): number;
sqrt(x: number): number;
cbrt(x: number): number;
hypot(...vals: number[]): number;
imul(a: number, b: number): number;
exp(x: number): number;
expm1(x: number): number;
pow(x: number, y: number): number;
log(x: number): number;
log10(x: number): number;
log1p(x: number): number;
log2(x: number): number;
ceil(x: number): number;
floor(x: number): number;
round(x: number): number;
fround(x: number): number;
trunc(x: number): number;
abs(x: number): number;
max(...vals: number[]): number;
min(...vals: number[]): number;
sign(x: number): number;
random(): number;
clz32(x: number): number;
}
//@ts-ignore
declare const arguments: IArguments;
declare const Math: MathObject;
declare var setTimeout: <T extends any[]>(handle: (...args: [ ...T, ...any[] ]) => void, delay?: number, ...args: T) => number;
declare var setInterval: <T extends any[]>(handle: (...args: [ ...T, ...any[] ]) => void, delay?: number, ...args: T) => number;
declare var clearTimeout: (id: number) => void;
declare var clearInterval: (id: number) => void;
/** @internal */
declare var internals: any;
/** @internal */
declare function run(file: string, pollute?: boolean): void;
/** @internal */
type ReplaceThis<T, ThisT> = T extends ((...args: infer ArgsT) => infer RetT) ?
((this: ThisT, ...args: ArgsT) => RetT) :
T;
/** @internal */
declare var setProps: <
TargetT extends object,
DescT extends { [x in Exclude<keyof TargetT, 'constructor'> ]?: ReplaceThis<TargetT[x], TargetT> }
>(target: TargetT, desc: DescT) => void;
/** @internal */
declare var setConstr: <ConstrT, T extends { constructor: ConstrT }>(target: T, constr: ConstrT) => void;
declare function log(...vals: any[]): void;
/** @internal */
declare var lgt: typeof globalThis, gt: typeof globalThis;
declare function assert(condition: () => unknown, message?: string): boolean;
gt.assert = (cond, msg) => {
try {
if (!cond()) throw 'condition not satisfied';
log('Passed ' + msg);
return true;
}
catch (e) {
log('Failed ' + msg + ' because of: ' + e);
return false;
}
}
try {
lgt.setProps = (target, desc) => {
var props = internals.keys(desc, false);
for (var i = 0; i < props.length; i++) {
var key = props[i];
internals.defineField(
target, key, (desc as any)[key],
true, // writable
false, // enumerable
true // configurable
);
}
}
lgt.setConstr = (target, constr) => {
internals.defineField(
target, 'constructor', constr,
true, // writable
false, // enumerable
true // configurable
);
}
run('values/object.js');
run('values/symbol.js');
run('values/function.js');
run('values/errors.js');
run('values/string.js');
run('values/number.js');
run('values/boolean.js');
run('values/array.js');
internals.special(Object, Function, Error, Array);
gt.setTimeout = (func, delay, ...args) => {
if (typeof func !== 'function') throw new TypeError("func must be a function.");
delay = (delay ?? 0) - 0;
return internals.setTimeout(() => func(...args), delay)
};
gt.setInterval = (func, delay, ...args) => {
if (typeof func !== 'function') throw new TypeError("func must be a function.");
delay = (delay ?? 0) - 0;
return internals.setInterval(() => func(...args), delay)
};
gt.clearTimeout = (id) => {
id = id | 0;
internals.clearTimeout(id);
};
gt.clearInterval = (id) => {
id = id | 0;
internals.clearInterval(id);
};
run('iterators.js');
run('promise.js');
run('map.js', true);
run('set.js', true);
run('regex.js');
run('require.js');
log('Loaded polyfills!');
}
catch (e: any) {
var err = 'Uncaught error while loading polyfills: ';
if (typeof Error !== 'undefined' && e instanceof Error && e.toString !== {}.toString) err += e;
else if ('message' in e) {
if ('name' in e) err += e.name + ": " + e.message;
else err += 'Error: ' + e.message;
}
else err += e;
}

View File

@@ -1,216 +0,0 @@
interface SymbolConstructor {
readonly iterator: unique symbol;
readonly asyncIterator: unique symbol;
}
type IteratorYieldResult<TReturn> =
{ done?: false; } &
(TReturn extends undefined ? { value?: undefined; } : { value: TReturn; });
type IteratorReturnResult<TReturn> =
{ done: true } &
(TReturn extends undefined ? { value?: undefined; } : { value: TReturn; });
type IteratorResult<T, TReturn = any> = IteratorYieldResult<T> | IteratorReturnResult<TReturn>;
interface Iterator<T, TReturn = any, TNext = undefined> {
next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
return?(value?: TReturn): IteratorResult<T, TReturn>;
throw?(e?: any): IteratorResult<T, TReturn>;
}
interface Iterable<T> {
[Symbol.iterator](): Iterator<T>;
}
interface IterableIterator<T> extends Iterator<T> {
[Symbol.iterator](): IterableIterator<T>;
}
interface Generator<T = unknown, TReturn = any, TNext = unknown> extends Iterator<T, TReturn, TNext> {
// NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places.
next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
return(value: TReturn): IteratorResult<T, TReturn>;
throw(e: any): IteratorResult<T, TReturn>;
[Symbol.iterator](): Generator<T, TReturn, TNext>;
}
interface GeneratorFunction {
/**
* Creates a new Generator object.
* @param args A list of arguments the function accepts.
*/
new (...args: any[]): Generator;
/**
* Creates a new Generator object.
* @param args A list of arguments the function accepts.
*/
(...args: any[]): Generator;
/**
* The length of the arguments.
*/
readonly length: number;
/**
* Returns the name of the function.
*/
readonly name: string;
/**
* A reference to the prototype.
*/
readonly prototype: Generator;
}
interface GeneratorFunctionConstructor {
/**
* Creates a new Generator function.
* @param args A list of arguments the function accepts.
*/
new (...args: string[]): GeneratorFunction;
/**
* Creates a new Generator function.
* @param args A list of arguments the function accepts.
*/
(...args: string[]): GeneratorFunction;
/**
* The length of the arguments.
*/
readonly length: number;
/**
* Returns the name of the function.
*/
readonly name: string;
/**
* A reference to the prototype.
*/
}
interface AsyncIterator<T, TReturn = any, TNext = undefined> {
// NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places.
next(...args: [] | [TNext]): Promise<IteratorResult<T, TReturn>>;
return?(value?: TReturn | Thenable<TReturn>): Promise<IteratorResult<T, TReturn>>;
throw?(e?: any): Promise<IteratorResult<T, TReturn>>;
}
interface AsyncIterable<T> {
[Symbol.asyncIterator](): AsyncIterator<T>;
}
interface AsyncIterableIterator<T> extends AsyncIterator<T> {
[Symbol.asyncIterator](): AsyncIterableIterator<T>;
}
interface AsyncGenerator<T = unknown, TReturn = any, TNext = unknown> extends AsyncIterator<T, TReturn, TNext> {
// NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places.
next(...args: [] | [TNext]): Promise<IteratorResult<T, TReturn>>;
return(value: TReturn | Thenable<TReturn>): Promise<IteratorResult<T, TReturn>>;
throw(e: any): Promise<IteratorResult<T, TReturn>>;
[Symbol.asyncIterator](): AsyncGenerator<T, TReturn, TNext>;
}
interface AsyncGeneratorFunction {
/**
* Creates a new AsyncGenerator object.
* @param args A list of arguments the function accepts.
*/
new (...args: any[]): AsyncGenerator;
/**
* Creates a new AsyncGenerator object.
* @param args A list of arguments the function accepts.
*/
(...args: any[]): AsyncGenerator;
/**
* The length of the arguments.
*/
readonly length: number;
/**
* Returns the name of the function.
*/
readonly name: string;
/**
* A reference to the prototype.
*/
readonly prototype: AsyncGenerator;
}
interface AsyncGeneratorFunctionConstructor {
/**
* Creates a new AsyncGenerator function.
* @param args A list of arguments the function accepts.
*/
new (...args: string[]): AsyncGeneratorFunction;
/**
* Creates a new AsyncGenerator function.
* @param args A list of arguments the function accepts.
*/
(...args: string[]): AsyncGeneratorFunction;
/**
* The length of the arguments.
*/
readonly length: number;
/**
* Returns the name of the function.
*/
readonly name: string;
/**
* A reference to the prototype.
*/
readonly prototype: AsyncGeneratorFunction;
}
interface Array<T> extends IterableIterator<T> {
entries(): IterableIterator<[number, T]>;
values(): IterableIterator<T>;
keys(): IterableIterator<number>;
}
setProps(Symbol, {
iterator: Symbol("Symbol.iterator") as any,
asyncIterator: Symbol("Symbol.asyncIterator") as any,
});
setProps(Array.prototype, {
[Symbol.iterator]: function() {
return this.values();
},
values() {
var i = 0;
return {
next: () => {
while (i < this.length) {
if (i++ in this) return { done: false, value: this[i - 1] };
}
return { done: true, value: undefined };
},
[Symbol.iterator]() { return this; }
};
},
keys() {
var i = 0;
return {
next: () => {
while (i < this.length) {
if (i++ in this) return { done: false, value: i - 1 };
}
return { done: true, value: undefined };
},
[Symbol.iterator]() { return this; }
};
},
entries() {
var i = 0;
return {
next: () => {
while (i < this.length) {
if (i++ in this) return { done: false, value: [i - 1, this[i - 1]] };
}
return { done: true, value: undefined };
},
[Symbol.iterator]() { return this; }
};
},
});

View File

@@ -1,44 +0,0 @@
declare class Map<KeyT, ValueT> {
public [Symbol.iterator](): IterableIterator<[KeyT, ValueT]>;
public clear(): void;
public delete(key: KeyT): boolean;
public entries(): IterableIterator<[KeyT, ValueT]>;
public keys(): IterableIterator<KeyT>;
public values(): IterableIterator<ValueT>;
public get(key: KeyT): ValueT;
public set(key: KeyT, val: ValueT): this;
public has(key: KeyT): boolean;
public get size(): number;
public forEach(func: (key: KeyT, val: ValueT, map: Map<KeyT, ValueT>) => void, thisArg?: any): void;
public constructor();
}
Map.prototype[Symbol.iterator] = function() {
return this.entries();
};
var entries = Map.prototype.entries;
var keys = Map.prototype.keys;
var values = Map.prototype.values;
Map.prototype.entries = function() {
var it = entries.call(this);
it[Symbol.iterator] = () => it;
return it;
};
Map.prototype.keys = function() {
var it = keys.call(this);
it[Symbol.iterator] = () => it;
return it;
};
Map.prototype.values = function() {
var it = values.call(this);
it[Symbol.iterator] = () => it;
return it;
};

View File

@@ -1,43 +0,0 @@
type PromiseFulfillFunc<T> = (val: T) => void;
type PromiseThenFunc<T, NextT> = (val: T) => NextT;
type PromiseRejectFunc = (err: unknown) => void;
type PromiseFunc<T> = (resolve: PromiseFulfillFunc<T>, reject: PromiseRejectFunc) => void;
type PromiseResult<T> ={ type: 'fulfilled'; value: T; } | { type: 'rejected'; reason: any; }
interface Thenable<T> {
then<NextT>(this: Promise<T>, onFulfilled: PromiseThenFunc<T, NextT>, onRejected?: PromiseRejectFunc): Promise<Awaited<NextT>>;
then(this: Promise<T>, onFulfilled: undefined, onRejected?: PromiseRejectFunc): Promise<T>;
}
// wippidy-wine, this code is now mine :D
type Awaited<T> =
T extends null | undefined ? T : // special case for `null | undefined` when not in `--strictNullChecks` mode
T extends object & { then(onfulfilled: infer F, ...args: infer _): any } ? // `await` only unwraps object types with a callable `then`. Non-object types are not unwrapped
F extends ((value: infer V, ...args: infer _) => any) ? // if the argument to `then` is callable, extracts the first argument
Awaited<V> : // recursively unwrap the value
never : // the argument to `then` was not callable
T;
interface PromiseConstructor {
prototype: Promise<any>;
new <T>(func: PromiseFunc<T>): Promise<Awaited<T>>;
resolve<T>(val: T): Promise<Awaited<T>>;
reject(val: any): Promise<never>;
any<T>(promises: (Promise<T>|T)[]): Promise<T>;
race<T>(promises: (Promise<T>|T)[]): Promise<T>;
all<T extends any[]>(promises: T): Promise<{ [Key in keyof T]: Awaited<T[Key]> }>;
allSettled<T extends any[]>(...promises: T): Promise<[...{ [P in keyof T]: PromiseResult<Awaited<T[P]>>}]>;
}
interface Promise<T> extends Thenable<T> {
constructor: PromiseConstructor;
catch(func: PromiseRejectFunc): Promise<T>;
finally(func: () => void): Promise<T>;
}
declare var Promise: PromiseConstructor;
(Promise.prototype as any)[Symbol.typeName] = 'Promise';

View File

@@ -1,211 +0,0 @@
interface RegExpResultIndices extends Array<[number, number]> {
groups?: { [name: string]: [number, number]; };
}
interface RegExpResult extends Array<string> {
groups?: { [name: string]: string; };
index: number;
input: string;
indices?: RegExpResultIndices;
escape(raw: string, flags: string): RegExp;
}
interface SymbolConstructor {
readonly match: unique symbol;
readonly matchAll: unique symbol;
readonly split: unique symbol;
readonly replace: unique symbol;
readonly search: unique symbol;
}
type ReplaceFunc = (match: string, ...args: any[]) => string;
interface Matcher {
[Symbol.match](target: string): RegExpResult | string[] | null;
[Symbol.matchAll](target: string): IterableIterator<RegExpResult>;
}
interface Splitter {
[Symbol.split](target: string, limit?: number, sensible?: boolean): string[];
}
interface Replacer {
[Symbol.replace](target: string, replacement: string): string;
}
interface Searcher {
[Symbol.search](target: string, reverse?: boolean, start?: number): number;
}
declare class RegExp implements Matcher, Splitter, Replacer, Searcher {
static escape(raw: any, flags?: string): RegExp;
prototype: RegExp;
exec(val: string): RegExpResult | null;
test(val: string): boolean;
toString(): string;
[Symbol.match](target: string): RegExpResult | string[] | null;
[Symbol.matchAll](target: string): IterableIterator<RegExpResult>;
[Symbol.split](target: string, limit?: number, sensible?: boolean): string[];
[Symbol.replace](target: string, replacement: string | ReplaceFunc): string;
[Symbol.search](target: string, reverse?: boolean, start?: number): number;
readonly dotAll: boolean;
readonly global: boolean;
readonly hasIndices: boolean;
readonly ignoreCase: boolean;
readonly multiline: boolean;
readonly sticky: boolean;
readonly unicode: boolean;
readonly source: string;
readonly flags: string;
lastIndex: number;
constructor(pattern?: string, flags?: string);
constructor(pattern?: RegExp, flags?: string);
}
(Symbol as any).replace = Symbol('Symbol.replace');
(Symbol as any).match = Symbol('Symbol.match');
(Symbol as any).matchAll = Symbol('Symbol.matchAll');
(Symbol as any).split = Symbol('Symbol.split');
(Symbol as any).search = Symbol('Symbol.search');
setProps(RegExp.prototype, {
[Symbol.typeName]: 'RegExp',
test(val) {
return !!this.exec(val);
},
toString() {
return '/' + this.source + '/' + this.flags;
},
[Symbol.match](target) {
if (this.global) {
const res: string[] = [];
let val;
while (val = this.exec(target)) {
res.push(val[0]);
}
this.lastIndex = 0;
return res;
}
else {
const res = this.exec(target);
if (!this.sticky) this.lastIndex = 0;
return res;
}
},
[Symbol.matchAll](target) {
let pattern: RegExp | undefined = new this.constructor(this, this.flags + "g") as RegExp;
return {
next: (): IteratorResult<RegExpResult, undefined> => {
const val = pattern?.exec(target);
if (val === null || val === undefined) {
pattern = undefined;
return { done: true };
}
else return { value: val };
},
[Symbol.iterator]() { return this; }
}
},
[Symbol.split](target, limit, sensible) {
const pattern = new this.constructor(this, this.flags + "g") as RegExp;
let match: RegExpResult | null;
let lastEnd = 0;
const res: string[] = [];
while ((match = pattern.exec(target)) !== null) {
let added: string[] = [];
if (match.index >= target.length) break;
if (match[0].length === 0) {
added = [ target.substring(lastEnd, pattern.lastIndex), ];
if (pattern.lastIndex < target.length) added.push(...match.slice(1));
}
else if (match.index - lastEnd > 0) {
added = [ target.substring(lastEnd, match.index), ...match.slice(1) ];
}
else {
for (let i = 1; i < match.length; i++) {
res[res.length - match.length + i] = match[i];
}
}
if (sensible) {
if (limit !== undefined && res.length + added.length >= limit) break;
else res.push(...added);
}
else {
for (let i = 0; i < added.length; i++) {
if (limit !== undefined && res.length >= limit) return res;
else res.push(added[i]);
}
}
lastEnd = pattern.lastIndex;
}
if (lastEnd < target.length) {
res.push(target.substring(lastEnd));
}
return res;
},
[Symbol.replace](target, replacement) {
const pattern = new this.constructor(this, this.flags + "d") as RegExp;
let match: RegExpResult | null;
let lastEnd = 0;
const res: string[] = [];
// log(pattern.toString());
while ((match = pattern.exec(target)) !== null) {
const indices = match.indices![0];
res.push(target.substring(lastEnd, indices[0]));
if (replacement instanceof Function) {
res.push(replacement(target.substring(indices[0], indices[1]), ...match.slice(1), indices[0], target));
}
else {
res.push(replacement);
}
lastEnd = indices[1];
if (!pattern.global) break;
}
if (lastEnd < target.length) {
res.push(target.substring(lastEnd));
}
return res.join('');
},
[Symbol.search](target, reverse, start) {
const pattern: RegExp | undefined = new this.constructor(this, this.flags + "g") as RegExp;
if (!reverse) {
pattern.lastIndex = (start as any) | 0;
const res = pattern.exec(target);
if (res) return res.index;
else return -1;
}
else {
start ??= target.length;
start |= 0;
let res: RegExpResult | null = null;
while (true) {
const tmp = pattern.exec(target);
if (tmp === null || tmp.index > start) break;
res = tmp;
}
if (res && res.index <= start) return res.index;
else return -1;
}
},
});

View File

@@ -1,15 +0,0 @@
type RequireFunc = (path: string) => any;
interface Module {
exports: any;
name: string;
}
declare var require: RequireFunc;
declare var exports: any;
declare var module: Module;
gt.require = function(path: string) {
if (typeof path !== 'string') path = path + '';
return internals.require(path);
};

View File

@@ -1,45 +0,0 @@
declare class Set<T> {
public [Symbol.iterator](): IterableIterator<T>;
public entries(): IterableIterator<[T, T]>;
public keys(): IterableIterator<T>;
public values(): IterableIterator<T>;
public clear(): void;
public add(val: T): this;
public delete(val: T): boolean;
public has(key: T): boolean;
public get size(): number;
public forEach(func: (key: T, set: Set<T>) => void, thisArg?: any): void;
public constructor();
}
Set.prototype[Symbol.iterator] = function() {
return this.values();
};
(() => {
var entries = Set.prototype.entries;
var keys = Set.prototype.keys;
var values = Set.prototype.values;
Set.prototype.entries = function() {
var it = entries.call(this);
it[Symbol.iterator] = () => it;
return it;
};
Set.prototype.keys = function() {
var it = keys.call(this);
it[Symbol.iterator] = () => it;
return it;
};
Set.prototype.values = function() {
var it = values.call(this);
it[Symbol.iterator] = () => it;
return it;
};
})();

View File

@@ -1,369 +0,0 @@
// god this is awful
type FlatArray<Arr, Depth extends number> = {
"done": Arr,
"recur": Arr extends Array<infer InnerArr>
? FlatArray<InnerArr, [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20][Depth]>
: Arr
}[Depth extends -1 ? "done" : "recur"];
interface Array<T> {
[i: number]: T;
constructor: ArrayConstructor;
length: number;
toString(): string;
// toLocaleString(): string;
join(separator?: string): string;
fill(val: T, start?: number, end?: number): T[];
pop(): T | undefined;
push(...items: T[]): number;
concat(...items: (T | T[])[]): T[];
concat(...items: (T | T[])[]): T[];
join(separator?: string): string;
reverse(): T[];
shift(): T | undefined;
slice(start?: number, end?: number): T[];
sort(compareFn?: (a: T, b: T) => number): this;
splice(start: number, deleteCount?: number | undefined, ...items: T[]): T[];
unshift(...items: T[]): number;
indexOf(searchElement: T, fromIndex?: number): number;
lastIndexOf(searchElement: T, fromIndex?: number): number;
every(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean;
some(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean;
forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void;
includes(el: any, start?: number): boolean;
map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];
filter(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): T[];
find(predicate: (value: T, index: number, array: T[]) => boolean, thisArg?: any): T[];
findIndex(predicate: (value: T, index: number, array: T[]) => boolean, thisArg?: any): number;
findLast(predicate: (value: T, index: number, array: T[]) => boolean, thisArg?: any): T[];
findLastIndex(predicate: (value: T, index: number, array: T[]) => boolean, thisArg?: any): number;
flat<D extends number = 1>(depth?: D): FlatArray<T, D>;
flatMap(func: (val: T, i: number, arr: T[]) => T | T[], thisAarg?: any): FlatArray<T[], 1>;
sort(func?: (a: T, b: T) => number): this;
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T;
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
reduce<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U;
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T;
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
reduceRight<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U;
}
interface ArrayConstructor {
new <T>(arrayLength?: number): T[];
new <T>(...items: T[]): T[];
<T>(arrayLength?: number): T[];
<T>(...items: T[]): T[];
isArray(arg: any): arg is any[];
prototype: Array<any>;
}
declare var Array: ArrayConstructor;
gt.Array = function(len?: number) {
var res = [];
if (typeof len === 'number' && arguments.length === 1) {
if (len < 0) throw 'Invalid array length.';
res.length = len;
}
else {
for (var i = 0; i < arguments.length; i++) {
res[i] = arguments[i];
}
}
return res;
} as ArrayConstructor;
Array.prototype = ([] as any).__proto__ as Array<any>;
setConstr(Array.prototype, Array);
function wrapI(max: number, i: number) {
i |= 0;
if (i < 0) i = max + i;
return i;
}
function clampI(max: number, i: number) {
if (i < 0) i = 0;
if (i > max) i = max;
return i;
}
lgt.wrapI = wrapI;
lgt.clampI = clampI;
(Array.prototype as any)[Symbol.typeName] = "Array";
setProps(Array.prototype, {
concat() {
var res = [] as any[];
res.push.apply(res, this);
for (var i = 0; i < arguments.length; i++) {
var arg = arguments[i];
if (arg instanceof Array) {
res.push.apply(res, arg);
}
else {
res.push(arg);
}
}
return res;
},
every(func, thisArg) {
if (typeof func !== 'function') throw new TypeError("Given argument not a function.");
func = func.bind(thisArg);
for (var i = 0; i < this.length; i++) {
if (!func(this[i], i, this)) return false;
}
return true;
},
some(func, thisArg) {
if (typeof func !== 'function') throw new TypeError("Given argument not a function.");
func = func.bind(thisArg);
for (var i = 0; i < this.length; i++) {
if (func(this[i], i, this)) return true;
}
return false;
},
fill(val, start, end) {
if (arguments.length < 3) end = this.length;
if (arguments.length < 2) start = 0;
start = clampI(this.length, wrapI(this.length + 1, start ?? 0));
end = clampI(this.length, wrapI(this.length + 1, end ?? this.length));
for (; start < end; start++) {
this[start] = val;
}
return this;
},
filter(func, thisArg) {
if (typeof func !== 'function') throw new TypeError("Given argument is not a function.");
var res = [];
for (var i = 0; i < this.length; i++) {
if (i in this && func.call(thisArg, this[i], i, this)) res.push(this[i]);
}
return res;
},
find(func, thisArg) {
if (typeof func !== 'function') throw new TypeError("Given argument is not a function.");
for (var i = 0; i < this.length; i++) {
if (i in this && func.call(thisArg, this[i], i, this)) return this[i];
}
return undefined;
},
findIndex(func, thisArg) {
if (typeof func !== 'function') throw new TypeError("Given argument is not a function.");
for (var i = 0; i < this.length; i++) {
if (i in this && func.call(thisArg, this[i], i, this)) return i;
}
return -1;
},
findLast(func, thisArg) {
if (typeof func !== 'function') throw new TypeError("Given argument is not a function.");
for (var i = this.length - 1; i >= 0; i--) {
if (i in this && func.call(thisArg, this[i], i, this)) return this[i];
}
return undefined;
},
findLastIndex(func, thisArg) {
if (typeof func !== 'function') throw new TypeError("Given argument is not a function.");
for (var i = this.length - 1; i >= 0; i--) {
if (i in this && func.call(thisArg, this[i], i, this)) return i;
}
return -1;
},
flat(depth) {
var res = [] as any[];
var buff = [];
res.push(...this);
for (var i = 0; i < (depth ?? 1); i++) {
var anyArrays = false;
for (var el of res) {
if (el instanceof Array) {
buff.push(...el);
anyArrays = true;
}
else buff.push(el);
}
res = buff;
buff = [];
if (!anyArrays) break;
}
return res;
},
flatMap(func, th) {
return this.map(func, th).flat();
},
forEach(func, thisArg) {
for (var i = 0; i < this.length; i++) {
if (i in this) func.call(thisArg, this[i], i, this);
}
},
map(func, thisArg) {
if (typeof func !== 'function') throw new TypeError("Given argument is not a function.");
var res = [];
for (var i = 0; i < this.length; i++) {
if (i in this) res[i] = func.call(thisArg, this[i], i, this);
}
return res;
},
pop() {
if (this.length === 0) return undefined;
var val = this[this.length - 1];
this.length--;
return val;
},
push() {
for (var i = 0; i < arguments.length; i++) {
this[this.length] = arguments[i];
}
return arguments.length;
},
shift() {
if (this.length === 0) return undefined;
var res = this[0];
for (var i = 0; i < this.length - 1; i++) {
this[i] = this[i + 1];
}
this.length--;
return res;
},
unshift() {
for (var i = this.length - 1; i >= 0; i--) {
this[i + arguments.length] = this[i];
}
for (var i = 0; i < arguments.length; i++) {
this[i] = arguments[i];
}
return arguments.length;
},
slice(start, end) {
start = clampI(this.length, wrapI(this.length + 1, start ?? 0));
end = clampI(this.length, wrapI(this.length + 1, end ?? this.length));
var res: any[] = [];
var n = end - start;
if (n <= 0) return res;
for (var i = 0; i < n; i++) {
res[i] = this[start + i];
}
return res;
},
toString() {
let res = '';
for (let i = 0; i < this.length; i++) {
if (i > 0) res += ',';
if (i in this && this[i] !== undefined && this[i] !== null) res += this[i];
}
return res;
},
indexOf(el, start) {
start = start! | 0;
for (var i = Math.max(0, start); i < this.length; i++) {
if (i in this && this[i] == el) return i;
}
return -1;
},
lastIndexOf(el, start) {
start = start! | 0;
for (var i = this.length; i >= start; i--) {
if (i in this && this[i] == el) return i;
}
return -1;
},
includes(el, start) {
return this.indexOf(el, start) >= 0;
},
join(val = ',') {
let res = '', first = true;
for (let i = 0; i < this.length; i++) {
if (!(i in this)) continue;
if (!first) res += val;
first = false;
res += this[i];
}
return res;
},
sort(func) {
func ??= (a, b) => {
const _a = a + '';
const _b = b + '';
if (_a > _b) return 1;
if (_a < _b) return -1;
return 0;
};
if (typeof func !== 'function') throw new TypeError('Expected func to be undefined or a function.');
internals.sort(this, func);
return this;
},
splice(start, deleteCount, ...items) {
start = clampI(this.length, wrapI(this.length, start ?? 0));
deleteCount = (deleteCount ?? Infinity | 0);
if (start + deleteCount >= this.length) deleteCount = this.length - start;
const res = this.slice(start, start + deleteCount);
const moveN = items.length - deleteCount;
const len = this.length;
if (moveN < 0) {
for (let i = start - moveN; i < len; i++) {
this[i + moveN] = this[i];
}
}
else if (moveN > 0) {
for (let i = len - 1; i >= start; i--) {
this[i + moveN] = this[i];
}
}
for (let i = 0; i < items.length; i++) {
this[i + start] = items[i];
}
this.length = len + moveN;
return res;
}
});
setProps(Array, {
isArray(val: any) { return internals.isArr(val); }
});

View File

@@ -1,22 +0,0 @@
interface Boolean {
valueOf(): boolean;
constructor: BooleanConstructor;
}
interface BooleanConstructor {
(val: any): boolean;
new (val: any): Boolean;
prototype: Boolean;
}
declare var Boolean: BooleanConstructor;
gt.Boolean = function (this: Boolean | undefined, arg) {
var val;
if (arguments.length === 0) val = false;
else val = !!arg;
if (this === undefined || this === null) return val;
else (this as any).value = val;
} as BooleanConstructor;
Boolean.prototype = (false as any).__proto__ as Boolean;
setConstr(Boolean.prototype, Boolean);

View File

@@ -1,89 +0,0 @@
interface Error {
constructor: ErrorConstructor;
name: string;
message: string;
stack: string[];
}
interface ErrorConstructor {
(msg?: any): Error;
new (msg?: any): Error;
prototype: Error;
}
interface TypeErrorConstructor extends ErrorConstructor {
(msg?: any): TypeError;
new (msg?: any): TypeError;
prototype: Error;
}
interface TypeError extends Error {
constructor: TypeErrorConstructor;
name: 'TypeError';
}
interface RangeErrorConstructor extends ErrorConstructor {
(msg?: any): RangeError;
new (msg?: any): RangeError;
prototype: Error;
}
interface RangeError extends Error {
constructor: RangeErrorConstructor;
name: 'RangeError';
}
interface SyntaxErrorConstructor extends ErrorConstructor {
(msg?: any): RangeError;
new (msg?: any): RangeError;
prototype: Error;
}
interface SyntaxError extends Error {
constructor: SyntaxErrorConstructor;
name: 'SyntaxError';
}
declare var Error: ErrorConstructor;
declare var RangeError: RangeErrorConstructor;
declare var TypeError: TypeErrorConstructor;
declare var SyntaxError: SyntaxErrorConstructor;
gt.Error = function Error(msg: string) {
if (msg === undefined) msg = '';
else msg += '';
return Object.setPrototypeOf({
message: msg,
stack: [] as string[],
}, Error.prototype);
} as ErrorConstructor;
Error.prototype = internals.err ?? {};
Error.prototype.name = 'Error';
setConstr(Error.prototype, Error);
Error.prototype.toString = function() {
if (!(this instanceof Error)) return '';
if (this.message === '') return this.name;
else return this.name + ': ' + this.message;
};
function makeError<T extends ErrorConstructor>(name: string, proto: any): T {
var err = function (msg: string) {
var res = new Error(msg);
(res as any).__proto__ = err.prototype;
return res;
} as T;
err.prototype = proto;
err.prototype.name = name;
setConstr(err.prototype, err as ErrorConstructor);
(err.prototype as any).__proto__ = Error.prototype;
(err as any).__proto__ = Error;
internals.special(err);
return err;
}
gt.RangeError = makeError('RangeError', internals.range ?? {});
gt.TypeError = makeError('TypeError', internals.type ?? {});
gt.SyntaxError = makeError('SyntaxError', internals.syntax ?? {});

View File

@@ -1,181 +0,0 @@
interface Function {
apply(this: Function, thisArg: any, argArray?: any): any;
call(this: Function, thisArg: any, ...argArray: any[]): any;
bind(this: Function, thisArg: any, ...argArray: any[]): Function;
toString(): string;
prototype: any;
constructor: FunctionConstructor;
readonly length: number;
name: string;
}
interface FunctionConstructor extends Function {
(...args: string[]): (...args: any[]) => any;
new (...args: string[]): (...args: any[]) => any;
prototype: Function;
async<ArgsT extends any[], RetT>(
func: (await: <T>(val: T) => Awaited<T>) => (...args: ArgsT) => RetT
): (...args: ArgsT) => Promise<RetT>;
asyncGenerator<ArgsT extends any[], RetT>(
func: (await: <T>(val: T) => Awaited<T>, _yield: <T>(val: T) => void) => (...args: ArgsT) => RetT
): (...args: ArgsT) => AsyncGenerator<RetT>;
generator<ArgsT extends any[], T = unknown, RetT = unknown, TNext = unknown>(
func: (_yield: <T>(val: T) => TNext) => (...args: ArgsT) => RetT
): (...args: ArgsT) => Generator<T, RetT, TNext>;
}
interface CallableFunction extends Function {
(...args: any[]): any;
apply<ThisArg, Args extends any[], RetT>(this: (this: ThisArg, ...args: Args) => RetT, thisArg: ThisArg, argArray?: Args): RetT;
call<ThisArg, Args extends any[], RetT>(this: (this: ThisArg, ...args: Args) => RetT, thisArg: ThisArg, ...argArray: Args): RetT;
bind<ThisArg, Args extends any[], Rest extends any[], RetT>(this: (this: ThisArg, ...args: [ ...Args, ...Rest ]) => RetT, thisArg: ThisArg, ...argArray: Args): (this: void, ...args: Rest) => RetT;
}
interface NewableFunction extends Function {
new(...args: any[]): any;
apply<Args extends any[], RetT>(this: new (...args: Args) => RetT, thisArg: any, argArray?: Args): RetT;
call<Args extends any[], RetT>(this: new (...args: Args) => RetT, thisArg: any, ...argArray: Args): RetT;
bind<Args extends any[], RetT>(this: new (...args: Args) => RetT, thisArg: any, ...argArray: Args): new (...args: Args) => RetT;
}
declare var Function: FunctionConstructor;
gt.Function = function() {
throw 'Using the constructor Function() is forbidden.';
} as unknown as FunctionConstructor;
Function.prototype = (Function as any).__proto__ as Function;
setConstr(Function.prototype, Function);
setProps(Function.prototype, {
apply(thisArg, args) {
if (typeof args !== 'object') throw 'Expected arguments to be an array-like object.';
var len = args.length - 0;
let newArgs: any[];
if (Array.isArray(args)) newArgs = args;
else {
newArgs = [];
while (len >= 0) {
len--;
newArgs[len] = args[len];
}
}
return internals.apply(this, thisArg, newArgs);
},
call(thisArg, ...args) {
return this.apply(thisArg, args);
},
bind(thisArg, ...args) {
var func = this;
var res = function() {
var resArgs = [];
for (var i = 0; i < args.length; i++) {
resArgs[i] = args[i];
}
for (var i = 0; i < arguments.length; i++) {
resArgs[i + args.length] = arguments[i];
}
return func.apply(thisArg, resArgs);
};
res.name = "<bound> " + func.name;
return res;
},
toString() {
return 'function (...) { ... }';
},
});
setProps(Function, {
async(func) {
if (typeof func !== 'function') throw new TypeError('Expected func to be function.');
return function (this: any) {
const args = arguments;
return new Promise((res, rej) => {
const gen = Function.generator(func as any).apply(this, args as any);
(function next(type: 'none' | 'err' | 'ret', val?: any) {
try {
let result;
switch (type) {
case 'err': result = gen.throw(val); break;
case 'ret': result = gen.next(val); break;
case 'none': result = gen.next(); break;
}
if (result.done) res(result.value);
else Promise.resolve(result.value).then(
v => next('ret', v),
v => next('err', v)
)
}
catch (e) {
rej(e);
}
})('none');
});
};
},
asyncGenerator(func) {
if (typeof func !== 'function') throw new TypeError('Expected func to be function.');
return function(this: any) {
const gen = Function.generator<any[], ['await' | 'yield', any]>((_yield) => func(
val => _yield(['await', val]) as any,
val => _yield(['yield', val])
)).apply(this, arguments as any);
const next = (resolve: Function, reject: Function, type: 'none' | 'val' | 'ret' | 'err', val?: any) => {
let res;
try {
switch (type) {
case 'val': res = gen.next(val); break;
case 'ret': res = gen.return(val); break;
case 'err': res = gen.throw(val); break;
default: res = gen.next(); break;
}
}
catch (e) { return reject(e); }
if (res.done) return { done: true, res: <any>res };
else if (res.value[0] === 'await') Promise.resolve(res.value[1]).then(
v => next(resolve, reject, 'val', v),
v => next(resolve, reject, 'err', v),
)
else resolve({ done: false, value: res.value[1] });
};
return {
next() {
const args = arguments;
if (arguments.length === 0) return new Promise((res, rej) => next(res, rej, 'none'));
else return new Promise((res, rej) => next(res, rej, 'val', args[0]));
},
return: (value) => new Promise((res, rej) => next(res, rej, 'ret', value)),
throw: (value) => new Promise((res, rej) => next(res, rej, 'err', value)),
[Symbol.asyncIterator]() { return this; }
}
}
},
generator(func) {
if (typeof func !== 'function') throw new TypeError('Expected func to be function.');
const gen = internals.makeGenerator(func);
return (...args: any[]) => {
const it = gen(args);
return {
next: it.next,
return: it.return,
throw: it.throw,
[Symbol.iterator]() { return this; }
}
}
}
})

View File

@@ -1,50 +0,0 @@
interface Number {
toString(): string;
valueOf(): number;
constructor: NumberConstructor;
}
interface NumberConstructor {
(val: any): number;
new (val: any): Number;
prototype: Number;
parseInt(val: unknown): number;
parseFloat(val: unknown): number;
}
declare var Number: NumberConstructor;
declare var parseInt: typeof Number.parseInt;
declare var parseFloat: typeof Number.parseFloat;
declare var NaN: number;
declare var Infinity: number;
gt.Number = function(this: Number | undefined, arg: any) {
var val;
if (arguments.length === 0) val = 0;
else val = arg - 0;
if (this === undefined || this === null) return val;
else (this as any).value = val;
} as NumberConstructor;
Number.prototype = (0 as any).__proto__ as Number;
setConstr(Number.prototype, Number);
setProps(Number.prototype, {
valueOf() {
if (typeof this === 'number') return this;
else return (this as any).value;
},
toString() {
if (typeof this === 'number') return this + '';
else return (this as any).value + '';
}
});
setProps(Number, {
parseInt(val) { return Math.trunc(Number.parseFloat(val)); },
parseFloat(val) { return internals.parseFloat(val); },
});
Object.defineProperty(gt, 'parseInt', { value: Number.parseInt, writable: false });
Object.defineProperty(gt, 'parseFloat', { value: Number.parseFloat, writable: false });
Object.defineProperty(gt, 'NaN', { value: 0 / 0, writable: false });
Object.defineProperty(gt, 'Infinity', { value: 1 / 0, writable: false });

View File

@@ -1,234 +0,0 @@
interface Object {
constructor: NewableFunction;
[Symbol.typeName]: string;
valueOf(): this;
toString(): string;
hasOwnProperty(key: any): boolean;
}
interface ObjectConstructor extends Function {
(arg: string): String;
(arg: number): Number;
(arg: boolean): Boolean;
(arg?: undefined | null): {};
<T extends object>(arg: T): T;
new (arg: string): String;
new (arg: number): Number;
new (arg: boolean): Boolean;
new (arg?: undefined | null): {};
new <T extends object>(arg: T): T;
prototype: Object;
assign<T extends object>(target: T, ...src: object[]): T;
create<T extends object>(proto: T, props?: { [key: string]: PropertyDescriptor<any, T> }): T;
keys<T extends object>(obj: T, onlyString?: true): (keyof T)[];
keys<T extends object>(obj: T, onlyString: false): any[];
entries<T extends object>(obj: T, onlyString?: true): [keyof T, T[keyof T]][];
entries<T extends object>(obj: T, onlyString: false): [any, any][];
values<T extends object>(obj: T, onlyString?: true): (T[keyof T])[];
values<T extends object>(obj: T, onlyString: false): any[];
fromEntries(entries: Iterable<[any, any]>): object;
defineProperty<T, ThisT extends object>(obj: ThisT, key: any, desc: PropertyDescriptor<T, ThisT>): ThisT;
defineProperties<ThisT extends object>(obj: ThisT, desc: { [key: string]: PropertyDescriptor<any, ThisT> }): ThisT;
getOwnPropertyNames<T extends object>(obj: T): (keyof T)[];
getOwnPropertySymbols<T extends object>(obj: T): (keyof T)[];
hasOwn<T extends object, KeyT>(obj: T, key: KeyT): boolean;
getOwnPropertyDescriptor<T extends object, KeyT extends keyof T>(obj: T, key: KeyT): PropertyDescriptor<T[KeyT], T>;
getOwnPropertyDescriptors<T extends object>(obj: T): { [x in keyof T]: PropertyDescriptor<T[x], T> };
getPrototypeOf(obj: any): object | null;
setPrototypeOf<T>(obj: T, proto: object | null): T;
preventExtensions<T extends object>(obj: T): T;
seal<T extends object>(obj: T): T;
freeze<T extends object>(obj: T): T;
isExtensible(obj: object): boolean;
isSealed(obj: object): boolean;
isFrozen(obj: object): boolean;
}
declare var Object: ObjectConstructor;
gt.Object = function(arg: any) {
if (arg === undefined || arg === null) return {};
else if (typeof arg === 'boolean') return new Boolean(arg);
else if (typeof arg === 'number') return new Number(arg);
else if (typeof arg === 'string') return new String(arg);
return arg;
} as ObjectConstructor;
Object.prototype = ({} as any).__proto__ as Object;
setConstr(Object.prototype, Object as any);
function throwNotObject(obj: any, name: string) {
if (obj === null || typeof obj !== 'object' && typeof obj !== 'function') {
throw new TypeError(`Object.${name} may only be used for objects.`);
}
}
function check(obj: any) {
return typeof obj === 'object' && obj !== null || typeof obj === 'function';
}
setProps(Object, {
assign: function(dst, ...src) {
throwNotObject(dst, 'assign');
for (let i = 0; i < src.length; i++) {
const obj = src[i];
throwNotObject(obj, 'assign');
for (const key of Object.keys(obj)) {
(dst as any)[key] = (obj as any)[key];
}
}
return dst;
},
create(obj, props) {
props ??= {};
return Object.defineProperties({ __proto__: obj }, props as any) as any;
},
defineProperty(obj, key, attrib) {
throwNotObject(obj, 'defineProperty');
if (typeof attrib !== 'object') throw new TypeError('Expected attributes to be an object.');
if ('value' in attrib) {
if ('get' in attrib || 'set' in attrib) throw new TypeError('Cannot specify a value and accessors for a property.');
if (!internals.defineField(
obj, key,
attrib.value,
!!attrib.writable,
!!attrib.enumerable,
!!attrib.configurable
)) throw new TypeError('Can\'t define property \'' + key + '\'.');
}
else {
if (typeof attrib.get !== 'function' && attrib.get !== undefined) throw new TypeError('Get accessor must be a function.');
if (typeof attrib.set !== 'function' && attrib.set !== undefined) throw new TypeError('Set accessor must be a function.');
if (!internals.defineProp(
obj, key,
attrib.get,
attrib.set,
!!attrib.enumerable,
!!attrib.configurable
)) throw new TypeError('Can\'t define property \'' + key + '\'.');
}
return obj;
},
defineProperties(obj, attrib) {
throwNotObject(obj, 'defineProperties');
if (typeof attrib !== 'object' && typeof attrib !== 'function') throw 'Expected second argument to be an object.';
for (var key in attrib) {
Object.defineProperty(obj, key, attrib[key]);
}
return obj;
},
keys(obj, onlyString) {
onlyString = !!(onlyString ?? true);
return internals.keys(obj, onlyString);
},
entries(obj, onlyString) {
return Object.keys(obj, onlyString).map(v => [ v, (obj as any)[v] ]);
},
values(obj, onlyString) {
return Object.keys(obj, onlyString).map(v => (obj as any)[v]);
},
getOwnPropertyDescriptor(obj, key) {
return internals.ownProp(obj, key);
},
getOwnPropertyDescriptors(obj) {
return Object.fromEntries([
...Object.getOwnPropertyNames(obj),
...Object.getOwnPropertySymbols(obj)
].map(v => [ v, Object.getOwnPropertyDescriptor(obj, v) ])) as any;
},
getOwnPropertyNames(obj) {
return internals.ownPropKeys(obj, false);
},
getOwnPropertySymbols(obj) {
return internals.ownPropKeys(obj, true);
},
hasOwn(obj, key) {
if (Object.getOwnPropertyNames(obj).includes(key)) return true;
if (Object.getOwnPropertySymbols(obj).includes(key)) return true;
return false;
},
getPrototypeOf(obj) {
return obj.__proto__;
},
setPrototypeOf(obj, proto) {
(obj as any).__proto__ = proto;
return obj;
},
fromEntries(iterable) {
const res = {} as any;
for (const el of iterable) {
res[el[0]] = el[1];
}
return res;
},
preventExtensions(obj) {
throwNotObject(obj, 'preventExtensions');
internals.preventExtensions(obj);
return obj;
},
seal(obj) {
throwNotObject(obj, 'seal');
internals.seal(obj);
return obj;
},
freeze(obj) {
throwNotObject(obj, 'freeze');
internals.freeze(obj);
return obj;
},
isExtensible(obj) {
if (!check(obj)) return false;
return internals.extensible(obj);
},
isSealed(obj) {
if (!check(obj)) return true;
if (Object.isExtensible(obj)) return false;
return Object.getOwnPropertyNames(obj).every(v => !Object.getOwnPropertyDescriptor(obj, v).configurable);
},
isFrozen(obj) {
if (!check(obj)) return true;
if (Object.isExtensible(obj)) return false;
return Object.getOwnPropertyNames(obj).every(v => {
var prop = Object.getOwnPropertyDescriptor(obj, v);
if ('writable' in prop && prop.writable) return false;
return !prop.configurable;
});
}
});
setProps(Object.prototype, {
valueOf() {
return this;
},
toString() {
return '[object ' + (this[Symbol.typeName] ?? 'Unknown') + ']';
},
hasOwnProperty(key) {
return Object.hasOwn(this, key);
},
});

View File

@@ -1,261 +0,0 @@
interface Replacer {
[Symbol.replace](target: string, val: string | ((match: string, ...args: any[]) => string)): string;
}
interface String {
[i: number]: string;
toString(): string;
valueOf(): string;
charAt(pos: number): string;
charCodeAt(pos: number): number;
substring(start?: number, end?: number): string;
slice(start?: number, end?: number): string;
substr(start?: number, length?: number): string;
startsWith(str: string, pos?: number): string;
endsWith(str: string, pos?: number): string;
replace(pattern: string | Replacer, val: string): string;
replaceAll(pattern: string | Replacer, val: string): string;
match(pattern: string | Matcher): RegExpResult | string[] | null;
matchAll(pattern: string | Matcher): IterableIterator<RegExpResult>;
split(pattern: string | Splitter, limit?: number, sensible?: boolean): string;
concat(...others: string[]): string;
indexOf(term: string | Searcher, start?: number): number;
lastIndexOf(term: string | Searcher, start?: number): number;
toLowerCase(): string;
toUpperCase(): string;
trim(): string;
includes(term: string, start?: number): boolean;
length: number;
constructor: StringConstructor;
}
interface StringConstructor {
(val: any): string;
new (val: any): String;
fromCharCode(val: number): string;
prototype: String;
}
declare var String: StringConstructor;
gt.String = function(this: String | undefined, arg: any) {
var val;
if (arguments.length === 0) val = '';
else val = arg + '';
if (this === undefined || this === null) return val;
else (this as any).value = val;
} as StringConstructor;
String.prototype = ('' as any).__proto__ as String;
setConstr(String.prototype, String);
setProps(String.prototype, {
toString() {
if (typeof this === 'string') return this;
else return (this as any).value;
},
valueOf() {
if (typeof this === 'string') return this;
else return (this as any).value;
},
substring(start, end) {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.substring(start, end);
else throw new Error('This function may be used only with primitive or object strings.');
}
start = start ?? 0 | 0;
end = (end ?? this.length) | 0;
return internals.substring(this, start, end);
},
substr(start, length) {
start = start ?? 0 | 0;
if (start >= this.length) start = this.length - 1;
if (start < 0) start = 0;
length = (length ?? this.length - start) | 0;
return this.substring(start, length + start);
},
toLowerCase() {
return internals.toLower(this + '');
},
toUpperCase() {
return internals.toUpper(this + '');
},
charAt(pos) {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.charAt(pos);
else throw new Error('This function may be used only with primitive or object strings.');
}
pos = pos | 0;
if (pos < 0 || pos >= this.length) return '';
return this[pos];
},
charCodeAt(pos) {
var res = this.charAt(pos);
if (res === '') return NaN;
else return internals.toCharCode(res);
},
startsWith(term, pos) {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.startsWith(term, pos);
else throw new Error('This function may be used only with primitive or object strings.');
}
pos = pos! | 0;
return internals.startsWith(this, term + '', pos);
},
endsWith(term, pos) {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.endsWith(term, pos);
else throw new Error('This function may be used only with primitive or object strings.');
}
pos = (pos ?? this.length) | 0;
return internals.endsWith(this, term + '', pos);
},
indexOf(term: any, start) {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.indexOf(term, start);
else throw new Error('This function may be used only with primitive or object strings.');
}
if (typeof term[Symbol.search] !== 'function') term = RegExp.escape(term);
return term[Symbol.search](this, false, start);
},
lastIndexOf(term: any, start) {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.indexOf(term, start);
else throw new Error('This function may be used only with primitive or object strings.');
}
if (typeof term[Symbol.search] !== 'function') term = RegExp.escape(term);
return term[Symbol.search](this, true, start);
},
includes(term, start) {
return this.indexOf(term, start) >= 0;
},
replace(pattern: any, val) {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.replace(pattern, val);
else throw new Error('This function may be used only with primitive or object strings.');
}
if (typeof pattern[Symbol.replace] !== 'function') pattern = RegExp.escape(pattern);
return pattern[Symbol.replace](this, val);
},
replaceAll(pattern: any, val) {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.replace(pattern, val);
else throw new Error('This function may be used only with primitive or object strings.');
}
if (typeof pattern[Symbol.replace] !== 'function') pattern = RegExp.escape(pattern, "g");
if (pattern instanceof RegExp && !pattern.global) pattern = new pattern.constructor(pattern.source, pattern.flags + "g");
return pattern[Symbol.replace](this, val);
},
match(pattern: any) {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.match(pattern);
else throw new Error('This function may be used only with primitive or object strings.');
}
if (typeof pattern[Symbol.match] !== 'function') pattern = RegExp.escape(pattern);
return pattern[Symbol.match](this);
},
matchAll(pattern: any) {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.matchAll(pattern);
else throw new Error('This function may be used only with primitive or object strings.');
}
if (typeof pattern[Symbol.match] !== 'function') pattern = RegExp.escape(pattern, "g");
if (pattern instanceof RegExp && !pattern.global) pattern = new pattern.constructor(pattern.source, pattern.flags + "g");
return pattern[Symbol.match](this);
},
split(pattern: any, lim, sensible) {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.split(pattern, lim, sensible);
else throw new Error('This function may be used only with primitive or object strings.');
}
if (typeof pattern[Symbol.split] !== 'function') pattern = RegExp.escape(pattern, "g");
return pattern[Symbol.split](this, lim, sensible);
},
slice(start, end) {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.slice(start, end);
else throw new Error('This function may be used only with primitive or object strings.');
}
start = wrapI(this.length, start ?? 0 | 0);
end = wrapI(this.length, end ?? this.length | 0);
if (start > end) return '';
return this.substring(start, end);
},
concat(...args) {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.concat(...args);
else throw new Error('This function may be used only with primitive or object strings.');
}
var res = this;
for (var arg of args) res += arg;
return res;
},
trim() {
return this
.replace(/^\s+/g, '')
.replace(/\s+$/g, '');
}
});
setProps(String, {
fromCharCode(val) {
return internals.fromCharCode(val | 0);
},
})
Object.defineProperty(String.prototype, 'length', {
get() {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.length;
else throw new Error('This function may be used only with primitive or object strings.');
}
return internals.strlen(this);
},
configurable: true,
enumerable: false,
});

View File

@@ -1,38 +0,0 @@
interface Symbol {
valueOf(): symbol;
constructor: SymbolConstructor;
}
interface SymbolConstructor {
(val?: any): symbol;
prototype: Symbol;
for(key: string): symbol;
keyFor(sym: symbol): string;
readonly typeName: unique symbol;
}
declare var Symbol: SymbolConstructor;
gt.Symbol = function(this: any, val?: string) {
if (this !== undefined && this !== null) throw new TypeError("Symbol may not be called with 'new'.");
if (typeof val !== 'string' && val !== undefined) throw new TypeError('val must be a string or undefined.');
return internals.symbol(val, true);
} as SymbolConstructor;
Symbol.prototype = internals.symbolProto;
setConstr(Symbol.prototype, Symbol);
(Symbol as any).typeName = Symbol("Symbol.name");
setProps(Symbol, {
for(key) {
if (typeof key !== 'string' && key !== undefined) throw new TypeError('key must be a string or undefined.');
return internals.symbol(key, false);
},
keyFor(sym) {
if (typeof sym !== 'symbol') throw new TypeError('sym must be a symbol.');
return internals.symStr(sym);
},
typeName: Symbol('Symbol.name') as any,
});
Object.defineProperty(Object.prototype, Symbol.typeName, { value: 'Object' });
Object.defineProperty(gt, Symbol.typeName, { value: 'Window' });

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JScript Debugger</title>
</head>
<body>
<p>
This is the debugger of JScript. It implement the <a href="https://chromedevtools.github.io/devtools-protocol/1-2/">V8 Debugging protocol</a>,
so you can use the devtools in chrome. <br>
The debugger is still in early development, so please report any issues to
<a href="https://github.com/TopchetoEU/java-jscript/issues">the github repo</a>.
</p>
<p>
Here are the available entrypoints:
<ul>
<li><a href="json/version">/json/version</a> - version and other stuff about the JScript engine</li>
<li><a href="json/list">/json/list</a> - a list of all entrypoints</li>
<li><a href="json/protocol">/json/protocol</a> - documentation of the implemented V8 protocol</li>
<li>/(any target) - websocket entrypoints for debugging</li>
</ul>
</p>
<p>
Running ${NAME} v${VERSION} by ${AUTHOR}
</p>
</body>
</html>

File diff suppressed because it is too large Load Diff

113
src/assets/js/bootstrap.js vendored Normal file
View File

@@ -0,0 +1,113 @@
(function (ts, env, libs) {
var src = '', version = 0;
var lib = libs.concat([
'declare function exit(): never;',
'declare function go(): any;',
'declare function getTsDeclarations(): string[];'
]).join('');
var libSnapshot = ts.ScriptSnapshot.fromString(lib);
var environments = {};
var declSnapshots = [];
var settings = {
outDir: "/out",
declarationDir: "/out",
target: ts.ScriptTarget.ES5,
lib: [ ],
module: ts.ModuleKind.None,
declaration: true,
stripInternal: true,
downlevelIteration: true,
forceConsistentCasingInFileNames: true,
experimentalDecorators: true,
strict: true,
sourceMap: true,
};
var reg = ts.createDocumentRegistry();
var service = ts.createLanguageService({
getCurrentDirectory: function() { return "/"; },
getDefaultLibFileName: function() { return "/lib.d.ts"; },
getScriptFileNames: function() {
var res = [ "/src.ts", "/lib.d.ts" ];
for (var i = 0; i < declSnapshots.length; i++) res.push("/glob." + (i + 1) + ".d.ts");
return res;
},
getCompilationSettings: function () { return settings; },
fileExists: function(filename) { return filename === "/lib.d.ts" || filename === "/src.ts" || filename === "/glob.d.ts"; },
getScriptSnapshot: function(filename) {
if (filename === "/lib.d.ts") return libSnapshot;
if (filename === "/src.ts") return ts.ScriptSnapshot.fromString(src);
var index = /\/glob\.(\d+)\.d\.ts/g.exec(filename);
if (index && index[1] && (index = Number(index[1])) && index > 0 && index <= declSnapshots.length) {
return declSnapshots[index - 1];
}
throw new Error("File '" + filename + "' doesn't exist.");
},
getScriptVersion: function (filename) {
if (filename === "/lib.d.ts" || filename.startsWith("/glob.")) return 0;
else return version;
},
}, reg);
service.getEmitOutput("/lib.d.ts");
log("Loaded libraries!");
var oldCompile = env.compile;
function compile(code, filename, env) {
src = code;
version++;
if (!environments[env.id]) environments[env.id] = []
var decls = declSnapshots = environments[env.id];
var emit = service.getEmitOutput("/src.ts");
var diagnostics = []
.concat(service.getCompilerOptionsDiagnostics())
.concat(service.getSyntacticDiagnostics("/src.ts"))
.concat(service.getSemanticDiagnostics("/src.ts"))
.map(function (diagnostic) {
var message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
if (diagnostic.file) {
var pos = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
var file = diagnostic.file.fileName.substring(1);
if (file === "src.ts") file = filename;
return file + ":" + (pos.line + 1) + ":" + (pos.character + 1) + ": " + message;
}
else return message;
});
if (diagnostics.length > 0) {
throw new SyntaxError(diagnostics.join("\n"));
}
var map = JSON.parse(emit.outputFiles[0].text);
var result = emit.outputFiles[1].text;
var declaration = emit.outputFiles[2].text;
var compiled = oldCompile(result, filename, env);
return {
function: function () {
var val = compiled.function.apply(this, arguments);
if (declaration !== '') decls.push(ts.ScriptSnapshot.fromString(declaration));
return val;
},
breakpoints: compiled.breakpoints,
mapChain: compiled.mapChain.concat(map.mappings),
};
}
function apply(env) {
env.compile = compile;
env.global.getTsDeclarations = function() {
return environments[env.id];
}
}
apply(env);
})(arguments[0], arguments[1], arguments[2]);

623
src/assets/js/lib.d.ts vendored Normal file
View File

@@ -0,0 +1,623 @@
type PropertyDescriptor<T, ThisT> = {
value: any;
writable?: boolean;
enumerable?: boolean;
configurable?: boolean;
} | {
get?(this: ThisT): T;
set(this: ThisT, val: T): void;
enumerable?: boolean;
configurable?: boolean;
} | {
get(this: ThisT): T;
set?(this: ThisT, val: T): void;
enumerable?: boolean;
configurable?: boolean;
};
type Exclude<T, U> = T extends U ? never : T;
type Extract<T, U> = T extends U ? T : never;
type Record<KeyT extends string | number | symbol, ValT> = { [x in KeyT]: ValT }
type ReplaceFunc = (match: string, ...args: any[]) => string;
type PromiseResult<T> = { type: 'fulfilled'; value: T; } | { type: 'rejected'; reason: any; }
// wippidy-wine, this code is now mine :D
type Awaited<T> =
T extends null | undefined ? T : // special case for `null | undefined` when not in `--strictNullChecks` mode
T extends object & { then(onfulfilled: infer F, ...args: infer _): any } ? // `await` only unwraps object types with a callable `then`. Non-object types are not unwrapped
F extends ((value: infer V, ...args: infer _) => any) ? // if the argument to `then` is callable, extracts the first argument
Awaited<V> : // recursively unwrap the value
never : // the argument to `then` was not callable
T;
type IteratorYieldResult<TReturn> =
{ done?: false; } &
(TReturn extends undefined ? { value?: undefined; } : { value: TReturn; });
type IteratorReturnResult<TReturn> =
{ done: true } &
(TReturn extends undefined ? { value?: undefined; } : { value: TReturn; });
type IteratorResult<T, TReturn = any> = IteratorYieldResult<T> | IteratorReturnResult<TReturn>;
interface Thenable<T> {
then<NextT = void>(onFulfilled?: (val: T) => NextT, onRejected?: (err: any) => NextT): Promise<Awaited<NextT>>;
}
interface RegExpResultIndices extends Array<[number, number]> {
groups?: { [name: string]: [number, number]; };
}
interface RegExpResult extends Array<string> {
groups?: { [name: string]: string; };
index: number;
input: string;
indices?: RegExpResultIndices;
escape(raw: string, flags: string): RegExp;
}
interface Matcher {
[Symbol.match](target: string): RegExpResult | string[] | null;
[Symbol.matchAll](target: string): IterableIterator<RegExpResult>;
}
interface Splitter {
[Symbol.split](target: string, limit?: number, sensible?: boolean): string[];
}
interface Replacer {
[Symbol.replace](target: string, replacement: string | ReplaceFunc): string;
}
interface Searcher {
[Symbol.search](target: string, reverse?: boolean, start?: number): number;
}
type FlatArray<Arr, Depth extends number> = {
"done": Arr,
"recur": Arr extends Array<infer InnerArr>
? FlatArray<InnerArr, [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20][Depth]>
: Arr
}[Depth extends -1 ? "done" : "recur"];
interface IArguments {
[i: number]: any;
length: number;
}
interface Iterator<T, TReturn = any, TNext = undefined> {
next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
return?(value?: TReturn): IteratorResult<T, TReturn>;
throw?(e?: any): IteratorResult<T, TReturn>;
}
interface Iterable<T> {
[Symbol.iterator](): Iterator<T>;
}
interface IterableIterator<T> extends Iterator<T> {
[Symbol.iterator](): IterableIterator<T>;
}
interface AsyncIterator<T, TReturn = any, TNext = undefined> {
next(...args: [] | [TNext]): Promise<IteratorResult<T, TReturn>>;
return?(value?: TReturn | Thenable<TReturn>): Promise<IteratorResult<T, TReturn>>;
throw?(e?: any): Promise<IteratorResult<T, TReturn>>;
}
interface AsyncIterable<T> {
[Symbol.asyncIterator](): AsyncIterator<T>;
}
interface AsyncIterableIterator<T> extends AsyncIterator<T> {
[Symbol.asyncIterator](): AsyncIterableIterator<T>;
}
interface Generator<T = unknown, TReturn = void, TNext = unknown> extends Iterator<T, TReturn, TNext> {
[Symbol.iterator](): Generator<T, TReturn, TNext>;
return(value: TReturn): IteratorResult<T, TReturn>;
throw(e: any): IteratorResult<T, TReturn>;
}
interface GeneratorFunction {
new (...args: any[]): Generator;
(...args: any[]): Generator;
readonly length: number;
readonly name: string;
readonly prototype: Generator;
}
interface AsyncGenerator<T = unknown, TReturn = void, TNext = unknown> extends AsyncIterator<T, TReturn, TNext> {
return(value: TReturn | Thenable<TReturn>): Promise<IteratorResult<T, TReturn>>;
throw(e: any): Promise<IteratorResult<T, TReturn>>;
[Symbol.asyncIterator](): AsyncGenerator<T, TReturn, TNext>;
}
interface AsyncGeneratorFunction {
new (...args: any[]): AsyncGenerator;
(...args: any[]): AsyncGenerator;
readonly length: number;
readonly name: string;
readonly prototype: AsyncGenerator;
}
interface MathObject {
readonly E: number;
readonly PI: number;
readonly SQRT2: number;
readonly SQRT1_2: number;
readonly LN2: number;
readonly LN10: number;
readonly LOG2E: number;
readonly LOG10E: number;
asin(x: number): number;
acos(x: number): number;
atan(x: number): number;
atan2(y: number, x: number): number;
asinh(x: number): number;
acosh(x: number): number;
atanh(x: number): number;
sin(x: number): number;
cos(x: number): number;
tan(x: number): number;
sinh(x: number): number;
cosh(x: number): number;
tanh(x: number): number;
sqrt(x: number): number;
cbrt(x: number): number;
hypot(...vals: number[]): number;
imul(a: number, b: number): number;
exp(x: number): number;
expm1(x: number): number;
pow(x: number, y: number): number;
log(x: number): number;
log10(x: number): number;
log1p(x: number): number;
log2(x: number): number;
ceil(x: number): number;
floor(x: number): number;
round(x: number): number;
fround(x: number): number;
trunc(x: number): number;
abs(x: number): number;
max(...vals: number[]): number;
min(...vals: number[]): number;
sign(x: number): number;
random(): number;
clz32(x: number): number;
}
interface Array<T> extends IterableIterator<T> {
[i: number]: T;
length: number;
toString(): string;
// toLocaleString(): string;
join(separator?: string): string;
fill(val: T, start?: number, end?: number): T[];
pop(): T | undefined;
push(...items: T[]): number;
concat(...items: (T | T[])[]): T[];
concat(...items: (T | T[])[]): T[];
join(separator?: string): string;
reverse(): T[];
shift(): T | undefined;
slice(start?: number, end?: number): T[];
sort(compareFn?: (a: T, b: T) => number): this;
splice(start: number, deleteCount?: number | undefined, ...items: T[]): T[];
unshift(...items: T[]): number;
indexOf(searchElement: T, fromIndex?: number): number;
lastIndexOf(searchElement: T, fromIndex?: number): number;
every(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean;
some(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean;
forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void;
includes(el: any, start?: number): boolean;
map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];
filter(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): T[];
find(predicate: (value: T, index: number, array: T[]) => boolean, thisArg?: any): T[];
findIndex(predicate: (value: T, index: number, array: T[]) => boolean, thisArg?: any): number;
findLast(predicate: (value: T, index: number, array: T[]) => boolean, thisArg?: any): T[];
findLastIndex(predicate: (value: T, index: number, array: T[]) => boolean, thisArg?: any): number;
flat<D extends number = 1>(depth?: D): FlatArray<T, D>;
flatMap(func: (val: T, i: number, arr: T[]) => T | T[], thisAarg?: any): FlatArray<T[], 1>;
sort(func?: (a: T, b: T) => number): this;
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T;
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
reduce<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U;
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T;
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
reduceRight<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U;
entries(): IterableIterator<[number, T]>;
values(): IterableIterator<T>;
keys(): IterableIterator<number>;
}
interface ArrayConstructor {
new <T>(arrayLength?: number): T[];
new <T>(...items: T[]): T[];
<T>(arrayLength?: number): T[];
<T>(...items: T[]): T[];
isArray(arg: any): arg is any[];
prototype: Array<any>;
}
interface Boolean {
toString(): string;
valueOf(): boolean;
}
interface BooleanConstructor {
(val: any): boolean;
new (val: any): Boolean;
prototype: Boolean;
}
interface Error {
name: string;
message: string;
stack: string[];
toString(): string;
}
interface ErrorConstructor {
(msg?: any): Error;
new (msg?: any): Error;
prototype: Error;
}
interface TypeErrorConstructor extends ErrorConstructor {
(msg?: any): TypeError;
new (msg?: any): TypeError;
prototype: Error;
}
interface TypeError extends Error {
name: 'TypeError';
}
interface RangeErrorConstructor extends ErrorConstructor {
(msg?: any): RangeError;
new (msg?: any): RangeError;
prototype: Error;
}
interface RangeError extends Error {
name: 'RangeError';
}
interface SyntaxErrorConstructor extends ErrorConstructor {
(msg?: any): RangeError;
new (msg?: any): RangeError;
prototype: Error;
}
interface SyntaxError extends Error {
name: 'SyntaxError';
}
interface Function {
apply(this: Function, thisArg: any, argArray?: any): any;
call(this: Function, thisArg: any, ...argArray: any[]): any;
bind(this: Function, thisArg: any, ...argArray: any[]): Function;
toString(): string;
prototype: any;
readonly length: number;
name: string;
}
interface CallableFunction extends Function {
(...args: any[]): any;
apply<ThisArg, Args extends any[], RetT>(this: (this: ThisArg, ...args: Args) => RetT, thisArg: ThisArg, argArray?: Args): RetT;
call<ThisArg, Args extends any[], RetT>(this: (this: ThisArg, ...args: Args) => RetT, thisArg: ThisArg, ...argArray: Args): RetT;
bind<ThisArg, Args extends any[], Rest extends any[], RetT>(this: (this: ThisArg, ...args: [ ...Args, ...Rest ]) => RetT, thisArg: ThisArg, ...argArray: Args): (this: void, ...args: Rest) => RetT;
}
interface NewableFunction extends Function {
new(...args: any[]): any;
apply<Args extends any[], RetT>(this: new (...args: Args) => RetT, thisArg: any, argArray?: Args): RetT;
call<Args extends any[], RetT>(this: new (...args: Args) => RetT, thisArg: any, ...argArray: Args): RetT;
bind<Args extends any[], RetT>(this: new (...args: Args) => RetT, thisArg: any, ...argArray: Args): new (...args: Args) => RetT;
}
interface FunctionConstructor extends Function {
(...args: string[]): (...args: any[]) => any;
new (...args: string[]): (...args: any[]) => any;
prototype: Function;
async<ArgsT extends any[], RetT>(
func: (await: <T>(val: T) => Awaited<T>) => (...args: ArgsT) => RetT
): (...args: ArgsT) => Promise<RetT>;
asyncGenerator<ArgsT extends any[], RetT>(
func: (await: <T>(val: T) => Awaited<T>, _yield: <T>(val: T) => void) => (...args: ArgsT) => RetT
): (...args: ArgsT) => AsyncGenerator<RetT>;
generator<ArgsT extends any[], T = unknown, RetT = unknown, TNext = unknown>(
func: (_yield: <T>(val: T) => TNext) => (...args: ArgsT) => RetT
): (...args: ArgsT) => Generator<T, RetT, TNext>;
}
interface Number {
toString(): string;
valueOf(): number;
}
interface NumberConstructor {
(val: any): number;
new (val: any): Number;
prototype: Number;
parseInt(val: unknown): number;
parseFloat(val: unknown): number;
}
interface Object {
constructor: NewableFunction;
[Symbol.typeName]: string;
valueOf(): this;
toString(): string;
hasOwnProperty(key: any): boolean;
}
interface ObjectConstructor {
(arg: string): String;
(arg: number): Number;
(arg: boolean): Boolean;
(arg?: undefined | null): {};
<T extends object>(arg: T): T;
new (arg: string): String;
new (arg: number): Number;
new (arg: boolean): Boolean;
new (arg?: undefined | null): {};
new <T extends object>(arg: T): T;
prototype: Object;
assign<T extends object>(target: T, ...src: object[]): T;
create<T extends object>(proto: T, props?: { [key: string]: PropertyDescriptor<any, T> }): T;
keys<T extends object>(obj: T, onlyString?: true): (keyof T)[];
keys<T extends object>(obj: T, onlyString: false): any[];
entries<T extends object>(obj: T, onlyString?: true): [keyof T, T[keyof T]][];
entries<T extends object>(obj: T, onlyString: false): [any, any][];
values<T extends object>(obj: T, onlyString?: true): (T[keyof T])[];
values<T extends object>(obj: T, onlyString: false): any[];
fromEntries(entries: Iterable<[any, any]>): object;
defineProperty<T, ThisT extends object>(obj: ThisT, key: any, desc: PropertyDescriptor<T, ThisT>): ThisT;
defineProperties<ThisT extends object>(obj: ThisT, desc: { [key: string]: PropertyDescriptor<any, ThisT> }): ThisT;
getOwnPropertyNames<T extends object>(obj: T): (keyof T)[];
getOwnPropertySymbols<T extends object>(obj: T): (keyof T)[];
hasOwn<T extends object, KeyT>(obj: T, key: KeyT): boolean;
getOwnPropertyDescriptor<T extends object, KeyT extends keyof T>(obj: T, key: KeyT): PropertyDescriptor<T[KeyT], T>;
getOwnPropertyDescriptors<T extends object>(obj: T): { [x in keyof T]: PropertyDescriptor<T[x], T> };
getPrototypeOf(obj: any): object | null;
setPrototypeOf<T>(obj: T, proto: object | null): T;
preventExtensions<T extends object>(obj: T): T;
seal<T extends object>(obj: T): T;
freeze<T extends object>(obj: T): T;
isExtensible(obj: object): boolean;
isSealed(obj: object): boolean;
isFrozen(obj: object): boolean;
}
interface String {
[i: number]: string;
toString(): string;
valueOf(): string;
charAt(pos: number): string;
charCodeAt(pos: number): number;
substring(start?: number, end?: number): string;
slice(start?: number, end?: number): string;
substr(start?: number, length?: number): string;
startsWith(str: string, pos?: number): string;
endsWith(str: string, pos?: number): string;
replace(pattern: string | Replacer, val: string | ReplaceFunc): string;
replaceAll(pattern: string | Replacer, val: string | ReplaceFunc): string;
match(pattern: string | Matcher): RegExpResult | string[] | null;
matchAll(pattern: string | Matcher): IterableIterator<RegExpResult>;
split(pattern: string | Splitter, limit?: number, sensible?: boolean): string;
concat(...others: string[]): string;
indexOf(term: string | Searcher, start?: number): number;
lastIndexOf(term: string | Searcher, start?: number): number;
toLowerCase(): string;
toUpperCase(): string;
trim(): string;
includes(term: string, start?: number): boolean;
length: number;
}
interface StringConstructor {
(val: any): string;
new (val: any): String;
fromCharCode(val: number): string;
prototype: String;
}
interface Symbol {
valueOf(): symbol;
}
interface SymbolConstructor {
(val?: any): symbol;
new(...args: any[]): never;
prototype: Symbol;
for(key: string): symbol;
keyFor(sym: symbol): string;
readonly typeName: unique symbol;
readonly match: unique symbol;
readonly matchAll: unique symbol;
readonly split: unique symbol;
readonly replace: unique symbol;
readonly search: unique symbol;
readonly iterator: unique symbol;
readonly asyncIterator: unique symbol;
}
interface Promise<T> extends Thenable<T> {
catch<ResT = void>(func: (err: unknown) => ResT): Promise<ResT>;
finally(func: () => void): Promise<T>;
constructor: PromiseConstructor;
}
interface PromiseConstructorLike {
new <T>(func: (res: (val: T) => void, rej: (err: unknown) => void) => void): Thenable<Awaited<T>>;
}
interface PromiseConstructor extends PromiseConstructorLike {
prototype: Promise<any>;
new <T>(func: (res: (val: T) => void, rej: (err: unknown) => void) => void): Promise<Awaited<T>>;
resolve<T>(val: T): Promise<Awaited<T>>;
reject(val: any): Promise<never>;
isAwaitable(val: unknown): val is Thenable<any>;
any<T>(promises: T[]): Promise<Awaited<T>>;
race<T>(promises: (Promise<T>|T)[]): Promise<T>;
all<T extends any[]>(promises: T): Promise<{ [Key in keyof T]: Awaited<T[Key]> }>;
allSettled<T extends any[]>(...promises: T): Promise<[...{ [P in keyof T]: PromiseResult<Awaited<T[P]>>}]>;
}
interface FileStat {
type: 'file' | 'folder';
mode: 'r' | 'rw';
}
interface File {
readonly pointer: Promise<number>;
readonly length: Promise<number>;
read(n: number): Promise<number[]>;
write(buff: number[]): Promise<void>;
close(): Promise<void>;
seek(offset: number, whence: number): Promise<void>;
}
interface Filesystem {
readonly SEEK_SET: 0;
readonly SEEK_CUR: 1;
readonly SEEK_END: 2;
open(path: string, mode: 'r' | 'rw'): Promise<File>;
ls(path: string): AsyncIterableIterator<string>;
mkdir(path: string): Promise<void>;
mkfile(path: string): Promise<void>;
rm(path: string, recursive?: boolean): Promise<void>;
stat(path: string): Promise<FileStat>;
exists(path: string): Promise<boolean>;
normalize(...paths: string[]): string;
}
interface Encoding {
encode(val: string): number[];
decode(val: number[]): string;
}
declare var String: StringConstructor;
//@ts-ignore
declare const arguments: IArguments;
declare var NaN: number;
declare var Infinity: number;
declare var setTimeout: <T extends any[]>(handle: (...args: [ ...T, ...any[] ]) => void, delay?: number, ...args: T) => number;
declare var setInterval: <T extends any[]>(handle: (...args: [ ...T, ...any[] ]) => void, delay?: number, ...args: T) => number;
declare var clearTimeout: (id: number) => void;
declare var clearInterval: (id: number) => void;
declare var parseInt: typeof Number.parseInt;
declare var parseFloat: typeof Number.parseFloat;
declare function log(...vals: any[]): void;
declare function require(name: string): any;
declare var Array: ArrayConstructor;
declare var Boolean: BooleanConstructor;
declare var Promise: PromiseConstructor;
declare var Function: FunctionConstructor;
declare var Number: NumberConstructor;
declare var Object: ObjectConstructor;
declare var Symbol: SymbolConstructor;
declare var Promise: PromiseConstructor;
declare var Math: MathObject;
declare var Encoding: Encoding;
declare var Filesystem: Filesystem;
declare var Error: ErrorConstructor;
declare var RangeError: RangeErrorConstructor;
declare var TypeError: TypeErrorConstructor;
declare var SyntaxError: SyntaxErrorConstructor;
declare var self: typeof globalThis;
declare class Map<KeyT, ValueT> {
public [Symbol.iterator](): IterableIterator<[KeyT, ValueT]>;
public clear(): void;
public delete(key: KeyT): boolean;
public entries(): IterableIterator<[KeyT, ValueT]>;
public keys(): IterableIterator<KeyT>;
public values(): IterableIterator<ValueT>;
public get(key: KeyT): ValueT;
public set(key: KeyT, val: ValueT): this;
public has(key: KeyT): boolean;
public get size(): number;
public forEach(func: (val: ValueT, key: KeyT, map: Map<KeyT, ValueT>) => void, thisArg?: any): void;
public constructor();
}
declare class Set<T> {
public [Symbol.iterator](): IterableIterator<T>;
public entries(): IterableIterator<[T, T]>;
public keys(): IterableIterator<T>;
public values(): IterableIterator<T>;
public clear(): void;
public add(val: T): this;
public delete(val: T): boolean;
public has(key: T): boolean;
public get size(): number;
public forEach(func: (key: T, value: T, set: Set<T>) => void, thisArg?: any): void;
public constructor();
}
declare class RegExp implements Matcher, Splitter, Replacer, Searcher {
static escape(raw: any, flags?: string): RegExp;
prototype: RegExp;
exec(val: string): RegExpResult | null;
test(val: string): boolean;
toString(): string;
[Symbol.match](target: string): RegExpResult | string[] | null;
[Symbol.matchAll](target: string): IterableIterator<RegExpResult>;
[Symbol.split](target: string, limit?: number, sensible?: boolean): string[];
[Symbol.replace](target: string, replacement: string | ReplaceFunc): string;
[Symbol.search](target: string, reverse?: boolean, start?: number): number;
readonly dotAll: boolean;
readonly global: boolean;
readonly hasIndices: boolean;
readonly ignoreCase: boolean;
readonly multiline: boolean;
readonly sticky: boolean;
readonly unicode: boolean;
readonly source: string;
readonly flags: string;
lastIndex: number;
constructor(pattern?: string, flags?: string);
constructor(pattern?: RegExp, flags?: string);
}

View File

@@ -0,0 +1,53 @@
package me.topchetoeu.jscript;
public class Buffer {
private byte[] data;
private int length;
public void write(int i, byte[] val) {
if (i + val.length > data.length) {
var newCap = i + val.length + 1;
if (newCap < data.length * 2) newCap = data.length * 2;
var tmp = new byte[newCap];
System.arraycopy(this.data, 0, tmp, 0, length);
this.data = tmp;
}
System.arraycopy(val, 0, data, i, val.length);
if (i + val.length > length) length = i + val.length;
}
public int read(int i, byte[] buff) {
int n = buff.length;
if (i + n > length) n = length - i;
System.arraycopy(data, i, buff, 0, n);
return n;
}
public void append(byte b) {
write(length, new byte[] { b });
}
public byte[] data() {
var res = new byte[length];
System.arraycopy(this.data, 0, res, 0, length);
return res;
}
public int length() {
return length;
}
public Buffer(byte[] data) {
this.data = new byte[data.length];
this.length = data.length;
System.arraycopy(data, 0, this.data, 0, data.length);
}
public Buffer(int capacity) {
this.data = new byte[capacity];
this.length = 0;
}
public Buffer() {
this.data = new byte[128];
this.length = 0;
}
}

View File

@@ -0,0 +1,65 @@
package me.topchetoeu.jscript;
import java.io.File;
import java.nio.file.Path;
public class Filename {
public final String protocol;
public final String path;
public String toString() {
return protocol + "://" + path;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + protocol.hashCode();
result = prime * result + path.hashCode();
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
var other = (Filename) obj;
if (protocol == null) {
if (other.protocol != null) return false;
}
else if (!protocol.equals(other.protocol)) return false;
if (path == null) {
if (other.path != null) return false;
}
else if (!path.equals(other.path)) return false;
return true;
}
public Filename(String protocol, String path) {
path = path.trim();
protocol = protocol.trim();
this.protocol = protocol;
this.path = path;
}
public static Filename parse(String val) {
var i = val.indexOf("://");
if (i >= 0) return new Filename(val.substring(0, i).trim(), val.substring(i + 3).trim());
else return new Filename("file", val.trim());
}
public static Path normalize(String path) {
return Path.of(Path.of("/" + path.trim().replace("\\", "/")).normalize().toString().substring(1));
}
public static Filename fromFile(File file) {
return new Filename("file", file.getAbsolutePath());
}
}

View File

@@ -1,18 +1,18 @@
package me.topchetoeu.jscript; package me.topchetoeu.jscript;
public class Location { public class Location implements Comparable<Location> {
public static final Location INTERNAL = new Location(0, 0, "<internal>"); public static final Location INTERNAL = new Location(0, 0, new Filename("jscript", "native"));
private int line; private int line;
private int start; private int start;
private String filename; private Filename filename;
public int line() { return line; } public int line() { return line; }
public int start() { return start; } public int start() { return start; }
public String filename() { return filename; } public Filename filename() { return filename; }
@Override @Override
public String toString() { public String toString() {
return filename + ":" + line + ":" + start; return filename.toString() + ":" + line + ":" + start;
} }
public Location add(int n, boolean clone) { public Location add(int n, boolean clone) {
@@ -55,9 +55,39 @@ public class Location {
return true; return true;
} }
public Location(int line, int start, String filename) { @Override
public int compareTo(Location other) {
int a = filename.toString().compareTo(other.filename.toString());
int b = Integer.compare(line, other.line);
int c = Integer.compare(start, other.start);
if (a != 0) return a;
if (b != 0) return b;
return c;
}
public Location(int line, int start, Filename filename) {
this.line = line; this.line = line;
this.start = start; this.start = start;
this.filename = filename; this.filename = filename;
} }
public static Location parse(String raw) {
int i0 = -1, i1 = -1;
for (var i = raw.length() - 1; i >= 0; i--) {
if (raw.charAt(i) == ':') {
if (i1 == -1) i1 = i;
else if (i0 == -1) {
i0 = i;
break;
}
}
}
return new Location(
Integer.parseInt(raw.substring(i0 + 1, i1)),
Integer.parseInt(raw.substring(i1 + 1)),
Filename.parse(raw.substring(0, i0))
);
}
} }

View File

@@ -1,114 +1,170 @@
package me.topchetoeu.jscript; package me.topchetoeu.jscript;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.net.InetSocketAddress;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Map;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.Engine; import me.topchetoeu.jscript.engine.Engine;
import me.topchetoeu.jscript.engine.Environment;
import me.topchetoeu.jscript.engine.debug.DebugContext;
import me.topchetoeu.jscript.engine.debug.DebugServer;
import me.topchetoeu.jscript.engine.debug.SimpleDebugger;
import me.topchetoeu.jscript.engine.values.ArrayValue;
import me.topchetoeu.jscript.engine.values.NativeFunction;
import me.topchetoeu.jscript.engine.values.ObjectValue;
import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.events.Observer;
import me.topchetoeu.jscript.exceptions.EngineException; import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.exceptions.InterruptException;
import me.topchetoeu.jscript.exceptions.SyntaxException; import me.topchetoeu.jscript.exceptions.SyntaxException;
import me.topchetoeu.jscript.polyfills.PolyfillEngine; import me.topchetoeu.jscript.filesystem.Filesystem;
import me.topchetoeu.jscript.polyfills.TypescriptEngine; import me.topchetoeu.jscript.filesystem.MemoryFilesystem;
import me.topchetoeu.jscript.filesystem.Mode;
import me.topchetoeu.jscript.filesystem.PhysicalFilesystem;
import me.topchetoeu.jscript.filesystem.RootFilesystem;
import me.topchetoeu.jscript.lib.EnvironmentLib;
import me.topchetoeu.jscript.lib.Internals;
import me.topchetoeu.jscript.modules.ModuleRepo;
import me.topchetoeu.jscript.permissions.PermissionsManager;
import me.topchetoeu.jscript.permissions.PermissionsProvider;
public class Main { public class Main {
static Thread task; static Thread engineTask, debugTask;
static Engine engine; static Engine engine = new Engine();
static DebugServer debugServer = new DebugServer();
static Environment environment = new Environment();
private static Observer<Object> valuePrinter = new Observer<Object>() { static int j = 0;
public void next(Object data) { static boolean exited = false;
try { static String[] args;
Values.printValue(engine.context(), data);
}
catch (InterruptedException e) { }
System.out.println();
}
public void error(RuntimeException err) { private static void reader() {
try { try {
if (err instanceof EngineException) { for (var arg : args) {
System.out.println("Uncaught " + ((EngineException)err).toString(engine.context())); try {
} if (arg.equals("--ts")) initTypescript();
else if (err instanceof SyntaxException) { else {
System.out.println("Syntax error:" + ((SyntaxException)err).msg); var file = Path.of(arg);
} var raw = Files.readString(file);
else if (err.getCause() instanceof InterruptedException) return; var res = engine.pushMsg(
else { false, environment,
System.out.println("Internal error ocurred:"); Filename.fromFile(file.toFile()),
err.printStackTrace(); raw, null
).await();
Values.printValue(null, res);
System.out.println();
}
} }
catch (EngineException e) { Values.printError(e, null); }
} }
catch (EngineException ex) { for (var i = 0; ; i++) {
System.out.println("Uncaught [error while converting to string]"); try {
} var raw = Reading.readline();
catch (InterruptedException ex) {
return; if (raw == null) break;
var res = engine.pushMsg(
false, environment,
new Filename("jscript", "repl/" + i + ".js"),
raw, null
).await();
Values.printValue(null, res);
System.out.println();
}
catch (EngineException e) { Values.printError(e, null); }
catch (SyntaxException e) { Values.printError(e, null); }
} }
} }
}; catch (IOException e) {
System.out.println(e.toString());
exited = true;
}
catch (RuntimeException ex) {
if (!exited) {
System.out.println("Internal error ocurred:");
ex.printStackTrace();
}
}
if (exited) {
debugTask.interrupt();
engineTask.interrupt();
}
}
public static void main(String args[]) { private static void initEnv() {
var in = new BufferedReader(new InputStreamReader(System.in)); environment = Internals.apply(environment);
engine = new TypescriptEngine(new File("."));
var scope = engine.global().globalChild();
var exited = new boolean[1];
scope.define("exit", ctx -> { environment.global.define(false, new NativeFunction("exit", args -> {
exited[0] = true; exited = true;
task.interrupt(); throw new InterruptException();
throw new InterruptedException(); }));
}); environment.global.define(false, new NativeFunction("go", args -> {
scope.define("go", ctx -> {
try { try {
var func = engine.compile(scope, "do.js", new String(Files.readAllBytes(Path.of("do.js")))); var f = Path.of("do.js");
return func.call(ctx); var func = args.ctx.compile(new Filename("do", "do/" + j++ + ".js"), new String(Files.readAllBytes(f)));
return func.call(args.ctx);
} }
catch (IOException e) { catch (IOException e) {
throw new EngineException("Couldn't open do.js"); throw new EngineException("Couldn't open do.js");
} }
}); }));
task = engine.start(); var fs = new RootFilesystem(PermissionsProvider.get(environment));
var reader = new Thread(() -> { fs.protocols.put("temp", new MemoryFilesystem(Mode.READ_WRITE));
try { fs.protocols.put("file", new PhysicalFilesystem("."));
while (true) {
try { environment.add(PermissionsProvider.ENV_KEY, PermissionsManager.ALL_PERMS);
var raw = in.readLine(); environment.add(Filesystem.ENV_KEY, fs);
environment.add(ModuleRepo.ENV_KEY, ModuleRepo.ofFilesystem(fs));
}
private static void initEngine() {
var ctx = new DebugContext();
engine.add(DebugContext.ENV_KEY, ctx);
debugServer.targets.put("target", (ws, req) -> new SimpleDebugger(ws).attach(ctx));
engineTask = engine.start();
debugTask = debugServer.start(new InetSocketAddress("127.0.0.1", 9229), true);
}
private static void initTypescript() throws IOException {
var tsEnv = Internals.apply(new Environment());
var bsEnv = Internals.apply(new Environment());
try {
tsEnv.global.define(null, "module", false, new ObjectValue());
engine.pushMsg(
false, tsEnv,
new Filename("jscript", "ts.js"),
Reading.resourceToString("assets/js/ts.js"), null
).await();
System.out.println("Loaded typescript!");
var typescript = tsEnv.global.get(new Context(engine, bsEnv), "ts");
var libs = new ArrayValue(null, Reading.resourceToString("assets/js/lib.d.ts"));
engine.pushMsg(
false, bsEnv,
new Filename("jscript", "bootstrap.js"), Reading.resourceToString("assets/js/bootstrap.js"), null,
typescript, new EnvironmentLib(environment), libs
).await();
}
catch (EngineException e) {
Values.printError(e, "(while initializing TS)");
}
bsEnv.add(Environment.HIDE_STACK, true);
tsEnv.add(Environment.HIDE_STACK, true);
}
public static void main(String args[]) {
System.out.println(String.format("Running %s v%s by %s", Metadata.name(), Metadata.version(), Metadata.author()));
Main.args = args;
var reader = new Thread(Main::reader);
initEnv();
initEngine();
if (raw == null) break;
engine.pushMsg(false, scope, Map.of(), "<stdio>", raw, null).toObservable().once(valuePrinter);
}
catch (EngineException e) {
try {
System.out.println("Uncaught " + e.toString(engine.context()));
}
catch (EngineException ex) {
System.out.println("Uncaught [error while converting to string]");
}
}
}
}
catch (IOException e) {
e.printStackTrace();
return;
}
catch (SyntaxException ex) {
if (exited[0]) return;
System.out.println("Syntax error:" + ex.msg);
}
catch (RuntimeException ex) {
if (exited[0]) return;
System.out.println("Internal error ocurred:");
ex.printStackTrace();
}
catch (InterruptedException e) { return; }
if (exited[0]) return;
});
reader.setDaemon(true); reader.setDaemon(true);
reader.setName("STD Reader"); reader.setName("STD Reader");
reader.start(); reader.start();

View File

@@ -1,6 +0,0 @@
package me.topchetoeu.jscript;
public interface MessageReceiver {
void sendMessage(String msg);
void sendError(String msg);
}

View File

@@ -0,0 +1,20 @@
package me.topchetoeu.jscript;
public class Metadata {
private static final String VERSION = "${VERSION}";
private static final String AUTHOR = "${AUTHOR}";
private static final String NAME = "${NAME}";
public static String version() {
if (VERSION.equals("$" + "{VERSION}")) return "1337-devel";
else return VERSION;
}
public static String author() {
if (AUTHOR.equals("$" + "{AUTHOR}")) return "anonymous";
else return AUTHOR;
}
public static String name() {
if (NAME.equals("$" + "{NAME}")) return "some-product";
else return NAME;
}
}

View File

@@ -0,0 +1,33 @@
package me.topchetoeu.jscript;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UncheckedIOException;
public class Reading {
private static final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
public static synchronized String readline() throws IOException {
return reader.readLine();
}
/**
* Reads the given stream to a string
* @param in
* @return
*/
public static String streamToString(InputStream in) {
try {
return new String(in.readAllBytes());
}
catch (IOException e) { throw new UncheckedIOException(e); }
}
public static InputStream resourceToStream(String name) {
return Reading.class.getResourceAsStream("/" + name);
}
public static String resourceToString(String name) {
return streamToString(resourceToStream(name));
}
}

View File

@@ -0,0 +1,5 @@
package me.topchetoeu.jscript;
public interface ResultRunnable<T> {
T run();
}

View File

@@ -1,19 +0,0 @@
package me.topchetoeu.jscript.compilation;
import java.util.List;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public abstract class AssignStatement extends Statement {
public abstract void compile(List<Instruction> target, ScopeRecord scope, boolean retPrevValue);
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
compile(target, scope, false);
}
protected AssignStatement(Location loc) {
super(loc);
}
}

View File

@@ -4,7 +4,7 @@ import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.engine.Operation; import me.topchetoeu.jscript.engine.Operation;
public abstract class AssignableStatement extends Statement { public abstract class AssignableStatement extends Statement {
public abstract AssignStatement toAssign(Statement val, Operation operation); public abstract Statement toAssign(Statement val, Operation operation);
protected AssignableStatement(Location loc) { protected AssignableStatement(Location loc) {
super(loc); super(loc);

View File

@@ -0,0 +1,21 @@
package me.topchetoeu.jscript.compilation;
import me.topchetoeu.jscript.engine.values.Values;
public final class CalculateResult {
public final boolean exists;
public final Object value;
public final boolean isTruthy() {
return exists && Values.toBoolean(value);
}
public CalculateResult(Object value) {
this.exists = true;
this.value = value;
}
public CalculateResult() {
this.exists = false;
this.value = null;
}
}

View File

@@ -1,11 +0,0 @@
package me.topchetoeu.jscript.compilation;
public class CompileOptions {
public final boolean emitBpMap;
public final boolean emitVarNames;
public CompileOptions(boolean emitBpMap, boolean emitVarNames) {
this.emitBpMap = emitBpMap;
this.emitVarNames = emitVarNames;
}
}

View File

@@ -0,0 +1,66 @@
package me.topchetoeu.jscript.compilation;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeSet;
import java.util.Vector;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.engine.Environment;
import me.topchetoeu.jscript.engine.values.CodeFunction;
public class CompileTarget {
public final Vector<Instruction> target = new Vector<>();
public final Map<Long, FunctionBody> functions;
public final TreeSet<Location> breakpoints;
private final HashMap<Location, Instruction> bpToInstr = new HashMap<>();
public Instruction add(Instruction instr) {
target.add(instr);
return instr;
}
public Instruction set(int i, Instruction instr) {
return target.set(i, instr);
}
public void setDebug(int i, BreakpointType type) {
var instr = target.get(i);
instr.breakpoint = type;
if (type == BreakpointType.NONE) {
breakpoints.remove(target.get(i).location);
bpToInstr.remove(instr.location, instr);
}
else {
breakpoints.add(target.get(i).location);
var old = bpToInstr.put(instr.location, instr);
if (old != null) old.breakpoint = BreakpointType.NONE;
}
}
public void setDebug(BreakpointType type) {
setDebug(target.size() - 1, type);
}
public Instruction get(int i) {
return target.get(i);
}
public int size() { return target.size(); }
public Location lastLoc(Location fallback) {
if (target.size() == 0) return fallback;
else return target.get(target.size() - 1).location;
}
public Instruction[] array() { return target.toArray(Instruction[]::new); }
public FunctionBody body() {
return functions.get(0l);
}
public CodeFunction func(Environment env) {
return new CodeFunction(env, "", body());
}
public CompileTarget(Map<Long, FunctionBody> functions, TreeSet<Location> breakpoints) {
this.functions = functions;
this.breakpoints = breakpoints;
}
}

View File

@@ -1,77 +1,64 @@
package me.topchetoeu.jscript.compilation; package me.topchetoeu.jscript.compilation;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Vector;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.control.ContinueStatement; import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.control.ReturnStatement;
import me.topchetoeu.jscript.compilation.control.ThrowStatement;
import me.topchetoeu.jscript.compilation.values.FunctionStatement; import me.topchetoeu.jscript.compilation.values.FunctionStatement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class CompoundStatement extends Statement { public class CompoundStatement extends Statement {
public final Statement[] statements; public final Statement[] statements;
public final boolean separateFuncs;
public Location end;
@Override @Override public boolean pure() {
public boolean pollutesStack() {
for (var stm : statements) { for (var stm : statements) {
if (stm instanceof FunctionStatement) continue; if (!stm.pure()) return false;
return true;
} }
return false; return true;
} }
@Override @Override
public void declare(ScopeRecord varsScope) { public void declare(ScopeRecord varsScope) {
for (var stm : statements) { for (var stm : statements) stm.declare(varsScope);
stm.declare(varsScope);
}
} }
@Override @Override
public void compile(List<Instruction> target, ScopeRecord scope) { public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType type) {
for (var stm : statements) { List<Statement> statements = new Vector<Statement>();
if (stm instanceof FunctionStatement) { if (separateFuncs) for (var stm : this.statements) {
int start = target.size(); if (stm instanceof FunctionStatement && ((FunctionStatement)stm).statement) {
((FunctionStatement)stm).compile(target, scope, null, true); stm.compile(target, scope, false);
target.get(start).setDebug(true);
target.add(Instruction.discard());
} }
else statements.add(stm);
}
else statements = List.of(this.statements);
var polluted = false;
for (var i = 0; i < statements.size(); i++) {
var stm = statements.get(i);
if (i != statements.size() - 1) stm.compile(target, scope, false, BreakpointType.STEP_OVER);
else stm.compile(target, scope, polluted = pollute, BreakpointType.STEP_OVER);
} }
for (var i = 0; i < statements.length; i++) { if (!polluted && pollute) {
var stm = statements[i]; target.add(Instruction.loadValue(loc(), null));
if (stm instanceof FunctionStatement) continue;
if (i != statements.length - 1) stm.compileNoPollution(target, scope, true);
else stm.compileWithPollution(target, scope);
} }
} }
@Override public CompoundStatement setEnd(Location loc) {
public Statement optimize() { this.end = loc;
var res = new ArrayList<Statement>(); return this;
for (var i = 0; i < statements.length; i++) {
var stm = statements[i].optimize();
if (i < statements.length - 1 && stm.pure()) continue;
res.add(stm);
if (
stm instanceof ContinueStatement ||
stm instanceof ReturnStatement ||
stm instanceof ThrowStatement ||
stm instanceof ContinueStatement
) break;
}
if (res.size() == 1) return res.get(0);
else return new CompoundStatement(loc(), res.toArray(Statement[]::new));
} }
public CompoundStatement(Location loc, Statement... statements) { public CompoundStatement(Location loc, boolean separateFuncs, Statement ...statements) {
super(loc); super(loc);
this.separateFuncs = separateFuncs;
this.statements = statements; this.statements = statements;
} }
} }

View File

@@ -1,33 +0,0 @@
package me.topchetoeu.jscript.compilation;
import java.util.List;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.values.ConstantStatement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class DiscardStatement extends Statement {
public final Statement value;
@Override
public boolean pollutesStack() { return false; }
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
if (value == null) return;
value.compile(target, scope);
if (value.pollutesStack()) target.add(Instruction.discard());
}
@Override
public Statement optimize() {
if (value == null) return this;
var val = value.optimize();
if (val.pure()) return new ConstantStatement(loc(), null);
else return new DiscardStatement(loc(), val);
}
public DiscardStatement(Location loc, Statement val) {
super(loc);
this.value = val;
}
}

View File

@@ -0,0 +1,29 @@
package me.topchetoeu.jscript.compilation;
public class FunctionBody {
public final Instruction[] instructions;
public final String[] captureNames, localNames;
public final int localsN, argsN;
public FunctionBody(int localsN, int argsN, Instruction[] instructions, String[] captureNames, String[] localNames) {
this.argsN = argsN;
this.localsN = localsN;
this.instructions = instructions;
this.captureNames = captureNames;
this.localNames = localNames;
}
public FunctionBody(int localsN, int argsN, Instruction[] instructions) {
this.argsN = argsN;
this.localsN = localsN;
this.instructions = instructions;
this.captureNames = new String[0];
this.localNames = new String[0];
}
public FunctionBody(Instruction... instructions) {
this.argsN = 0;
this.localsN = 2;
this.instructions = instructions;
this.captureNames = new String[0];
this.localNames = new String[0];
}
}

View File

@@ -7,11 +7,11 @@ import me.topchetoeu.jscript.exceptions.SyntaxException;
public class Instruction { public class Instruction {
public static enum Type { public static enum Type {
RETURN, RETURN,
SIGNAL,
THROW, THROW,
THROW_SYNTAX, THROW_SYNTAX,
DELETE, DELETE,
TRY, TRY_START,
TRY_END,
NOP, NOP,
CALL, CALL,
@@ -34,7 +34,6 @@ public class Instruction {
LOAD_REGEX, LOAD_REGEX,
DUP, DUP,
MOVE,
STORE_VAR, STORE_VAR,
STORE_MEMBER, STORE_MEMBER,
@@ -46,61 +45,35 @@ public class Instruction {
TYPEOF, TYPEOF,
OPERATION; OPERATION;
// TYPEOF, }
// INSTANCEOF(true), public static enum BreakpointType {
// IN(true), NONE,
STEP_OVER,
STEP_IN;
// MULTIPLY(true), public boolean shouldStepIn() {
// DIVIDE(true), return this != NONE;
// MODULO(true), }
// ADD(true), public boolean shouldStepOver() {
// SUBTRACT(true), return this == STEP_OVER;
}
// USHIFT_RIGHT(true),
// SHIFT_RIGHT(true),
// SHIFT_LEFT(true),
// GREATER(true),
// LESS(true),
// GREATER_EQUALS(true),
// LESS_EQUALS(true),
// LOOSE_EQUALS(true),
// LOOSE_NOT_EQUALS(true),
// EQUALS(true),
// NOT_EQUALS(true),
// AND(true),
// OR(true),
// XOR(true),
// NEG(true),
// POS(true),
// NOT(true),
// INVERSE(true);
// final boolean isOperation;
// private Type(boolean isOperation) {
// this.isOperation = isOperation;
// }
// private Type() {
// this(false);
// }
} }
public final Type type; public final Type type;
public final Object[] params; public final Object[] params;
public Location location; public Location location;
public boolean debugged; public BreakpointType breakpoint = BreakpointType.NONE;
public Instruction setDbgData(Instruction other) {
this.location = other.location;
this.breakpoint = other.breakpoint;
return this;
}
public Instruction locate(Location loc) { public Instruction locate(Location loc) {
this.location = loc; this.location = loc;
return this; return this;
} }
public Instruction setDebug(boolean debug) {
debugged = debug;
return this;
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> T get(int i) { public <T> T get(int i) {
@@ -129,46 +102,38 @@ public class Instruction {
return params[i].equals(arg); return params[i].equals(arg);
} }
private Instruction(Location location, Type type, Object... params) { private Instruction(Location location, Type type, Object ...params) {
this.location = location; this.location = location;
this.type = type; this.type = type;
this.params = params; this.params = params;
} }
public static Instruction tryInstr(int n, int catchN, int finallyN) { public static Instruction tryStart(Location loc, int catchStart, int finallyStart, int end) {
return new Instruction(null, Type.TRY, n, catchN, finallyN); return new Instruction(loc, Type.TRY_START, catchStart, finallyStart, end);
} }
public static Instruction throwInstr() { public static Instruction tryEnd(Location loc) {
return new Instruction(null, Type.THROW); return new Instruction(loc, Type.TRY_END);
} }
public static Instruction throwSyntax(SyntaxException err) { public static Instruction throwInstr(Location loc) {
return new Instruction(null, Type.THROW_SYNTAX, err.getMessage()); return new Instruction(loc, Type.THROW);
} }
public static Instruction delete() { public static Instruction throwSyntax(Location loc, SyntaxException err) {
return new Instruction(null, Type.DELETE); return new Instruction(loc, Type.THROW_SYNTAX, err.getMessage());
} }
public static Instruction ret() { public static Instruction throwSyntax(Location loc, String err) {
return new Instruction(null, Type.RETURN); return new Instruction(loc, Type.THROW_SYNTAX, err);
} }
public static Instruction debug() { public static Instruction delete(Location loc) {
return new Instruction(null, Type.NOP, "debug"); return new Instruction(loc, Type.DELETE);
} }
public static Instruction debugVarNames(String[] names) { public static Instruction ret(Location loc) {
var args = new Object[names.length + 1]; return new Instruction(loc, Type.RETURN);
args[0] = "dbg_vars"; }
public static Instruction debug(Location loc) {
System.arraycopy(names, 0, args, 1, names.length); return new Instruction(loc, Type.NOP, "debug");
return new Instruction(null, Type.NOP, args);
} }
/** public static Instruction nop(Location loc, Object ...params) {
* ATTENTION: Usage outside of try/catch is broken af
*/
public static Instruction signal(String name) {
return new Instruction(null, Type.SIGNAL, name);
}
public static Instruction nop(Object ...params) {
for (var param : params) { for (var param : params) {
if (param instanceof String) continue; if (param instanceof String) continue;
if (param instanceof Boolean) continue; if (param instanceof Boolean) continue;
@@ -178,109 +143,104 @@ public class Instruction {
throw new RuntimeException("NOP params may contain only strings, booleans, doubles, integers and nulls."); throw new RuntimeException("NOP params may contain only strings, booleans, doubles, integers and nulls.");
} }
return new Instruction(null, Type.NOP, params); return new Instruction(loc, Type.NOP, params);
} }
public static Instruction call(int argn) { public static Instruction call(Location loc, int argn) {
return new Instruction(null, Type.CALL, argn); return new Instruction(loc, Type.CALL, argn);
} }
public static Instruction callNew(int argn) { public static Instruction callNew(Location loc, int argn) {
return new Instruction(null, Type.CALL_NEW, argn); return new Instruction(loc, Type.CALL_NEW, argn);
} }
public static Instruction jmp(int offset) { public static Instruction jmp(Location loc, int offset) {
return new Instruction(null, Type.JMP, offset); return new Instruction(loc, Type.JMP, offset);
} }
public static Instruction jmpIf(int offset) { public static Instruction jmpIf(Location loc, int offset) {
return new Instruction(null, Type.JMP_IF, offset); return new Instruction(loc, Type.JMP_IF, offset);
} }
public static Instruction jmpIfNot(int offset) { public static Instruction jmpIfNot(Location loc, int offset) {
return new Instruction(null, Type.JMP_IFN, offset); return new Instruction(loc, Type.JMP_IFN, offset);
} }
public static Instruction loadValue(Object val) { public static Instruction loadValue(Location loc, Object val) {
return new Instruction(null, Type.LOAD_VALUE, val); return new Instruction(loc, Type.LOAD_VALUE, val);
} }
public static Instruction makeVar(String name) { public static Instruction makeVar(Location loc, String name) {
return new Instruction(null, Type.MAKE_VAR, name); return new Instruction(loc, Type.MAKE_VAR, name);
} }
public static Instruction loadVar(Object i) { public static Instruction loadVar(Location loc, Object i) {
return new Instruction(null, Type.LOAD_VAR, i); return new Instruction(loc, Type.LOAD_VAR, i);
} }
public static Instruction loadGlob() { public static Instruction loadGlob(Location loc) {
return new Instruction(null, Type.LOAD_GLOB); return new Instruction(loc, Type.LOAD_GLOB);
} }
public static Instruction loadMember() { public static Instruction loadMember(Location loc) {
return new Instruction(null, Type.LOAD_MEMBER); return new Instruction(loc, Type.LOAD_MEMBER);
} }
public static Instruction loadMember(Object key) { public static Instruction loadMember(Location loc, Object key) {
if (key instanceof Number) key = ((Number)key).doubleValue(); if (key instanceof Number) key = ((Number)key).doubleValue();
return new Instruction(null, Type.LOAD_VAL_MEMBER, key); return new Instruction(loc, Type.LOAD_VAL_MEMBER, key);
} }
public static Instruction loadRegex(String pattern, String flags) { public static Instruction loadRegex(Location loc, String pattern, String flags) {
return new Instruction(null, Type.LOAD_REGEX, pattern, flags); return new Instruction(loc, Type.LOAD_REGEX, pattern, flags);
} }
public static Instruction loadFunc(int instrN, int varN, int len, int[] captures) { public static Instruction loadFunc(Location loc, long id, int[] captures) {
var args = new Object[3 + captures.length]; var args = new Object[1 + captures.length];
args[0] = instrN; args[0] = id;
args[1] = varN; for (var i = 0; i < captures.length; i++) args[i + 1] = captures[i];
args[2] = len; return new Instruction(loc, Type.LOAD_FUNC, args);
for (var i = 0; i < captures.length; i++) args[i + 3] = captures[i];
return new Instruction(null, Type.LOAD_FUNC, args);
} }
public static Instruction loadObj() { public static Instruction loadObj(Location loc) {
return new Instruction(null, Type.LOAD_OBJ); return new Instruction(loc, Type.LOAD_OBJ);
} }
public static Instruction loadArr(int count) { public static Instruction loadArr(Location loc, int count) {
return new Instruction(null, Type.LOAD_ARR, count); return new Instruction(loc, Type.LOAD_ARR, count);
} }
public static Instruction dup() { public static Instruction dup(Location loc) {
return new Instruction(null, Type.DUP, 0, 1); return new Instruction(loc, Type.DUP, 1);
} }
public static Instruction dup(int count, int offset) { public static Instruction dup(Location loc, int count) {
return new Instruction(null, Type.DUP, offset, count); return new Instruction(loc, Type.DUP, count);
}
public static Instruction move(int count, int offset) {
return new Instruction(null, Type.MOVE, offset, count);
} }
public static Instruction storeSelfFunc(int i) { public static Instruction storeSelfFunc(Location loc, int i) {
return new Instruction(null, Type.STORE_SELF_FUNC, i); return new Instruction(loc, Type.STORE_SELF_FUNC, i);
} }
public static Instruction storeVar(Object i) { public static Instruction storeVar(Location loc, Object i) {
return new Instruction(null, Type.STORE_VAR, i, false); return new Instruction(loc, Type.STORE_VAR, i, false);
} }
public static Instruction storeVar(Object i, boolean keep) { public static Instruction storeVar(Location loc, Object i, boolean keep) {
return new Instruction(null, Type.STORE_VAR, i, keep); return new Instruction(loc, Type.STORE_VAR, i, keep);
} }
public static Instruction storeMember() { public static Instruction storeMember(Location loc) {
return new Instruction(null, Type.STORE_MEMBER, false); return new Instruction(loc, Type.STORE_MEMBER, false);
} }
public static Instruction storeMember(boolean keep) { public static Instruction storeMember(Location loc, boolean keep) {
return new Instruction(null, Type.STORE_MEMBER, keep); return new Instruction(loc, Type.STORE_MEMBER, keep);
} }
public static Instruction discard() { public static Instruction discard(Location loc) {
return new Instruction(null, Type.DISCARD); return new Instruction(loc, Type.DISCARD);
} }
public static Instruction typeof() { public static Instruction typeof(Location loc) {
return new Instruction(null, Type.TYPEOF); return new Instruction(loc, Type.TYPEOF);
} }
public static Instruction typeof(Object varName) { public static Instruction typeof(Location loc, Object varName) {
return new Instruction(null, Type.TYPEOF, varName); return new Instruction(loc, Type.TYPEOF, varName);
} }
public static Instruction keys() { public static Instruction keys(Location loc, boolean forInFormat) {
return new Instruction(null, Type.KEYS); return new Instruction(loc, Type.KEYS, forInFormat);
} }
public static Instruction defProp() { public static Instruction defProp(Location loc) {
return new Instruction(null, Type.DEF_PROP); return new Instruction(loc, Type.DEF_PROP);
} }
public static Instruction operation(Operation op) { public static Instruction operation(Location loc, Operation op) {
return new Instruction(null, Type.OPERATION, op); return new Instruction(loc, Type.OPERATION, op);
} }
@Override @Override

View File

@@ -1,36 +1,26 @@
package me.topchetoeu.jscript.compilation; package me.topchetoeu.jscript.compilation;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public abstract class Statement { public abstract class Statement {
private Location _loc; private Location _loc;
public abstract boolean pollutesStack();
public boolean pure() { return false; } public boolean pure() { return false; }
public abstract void compile(List<Instruction> target, ScopeRecord scope);
public void declare(ScopeRecord varsScope) { } public void declare(ScopeRecord varsScope) { }
public Statement optimize() { return this; }
public void compileNoPollution(List<Instruction> target, ScopeRecord scope, boolean debug) { public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType type) {
int start = target.size(); int start = target.size();
compile(target, scope); compile(target, scope, pollute);
if (debug && target.size() != start) target.get(start).setDebug(true);
if (pollutesStack()) target.add(Instruction.discard().locate(loc())); if (target.size() != start) {
target.get(start).locate(loc());
target.setDebug(start, type);
}
} }
public void compileWithPollution(List<Instruction> target, ScopeRecord scope, boolean debug) { public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
int start = target.size(); compile(target, scope, pollute, BreakpointType.NONE);
compile(target, scope);
if (debug && target.size() != start) target.get(start).setDebug(true);
if (!pollutesStack()) target.add(Instruction.loadValue(null).locate(loc()));
}
public void compileNoPollution(List<Instruction> target, ScopeRecord scope) {
compileNoPollution(target, scope, false);
}
public void compileWithPollution(List<Instruction> target, ScopeRecord scope) {
compileWithPollution(target, scope, false);
} }
public Location loc() { return _loc; } public Location loc() { return _loc; }

View File

@@ -0,0 +1,18 @@
package me.topchetoeu.jscript.compilation;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.exceptions.SyntaxException;
public class ThrowSyntaxStatement extends Statement {
public final String name;
@Override
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
target.add(Instruction.throwSyntax(loc(), name));
}
public ThrowSyntaxStatement(SyntaxException e) {
super(e.loc);
this.name = e.msg;
}
}

View File

@@ -3,6 +3,7 @@ package me.topchetoeu.jscript.compilation;
import java.util.List; import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.values.FunctionStatement; import me.topchetoeu.jscript.compilation.values.FunctionStatement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
@@ -10,17 +11,17 @@ public class VariableDeclareStatement extends Statement {
public static class Pair { public static class Pair {
public final String name; public final String name;
public final Statement value; public final Statement value;
public final Location location;
public Pair(String name, Statement value) { public Pair(String name, Statement value, Location location) {
this.name = name; this.name = name;
this.value = value; this.value = value;
this.location = location;
} }
} }
public final List<Pair> values; public final List<Pair> values;
@Override
public boolean pollutesStack() { return false; }
@Override @Override
public void declare(ScopeRecord varsScope) { public void declare(ScopeRecord varsScope) {
for (var key : values) { for (var key : values) {
@@ -28,21 +29,20 @@ public class VariableDeclareStatement extends Statement {
} }
} }
@Override @Override
public void compile(List<Instruction> target, ScopeRecord scope) { public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
for (var entry : values) { for (var entry : values) {
if (entry.name == null) continue; if (entry.name == null) continue;
var key = scope.getKey(entry.name); var key = scope.getKey(entry.name);
if (key instanceof String) target.add(Instruction.makeVar((String)key).locate(loc()));
if (entry.value instanceof FunctionStatement) { if (key instanceof String) target.add(Instruction.makeVar(entry.location, (String)key));
((FunctionStatement)entry.value).compile(target, scope, entry.name, false);
target.add(Instruction.storeVar(key).locate(loc())); if (entry.value != null) {
} FunctionStatement.compileWithName(entry.value, target, scope, true, entry.name, BreakpointType.STEP_OVER);
else if (entry.value != null) { target.add(Instruction.storeVar(entry.location, key));
entry.value.compileWithPollution(target, scope);
target.add(Instruction.storeVar(key).locate(loc()));
} }
} }
if (pollute) target.add(Instruction.loadValue(loc(), null));
} }
public VariableDeclareStatement(Location loc, List<Pair> values) { public VariableDeclareStatement(Location loc, List<Pair> values) {

View File

@@ -1,8 +1,7 @@
package me.topchetoeu.jscript.compilation.control; package me.topchetoeu.jscript.compilation.control;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
@@ -11,11 +10,9 @@ public class BreakStatement extends Statement {
public final String label; public final String label;
@Override @Override
public boolean pollutesStack() { return false; } public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
target.add(Instruction.nop(loc(), "break", label));
@Override if (pollute) target.add(Instruction.loadValue(loc(), null));
public void compile(List<Instruction> target, ScopeRecord scope) {
target.add(Instruction.nop("break", label).locate(loc()));
} }
public BreakStatement(Location loc, String label) { public BreakStatement(Location loc, String label) {

View File

@@ -1,8 +1,7 @@
package me.topchetoeu.jscript.compilation.control; package me.topchetoeu.jscript.compilation.control;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
@@ -11,11 +10,9 @@ public class ContinueStatement extends Statement {
public final String label; public final String label;
@Override @Override
public boolean pollutesStack() { return false; } public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
target.add(Instruction.nop(loc(), "cont", label));
@Override if (pollute) target.add(Instruction.loadValue(loc(), null));
public void compile(List<Instruction> target, ScopeRecord scope) {
target.add(Instruction.nop("cont", label).locate(loc()));
} }
public ContinueStatement(Location loc, String label) { public ContinueStatement(Location loc, String label) {

View File

@@ -1,19 +1,16 @@
package me.topchetoeu.jscript.compilation.control; package me.topchetoeu.jscript.compilation.control;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class DebugStatement extends Statement { public class DebugStatement extends Statement {
@Override @Override
public boolean pollutesStack() { return false; } public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
target.add(Instruction.debug(loc()));
@Override if (pollute) target.add(Instruction.loadValue(loc(), null));
public void compile(List<Instruction> target, ScopeRecord scope) {
target.add(Instruction.debug().locate(loc()));
} }
public DebugStatement(Location loc) { public DebugStatement(Location loc) {

View File

@@ -1,8 +1,7 @@
package me.topchetoeu.jscript.compilation.control; package me.topchetoeu.jscript.compilation.control;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
@@ -12,13 +11,12 @@ public class DeleteStatement extends Statement {
public final Statement value; public final Statement value;
@Override @Override
public boolean pollutesStack() { return true; } public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
value.compile(target, scope, true);
key.compile(target, scope, true);
@Override target.add(Instruction.delete(loc()));
public void compile(List<Instruction> target, ScopeRecord scope) { if (pollute) target.add(Instruction.loadValue(loc(), true));
value.compile(target, scope);
key.compile(target, scope);
target.add(Instruction.delete().locate(loc()));
} }
public DeleteStatement(Location loc, Statement key, Statement value) { public DeleteStatement(Location loc, Statement key, Statement value) {

View File

@@ -1,76 +1,31 @@
package me.topchetoeu.jscript.compilation.control; package me.topchetoeu.jscript.compilation.control;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.CompoundStatement; import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.values.ConstantStatement; import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.engine.values.Values;
public class DoWhileStatement extends Statement { public class DoWhileStatement extends Statement {
public final Statement condition, body; public final Statement condition, body;
public final String label; public final String label;
@Override
public boolean pollutesStack() { return false; }
@Override @Override
public void declare(ScopeRecord globScope) { public void declare(ScopeRecord globScope) {
body.declare(globScope); body.declare(globScope);
} }
@Override @Override
public void compile(List<Instruction> target, ScopeRecord scope) { public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
if (condition instanceof ConstantStatement) {
int start = target.size();
body.compileNoPollution(target, scope);
int end = target.size();
if (Values.toBoolean(((ConstantStatement)condition).value)) {
WhileStatement.replaceBreaks(target, label, start, end, end + 1, end + 1);
}
else {
target.add(Instruction.jmp(start - end).locate(loc()));
WhileStatement.replaceBreaks(target, label, start, end, start, end + 1);
}
return;
}
int start = target.size(); int start = target.size();
body.compileNoPollution(target, scope, true); body.compile(target, scope, false, BreakpointType.STEP_OVER);
int mid = target.size(); int mid = target.size();
condition.compileWithPollution(target, scope); condition.compile(target, scope, true, BreakpointType.STEP_OVER);
int end = target.size(); int end = target.size();
WhileStatement.replaceBreaks(target, label, start, mid - 1, mid, end + 1); WhileStatement.replaceBreaks(target, label, start, mid - 1, mid, end + 1);
target.add(Instruction.jmpIf(start - end).locate(loc())); target.add(Instruction.jmpIf(loc(), start - end));
}
@Override
public Statement optimize() {
var cond = condition.optimize();
var b = body.optimize();
if (b instanceof CompoundStatement) {
var comp = (CompoundStatement)b;
if (comp.statements.length > 0) {
var last = comp.statements[comp.statements.length - 1];
if (last instanceof ContinueStatement) comp.statements[comp.statements.length - 1] = new CompoundStatement(loc());
if (last instanceof BreakStatement) {
comp.statements[comp.statements.length - 1] = new CompoundStatement(loc());
return new CompoundStatement(loc());
}
}
}
else if (b instanceof ContinueStatement) {
b = new CompoundStatement(loc());
}
else if (b instanceof BreakStatement) return new CompoundStatement(loc());
if (b.pure()) return new DoWhileStatement(loc(), label, cond, new CompoundStatement(loc()));
else return new DoWhileStatement(loc(), label, cond, b);
} }
public DoWhileStatement(Location loc, String label, Statement condition, Statement body) { public DoWhileStatement(Location loc, String label, Statement condition, Statement body) {

View File

@@ -1,10 +1,10 @@
package me.topchetoeu.jscript.compilation.control; package me.topchetoeu.jscript.compilation.control;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.engine.Operation; import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
@@ -13,9 +13,7 @@ public class ForInStatement extends Statement {
public final boolean isDeclaration; public final boolean isDeclaration;
public final Statement varValue, object, body; public final Statement varValue, object, body;
public final String label; public final String label;
public final Location varLocation;
@Override
public boolean pollutesStack() { return false; }
@Override @Override
public void declare(ScopeRecord globScope) { public void declare(ScopeRecord globScope) {
@@ -24,54 +22,47 @@ public class ForInStatement extends Statement {
} }
@Override @Override
public void compile(List<Instruction> target, ScopeRecord scope) { public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
var key = scope.getKey(varName); var key = scope.getKey(varName);
if (key instanceof String) target.add(Instruction.makeVar((String)key));
int first = target.size();
if (key instanceof String) target.add(Instruction.makeVar(loc(), (String)key));
if (varValue != null) { if (varValue != null) {
varValue.compileWithPollution(target, scope); varValue.compile(target, scope, true);
target.add(Instruction.storeVar(scope.getKey(varName))); target.add(Instruction.storeVar(loc(), scope.getKey(varName)));
} }
object.compileWithPollution(target, scope); object.compile(target, scope, true, BreakpointType.STEP_OVER);
target.add(Instruction.keys()); target.add(Instruction.keys(loc(), true));
int start = target.size(); int start = target.size();
target.add(Instruction.dup()); target.add(Instruction.dup(loc()));
target.add(Instruction.loadMember("length")); target.add(Instruction.loadValue(loc(), null));
target.add(Instruction.loadValue(0)); target.add(Instruction.operation(loc(), Operation.EQUALS));
target.add(Instruction.operation(Operation.LESS_EQUALS));
int mid = target.size(); int mid = target.size();
target.add(Instruction.nop()); target.add(Instruction.nop(loc()));
target.add(Instruction.dup()); target.add(Instruction.loadMember(varLocation, "value"));
target.add(Instruction.dup()); target.add(Instruction.storeVar(object.loc(), key));
target.add(Instruction.loadMember("length")); target.setDebug(BreakpointType.STEP_OVER);
target.add(Instruction.loadValue(1));
target.add(Instruction.operation(Operation.SUBTRACT));
target.add(Instruction.dup(1, 2));
target.add(Instruction.loadValue("length"));
target.add(Instruction.dup(1, 2));
target.add(Instruction.storeMember());
target.add(Instruction.loadMember());
target.add(Instruction.storeVar(key));
for (var i = start; i < target.size(); i++) target.get(i).locate(loc());
body.compileNoPollution(target, scope, true);
body.compile(target, scope, false, BreakpointType.STEP_OVER);
int end = target.size(); int end = target.size();
WhileStatement.replaceBreaks(target, label, mid + 1, end, start, end + 1); WhileStatement.replaceBreaks(target, label, mid + 1, end, start, end + 1);
target.add(Instruction.jmp(start - end).locate(loc())); target.add(Instruction.jmp(loc(), start - end));
target.add(Instruction.discard().locate(loc())); target.add(Instruction.discard(loc()));
target.set(mid, Instruction.jmpIf(end - mid + 1).locate(loc())); target.set(mid, Instruction.jmpIf(loc(), end - mid + 1));
if (pollute) target.add(Instruction.loadValue(loc(), null));
target.get(first).locate(loc());
} }
public ForInStatement(Location loc, String label, boolean isDecl, String varName, Statement varValue, Statement object, Statement body) { public ForInStatement(Location loc, Location varLocation, String label, boolean isDecl, String varName, Statement varValue, Statement object, Statement body) {
super(loc); super(loc);
this.varLocation = varLocation;
this.label = label; this.label = label;
this.isDeclaration = isDecl; this.isDeclaration = isDecl;
this.varName = varName; this.varName = varName;

View File

@@ -1,79 +1,39 @@
package me.topchetoeu.jscript.compilation.control; package me.topchetoeu.jscript.compilation.control;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.CompoundStatement; import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.values.ConstantStatement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.engine.values.Values;
public class ForStatement extends Statement { public class ForStatement extends Statement {
public final Statement declaration, assignment, condition, body; public final Statement declaration, assignment, condition, body;
public final String label; public final String label;
@Override
public boolean pollutesStack() { return false; }
@Override @Override
public void declare(ScopeRecord globScope) { public void declare(ScopeRecord globScope) {
declaration.declare(globScope); declaration.declare(globScope);
body.declare(globScope); body.declare(globScope);
} }
@Override @Override
public void compile(List<Instruction> target, ScopeRecord scope) { public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
declaration.compile(target, scope); declaration.compile(target, scope, false, BreakpointType.STEP_OVER);
if (condition instanceof ConstantStatement) {
if (Values.toBoolean(((ConstantStatement)condition).value)) {
int start = target.size();
body.compileNoPollution(target, scope);
int mid = target.size();
assignment.compileNoPollution(target, scope, true);
int end = target.size();
WhileStatement.replaceBreaks(target, label, start, mid, mid, end + 1);
target.add(Instruction.jmp(start - target.size()).locate(loc()));
return;
}
}
int start = target.size(); int start = target.size();
condition.compileWithPollution(target, scope); condition.compile(target, scope, true, BreakpointType.STEP_OVER);
int mid = target.size(); int mid = target.size();
target.add(Instruction.nop()); target.add(Instruction.nop(null));
body.compileNoPollution(target, scope); body.compile(target, scope, false, BreakpointType.STEP_OVER);
int beforeAssign = target.size(); int beforeAssign = target.size();
assignment.compile(target, scope); assignment.compile(target, scope, false, BreakpointType.STEP_OVER);
int end = target.size(); int end = target.size();
WhileStatement.replaceBreaks(target, label, mid + 1, end, beforeAssign, end + 1); WhileStatement.replaceBreaks(target, label, mid + 1, end, beforeAssign, end + 1);
target.add(Instruction.jmp(start - end).locate(loc())); target.add(Instruction.jmp(loc(), start - end));
target.set(mid, Instruction.jmpIfNot(end - mid + 1).locate(loc())); target.set(mid, Instruction.jmpIfNot(loc(), end - mid + 1));
} if (pollute) target.add(Instruction.loadValue(loc(), null));
@Override
public Statement optimize() {
var decl = declaration.optimize();
var asgn = assignment.optimize();
var cond = condition.optimize();
var b = body.optimize();
if (asgn.pure()) {
if (decl.pure()) return new WhileStatement(loc(), label, cond, b).optimize();
else return new CompoundStatement(loc(),
decl, new WhileStatement(loc(), label, cond, b)
).optimize();
}
else if (b instanceof ContinueStatement) return new CompoundStatement(loc(),
decl, new WhileStatement(loc(), label, cond, new CompoundStatement(loc(), b, asgn))
);
else if (b instanceof BreakStatement) return decl;
if (b.pure()) return new ForStatement(loc(), label, decl, cond, asgn, new CompoundStatement(null));
else return new ForStatement(loc(), label, decl, cond, asgn, b);
} }
public ForStatement(Location loc, String label, Statement declaration, Statement condition, Statement assignment, Statement body) { public ForStatement(Location loc, String label, Statement declaration, Statement condition, Statement assignment, Statement body) {
@@ -84,14 +44,4 @@ public class ForStatement extends Statement {
this.assignment = assignment; this.assignment = assignment;
this.body = body; this.body = body;
} }
public static CompoundStatement ofFor(Location loc, String label, Statement declaration, Statement condition, Statement increment, Statement body) {
return new CompoundStatement(loc,
declaration,
new WhileStatement(loc, label, condition, new CompoundStatement(loc,
body,
increment
))
);
}
} }

View File

@@ -1,74 +1,46 @@
package me.topchetoeu.jscript.compilation.control; package me.topchetoeu.jscript.compilation.control;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.CompoundStatement; import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.DiscardStatement;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.values.ConstantStatement; import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.engine.values.Values;
public class IfStatement extends Statement { public class IfStatement extends Statement {
public final Statement condition, body, elseBody; public final Statement condition, body, elseBody;
@Override
public boolean pollutesStack() { return false; }
@Override @Override
public void declare(ScopeRecord globScope) { public void declare(ScopeRecord globScope) {
body.declare(globScope); body.declare(globScope);
if (elseBody != null) elseBody.declare(globScope); if (elseBody != null) elseBody.declare(globScope);
} }
@Override @Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType breakpoint) {
public void compile(List<Instruction> target, ScopeRecord scope) { condition.compile(target, scope, true, breakpoint);
if (condition instanceof ConstantStatement) {
if (Values.not(((ConstantStatement)condition).value)) {
if (elseBody != null) elseBody.compileNoPollution(target, scope, true);
}
else {
body.compileNoPollution(target, scope, true);
}
return;
}
condition.compileWithPollution(target, scope);
if (elseBody == null) { if (elseBody == null) {
int i = target.size(); int i = target.size();
target.add(Instruction.nop()); target.add(Instruction.nop(null));
body.compileNoPollution(target, scope, true); body.compile(target, scope, pollute, breakpoint);
int endI = target.size(); int endI = target.size();
target.set(i, Instruction.jmpIfNot(endI - i).locate(loc())); target.set(i, Instruction.jmpIfNot(loc(), endI - i));
} }
else { else {
int start = target.size(); int start = target.size();
target.add(Instruction.nop()); target.add(Instruction.nop(null));
body.compileNoPollution(target, scope, true); body.compile(target, scope, pollute, breakpoint);
target.add(Instruction.nop()); target.add(Instruction.nop(null));
int mid = target.size(); int mid = target.size();
elseBody.compileNoPollution(target, scope, true); elseBody.compile(target, scope, pollute, breakpoint);
int end = target.size(); int end = target.size();
target.set(start, Instruction.jmpIfNot(mid - start).locate(loc())); target.set(start, Instruction.jmpIfNot(loc(), mid - start));
target.set(mid - 1, Instruction.jmp(end - mid + 1).locate(loc())); target.set(mid - 1, Instruction.jmp(loc(), end - mid + 1));
} }
} }
@Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
@Override compile(target, scope, pollute, BreakpointType.STEP_IN);
public Statement optimize() {
var cond = condition.optimize();
var b = body.optimize();
var e = elseBody == null ? null : elseBody.optimize();
if (b.pure()) b = new CompoundStatement(null);
if (e != null && e.pure()) e = null;
if (b.pure() && e == null) return new DiscardStatement(loc(), cond).optimize();
else return new IfStatement(loc(), cond, b, e);
} }
public IfStatement(Location loc, Statement condition, Statement body, Statement elseBody) { public IfStatement(Location loc, Statement condition, Statement body, Statement elseBody) {

View File

@@ -1,8 +1,7 @@
package me.topchetoeu.jscript.compilation.control; package me.topchetoeu.jscript.compilation.control;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
@@ -11,13 +10,10 @@ public class ReturnStatement extends Statement {
public final Statement value; public final Statement value;
@Override @Override
public boolean pollutesStack() { return false; } public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
if (value == null) target.add(Instruction.loadValue(loc(), null));
@Override else value.compile(target, scope, true);
public void compile(List<Instruction> target, ScopeRecord scope) { target.add(Instruction.ret(loc()));
if (value == null) target.add(Instruction.loadValue(null).locate(loc()));
else value.compileWithPollution(target, scope);
target.add(Instruction.ret().locate(loc()));
} }
public ReturnStatement(Location loc, Statement value) { public ReturnStatement(Location loc, Statement value) {

View File

@@ -1,11 +1,12 @@
package me.topchetoeu.jscript.compilation.control; package me.topchetoeu.jscript.compilation.control;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.Instruction.Type; import me.topchetoeu.jscript.compilation.Instruction.Type;
import me.topchetoeu.jscript.engine.Operation; import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
@@ -21,9 +22,6 @@ public class SwitchStatement extends Statement {
} }
} }
@Override
public boolean pollutesStack() { return false; }
public final Statement value; public final Statement value;
public final SwitchCase[] cases; public final SwitchCase[] cases;
public final Statement[] body; public final Statement[] body;
@@ -35,49 +33,48 @@ public class SwitchStatement extends Statement {
} }
@Override @Override
public void compile(List<Instruction> target, ScopeRecord scope) { public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
var caseMap = new HashMap<Integer, Integer>(); var caseToStatement = new HashMap<Integer, Integer>();
var stmIndexMap = new HashMap<Integer, Integer>(); var statementToIndex = new HashMap<Integer, Integer>();
value.compile(target, scope); value.compile(target, scope, true, BreakpointType.STEP_OVER);
for (var ccase : cases) { for (var ccase : cases) {
target.add(Instruction.dup().locate(loc())); target.add(Instruction.dup(loc()));
ccase.value.compileWithPollution(target, scope); ccase.value.compile(target, scope, true);
target.add(Instruction.operation(Operation.EQUALS).locate(loc())); target.add(Instruction.operation(loc(), Operation.EQUALS));
caseMap.put(target.size(), ccase.statementI); caseToStatement.put(target.size(), ccase.statementI);
target.add(Instruction.nop()); target.add(Instruction.nop(null));
} }
int start = target.size(); int start = target.size();
target.add(Instruction.nop()); target.add(Instruction.nop(null));
for (var stm : body) { for (var stm : body) {
stmIndexMap.put(stmIndexMap.size(), target.size()); statementToIndex.put(statementToIndex.size(), target.size());
stm.compileNoPollution(target, scope, true); stm.compile(target, scope, false, BreakpointType.STEP_OVER);
} }
if (defaultI < 0 || defaultI >= body.length) target.set(start, Instruction.jmp(target.size() - start).locate(loc())); int end = target.size();
else target.set(start, Instruction.jmp(stmIndexMap.get(defaultI) - start)).locate(loc()); target.add(Instruction.discard(loc()));
if (pollute) target.add(Instruction.loadValue(loc(), null));
for (int i = start; i < target.size(); i++) { if (defaultI < 0 || defaultI >= body.length) target.set(start, Instruction.jmp(loc(), end - start));
else target.set(start, Instruction.jmp(loc(), statementToIndex.get(defaultI) - start));
for (int i = start; i < end; i++) {
var instr = target.get(i); var instr = target.get(i);
if (instr.type == Type.NOP && instr.is(0, "break") && instr.get(1) == null) { if (instr.type == Type.NOP && instr.is(0, "break") && instr.get(1) == null) {
target.set(i, Instruction.jmp(target.size() - i).locate(instr.location)); target.set(i, Instruction.jmp(loc(), end - i).locate(instr.location));
}
if (instr.type == Type.NOP && instr.is(0, "try_break") && instr.get(1) == null) {
target.set(i, Instruction.signal("jmp_" + (target.size() - (Integer)instr.get(2))).locate(instr.location));
} }
} }
for (var el : caseMap.entrySet()) { for (var el : caseToStatement.entrySet()) {
var loc = target.get(el.getKey()).location; var i = statementToIndex.get(el.getValue());
var i = stmIndexMap.get(el.getValue()); if (i == null) i = end;
if (i == null) i = target.size(); target.set(el.getKey(), Instruction.jmpIf(loc(), i - el.getKey()));
target.set(el.getKey(), Instruction.jmpIf(i - el.getKey()).locate(loc).setDebug(true));
} }
target.add(Instruction.discard().locate(loc()));
} }
public SwitchStatement(Location loc, Statement value, int defaultI, SwitchCase[] cases, Statement[] body) { public SwitchStatement(Location loc, Statement value, int defaultI, SwitchCase[] cases, Statement[] body) {

View File

@@ -1,8 +1,7 @@
package me.topchetoeu.jscript.compilation.control; package me.topchetoeu.jscript.compilation.control;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
@@ -11,12 +10,9 @@ public class ThrowStatement extends Statement {
public final Statement value; public final Statement value;
@Override @Override
public boolean pollutesStack() { return false; } public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
value.compile(target, scope, true);
@Override target.add(Instruction.throwInstr(loc()));
public void compile(List<Instruction> target, ScopeRecord scope) {
value.compileWithPollution(target, scope);
target.add(Instruction.throwInstr().locate(loc()));
} }
public ThrowStatement(Location loc, Statement value) { public ThrowStatement(Location loc, Statement value) {

View File

@@ -1,10 +1,10 @@
package me.topchetoeu.jscript.compilation.control; package me.topchetoeu.jscript.compilation.control;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.engine.scope.GlobalScope; import me.topchetoeu.jscript.engine.scope.GlobalScope;
import me.topchetoeu.jscript.engine.scope.LocalScopeRecord; import me.topchetoeu.jscript.engine.scope.LocalScopeRecord;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
@@ -15,9 +15,6 @@ public class TryStatement extends Statement {
public final Statement finallyBody; public final Statement finallyBody;
public final String name; public final String name;
@Override
public boolean pollutesStack() { return false; }
@Override @Override
public void declare(ScopeRecord globScope) { public void declare(ScopeRecord globScope) {
tryBody.declare(globScope); tryBody.declare(globScope);
@@ -26,42 +23,32 @@ public class TryStatement extends Statement {
} }
@Override @Override
public void compile(List<Instruction> target, ScopeRecord scope) { public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType bpt) {
target.add(Instruction.nop()); target.add(Instruction.nop(null));
int start = target.size(), tryN, catchN = -1, finN = -1; int start = target.size(), catchStart = -1, finallyStart = -1;
tryBody.compileNoPollution(target, scope); tryBody.compile(target, scope, false);
tryN = target.size() - start; target.add(Instruction.tryEnd(loc()));
if (catchBody != null) { if (catchBody != null) {
int tmp = target.size(); catchStart = target.size() - start;
var local = scope instanceof GlobalScope ? scope.child() : (LocalScopeRecord)scope; var local = scope instanceof GlobalScope ? scope.child() : (LocalScopeRecord)scope;
local.define(name, true); local.define(name, true);
catchBody.compileNoPollution(target, scope); catchBody.compile(target, scope, false);
local.undefine(); local.undefine();
catchN = target.size() - tmp; target.add(Instruction.tryEnd(loc()));
} }
if (finallyBody != null) { if (finallyBody != null) {
int tmp = target.size(); finallyStart = target.size() - start;
finallyBody.compileNoPollution(target, scope); finallyBody.compile(target, scope, false);
finN = target.size() - tmp; target.add(Instruction.tryEnd(loc()));
} }
// for (int i = start; i < target.size(); i++) { target.set(start - 1, Instruction.tryStart(loc(), catchStart, finallyStart, target.size() - start));
// if (target.get(i).type == Type.NOP) { target.setDebug(start - 1, BreakpointType.STEP_OVER);
// var instr = target.get(i); if (pollute) target.add(Instruction.loadValue(loc(), null));
// if (instr.is(0, "break")) {
// target.set(i, Instruction.nop("try_break", instr.get(1), target.size()).locate(instr.location));
// }
// else if (instr.is(0, "cont")) {
// target.set(i, Instruction.nop("try_cont", instr.get(1), target.size()).locate(instr.location));
// }
// }
// }
target.set(start - 1, Instruction.tryInstr(tryN, catchN, finN).locate(loc()));
} }
public TryStatement(Location loc, Statement tryBody, Statement catchBody, Statement finallyBody, String name) { public TryStatement(Location loc, Statement tryBody, Statement catchBody, Statement finallyBody, String name) {

View File

@@ -1,66 +1,36 @@
package me.topchetoeu.jscript.compilation.control; package me.topchetoeu.jscript.compilation.control;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.CompoundStatement; import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.DiscardStatement;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.Instruction.Type; import me.topchetoeu.jscript.compilation.Instruction.Type;
import me.topchetoeu.jscript.compilation.values.ConstantStatement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.engine.values.Values;
public class WhileStatement extends Statement { public class WhileStatement extends Statement {
public final Statement condition, body; public final Statement condition, body;
public final String label; public final String label;
@Override
public boolean pollutesStack() { return false; }
@Override @Override
public void declare(ScopeRecord globScope) { public void declare(ScopeRecord globScope) {
body.declare(globScope); body.declare(globScope);
} }
@Override @Override
public void compile(List<Instruction> target, ScopeRecord scope) { public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
if (condition instanceof ConstantStatement) {
if (Values.toBoolean(((ConstantStatement)condition).value)) {
int start = target.size();
body.compileNoPollution(target, scope);
int end = target.size();
replaceBreaks(target, label, start, end, start, end + 1);
target.add(Instruction.jmp(start - target.size()).locate(loc()));
return;
}
}
int start = target.size(); int start = target.size();
condition.compileWithPollution(target, scope); condition.compile(target, scope, true);
int mid = target.size(); int mid = target.size();
target.add(Instruction.nop()); target.add(Instruction.nop(null));
body.compileNoPollution(target, scope); body.compile(target, scope, false, BreakpointType.STEP_OVER);
int end = target.size(); int end = target.size();
replaceBreaks(target, label, mid + 1, end, start, end + 1); replaceBreaks(target, label, mid + 1, end, start, end + 1);
target.add(Instruction.jmp(start - end).locate(loc())); target.add(Instruction.jmp(loc(), start - end));
target.set(mid, Instruction.jmpIfNot(end - mid + 1).locate(loc())); target.set(mid, Instruction.jmpIfNot(loc(), end - mid + 1));
} if (pollute) target.add(Instruction.loadValue(loc(), null));
@Override
public Statement optimize() {
var cond = condition.optimize();
var b = body.optimize();
if (b instanceof ContinueStatement) {
b = new CompoundStatement(loc());
}
else if (b instanceof BreakStatement) return new DiscardStatement(loc(), cond).optimize();
if (b.pure()) return new WhileStatement(loc(), label, cond, new CompoundStatement(null));
else return new WhileStatement(loc(), label, cond, b);
} }
public WhileStatement(Location loc, String label, Statement condition, Statement body) { public WhileStatement(Location loc, String label, Statement condition, Statement body) {
@@ -70,35 +40,15 @@ public class WhileStatement extends Statement {
this.body = body; this.body = body;
} }
public static void replaceBreaks(List<Instruction> target, String label, int start, int end, int continuePoint, int breakPoint) { public static void replaceBreaks(CompileTarget target, String label, int start, int end, int continuePoint, int breakPoint) {
for (int i = start; i < end; i++) { for (int i = start; i < end; i++) {
var instr = target.get(i); var instr = target.get(i);
if (instr.type == Type.NOP && instr.is(0, "cont") && (instr.get(1) == null || instr.is(1, label))) { if (instr.type == Type.NOP && instr.is(0, "cont") && (instr.get(1) == null || instr.is(1, label))) {
target.set(i, Instruction.jmp(continuePoint - i)); target.set(i, Instruction.jmp(instr.location, continuePoint - i).setDbgData(target.get(i)));
target.get(i).location = instr.location;
} }
if (instr.type == Type.NOP && instr.is(0, "break") && (instr.get(1) == null || instr.is(1, label))) { if (instr.type == Type.NOP && instr.is(0, "break") && (instr.get(1) == null || instr.is(1, label))) {
target.set(i, Instruction.jmp(breakPoint - i)); target.set(i, Instruction.jmp(instr.location, breakPoint - i).setDbgData(target.get(i)));
target.get(i).location = instr.location;
}
if (instr.type == Type.NOP && instr.is(0, "try_cont") && (instr.get(1) == null || instr.is(1, label))) {
target.set(i, Instruction.signal("jmp_" + (continuePoint - (Integer)instr.get(2))));
target.get(i).location = instr.location;
}
if (instr.type == Type.NOP && instr.is(0, "try_break") && (instr.get(1) == null || instr.is(1, label))) {
target.set(i, Instruction.signal("jmp_" + (breakPoint - (Integer)instr.get(2))));
target.get(i).location = instr.location;
} }
} }
} }
// public static CompoundStatement ofFor(Location loc, String label, Statement declaration, Statement condition, Statement increment, Statement body) {
// return new CompoundStatement(loc,
// declaration,
// new WhileStatement(loc, label, condition, new CompoundStatement(loc,
// body,
// increment
// ))
// );
// }
} }

View File

@@ -1,8 +1,7 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
@@ -10,24 +9,29 @@ import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class ArrayStatement extends Statement { public class ArrayStatement extends Statement {
public final Statement[] statements; public final Statement[] statements;
@Override @Override public boolean pure() {
public boolean pollutesStack() { return true; } for (var stm : statements) {
@Override if (!stm.pure()) return false;
public boolean pure() { return true; } }
return true;
}
@Override @Override
public void compile(List<Instruction> target, ScopeRecord scope) { public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
target.add(Instruction.loadArr(statements.length).locate(loc())); target.add(Instruction.loadArr(loc(), statements.length));
var i = 0;
for (var el : statements) { for (var i = 0; i < statements.length; i++) {
var el = statements[i];
if (el != null) { if (el != null) {
target.add(Instruction.dup().locate(loc())); target.add(Instruction.dup(loc()));
target.add(Instruction.loadValue(i).locate(loc())); target.add(Instruction.loadValue(loc(), i));
el.compileWithPollution(target, scope); el.compile(target, scope, true);
target.add(Instruction.storeMember().locate(loc())); target.add(Instruction.storeMember(loc()));
} }
i++;
} }
if (!pollute) target.add(Instruction.discard(loc()));
} }
public ArrayStatement(Location loc, Statement[] statements) { public ArrayStatement(Location loc, Statement[] statements) {

View File

@@ -1,43 +1,50 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class CallStatement extends Statement { public class CallStatement extends Statement {
public final Statement func; public final Statement func;
public final Statement[] args; public final Statement[] args;
public final boolean isNew;
@Override @Override
public boolean pollutesStack() { return true; } public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType type) {
if (isNew) func.compile(target, scope, true);
@Override else if (func instanceof IndexStatement) {
public void compile(List<Instruction> target, ScopeRecord scope) { ((IndexStatement)func).compile(target, scope, true, true);
if (func instanceof IndexStatement) {
((IndexStatement)func).compile(target, scope, true);
} }
else { else {
target.add(Instruction.loadValue(null).locate(loc())); target.add(Instruction.loadValue(loc(), null));
func.compileWithPollution(target, scope); func.compile(target, scope, true);
} }
for (var arg : args) { for (var arg : args) arg.compile(target, scope, true);
arg.compileWithPollution(target, scope);
}
target.add(Instruction.call(args.length).locate(loc()).setDebug(true)); if (isNew) target.add(Instruction.callNew(loc(), args.length));
else target.add(Instruction.call(loc(), args.length));
target.setDebug(type);
if (!pollute) target.add(Instruction.discard(loc()));
}
@Override
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
compile(target, scope, pollute, BreakpointType.STEP_IN);
} }
public CallStatement(Location loc, Statement func, Statement... args) { public CallStatement(Location loc, boolean isNew, Statement func, Statement ...args) {
super(loc); super(loc);
this.isNew = isNew;
this.func = func; this.func = func;
this.args = args; this.args = args;
} }
public CallStatement(Location loc, Statement obj, Object key, Statement... args) { public CallStatement(Location loc, boolean isNew, Statement obj, Object key, Statement ...args) {
super(loc); super(loc);
this.isNew = isNew;
this.func = new IndexStatement(loc, obj, new ConstantStatement(loc, key)); this.func = new IndexStatement(loc, obj, new ConstantStatement(loc, key));
this.args = args; this.args = args;
} }

View File

@@ -1,9 +1,8 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.AssignableStatement; import me.topchetoeu.jscript.compilation.AssignableStatement;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.Operation; import me.topchetoeu.jscript.engine.Operation;
@@ -15,11 +14,13 @@ public class ChangeStatement extends Statement {
public final boolean postfix; public final boolean postfix;
@Override @Override
public boolean pollutesStack() { return true; } public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
value.toAssign(new ConstantStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, scope, true);
@Override if (!pollute) target.add(Instruction.discard(loc()));
public void compile(List<Instruction> target, ScopeRecord scope) { else if (postfix) {
value.toAssign(new ConstantStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, scope, postfix); target.add(Instruction.loadValue(loc(), addAmount));
target.add(Instruction.operation(loc(), Operation.SUBTRACT));
}
} }
public ChangeStatement(Location loc, AssignableStatement value, double addAmount, boolean postfix) { public ChangeStatement(Location loc, AssignableStatement value, double addAmount, boolean postfix) {

View File

@@ -1,38 +0,0 @@
package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class CommaStatement extends Statement {
public final Statement first;
public final Statement second;
@Override
public boolean pollutesStack() { return true; }
@Override
public boolean pure() { return first.pure() && second.pure(); }
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
first.compileNoPollution(target, scope);
second.compileWithPollution(target, scope);
}
@Override
public Statement optimize() {
var f = first.optimize();
var s = second.optimize();
if (f.pure()) return s;
else return new CommaStatement(loc(), f, s);
}
public CommaStatement(Location loc, Statement first, Statement second) {
super(loc);
this.first = first;
this.second = second;
}
}

View File

@@ -1,8 +1,7 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
@@ -10,14 +9,11 @@ import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class ConstantStatement extends Statement { public class ConstantStatement extends Statement {
public final Object value; public final Object value;
@Override @Override public boolean pure() { return true; }
public boolean pollutesStack() { return true; }
@Override
public boolean pure() { return true; }
@Override @Override
public void compile(List<Instruction> target, ScopeRecord scope) { public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
target.add(Instruction.loadValue(value).locate(loc())); if (pollute) target.add(Instruction.loadValue(loc(), value));
} }
public ConstantStatement(Location loc, Object val) { public ConstantStatement(Location loc, Object val) {

View File

@@ -0,0 +1,24 @@
package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class DiscardStatement extends Statement {
public final Statement value;
@Override public boolean pure() { return value.pure(); }
@Override
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
value.compile(target, scope, false);
if (pollute) target.add(Instruction.loadValue(loc(), null));
}
public DiscardStatement(Location loc, Statement val) {
super(loc);
this.value = val;
}
}

View File

@@ -1,108 +1,139 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values;
import java.util.List; import java.util.Random;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.CompoundStatement; import me.topchetoeu.jscript.compilation.CompoundStatement;
import me.topchetoeu.jscript.compilation.FunctionBody;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.Instruction.Type; import me.topchetoeu.jscript.compilation.Instruction.Type;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.exceptions.SyntaxException; import me.topchetoeu.jscript.exceptions.SyntaxException;
public class FunctionStatement extends Statement { public class FunctionStatement extends Statement {
public final CompoundStatement body; public final CompoundStatement body;
public final String name; public final String varName;
public final String[] args; public final String[] args;
public final boolean statement;
public final Location end;
@Override private static Random rand = new Random();
public boolean pure() { return name == null; }
@Override @Override public boolean pure() { return varName == null && statement; }
public boolean pollutesStack() { return true; }
@Override @Override
public void declare(ScopeRecord scope) { public void declare(ScopeRecord scope) {
if (name != null) scope.define(name); if (varName != null && statement) scope.define(varName);
} }
public static void checkBreakAndCont(List<Instruction> target, int start) { public static void checkBreakAndCont(CompileTarget target, int start) {
for (int i = start; i < target.size(); i++) { for (int i = start; i < target.size(); i++) {
if (target.get(i).type == Type.NOP) { if (target.get(i).type == Type.NOP) {
if (target.get(i).is(0, "break") || target.get(i).is(0, "try_break")) { if (target.get(i).is(0, "break") ) {
throw new SyntaxException(target.get(i).location, "Break was placed outside a loop."); throw new SyntaxException(target.get(i).location, "Break was placed outside a loop.");
} }
if (target.get(i).is(0, "cont") || target.get(i).is(0, "try_cont")) { if (target.get(i).is(0, "cont")) {
throw new SyntaxException(target.get(i).location, "Continue was placed outside a loop."); throw new SyntaxException(target.get(i).location, "Continue was placed outside a loop.");
} }
} }
} }
} }
public void compile(List<Instruction> target, ScopeRecord scope, String name, boolean isStatement) { private long compileBody(CompileTarget target, ScopeRecord scope, boolean polute, BreakpointType bp) {
for (var i = 0; i < args.length; i++) { for (var i = 0; i < args.length; i++) {
for (var j = 0; j < i; j++) { for (var j = 0; j < i; j++) {
if (args[i].equals(args[j])){ if (args[i].equals(args[j])) {
target.add(Instruction.throwSyntax(new SyntaxException(loc(), "Duplicate parameter '" + args[i] + "'."))); throw new SyntaxException(loc(), "Duplicate parameter '" + args[i] + "'.");
return;
} }
} }
} }
var id = rand.nextLong();
var subscope = scope.child(); var subscope = scope.child();
var subtarget = new CompileTarget(target.functions, target.breakpoints);
int start = target.size();
target.add(Instruction.nop());
subscope.define("this"); subscope.define("this");
var argsVar = subscope.define("arguments"); var argsVar = subscope.define("arguments");
if (args.length > 0) { if (args.length > 0) {
for (var i = 0; i < args.length; i++) { for (var i = 0; i < args.length; i++) {
target.add(Instruction.loadVar(argsVar).locate(loc())); subtarget.add(Instruction.loadVar(loc(), argsVar));
target.add(Instruction.loadMember(i).locate(loc())); subtarget.add(Instruction.loadMember(loc(), i));
target.add(Instruction.storeVar(subscope.define(args[i])).locate(loc())); subtarget.add(Instruction.storeVar(loc(), subscope.define(args[i])));
} }
} }
if (!isStatement && this.name != null) { if (!statement && this.varName != null) {
target.add(Instruction.storeSelfFunc((int)subscope.define(this.name))); subtarget.add(Instruction.storeSelfFunc(loc(), (int)subscope.define(this.varName)));
subtarget.setDebug(bp);
} }
body.declare(subscope); body.declare(subscope);
target.add(Instruction.debugVarNames(subscope.locals())); body.compile(subtarget, subscope, false);
body.compile(target, subscope); subtarget.add(Instruction.ret(end));
checkBreakAndCont(subtarget, 0);
checkBreakAndCont(target, start); if (polute) target.add(Instruction.loadFunc(loc(), id, subscope.getCaptures()));
target.functions.put(id, new FunctionBody(
subscope.localsCount(), args.length,
subtarget.array(), subscope.captures(), subscope.locals()
));
if (!(body instanceof CompoundStatement)) target.add(Instruction.ret().locate(loc())); return id;
target.set(start, Instruction.loadFunc(target.size() - start, subscope.localsCount(), args.length, subscope.getCaptures()).locate(loc()));
if (name == null) name = this.name;
if (name != null) {
target.add(Instruction.dup().locate(loc()));
target.add(Instruction.loadValue("name").locate(loc()));
target.add(Instruction.loadValue(name).locate(loc()));
target.add(Instruction.storeMember().locate(loc()));
}
if (this.name != null && isStatement) {
var key = scope.getKey(this.name);
if (key instanceof String) target.add(Instruction.makeVar((String)key).locate(loc()));
target.add(Instruction.storeVar(scope.getKey(this.name), true).locate(loc()));
}
}
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
compile(target, scope, null, false);
} }
public FunctionStatement(Location loc, String name, String[] args, CompoundStatement body) { public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, String name, BreakpointType bp) {
if (this.varName != null) name = this.varName;
var hasVar = this.varName != null && statement;
var hasName = name != null;
compileBody(target, scope, pollute || hasVar || hasName, bp);
if (hasName) {
if (pollute || hasVar) target.add(Instruction.dup(loc()));
target.add(Instruction.loadValue(loc(), "name"));
target.add(Instruction.loadValue(loc(), name));
target.add(Instruction.storeMember(loc()));
}
if (hasVar) {
var key = scope.getKey(this.varName);
if (key instanceof String) target.add(Instruction.makeVar(loc(), (String)key));
target.add(Instruction.storeVar(loc(), scope.getKey(this.varName), false));
}
}
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, String name) {
compile(target, scope, pollute, name, BreakpointType.NONE);
}
@Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType bp) {
compile(target, scope, pollute, (String)null, bp);
}
@Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
compile(target, scope, pollute, (String)null, BreakpointType.NONE);
}
public FunctionStatement(Location loc, Location end, String varName, String[] args, boolean statement, CompoundStatement body) {
super(loc); super(loc);
this.name = name;
this.end = end;
this.varName = varName;
this.statement = statement;
this.args = args; this.args = args;
this.body = body; this.body = body;
} }
public static void compileWithName(Statement stm, CompileTarget target, ScopeRecord scope, boolean pollute, String name) {
if (stm instanceof FunctionStatement) ((FunctionStatement)stm).compile(target, scope, pollute, name);
else stm.compile(target, scope, pollute);
}
public static void compileWithName(Statement stm, CompileTarget target, ScopeRecord scope, boolean pollute, String name, BreakpointType bp) {
if (stm instanceof FunctionStatement) ((FunctionStatement)stm).compile(target, scope, pollute, name, bp);
else stm.compile(target, scope, pollute, bp);
}
} }

View File

@@ -1,21 +1,17 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class GlobalThisStatement extends Statement { public class GlobalThisStatement extends Statement {
@Override @Override public boolean pure() { return true; }
public boolean pollutesStack() { return true; }
@Override
public boolean pure() { return true; }
@Override @Override
public void compile(List<Instruction> target, ScopeRecord scope) { public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
target.add(Instruction.loadGlob().locate(loc())); if (pollute) target.add(Instruction.loadGlob(loc()));
} }
public GlobalThisStatement(Location loc) { public GlobalThisStatement(Location loc) {

View File

@@ -1,51 +1,41 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.AssignStatement; import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.engine.Operation; import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class IndexAssignStatement extends AssignStatement { public class IndexAssignStatement extends Statement {
public final Statement object; public final Statement object;
public final Statement index; public final Statement index;
public final Statement value; public final Statement value;
public final Operation operation; public final Operation operation;
@Override @Override
public boolean pollutesStack() { return true; } public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
@Override
public void compile(List<Instruction> target, ScopeRecord scope, boolean retPrevValue) {
int start = 0;
if (operation != null) { if (operation != null) {
object.compileWithPollution(target, scope); object.compile(target, scope, true);
index.compileWithPollution(target, scope); index.compile(target, scope, true);
target.add(Instruction.dup(2, 0).locate(loc())); target.add(Instruction.dup(loc(), 2));
target.add(Instruction.loadMember().locate(loc())); target.add(Instruction.loadMember(loc()));
if (retPrevValue) { value.compile(target, scope, true);
target.add(Instruction.dup().locate(loc())); target.add(Instruction.operation(loc(), operation));
target.add(Instruction.move(3, 1).locate(loc()));
}
value.compileWithPollution(target, scope);
target.add(Instruction.operation(operation).locate(loc()));
target.add(Instruction.storeMember(!retPrevValue).locate(loc()).setDebug(true)); target.add(Instruction.storeMember(loc(), pollute));
target.setDebug(BreakpointType.STEP_IN);
} }
else { else {
object.compileWithPollution(target, scope); object.compile(target, scope, true);
if (retPrevValue) target.add(Instruction.dup().locate(loc())); index.compile(target, scope, true);
index.compileWithPollution(target, scope); value.compile(target, scope, true);
value.compileWithPollution(target, scope);
target.add(Instruction.storeMember(!retPrevValue).locate(loc()).setDebug(true)); target.add(Instruction.storeMember(loc(), pollute));
target.setDebug(BreakpointType.STEP_IN);
} }
target.get(start);
} }
public IndexAssignStatement(Location loc, Statement object, Statement index, Statement value, Operation operation) { public IndexAssignStatement(Location loc, Statement object, Statement index, Statement value, Operation operation) {

View File

@@ -1,12 +1,11 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.AssignStatement;
import me.topchetoeu.jscript.compilation.AssignableStatement; import me.topchetoeu.jscript.compilation.AssignableStatement;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.engine.Operation; import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
@@ -15,30 +14,26 @@ public class IndexStatement extends AssignableStatement {
public final Statement index; public final Statement index;
@Override @Override
public boolean pollutesStack() { return true; } public Statement toAssign(Statement val, Operation operation) {
@Override
public boolean pure() { return true; }
@Override
public AssignStatement toAssign(Statement val, Operation operation) {
return new IndexAssignStatement(loc(), object, index, val, operation); return new IndexAssignStatement(loc(), object, index, val, operation);
} }
public void compile(List<Instruction> target, ScopeRecord scope, boolean dupObj) { public void compile(CompileTarget target, ScopeRecord scope, boolean dupObj, boolean pollute) {
int start = 0; object.compile(target, scope, true);
object.compileWithPollution(target, scope); if (dupObj) target.add(Instruction.dup(loc()));
if (dupObj) target.add(Instruction.dup().locate(loc()));
if (index instanceof ConstantStatement) { if (index instanceof ConstantStatement) {
target.add(Instruction.loadMember(((ConstantStatement)index).value).locate(loc())); target.add(Instruction.loadMember(loc(), ((ConstantStatement)index).value));
target.setDebug(BreakpointType.STEP_IN);
return; return;
} }
index.compileWithPollution(target, scope); index.compile(target, scope, true);
target.add(Instruction.loadMember().locate(loc())); target.add(Instruction.loadMember(loc()));
target.get(start).setDebug(true); target.setDebug(BreakpointType.STEP_IN);
if (!pollute) target.add(Instruction.discard(loc()));
} }
@Override @Override
public void compile(List<Instruction> target, ScopeRecord scope) { public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
compile(target, scope, false); compile(target, scope, false, pollute);
} }
public IndexStatement(Location loc, Statement object, Statement index) { public IndexStatement(Location loc, Statement object, Statement index) {

View File

@@ -1,8 +1,7 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
@@ -11,30 +10,25 @@ import me.topchetoeu.jscript.engine.values.Values;
public class LazyAndStatement extends Statement { public class LazyAndStatement extends Statement {
public final Statement first, second; public final Statement first, second;
@Override @Override public boolean pure() { return first.pure() && second.pure(); }
public boolean pollutesStack() { return true; }
@Override
public boolean pure() {
return first.pure() && second.pure();
}
@Override @Override
public void compile(List<Instruction> target, ScopeRecord scope) { public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
if (first instanceof ConstantStatement) { if (first instanceof ConstantStatement) {
if (Values.not(((ConstantStatement)first).value)) { if (Values.not(((ConstantStatement)first).value)) {
first.compileWithPollution(target, scope); first.compile(target, scope, pollute);
} }
else second.compileWithPollution(target, scope); else second.compile(target, scope, pollute);
return; return;
} }
first.compileWithPollution(target, scope); first.compile(target, scope, true);
target.add(Instruction.dup().locate(loc())); if (pollute) target.add(Instruction.dup(loc()));
int start = target.size(); int start = target.size();
target.add(Instruction.nop()); target.add(Instruction.nop(null));
target.add(Instruction.discard().locate(loc())); if (pollute) target.add(Instruction.discard(loc()));
second.compileWithPollution(target, scope); second.compile(target, scope, pollute);
target.set(start, Instruction.jmpIfNot(target.size() - start).locate(loc())); target.set(start, Instruction.jmpIfNot(loc(), target.size() - start));
} }
public LazyAndStatement(Location loc, Statement first, Statement second) { public LazyAndStatement(Location loc, Statement first, Statement second) {

View File

@@ -1,8 +1,7 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
@@ -11,30 +10,25 @@ import me.topchetoeu.jscript.engine.values.Values;
public class LazyOrStatement extends Statement { public class LazyOrStatement extends Statement {
public final Statement first, second; public final Statement first, second;
@Override @Override public boolean pure() { return first.pure() && second.pure(); }
public boolean pollutesStack() { return true; }
@Override
public boolean pure() {
return first.pure() && second.pure();
}
@Override @Override
public void compile(List<Instruction> target, ScopeRecord scope) { public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
if (first instanceof ConstantStatement) { if (first instanceof ConstantStatement) {
if (Values.not(((ConstantStatement)first).value)) { if (Values.not(((ConstantStatement)first).value)) {
second.compileWithPollution(target, scope); second.compile(target, scope, pollute);
} }
else first.compileWithPollution(target, scope); else first.compile(target, scope, pollute);
return; return;
} }
first.compileWithPollution(target, scope); first.compile(target, scope, true);
target.add(Instruction.dup().locate(loc())); if (pollute) target.add(Instruction.dup(loc()));
int start = target.size(); int start = target.size();
target.add(Instruction.nop()); target.add(Instruction.nop(null));
target.add(Instruction.discard().locate(loc())); if (pollute) target.add(Instruction.discard(loc()));
second.compileWithPollution(target, scope); second.compile(target, scope, pollute);
target.set(start, Instruction.jmpIf(target.size() - start).locate(loc())); target.set(start, Instruction.jmpIf(loc(), target.size() - start));
} }
public LazyOrStatement(Location loc, Statement first, Statement second) { public LazyOrStatement(Location loc, Statement first, Statement second) {

View File

@@ -1,32 +0,0 @@
package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class NewStatement extends Statement {
public final Statement func;
public final Statement[] args;
@Override
public boolean pollutesStack() { return true; }
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
func.compileWithPollution(target, scope);
for (var arg : args) {
arg.compileWithPollution(target, scope);
}
target.add(Instruction.callNew(args.length).locate(loc()).setDebug(true));
}
public NewStatement(Location loc, Statement func, Statement... args) {
super(loc);
this.func = func;
this.args = args;
}
}

View File

@@ -1,10 +1,10 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
@@ -14,20 +14,24 @@ public class ObjectStatement extends Statement {
public final Map<Object, FunctionStatement> getters; public final Map<Object, FunctionStatement> getters;
public final Map<Object, FunctionStatement> setters; public final Map<Object, FunctionStatement> setters;
@Override @Override public boolean pure() {
public boolean pollutesStack() { return true; } for (var el : map.values()) {
if (!el.pure()) return false;
}
return true;
}
@Override @Override
public void compile(List<Instruction> target, ScopeRecord scope) { public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
target.add(Instruction.loadObj().locate(loc())); target.add(Instruction.loadObj(loc()));
for (var el : map.entrySet()) { for (var el : map.entrySet()) {
target.add(Instruction.dup().locate(loc())); target.add(Instruction.dup(loc()));
target.add(Instruction.loadValue(el.getKey()).locate(loc())); target.add(Instruction.loadValue(loc(), el.getKey()));
var val = el.getValue(); var val = el.getValue();
if (val instanceof FunctionStatement) ((FunctionStatement)val).compile(target, scope, el.getKey().toString(), false); FunctionStatement.compileWithName(val, target, scope, true, el.getKey().toString());
else val.compileWithPollution(target, scope); target.add(Instruction.storeMember(loc()));
target.add(Instruction.storeMember().locate(loc()));
} }
var keys = new ArrayList<Object>(); var keys = new ArrayList<Object>();
@@ -35,17 +39,19 @@ public class ObjectStatement extends Statement {
keys.addAll(setters.keySet()); keys.addAll(setters.keySet());
for (var key : keys) { for (var key : keys) {
if (key instanceof String) target.add(Instruction.loadValue((String)key).locate(loc())); if (key instanceof String) target.add(Instruction.loadValue(loc(), (String)key));
else target.add(Instruction.loadValue((Double)key).locate(loc())); else target.add(Instruction.loadValue(loc(), (Double)key));
if (getters.containsKey(key)) getters.get(key).compileWithPollution(target, scope); if (getters.containsKey(key)) getters.get(key).compile(target, scope, true);
else target.add(Instruction.loadValue(null).locate(loc())); else target.add(Instruction.loadValue(loc(), null));
if (setters.containsKey(key)) setters.get(key).compileWithPollution(target, scope); if (setters.containsKey(key)) setters.get(key).compile(target, scope, true);
else target.add(Instruction.loadValue(null).locate(loc())); else target.add(Instruction.loadValue(loc(), null));
target.add(Instruction.defProp().locate(loc())); target.add(Instruction.defProp(loc()));
} }
if (!pollute) target.add(Instruction.discard(loc()));
} }
public ObjectStatement(Location loc, Map<Object, Statement> map, Map<Object, FunctionStatement> getters, Map<Object, FunctionStatement> setters) { public ObjectStatement(Location loc, Map<Object, Statement> map, Map<Object, FunctionStatement> getters, Map<Object, FunctionStatement> setters) {

View File

@@ -1,103 +1,35 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.control.ThrowStatement;
import me.topchetoeu.jscript.engine.CallContext;
import me.topchetoeu.jscript.engine.Operation; import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.exceptions.EngineException;
public class OperationStatement extends Statement { public class OperationStatement extends Statement {
public final Statement[] args; public final Statement[] args;
public final Operation operation; public final Operation operation;
@Override @Override public boolean pure() {
public void compile(List<Instruction> target, ScopeRecord scope) { for (var el : args) {
for (var arg : args) { if (!el.pure()) return false;
arg.compileWithPollution(target, scope);
} }
target.add(Instruction.operation(operation).locate(loc()));
}
@Override
public boolean pollutesStack() { return true; }
@Override
public boolean pure() {
for (var arg : args) {
if (!arg.pure()) return false;
}
return true; return true;
} }
@Override @Override
public Statement optimize() { public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
var args = new Statement[this.args.length]; for (var arg : args) {
var allConst = true; arg.compile(target, scope, true);
for (var i = 0; i < this.args.length; i++) {
args[i] = this.args[i].optimize();
if (!(args[i] instanceof ConstantStatement)) allConst = false;
} }
if (allConst) { if (pollute) target.add(Instruction.operation(loc(), operation));
var vals = new Object[this.args.length]; else target.add(Instruction.discard(loc()));
for (var i = 0; i < args.length; i++) {
vals[i] = ((ConstantStatement)args[i]).value;
}
try {
var ctx = new CallContext(null);
switch (operation) {
case ADD: return new ConstantStatement(loc(), Values.add(ctx, vals[0], vals[1]));
case SUBTRACT: return new ConstantStatement(loc(), Values.subtract(ctx, vals[0], vals[1]));
case DIVIDE: return new ConstantStatement(loc(), Values.divide(ctx, vals[0], vals[1]));
case MULTIPLY: return new ConstantStatement(loc(), Values.multiply(ctx, vals[0], vals[1]));
case MODULO: return new ConstantStatement(loc(), Values.modulo(ctx, vals[0], vals[1]));
case AND: return new ConstantStatement(loc(), Values.and(ctx, vals[0], vals[1]));
case OR: return new ConstantStatement(loc(), Values.or(ctx, vals[0], vals[1]));
case XOR: return new ConstantStatement(loc(), Values.xor(ctx, vals[0], vals[1]));
case EQUALS: return new ConstantStatement(loc(), Values.strictEquals(vals[0], vals[1]));
case NOT_EQUALS: return new ConstantStatement(loc(), !Values.strictEquals(vals[0], vals[1]));
case LOOSE_EQUALS: return new ConstantStatement(loc(), Values.looseEqual(ctx, vals[0], vals[1]));
case LOOSE_NOT_EQUALS: return new ConstantStatement(loc(), !Values.looseEqual(ctx, vals[0], vals[1]));
case GREATER: return new ConstantStatement(loc(), Values.compare(ctx, vals[0], vals[1]) < 0);
case GREATER_EQUALS: return new ConstantStatement(loc(), Values.compare(ctx, vals[0], vals[1]) <= 0);
case LESS: return new ConstantStatement(loc(), Values.compare(ctx, vals[0], vals[1]) > 0);
case LESS_EQUALS: return new ConstantStatement(loc(), Values.compare(ctx, vals[0], vals[1]) >= 0);
case INVERSE: return new ConstantStatement(loc(), Values.bitwiseNot(ctx, vals[0]));
case NOT: return new ConstantStatement(loc(), Values.not(vals[0]));
case POS: return new ConstantStatement(loc(), Values.toNumber(ctx, vals[0]));
case NEG: return new ConstantStatement(loc(), Values.negative(ctx, vals[0]));
case SHIFT_LEFT: return new ConstantStatement(loc(), Values.shiftLeft(ctx, vals[0], vals[1]));
case SHIFT_RIGHT: return new ConstantStatement(loc(), Values.shiftRight(ctx, vals[0], vals[1]));
case USHIFT_RIGHT: return new ConstantStatement(loc(), Values.unsignedShiftRight(ctx, vals[0], vals[1]));
default: break;
}
}
catch (EngineException e) {
return new ThrowStatement(loc(), new ConstantStatement(loc(), e.value));
}
catch (InterruptedException e) { return null; }
}
return new OperationStatement(loc(), operation, args);
} }
public OperationStatement(Location loc, Operation operation, Statement... args) { public OperationStatement(Location loc, Operation operation, Statement ...args) {
super(loc); super(loc);
this.operation = operation; this.operation = operation;
this.args = args; this.args = args;

View File

@@ -1,8 +1,7 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
@@ -10,14 +9,13 @@ import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class RegexStatement extends Statement { public class RegexStatement extends Statement {
public final String pattern, flags; public final String pattern, flags;
@Override // Not really pure, since a function is called, but can be ignored.
public boolean pollutesStack() { return true; } @Override public boolean pure() { return true; }
@Override
public boolean pure() { return true; }
@Override @Override
public void compile(List<Instruction> target, ScopeRecord scope) { public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
target.add(Instruction.loadRegex(pattern, flags).locate(loc())); target.add(Instruction.loadRegex(loc(), pattern, flags));
if (!pollute) target.add(Instruction.discard(loc()));
} }
public RegexStatement(Location loc, String pattern, String flags) { public RegexStatement(Location loc, String pattern, String flags) {

View File

@@ -1,58 +0,0 @@
package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.engine.values.Values;
public class TernaryStatement extends Statement {
public final Statement condition;
public final Statement first;
public final Statement second;
@Override
public boolean pollutesStack() { return true; }
@Override
public boolean pure() { return true; }
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
if (condition instanceof ConstantStatement) {
if (!Values.toBoolean(((ConstantStatement)condition).value)) {
second.compileWithPollution(target, scope);
}
else first.compileWithPollution(target, scope);
return;
}
condition.compileWithPollution(target, scope);
int start = target.size();
target.add(Instruction.nop());
first.compileWithPollution(target, scope);
int mid = target.size();
target.add(Instruction.nop());
second.compileWithPollution(target, scope);
int end = target.size();
target.set(start, Instruction.jmpIfNot(mid - start + 1).locate(loc()));
target.set(mid, Instruction.jmp(end - mid).locate(loc()));
}
@Override
public Statement optimize() {
var cond = condition.optimize();
var f = first.optimize();
var s = second.optimize();
return new TernaryStatement(loc(), cond, f, s);
}
public TernaryStatement(Location loc, Statement condition, Statement first, Statement second) {
super(loc);
this.condition = condition;
this.first = first;
this.second = second;
}
}

View File

@@ -1,49 +1,29 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.engine.values.Values;
public class TypeofStatement extends Statement { public class TypeofStatement extends Statement {
public final Statement value; public final Statement value;
@Override // Not really pure, since a variable from the global scope could be accessed,
public boolean pollutesStack() { return true; } // which could lead to code execution, that would get omitted
@Override @Override public boolean pure() { return true; }
public boolean pure() { return true; }
@Override @Override
public void compile(List<Instruction> target, ScopeRecord scope) { public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
if (value instanceof VariableStatement) { if (value instanceof VariableStatement) {
var i = scope.getKey(((VariableStatement)value).name); var i = scope.getKey(((VariableStatement)value).name);
if (i instanceof String) { if (i instanceof String) {
target.add(Instruction.typeof((String)i).locate(loc())); target.add(Instruction.typeof(loc(), (String)i));
return; return;
} }
} }
value.compileWithPollution(target, scope); value.compile(target, scope, pollute);
target.add(Instruction.typeof().locate(loc())); target.add(Instruction.typeof(loc()));
}
@Override
public Statement optimize() {
var val = value.optimize();
if (val instanceof ConstantStatement) {
return new ConstantStatement(loc(), Values.type(((ConstantStatement)val).value));
}
else if (
val instanceof ObjectStatement ||
val instanceof ArrayStatement ||
val instanceof GlobalThisStatement
) return new ConstantStatement(loc(), "object");
else if(val instanceof FunctionStatement) return new ConstantStatement(loc(), "function");
return new TypeofStatement(loc(), val);
} }
public TypeofStatement(Location loc, Statement value) { public TypeofStatement(Location loc, Statement value) {

View File

@@ -1,38 +1,31 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.AssignStatement; import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.engine.Operation; import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class VariableAssignStatement extends AssignStatement { public class VariableAssignStatement extends Statement {
public final String name; public final String name;
public final Statement value; public final Statement value;
public final Operation operation; public final Operation operation;
@Override @Override public boolean pure() { return false; }
public boolean pollutesStack() { return true; }
@Override @Override
public void compile(List<Instruction> target, ScopeRecord scope, boolean retPrevValue) { public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
var i = scope.getKey(name); var i = scope.getKey(name);
if (operation != null) { if (operation != null) {
target.add(Instruction.loadVar(i).locate(loc())); target.add(Instruction.loadVar(loc(), i));
if (retPrevValue) target.add(Instruction.dup().locate(loc())); FunctionStatement.compileWithName(value, target, scope, true, name);
if (value instanceof FunctionStatement) ((FunctionStatement)value).compile(target, scope, name, false); target.add(Instruction.operation(loc(), operation));
else value.compileWithPollution(target, scope); target.add(Instruction.storeVar(loc(), i, pollute));
target.add(Instruction.operation(operation).locate(loc()));
target.add(Instruction.storeVar(i, !retPrevValue).locate(loc()));
} }
else { else {
if (retPrevValue) target.add(Instruction.loadVar(i).locate(loc())); FunctionStatement.compileWithName(value, target, scope, true, name);
if (value instanceof FunctionStatement) ((FunctionStatement)value).compile(target, scope, name, false); target.add(Instruction.storeVar(loc(), i, pollute));
else value.compileWithPollution(target, scope);
target.add(Instruction.storeVar(i, !retPrevValue).locate(loc()));
} }
} }

View File

@@ -1,23 +1,19 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class VariableIndexStatement extends Statement { public class VariableIndexStatement extends Statement {
public final int index; public final int index;
@Override @Override public boolean pure() { return true; }
public boolean pollutesStack() { return true; }
@Override
public boolean pure() { return true; }
@Override @Override
public void compile(List<Instruction> target, ScopeRecord scope) { public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
target.add(Instruction.loadVar(index).locate(loc())); if (pollute) target.add(Instruction.loadVar(loc(), index));
} }
public VariableIndexStatement(Location loc, int i) { public VariableIndexStatement(Location loc, int i) {

View File

@@ -1,10 +1,8 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.AssignStatement;
import me.topchetoeu.jscript.compilation.AssignableStatement; import me.topchetoeu.jscript.compilation.AssignableStatement;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.Operation; import me.topchetoeu.jscript.engine.Operation;
@@ -13,20 +11,18 @@ import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class VariableStatement extends AssignableStatement { public class VariableStatement extends AssignableStatement {
public final String name; public final String name;
@Override @Override public boolean pure() { return false; }
public boolean pollutesStack() { return true; }
@Override
public boolean pure() { return true; }
@Override @Override
public AssignStatement toAssign(Statement val, Operation operation) { public Statement toAssign(Statement val, Operation operation) {
return new VariableAssignStatement(loc(), name, val, operation); return new VariableAssignStatement(loc(), name, val, operation);
} }
@Override @Override
public void compile(List<Instruction> target, ScopeRecord scope) { public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
var i = scope.getKey(name); var i = scope.getKey(name);
target.add(Instruction.loadVar(i).locate(loc())); target.add(Instruction.loadVar(loc(), i));
if (!pollute) target.add(Instruction.discard(loc()));
} }
public VariableStatement(Location loc, String name) { public VariableStatement(Location loc, String name) {

View File

@@ -1,34 +0,0 @@
package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.compilation.Instruction;
public class VoidStatement extends Statement {
public final Statement value;
@Override
public boolean pollutesStack() { return true; }
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
if (value != null) value.compileNoPollution(target, scope);
target.add(Instruction.loadValue(null).locate(loc()));
}
@Override
public Statement optimize() {
if (value == null) return this;
var val = value.optimize();
if (val.pure()) return new ConstantStatement(loc(), null);
else return new VoidStatement(loc(), val);
}
public VoidStatement(Location loc, Statement value) {
super(loc);
this.value = value;
}
}

View File

@@ -1,13 +0,0 @@
package me.topchetoeu.jscript.engine;
import me.topchetoeu.jscript.Location;
public class BreakpointData {
public final Location loc;
public final CallContext ctx;
public BreakpointData(Location loc, CallContext ctx) {
this.loc = loc;
this.ctx = ctx;
}
}

View File

@@ -1,60 +0,0 @@
package me.topchetoeu.jscript.engine;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Map;
public class CallContext {
public static final class DataKey<T> {}
public final Engine engine;
private final Map<DataKey<?>, Object> data = new Hashtable<>();
public Engine engine() { return engine; }
public Map<DataKey<?>, Object> data() { return Collections.unmodifiableMap(data); }
public CallContext copy() {
return new CallContext(engine).mergeData(data);
}
public CallContext mergeData(Map<DataKey<?>, Object> objs) {
data.putAll(objs);
return this;
}
public <T> CallContext setData(DataKey<T> key, T val) {
if (val == null) data.remove(key);
else data.put(key, val);
return this;
}
@SuppressWarnings("unchecked")
public <T> T addData(DataKey<T> key, T val) {
if (data.containsKey(key)) return (T)data.get(key);
else {
if (val == null) data.remove(key);
else data.put(key, val);
return val;
}
}
public boolean hasData(DataKey<?> key) { return data.containsKey(key); }
public <T> T getData(DataKey<T> key) {
return getData(key, null);
}
@SuppressWarnings("unchecked")
public <T> T getData(DataKey<T> key, T defaultVal) {
if (!hasData(key)) return defaultVal;
else return (T)data.get(key);
}
public CallContext changeData(DataKey<Integer> key, int n, int start) {
return setData(key, getData(key, start) + n);
}
public CallContext changeData(DataKey<Integer> key, int n) {
return changeData(key, n, 0);
}
public CallContext changeData(DataKey<Integer> key) {
return changeData(key, 1, 0);
}
public CallContext(Engine engine) {
this.engine = engine;
}
}

View File

@@ -0,0 +1,164 @@
package me.topchetoeu.jscript.engine;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import me.topchetoeu.jscript.Filename;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.engine.debug.DebugContext;
import me.topchetoeu.jscript.engine.frame.CodeFrame;
import me.topchetoeu.jscript.engine.values.ArrayValue;
import me.topchetoeu.jscript.engine.values.FunctionValue;
import me.topchetoeu.jscript.engine.values.Symbol;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.lib.EnvironmentLib;
import me.topchetoeu.jscript.mapping.SourceMap;
public class Context implements Extensions {
public final Context parent;
public final Environment environment;
public final CodeFrame frame;
public final Engine engine;
public final int stackSize;
@Override public <T> void add(Symbol key, T obj) {
if (environment != null) environment.add(key, obj);
else if (engine != null) engine.add(key, obj);
}
@Override public <T> T get(Symbol key) {
if (environment != null && environment.has(key)) return environment.get(key);
else if (engine != null && engine.has(key)) return engine.get(key);
return null;
}
@Override public boolean has(Symbol key) {
return
environment != null && environment.has(key) ||
engine != null && engine.has(key);
}
@Override public boolean remove(Symbol key) {
var res = false;
if (environment != null) res |= environment.remove(key);
else if (engine != null) res |= engine.remove(key);
return res;
}
@Override public Iterable<Symbol> keys() {
if (engine == null && environment == null) return List.of();
if (engine == null) return environment.keys();
if (environment == null) return engine.keys();
return () -> Stream.concat(
StreamSupport.stream(engine.keys().spliterator(), false),
StreamSupport.stream(environment.keys().spliterator(), false)
).distinct().iterator();
}
public FunctionValue compile(Filename filename, String raw) {
var env = environment;
var result = Environment.compileFunc(this).call(this, null, raw, filename.toString(), new EnvironmentLib(env));
var function = (FunctionValue)Values.getMember(this, result, "function");
if (!DebugContext.enabled(this)) return function;
var rawMapChain = ((ArrayValue)Values.getMember(this, result, "mapChain")).toArray();
var breakpoints = new TreeSet<>(
Arrays.stream(((ArrayValue)Values.getMember(this, result, "breakpoints")).toArray())
.map(v -> Location.parse(Values.toString(this, v)))
.collect(Collectors.toList())
);
var maps = new SourceMap[rawMapChain.length];
for (var i = 0; i < maps.length; i++) maps[i] = SourceMap.parse(Values.toString(this, (String)rawMapChain[i]));
var map = SourceMap.chain(maps);
if (map != null) {
var newBreakpoints = new TreeSet<Location>();
for (var bp : breakpoints) {
bp = map.toCompiled(bp);
if (bp != null) newBreakpoints.add(bp);
}
breakpoints = newBreakpoints;
}
DebugContext.get(this).onSource(filename, raw, breakpoints, map);
return function;
}
public Context pushFrame(CodeFrame frame) {
var res = new Context(this, frame.function.environment, frame, engine, stackSize + 1);
return res;
}
public Iterable<CodeFrame> frames() {
var self = this;
return () -> new Iterator<CodeFrame>() {
private Context curr = self;
private void update() {
while (curr != null && curr.frame == null) curr = curr.parent;
}
@Override public boolean hasNext() {
update();
return curr != null;
}
@Override public CodeFrame next() {
update();
var res = curr.frame;
curr = curr.parent;
return res;
}
};
}
public List<String> stackTrace() {
var res = new ArrayList<String>();
for (var el : frames()) {
var name = el.function.name;
Location loc = null;
for (var j = el.codePtr; j >= 0 && loc == null; j--) loc = el.function.body[j].location;
if (loc == null) loc = el.function.loc();
var trace = "";
if (loc != null) trace += "at " + loc.toString() + " ";
if (name != null && !name.equals("")) trace += "in " + name + " ";
trace = trace.trim();
if (!trace.equals("")) res.add(trace);
}
return res;
}
private Context(Context parent, Environment environment, CodeFrame frame, Engine engine, int stackSize) {
this.parent = parent;
this.environment = environment;
this.frame = frame;
this.engine = engine;
this.stackSize = stackSize;
if (hasNotNull(Environment.MAX_STACK_COUNT) && stackSize > (int)get(Environment.MAX_STACK_COUNT)) {
throw EngineException.ofRange("Stack overflow!");
}
}
public Context(Engine engine) {
this(null, null, null, engine, 0);
}
public Context(Engine engine, Environment env) {
this(null, env, null, engine, 0);
}
}

View File

@@ -1,8 +0,0 @@
package me.topchetoeu.jscript.engine;
public enum DebugCommand {
NORMAL,
STEP_OVER,
STEP_OUT,
STEP_INTO,
}

View File

@@ -1,205 +1,57 @@
package me.topchetoeu.jscript.engine; package me.topchetoeu.jscript.engine;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.LinkedBlockingDeque;
import me.topchetoeu.jscript.engine.CallContext.DataKey; import me.topchetoeu.jscript.Filename;
import me.topchetoeu.jscript.engine.debug.DebugState; import me.topchetoeu.jscript.compilation.FunctionBody;
import me.topchetoeu.jscript.engine.modules.ModuleManager;
import me.topchetoeu.jscript.engine.scope.GlobalScope;
import me.topchetoeu.jscript.engine.values.FunctionValue; import me.topchetoeu.jscript.engine.values.FunctionValue;
import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.engine.values.Symbol;
import me.topchetoeu.jscript.engine.values.ObjectValue.PlaceholderProto;
import me.topchetoeu.jscript.events.Awaitable; import me.topchetoeu.jscript.events.Awaitable;
import me.topchetoeu.jscript.events.DataNotifier;
import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.interop.NativeTypeRegister;
import me.topchetoeu.jscript.parsing.Parsing;
public class Engine { public class Engine extends EventLoop implements Extensions {
private static class RawFunction { public static final HashMap<Long, FunctionBody> functions = new HashMap<>();
public final GlobalScope scope;
public final String filename;
public final String raw;
public RawFunction(GlobalScope scope, String filename, String raw) { private final Environment env = new Environment();
this.scope = scope;
this.filename = filename; @Override
this.raw = raw; public <T> void add(Symbol key, T obj) {
} this.env.add(key, obj);
}
@Override
public <T> T get(Symbol key) {
return this.env.get(key);
}
@Override
public boolean has(Symbol key) {
return this.env.has(key);
}
@Override
public boolean remove(Symbol key) {
return this.env.remove(key);
}
@Override
public Iterable<Symbol> keys() {
return env.keys();
} }
private static class Task { public Engine copy() {
public final Object func; var res = new Engine();
public final Object thisArg; res.env.addAll(env);
public final Object[] args; return res;
public final Map<DataKey<?>, Object> data;
public final DataNotifier<Object> notifier = new DataNotifier<>();
public Task(Object func, Map<DataKey<?>, Object> data, Object thisArg, Object[] args) {
this.func = func;
this.data = data;
this.thisArg = thisArg;
this.args = args;
}
} }
public static final DataKey<DebugState> DEBUG_STATE_KEY = new DataKey<>(); public Awaitable<Object> pushMsg(boolean micro, Environment env, FunctionValue func, Object thisArg, Object ...args) {
private static int nextId = 0; return pushMsg(() -> {
return func.call(new Context(this, env), thisArg, args);
private Map<DataKey<?>, Object> callCtxVals = new HashMap<>(); }, micro);
private GlobalScope global = new GlobalScope(); }
private ObjectValue arrayProto = new ObjectValue(); public Awaitable<Object> pushMsg(boolean micro, Environment env, Filename filename, String raw, Object thisArg, Object ...args) {
private ObjectValue boolProto = new ObjectValue(); return pushMsg(() -> {
private ObjectValue funcProto = new ObjectValue(); var ctx = new Context(this, env);
private ObjectValue numProto = new ObjectValue(); return ctx.compile(filename, raw).call(new Context(this, env), thisArg, args);
private ObjectValue objProto = new ObjectValue(PlaceholderProto.NONE); }, micro);
private ObjectValue strProto = new ObjectValue();
private ObjectValue symProto = new ObjectValue();
private ObjectValue errProto = new ObjectValue();
private ObjectValue syntaxErrProto = new ObjectValue(PlaceholderProto.ERROR);
private ObjectValue typeErrProto = new ObjectValue(PlaceholderProto.ERROR);
private ObjectValue rangeErrProto = new ObjectValue(PlaceholderProto.ERROR);
private NativeTypeRegister typeRegister;
private Thread thread;
private LinkedBlockingDeque<Task> macroTasks = new LinkedBlockingDeque<>();
private LinkedBlockingDeque<Task> microTasks = new LinkedBlockingDeque<>();
public final int id = ++nextId;
public final DebugState debugState = new DebugState();
public ObjectValue arrayProto() { return arrayProto; }
public ObjectValue booleanProto() { return boolProto; }
public ObjectValue functionProto() { return funcProto; }
public ObjectValue numberProto() { return numProto; }
public ObjectValue objectProto() { return objProto; }
public ObjectValue stringProto() { return strProto; }
public ObjectValue symbolProto() { return symProto; }
public ObjectValue errorProto() { return errProto; }
public ObjectValue syntaxErrorProto() { return syntaxErrProto; }
public ObjectValue typeErrorProto() { return typeErrProto; }
public ObjectValue rangeErrorProto() { return rangeErrProto; }
public GlobalScope global() { return global; }
public NativeTypeRegister typeRegister() { return typeRegister; }
public void copyFrom(Engine other) {
global = other.global;
typeRegister = other.typeRegister;
arrayProto = other.arrayProto;
boolProto = other.boolProto;
funcProto = other.funcProto;
numProto = other.numProto;
objProto = other.objProto;
strProto = other.strProto;
symProto = other.symProto;
errProto = other.errProto;
syntaxErrProto = other.syntaxErrProto;
typeErrProto = other.typeErrProto;
rangeErrProto = other.rangeErrProto;
} }
private void runTask(Task task) throws InterruptedException {
try {
FunctionValue func;
if (task.func instanceof FunctionValue) func = (FunctionValue)task.func;
else {
var raw = (RawFunction)task.func;
func = compile(raw.scope, raw.filename, raw.raw);
}
task.notifier.next(func.call(context().mergeData(task.data), task.thisArg, task.args));
}
catch (InterruptedException e) {
task.notifier.error(new RuntimeException(e));
throw e;
}
catch (EngineException e) {
task.notifier.error(e);
}
catch (RuntimeException e) {
task.notifier.error(e);
e.printStackTrace();
}
}
private void run() {
while (true) {
try {
runTask(macroTasks.take());
while (!microTasks.isEmpty()) {
runTask(microTasks.take());
}
}
catch (InterruptedException e) {
for (var msg : macroTasks) {
msg.notifier.error(new RuntimeException(e));
}
break;
}
}
}
public void exposeClass(String name, Class<?> clazz) {
global.define(name, true, typeRegister.getConstr(clazz));
}
public void exposeNamespace(String name, Class<?> clazz) {
global.define(name, true, NativeTypeRegister.makeNamespace(clazz));
}
public Thread start() {
if (this.thread == null) {
this.thread = new Thread(this::run, "JavaScript Runner #" + id);
this.thread.start();
}
return this.thread;
}
public void stop() {
thread.interrupt();
thread = null;
}
public boolean inExecThread() {
return Thread.currentThread() == thread;
}
public boolean isRunning() {
return this.thread != null;
}
public Object makeRegex(String pattern, String flags) {
throw EngineException.ofError("Regular expressions not supported.");
}
public ModuleManager modules() {
return null;
}
public ObjectValue getPrototype(Class<?> clazz) {
return typeRegister.getProto(clazz);
}
public CallContext context() { return new CallContext(this).mergeData(callCtxVals); }
public Awaitable<Object> pushMsg(boolean micro, FunctionValue func, Map<DataKey<?>, Object> data, Object thisArg, Object... args) {
var msg = new Task(func, data, thisArg, args);
if (micro) microTasks.addLast(msg);
else macroTasks.addLast(msg);
return msg.notifier;
}
public Awaitable<Object> pushMsg(boolean micro, GlobalScope scope, Map<DataKey<?>, Object> data, String filename, String raw, Object thisArg, Object... args) {
var msg = new Task(new RawFunction(scope, filename, raw), data, thisArg, args);
if (micro) microTasks.addLast(msg);
else macroTasks.addLast(msg);
return msg.notifier;
}
public FunctionValue compile(GlobalScope scope, String filename, String raw) throws InterruptedException {
return Parsing.compile(scope, filename, raw);
}
public Engine(NativeTypeRegister register) {
this.typeRegister = register;
this.callCtxVals.put(DEBUG_STATE_KEY, debugState);
}
public Engine() { public Engine() {
this(new NativeTypeRegister());
} }
} }

View File

@@ -0,0 +1,128 @@
package me.topchetoeu.jscript.engine;
import java.util.HashMap;
import java.util.stream.Collectors;
import me.topchetoeu.jscript.Filename;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.engine.debug.DebugContext;
import me.topchetoeu.jscript.engine.scope.GlobalScope;
import me.topchetoeu.jscript.engine.values.ArrayValue;
import me.topchetoeu.jscript.engine.values.FunctionValue;
import me.topchetoeu.jscript.engine.values.NativeFunction;
import me.topchetoeu.jscript.engine.values.ObjectValue;
import me.topchetoeu.jscript.engine.values.Symbol;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.interop.NativeWrapperProvider;
import me.topchetoeu.jscript.parsing.Parsing;
@SuppressWarnings("unchecked")
public class Environment implements Extensions {
public static final HashMap<String, Symbol> symbols = new HashMap<>();
public static final Symbol WRAPPERS = Symbol.get("Environment.wrappers");
public static final Symbol COMPILE_FUNC = Symbol.get("Environment.compile");
public static final Symbol REGEX_CONSTR = Symbol.get("Environment.regexConstructor");
public static final Symbol STACK = Symbol.get("Environment.stack");
public static final Symbol MAX_STACK_COUNT = Symbol.get("Environment.maxStackCount");
public static final Symbol HIDE_STACK = Symbol.get("Environment.hideStack");
public static final Symbol OBJECT_PROTO = Symbol.get("Environment.objectPrototype");
public static final Symbol FUNCTION_PROTO = Symbol.get("Environment.functionPrototype");
public static final Symbol ARRAY_PROTO = Symbol.get("Environment.arrayPrototype");
public static final Symbol BOOL_PROTO = Symbol.get("Environment.boolPrototype");
public static final Symbol NUMBER_PROTO = Symbol.get("Environment.numberPrototype");
public static final Symbol STRING_PROTO = Symbol.get("Environment.stringPrototype");
public static final Symbol SYMBOL_PROTO = Symbol.get("Environment.symbolPrototype");
public static final Symbol ERROR_PROTO = Symbol.get("Environment.errorPrototype");
public static final Symbol SYNTAX_ERR_PROTO = Symbol.get("Environment.syntaxErrorPrototype");
public static final Symbol TYPE_ERR_PROTO = Symbol.get("Environment.typeErrorPrototype");
public static final Symbol RANGE_ERR_PROTO = Symbol.get("Environment.rangeErrorPrototype");
private HashMap<Symbol, Object> data = new HashMap<>();
public GlobalScope global;
public WrappersProvider wrappers;
@Override public <T> void add(Symbol key, T obj) {
data.put(key, obj);
}
@Override public <T> T get(Symbol key) {
return (T)data.get(key);
}
@Override public boolean remove(Symbol key) {
if (data.containsKey(key)) {
data.remove(key);
return true;
}
return false;
}
@Override public boolean has(Symbol key) {
return data.containsKey(key);
}
@Override public Iterable<Symbol> keys() {
return data.keySet();
}
public static FunctionValue compileFunc(Extensions ext) {
return ext.init(COMPILE_FUNC, new NativeFunction("compile", args -> {
var source = args.getString(0);
var filename = args.getString(1);
var env = Values.wrapper(args.convert(2, ObjectValue.class).getMember(args.ctx, Symbol.get("env")), Environment.class);
var isDebug = DebugContext.enabled(args.ctx);
var res = new ObjectValue();
var target = Parsing.compile(env, Filename.parse(filename), source);
Engine.functions.putAll(target.functions);
Engine.functions.remove(0l);
res.defineProperty(args.ctx, "function", target.func(env));
res.defineProperty(args.ctx, "mapChain", new ArrayValue());
if (isDebug) res.defineProperty(
args.ctx, "breakpoints",
ArrayValue.of(args.ctx, target.breakpoints.stream().map(Location::toString).collect(Collectors.toList()))
);
return res;
}));
}
public static FunctionValue regexConstructor(Extensions ext) {
return ext.init(COMPILE_FUNC, new NativeFunction("RegExp", args -> {
throw EngineException.ofError("Regular expressions not supported.").setCtx(args.ctx.environment, args.ctx.engine);
}));
}
public Environment copy() {
var res = new Environment(null, global);
res.wrappers = wrappers.fork(res);
res.global = global;
res.data.putAll(data);
return res;
}
public Environment child() {
var res = copy();
res.global = res.global.globalChild();
return res;
}
public Context context(Engine engine) {
return new Context(engine, this);
}
public Environment(WrappersProvider nativeConverter, GlobalScope global) {
if (nativeConverter == null) nativeConverter = new NativeWrapperProvider(this);
if (global == null) global = new GlobalScope();
this.wrappers = nativeConverter;
this.global = global;
}
public Environment() {
this(null, null);
}
}

View File

@@ -0,0 +1,81 @@
package me.topchetoeu.jscript.engine;
import java.util.concurrent.PriorityBlockingQueue;
import me.topchetoeu.jscript.ResultRunnable;
import me.topchetoeu.jscript.events.Awaitable;
import me.topchetoeu.jscript.events.DataNotifier;
import me.topchetoeu.jscript.exceptions.InterruptException;
public class EventLoop {
private static class Task implements Comparable<Task> {
public final ResultRunnable<?> runnable;
public final DataNotifier<Object> notifier = new DataNotifier<>();
public final boolean micro;
public Task(ResultRunnable<?> runnable, boolean micro) {
this.runnable = runnable;
this.micro = micro;
}
@Override
public int compareTo(Task other) {
return Integer.compare(this.micro ? 0 : 1, other.micro ? 0 : 1);
}
}
private PriorityBlockingQueue<Task> tasks = new PriorityBlockingQueue<>();
private Thread thread;
@SuppressWarnings("unchecked")
public <T> Awaitable<T> pushMsg(ResultRunnable<T> runnable, boolean micro) {
var msg = new Task(runnable, micro);
tasks.add(msg);
return (Awaitable<T>)msg.notifier;
}
public Awaitable<Object> pushMsg(Runnable runnable, boolean micro) {
return pushMsg(() -> { runnable.run(); return null; }, micro);
}
public void run(boolean untilEmpty) {
while (!untilEmpty || !tasks.isEmpty()) {
try {
var task = tasks.take();
try {
task.notifier.next(task.runnable.run());
}
catch (RuntimeException e) {
if (e instanceof InterruptException) throw e;
task.notifier.error(e);
}
}
catch (InterruptedException | InterruptException e) {
for (var msg : tasks) msg.notifier.error(new InterruptException(e));
break;
}
}
}
public Thread thread() {
return thread;
}
public Thread start() {
if (thread == null) {
thread = new Thread(() -> run(false), "Event loop #" + hashCode());
thread.start();
}
return thread;
}
public void stop() {
if (thread != null) thread.interrupt();
thread = null;
}
public boolean inLoopThread() {
return Thread.currentThread() == thread;
}
public boolean isRunning() {
return this.thread != null;
}
}

View File

@@ -0,0 +1,39 @@
package me.topchetoeu.jscript.engine;
import me.topchetoeu.jscript.engine.values.Symbol;
public abstract class ExtensionStack implements Extensions {
protected abstract Extensions[] extensionStack();
@Override public <T> void add(Symbol key, T obj) {
for (var el : extensionStack()) {
if (el != null) {
el.add(key, obj);
return;
}
}
}
@Override public <T> T get(Symbol key) {
for (var el : extensionStack()) {
if (el != null && el.has(key)) return el.get(key);
}
return null;
}
@Override public boolean has(Symbol key) {
for (var el : extensionStack()) {
if (el != null && el.has(key)) return true;
}
return false;
}
@Override public boolean remove(Symbol key) {
var anyRemoved = false;
for (var el : extensionStack()) {
if (el != null) anyRemoved &= el.remove(key);
}
return anyRemoved;
}
}

View File

@@ -0,0 +1,34 @@
package me.topchetoeu.jscript.engine;
import me.topchetoeu.jscript.engine.values.Symbol;
public interface Extensions {
<T> T get(Symbol key);
<T> void add(Symbol key, T obj);
Iterable<Symbol> keys();
boolean has(Symbol key);
boolean remove(Symbol key);
default boolean hasNotNull(Symbol key) {
return has(key) && get(key) != null;
}
default <T> T get(Symbol key, T defaultVal) {
if (has(key)) return get(key);
else return defaultVal;
}
default <T> T init(Symbol key, T val) {
if (has(key)) return get(key);
else {
add(key, val);
return val;
}
}
default void addAll(Extensions source) {
for (var key : source.keys()) {
add(key, source.get(key));
}
}
}

View File

@@ -0,0 +1,12 @@
package me.topchetoeu.jscript.engine;
import me.topchetoeu.jscript.engine.values.FunctionValue;
import me.topchetoeu.jscript.engine.values.ObjectValue;
public interface WrappersProvider {
public ObjectValue getProto(Class<?> obj);
public ObjectValue getNamespace(Class<?> obj);
public FunctionValue getConstr(Class<?> obj);
public WrappersProvider fork(Environment env);
}

View File

@@ -0,0 +1,100 @@
package me.topchetoeu.jscript.engine.debug;
import java.util.HashMap;
import java.util.TreeSet;
import me.topchetoeu.jscript.Filename;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.Extensions;
import me.topchetoeu.jscript.engine.frame.CodeFrame;
import me.topchetoeu.jscript.engine.values.Symbol;
import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.mapping.SourceMap;
public class DebugContext implements DebugController {
public static final Symbol ENV_KEY = Symbol.get("Engine.debug");
public static final Symbol IGNORE = Symbol.get("Engine.ignoreDebug");
private HashMap<Filename, String> sources;
private HashMap<Filename, TreeSet<Location>> bpts;
private HashMap<Filename, SourceMap> maps;
private DebugController debugger;
public boolean attachDebugger(DebugController debugger) {
if (this.debugger != null) return false;
if (sources != null) {
for (var source : sources.entrySet()) debugger.onSource(
source.getKey(), source.getValue(),
bpts.get(source.getKey()),
maps.get(source.getKey())
);
}
this.debugger = debugger;
return true;
}
public boolean detachDebugger() {
this.debugger = null;
return true;
}
public DebugController debugger() {
if (debugger == null) return DebugController.empty();
else return debugger;
}
@Override public void onFramePop(Context ctx, CodeFrame frame) {
if (debugger != null) debugger.onFramePop(ctx, frame);
}
@Override public void onFramePush(Context ctx, CodeFrame frame) {
if (debugger != null) debugger.onFramePush(ctx, frame);
}
@Override public boolean onInstruction(Context ctx, CodeFrame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught) {
if (debugger != null) return debugger.onInstruction(ctx, frame, instruction, returnVal, error, caught);
else return false;
}
@Override public void onSource(Filename filename, String source, TreeSet<Location> breakpoints, SourceMap map) {
if (debugger != null) debugger.onSource(filename, source, breakpoints, map);
if (sources != null) sources.put(filename, source);
if (bpts != null) bpts.put(filename, breakpoints);
if (maps != null) maps.put(filename, map);
}
public Location mapToCompiled(Location location) {
if (maps == null) return location;
var map = maps.get(location.filename());
if (map == null) return location;
return map.toCompiled(location);
}
public Location mapToOriginal(Location location) {
if (maps == null) return location;
var map = maps.get(location.filename());
if (map == null) return location;
return map.toOriginal(location);
}
private DebugContext(boolean enabled) {
if (enabled) {
sources = new HashMap<>();
bpts = new HashMap<>();
maps = new HashMap<>();
}
}
public DebugContext() {
this(true);
}
public static boolean enabled(Extensions exts) {
return exts.hasNotNull(ENV_KEY) && !exts.has(IGNORE);
}
public static DebugContext get(Extensions exts) {
if (enabled(exts)) return exts.get(ENV_KEY);
else return new DebugContext(false);
}
}

View File

@@ -0,0 +1,62 @@
package me.topchetoeu.jscript.engine.debug;
import java.util.TreeSet;
import me.topchetoeu.jscript.Filename;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.frame.CodeFrame;
import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.mapping.SourceMap;
public interface DebugController {
/**
* Called when a script has been loaded
* @param filename The name of the source
* @param source The name of the source
* @param breakpoints A set of all the breakpointable locations in this source
* @param map The source map associated with this file. null if this source map isn't mapped
*/
void onSource(Filename filename, String source, TreeSet<Location> breakpoints, SourceMap map);
/**
* Called immediatly before an instruction is executed, as well as after an instruction, if it has threw or returned.
* This function might pause in order to await debugging commands.
* @param ctx The context of execution
* @param frame The frame in which execution is occuring
* @param instruction The instruction which was or will be executed
* @param loc The most recent location the code frame has been at
* @param returnVal The return value of the instruction, Values.NO_RETURN if none
* @param error The error that the instruction threw, null if none
* @param caught Whether or not the error has been caught
* @return Whether or not the frame should restart
*/
boolean onInstruction(Context ctx, CodeFrame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught);
/**
* Called immediatly before a frame has been pushed on the frame stack.
* This function might pause in order to await debugging commands.
* @param ctx The context of execution
* @param frame The code frame which was pushed
*/
void onFramePush(Context ctx, CodeFrame frame);
/**
* Called immediatly after a frame has been popped out of the frame stack.
* This function might pause in order to await debugging commands.
* @param ctx The context of execution
* @param frame The code frame which was popped out
*/
void onFramePop(Context ctx, CodeFrame frame);
public static DebugController empty() {
return new DebugController () {
@Override public void onFramePop(Context ctx, CodeFrame frame) { }
@Override public void onFramePush(Context ctx, CodeFrame frame) { }
@Override public boolean onInstruction(Context ctx, CodeFrame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught) {
return false;
}
@Override public void onSource(Filename filename, String source, TreeSet<Location> breakpoints, SourceMap map) { }
};
}
}

View File

@@ -0,0 +1,33 @@
package me.topchetoeu.jscript.engine.debug;
import java.io.IOException;
public interface DebugHandler {
void enable(V8Message msg) throws IOException;
void disable(V8Message msg) throws IOException;
void setBreakpointByUrl(V8Message msg) throws IOException;
void removeBreakpoint(V8Message msg) throws IOException;
void continueToLocation(V8Message msg) throws IOException;
void getScriptSource(V8Message msg) throws IOException;
void getPossibleBreakpoints(V8Message msg) throws IOException;
void resume(V8Message msg) throws IOException;
void pause(V8Message msg) throws IOException;
void stepInto(V8Message msg) throws IOException;
void stepOut(V8Message msg) throws IOException;
void stepOver(V8Message msg) throws IOException;
void setPauseOnExceptions(V8Message msg) throws IOException;
void evaluateOnCallFrame(V8Message msg) throws IOException;
void getProperties(V8Message msg) throws IOException;
void releaseObjectGroup(V8Message msg) throws IOException;
void releaseObject(V8Message msg) throws IOException;
void callFunctionOn(V8Message msg) throws IOException;
void runtimeEnable(V8Message msg) throws IOException;
}

View File

@@ -1,25 +1,33 @@
package me.topchetoeu.jscript.engine.debug; package me.topchetoeu.jscript.engine.debug;
import java.io.IOException; import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.util.Base64; import java.util.Base64;
import java.util.HashMap;
import me.topchetoeu.jscript.engine.Engine; import me.topchetoeu.jscript.Metadata;
import me.topchetoeu.jscript.Reading;
import me.topchetoeu.jscript.engine.debug.WebSocketMessage.Type; import me.topchetoeu.jscript.engine.debug.WebSocketMessage.Type;
import me.topchetoeu.jscript.engine.debug.handlers.DebuggerHandles; import me.topchetoeu.jscript.events.Notifier;
import me.topchetoeu.jscript.exceptions.SyntaxException; import me.topchetoeu.jscript.exceptions.SyntaxException;
import me.topchetoeu.jscript.json.JSON;
import me.topchetoeu.jscript.json.JSONList;
import me.topchetoeu.jscript.json.JSONMap;
public class DebugServer { public class DebugServer {
public static String browserDisplayName = "jscript"; public static String browserDisplayName = Metadata.name() + "/" + Metadata.version();
public static String targetName = "target";
public final Engine engine; public final HashMap<String, DebuggerProvider> targets = new HashMap<>();
private static void send(Socket socket, String val) throws IOException { private final byte[] favicon, index, protocol;
Http.writeResponse(socket.getOutputStream(), 200, "OK", "application/json", val.getBytes()); private final Notifier connNotifier = new Notifier();
private static void send(HttpRequest req, String val) throws IOException {
req.writeResponse(200, "OK", "application/json", val.getBytes());
} }
// SILENCE JAVA // SILENCE JAVA
@@ -27,7 +35,7 @@ public class DebugServer {
try { try {
return MessageDigest.getInstance("sha1"); return MessageDigest.getInstance("sha1");
} }
catch (Throwable a) { return null; } catch (Throwable e) { throw new RuntimeException(e); }
} }
private static Thread runAsync(Runnable func, String name) { private static Thread runAsync(Runnable func, String name) {
@@ -37,7 +45,7 @@ public class DebugServer {
return res; return res;
} }
private void handle(WebSocket ws) throws InterruptedException, IOException { private void handle(WebSocket ws, Debugger debugger) throws IOException {
WebSocketMessage raw; WebSocketMessage raw;
while ((raw = ws.receive()) != null) { while ((raw = ws.receive()) != null) {
@@ -57,18 +65,56 @@ public class DebugServer {
} }
switch (msg.name) { switch (msg.name) {
case "Debugger.enable": DebuggerHandles.enable(msg, engine, ws); continue; case "Debugger.enable":
case "Debugger.disable": DebuggerHandles.disable(msg, engine, ws); continue; connNotifier.next();
case "Debugger.stepInto": DebuggerHandles.stepInto(msg, engine, ws); continue; debugger.enable(msg);
continue;
case "Debugger.disable": debugger.close(); continue;
case "Debugger.setBreakpointByUrl": debugger.setBreakpointByUrl(msg); continue;
case "Debugger.removeBreakpoint": debugger.removeBreakpoint(msg); continue;
case "Debugger.continueToLocation": debugger.continueToLocation(msg); continue;
case "Debugger.getScriptSource": debugger.getScriptSource(msg); continue;
case "Debugger.getPossibleBreakpoints": debugger.getPossibleBreakpoints(msg); continue;
case "Debugger.resume": debugger.resume(msg); continue;
case "Debugger.pause": debugger.pause(msg); continue;
case "Debugger.stepInto": debugger.stepInto(msg); continue;
case "Debugger.stepOut": debugger.stepOut(msg); continue;
case "Debugger.stepOver": debugger.stepOver(msg); continue;
case "Debugger.setPauseOnExceptions": debugger.setPauseOnExceptions(msg); continue;
case "Debugger.evaluateOnCallFrame": debugger.evaluateOnCallFrame(msg); continue;
case "Runtime.releaseObjectGroup": debugger.releaseObjectGroup(msg); continue;
case "Runtime.releaseObject": debugger.releaseObject(msg); continue;
case "Runtime.getProperties": debugger.getProperties(msg); continue;
case "Runtime.callFunctionOn": debugger.callFunctionOn(msg); continue;
// case "NodeWorker.enable": debugger.nodeWorkerEnable(msg); continue;
case "Runtime.enable": debugger.runtimeEnable(msg); continue;
} }
if (
msg.name.startsWith("DOM.") ||
msg.name.startsWith("DOMDebugger.") ||
msg.name.startsWith("Emulation.") ||
msg.name.startsWith("Input.") ||
msg.name.startsWith("Network.") ||
msg.name.startsWith("Page.")
) ws.send(new V8Error("This isn't a browser..."));
else ws.send(new V8Error("This API is not supported yet."));
} }
debugger.close();
} }
private void onWsConnect(HttpRequest req, Socket socket) throws IOException { private void onWsConnect(HttpRequest req, Socket socket, DebuggerProvider debuggerProvider) {
var key = req.headers.get("sec-websocket-key"); var key = req.headers.get("sec-websocket-key");
if (key == null) { if (key == null) {
Http.writeResponse( req.writeResponse(
socket.getOutputStream(), 426, "Upgrade Required", "text/txt", 426, "Upgrade Required", "text/txt",
"Expected a WS upgrade".getBytes() "Expected a WS upgrade".getBytes()
); );
return; return;
@@ -78,77 +124,118 @@ public class DebugServer {
(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes() (key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes()
)); ));
Http.writeCode(socket.getOutputStream(), 101, "Switching Protocols"); req.writeCode(101, "Switching Protocols");
Http.writeHeader(socket.getOutputStream(), "Connection", "Upgrade"); req.writeHeader("Connection", "Upgrade");
Http.writeHeader(socket.getOutputStream(), "Sec-WebSocket-Accept", resKey); req.writeHeader("Sec-WebSocket-Accept", resKey);
Http.writeLastHeader(socket.getOutputStream(), "Upgrade", "WebSocket"); req.writeLastHeader("Upgrade", "WebSocket");
var ws = new WebSocket(socket); var ws = new WebSocket(socket);
var debugger = debuggerProvider.getDebugger(ws, req);
if (debugger == null) {
ws.close();
return;
}
runAsync(() -> { runAsync(() -> {
try { try { handle(ws, debugger); }
handle(ws); catch (RuntimeException | IOException e) {
try {
ws.send(new V8Error(e.getMessage()));
}
catch (IOException e2) { /* Shit outta luck */ }
} }
catch (InterruptedException e) { return; } finally { ws.close(); debugger.close(); }
catch (IOException e) { e.printStackTrace(); } }, "Debug Handler");
finally { ws.close(); }
}, "Debug Server Message Reader");
runAsync(() -> {
try {
handle(ws);
}
catch (InterruptedException e) { return; }
catch (IOException e) { e.printStackTrace(); }
finally { ws.close(); }
}, "Debug Server Event Writer");
} }
public void open(InetSocketAddress address) throws IOException { public void awaitConnection() {
ServerSocket server = new ServerSocket(); connNotifier.await();
server.bind(address); }
public void run(InetSocketAddress address) {
try { try {
while (true) { ServerSocket server = new ServerSocket();
var socket = server.accept(); server.bind(address);
var req = Http.readRequest(socket.getInputStream());
switch (req.path) { try {
case "/json/version": while (true) {
send(socket, "{\"Browser\":\"" + browserDisplayName + "\",\"Protocol-Version\":\"1.2\"}"); var socket = server.accept();
break; var req = HttpRequest.read(socket);
case "/json/list":
case "/json": if (req == null) continue;
var addr = "ws://" + address.getHostString() + ":" + address.getPort() + "/devtools/page/" + targetName;
send(socket, "{\"id\":\"" + browserDisplayName + "\",\"webSocketDebuggerUrl\":\"" + addr + "\"}"); switch (req.path) {
break; case "/json/version":
case "/json/new": send(req, "{\"Browser\":\"" + browserDisplayName + "\",\"Protocol-Version\":\"1.1\"}");
case "/json/activate": break;
case "/json/protocol": case "/json/list":
case "/json/close": case "/json": {
case "/devtools/inspector.html": var res = new JSONList();
Http.writeResponse(
socket.getOutputStream(), for (var el : targets.entrySet()) {
501, "Not Implemented", "text/txt", res.add(new JSONMap()
"This feature isn't (and won't be) implemented.".getBytes() .set("description", "JScript debugger")
); .set("favicon", "/favicon.ico")
break; .set("id", el.getKey())
default: .set("type", "node")
if (req.path.equals("/devtools/page/" + targetName)) onWsConnect(req, socket); .set("webSocketDebuggerUrl", "ws://" + address.getHostString() + ":" + address.getPort() + "/" + el.getKey())
else { );
Http.writeResponse( }
socket.getOutputStream(), send(req, JSON.stringify(res));
404, "Not Found", "text/txt", break;
"Not found :/".getBytes()
);
} }
break; case "/json/protocol":
req.writeResponse(200, "OK", "application/json", protocol);
break;
case "/json/new":
case "/json/activate":
case "/json/close":
case "/devtools/inspector.html":
req.writeResponse(
501, "Not Implemented", "text/txt",
"This feature isn't (and probably won't be) implemented.".getBytes()
);
break;
case "/":
case "/index.html":
req.writeResponse(200, "OK", "text/html", index);
break;
case "/favicon.ico":
req.writeResponse(200, "OK", "image/png", favicon);
break;
default:
if (req.path.length() > 1 && targets.containsKey(req.path.substring(1))) {
onWsConnect(req, socket, targets.get(req.path.substring(1)));
}
break;
}
} }
} }
finally { server.close(); }
} }
finally { server.close(); } catch (IOException e) { throw new UncheckedIOException(e); }
} }
public DebugServer(Engine engine) { public Thread start(InetSocketAddress address, boolean daemon) {
this.engine = engine; var res = new Thread(() -> run(address), "Debug Server");
res.setDaemon(daemon);
res.start();
return res;
}
public DebugServer() {
try {
this.favicon = Reading.resourceToStream("assets/debugger/favicon.png").readAllBytes();
this.protocol = Reading.resourceToStream("assets/debugger/protocol.json").readAllBytes();
this.index = Reading.resourceToString("assets/debugger/index.html")
.replace("${NAME}", Metadata.name())
.replace("${VERSION}", Metadata.version())
.replace("${AUTHOR}", Metadata.author())
.getBytes();
}
catch (IOException e) {
throw new UncheckedIOException(e);
}
} }
} }

View File

@@ -1,52 +0,0 @@
package me.topchetoeu.jscript.engine.debug;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.engine.BreakpointData;
import me.topchetoeu.jscript.engine.DebugCommand;
import me.topchetoeu.jscript.engine.frame.CodeFrame;
import me.topchetoeu.jscript.events.Event;
public class DebugState {
private boolean paused = false;
public final HashSet<Location> breakpoints = new HashSet<>();
public final List<CodeFrame> frames = new ArrayList<>();
public final Map<String, String> sources = new HashMap<>();
public final Event<BreakpointData> breakpointNotifier = new Event<>();
public final Event<DebugCommand> commandNotifier = new Event<>();
public final Event<String> sourceAdded = new Event<>();
public DebugState pushFrame(CodeFrame frame) {
frames.add(frame);
return this;
}
public DebugState popFrame() {
if (frames.size() > 0) frames.remove(frames.size() - 1);
return this;
}
public DebugCommand pause(BreakpointData data) throws InterruptedException {
paused = true;
breakpointNotifier.next(data);
return commandNotifier.toAwaitable().await();
}
public void resume(DebugCommand command) {
paused = false;
commandNotifier.next(command);
}
// public void addSource()?
public boolean paused() { return paused; }
public boolean isBreakpoint(Location loc) {
return breakpoints.contains(loc);
}
}

View File

@@ -0,0 +1,5 @@
package me.topchetoeu.jscript.engine.debug;
public interface Debugger extends DebugHandler, DebugController {
void close();
}

View File

@@ -0,0 +1,5 @@
package me.topchetoeu.jscript.engine.debug;
public interface DebuggerProvider {
Debugger getDebugger(WebSocket socket, HttpRequest req);
}

View File

@@ -1,65 +0,0 @@
package me.topchetoeu.jscript.engine.debug;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.IllegalFormatException;
// We dont need no http library
public class Http {
public static void writeCode(OutputStream str, int code, String name) throws IOException {
str.write(("HTTP/1.1 " + code + " " + name + "\r\n").getBytes());
}
public static void writeHeader(OutputStream str, String name, String value) throws IOException {
str.write((name + ": " + value + "\r\n").getBytes());
}
public static void writeLastHeader(OutputStream str, String name, String value) throws IOException {
str.write((name + ": " + value + "\r\n").getBytes());
writeHeadersEnd(str);
}
public static void writeHeadersEnd(OutputStream str) throws IOException {
str.write("\n".getBytes());
}
public static void writeResponse(OutputStream str, int code, String name, String type, byte[] data) throws IOException {
writeCode(str, code, name);
writeHeader(str, "Content-Type", type);
writeLastHeader(str, "Content-Length", data.length + "");
str.write(data);
str.close();
}
public static HttpRequest readRequest(InputStream str) throws IOException {
var lines = new BufferedReader(new InputStreamReader(str));
var line = lines.readLine();
var i1 = line.indexOf(" ");
var i2 = line.lastIndexOf(" ");
var method = line.substring(0, i1).trim().toUpperCase();
var path = line.substring(i1 + 1, i2).trim();
var headers = new HashMap<String, String>();
while (!(line = lines.readLine()).isEmpty()) {
var i = line.indexOf(":");
if (i < 0) continue;
var name = line.substring(0, i).trim().toLowerCase();
var value = line.substring(i + 1).trim();
if (name.length() == 0) continue;
headers.put(name, value);
}
if (headers.containsKey("content-length")) {
try {
var i = Integer.parseInt(headers.get("content-length"));
str.skip(i);
}
catch (IllegalFormatException e) { /* ¯\_(ツ)_/¯ */ }
}
return new HttpRequest(method, path, headers);
}
}

View File

@@ -1,16 +1,102 @@
package me.topchetoeu.jscript.engine.debug; package me.topchetoeu.jscript.engine.debug;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.IllegalFormatException;
import java.util.Map; import java.util.Map;
public class HttpRequest { public class HttpRequest {
public final String method; public final String method;
public final String path; public final String path;
public final Map<String, String> headers; public final Map<String, String> headers;
public final OutputStream out;
public HttpRequest(String method, String path, Map<String, String> headers) {
public void writeCode(int code, String name) {
try { out.write(("HTTP/1.1 " + code + " " + name + "\r\n").getBytes()); }
catch (IOException e) { }
}
public void writeHeader(String name, String value) {
try { out.write((name + ": " + value + "\r\n").getBytes()); }
catch (IOException e) { }
}
public void writeLastHeader(String name, String value) {
try { out.write((name + ": " + value + "\r\n\r\n").getBytes()); }
catch (IOException e) { }
}
public void writeHeadersEnd() {
try { out.write("\n".getBytes()); }
catch (IOException e) { }
}
public void writeResponse(int code, String name, String type, byte[] data) {
writeCode(code, name);
writeHeader("Content-Type", type);
writeLastHeader("Content-Length", data.length + "");
try {
out.write(data);
out.close();
}
catch (IOException e) { }
}
public void writeResponse(int code, String name, String type, InputStream data) {
try {
writeResponse(code, name, type, data.readAllBytes());
}
catch (IOException e) { }
}
public HttpRequest(String method, String path, Map<String, String> headers, OutputStream out) {
this.method = method; this.method = method;
this.path = path; this.path = path;
this.headers = headers; this.headers = headers;
this.out = out;
}
// We dont need no http library
public static HttpRequest read(Socket socket) {
try {
var str = socket.getInputStream();
var lines = new BufferedReader(new InputStreamReader(str));
var line = lines.readLine();
var i1 = line.indexOf(" ");
var i2 = line.indexOf(" ", i1 + 1);
if (i1 < 0 || i2 < 0) {
socket.close();
return null;
}
var method = line.substring(0, i1).trim().toUpperCase();
var path = line.substring(i1 + 1, i2).trim();
var headers = new HashMap<String, String>();
while (!(line = lines.readLine()).isEmpty()) {
var i = line.indexOf(":");
if (i < 0) continue;
var name = line.substring(0, i).trim().toLowerCase();
var value = line.substring(i + 1).trim();
if (name.length() == 0) continue;
headers.put(name, value);
}
if (headers.containsKey("content-length")) {
try {
var i = Integer.parseInt(headers.get("content-length"));
str.skip(i);
}
catch (IllegalFormatException e) { /* ¯\_(ツ)_/¯ */ }
}
return new HttpRequest(method, path, headers, socket.getOutputStream());
}
catch (IOException | NullPointerException e) { return null; }
} }
} }

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