Compare commits

..

102 Commits

Author SHA1 Message Date
c707f880f7 fix: use Extensions instead of Environment 2024-04-03 14:21:23 +03:00
0d629a6e82 fix: use correct class instead of proxy 2024-04-03 12:27:15 +03:00
6eea342d04 fix: fuck 2024-04-02 18:24:43 +03:00
ece9cf68dc fix: correctly update proto chain 2024-04-02 18:19:05 +03:00
11ecd8c68f fix: exec debugger close logic on application exit 2024-04-02 18:05:49 +03:00
48bd304c6e fix: environment forks fixes 2024-04-02 18:05:20 +03:00
d8e46c3149 fix: clone environment correctly 2024-03-31 16:11:32 +03:00
5fc5eb08f8 fix: update breakpoints when removing bp 2024-03-30 12:52:44 +02:00
8acbc003c4 fix: properly resolve breakpoints 2024-03-30 12:13:04 +02:00
fda33112a7 fix: load maps when attaching debugger 2024-03-30 11:13:45 +02:00
67b2413d7c bump2 2024-03-30 10:36:55 +02:00
3a05416510 bump 2024-03-30 10:30:26 +02:00
c291328cc3 fix: detach debugger after close 2024-03-30 10:22:12 +02:00
7cb267b0d9 fix: some issues with debugger 2024-03-30 09:55:20 +02:00
4e31766665 fix: add new vscode debugger functions 2024-03-29 21:53:15 +02:00
b5b63c4342 fix: make global cache of native wrappers 2024-03-28 16:08:07 +02:00
18f70a0d58 fix: i hate wrappers 2024-03-28 15:10:21 +02:00
d38b600366 fix: some more wrapper issues 2024-03-28 14:52:49 +02:00
0ac7af2ea3 fix: take into account empty classes 2024-03-28 14:21:23 +02:00
5185c93663 fix: don't include non-exposing wrappers in proto chain
feat: allow adding custom wrappers
2024-03-28 00:57:09 +02:00
510422cab7 feat: implement logic for exposing non-static fields 2024-03-27 23:39:33 +02:00
79e1d1cfaf Merge branch 'master' of https://github.com/TopchetoEU/java-jscript 2024-03-27 23:08:25 +02:00
e0f3274a95 feat: add simple for-of loop (not intended for production usage) 2024-03-27 23:08:21 +02:00
ef5d29105f Update README.md 2024-03-10 02:17:18 +02:00
d8ea6557df fix: buildline expects tag to start with 'v' 2024-03-09 00:45:00 +02:00
5ba858545a fix: defer handles of async functions 2024-03-09 00:28:30 +02:00
446ecd8f2b fix: promise defers callback twice 2024-03-08 17:23:50 +02:00
fbf103439a bump 2024-03-08 16:55:46 +02:00
b30f94de8f refactor: move function pushMsg signatures in EventLoop 2024-03-08 16:53:47 +02:00
47b4dd3c15 refactor: rename code to runtime 2024-03-06 23:23:01 +02:00
0fb336373a fix: make fs calls synchronized 2024-03-06 12:50:57 +02:00
b33325a98d fix: clear buffer of line writer file 2024-03-05 17:10:06 +02:00
ccf75d6066 fix: don't use Context.NULL in global scope 2024-03-05 16:51:50 +02:00
662dcc1ac1 bump 2024-03-05 16:30:13 +02:00
3e6214659b fix: use new global API 2024-03-05 15:54:51 +02:00
7c6622c53d fix: separate scope records from scopes 2024-03-05 15:45:02 +02:00
70d5871091 fix: properly check permissions 2024-03-03 20:47:54 +02:00
7b9bbe576b feat: add std streams as global variables 2024-03-03 20:31:20 +02:00
e6399c1546 refactor: remove unused var 2024-03-02 14:01:58 +02:00
c8253795b2 fix: make debugging work again 2024-03-02 13:56:48 +02:00
49dd725669 refactor: fully separate event loop from context 2024-02-29 00:23:14 +02:00
52489ad3a8 feat: separate compilation and runtime 2024-02-26 13:22:56 +02:00
c4d44547c8 fix: call move when passing same array to copyTo 2024-02-21 11:03:19 +02:00
c6dc031cfd fix: respect return value of constructors 2024-02-21 11:01:33 +02:00
285960bdd6 refactor: rework fs error system 2024-02-09 13:46:57 +02:00
cf99845f6b refactor: rework permission system 2024-01-13 11:05:43 +02:00
48bd1e2015 bump 2024-01-12 09:54:32 +02:00
304665904f feat: extract log API to console 2024-01-12 09:53:56 +02:00
56ae3a85a6 build: improve build scripts 2024-01-12 09:48:20 +02:00
0178cb2194 build: specify java toolchain 2024-01-11 11:46:51 +02:00
a2cb5cd473 build: add gradle wrapper props 2024-01-11 11:46:41 +02:00
c123427e77 bump 2024-01-11 10:59:07 +02:00
7ac5ded185 build: set main class in jar manifest 2024-01-11 10:58:40 +02:00
769d6ae8fc version unbump 2024-01-11 10:55:43 +02:00
afb99ffc70 action attempt 1 2024-01-11 10:52:18 +02:00
46136e77e2 build: improve build scripts 2024-01-11 10:47:41 +02:00
b460b87318 refactor: remove old build script 2024-01-11 09:57:41 +02:00
e772f0b50d feat: change custom build script to gradle 2024-01-11 09:56:50 +02:00
187ad55291 fix: fully remove typescript 2024-01-10 17:02:45 +02:00
8156a1733f refactor: move debugging logic out of core 2024-01-10 17:01:24 +02:00
d1937fdb63 remove typescript 2024-01-10 16:51:40 +02:00
3f826cc85d remove old test 2024-01-10 16:51:17 +02:00
af35d7f20b fix: oops 2024-01-10 11:25:29 +02:00
cfa0e001b9 refactor: split up code in 4 modules 2024-01-10 11:21:49 +02:00
c10d071346 feat: some API improvements 2024-01-10 11:21:35 +02:00
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
299 changed files with 10009 additions and 9676 deletions

View File

@@ -3,7 +3,7 @@ name: "tagged-release"
on: on:
push: push:
tags: tags:
- "v*" - "*"
jobs: jobs:
tagged-release: tagged-release:
@@ -16,15 +16,17 @@ jobs:
with: with:
distribution: 'adopt' distribution: 'adopt'
java-version: '11' java-version: '11'
- name: Setup Gradle
uses: gradle/gradle-build-action@v2
- 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 branch: 'master'
owner: 'TopchetoEU' owner: 'TopchetoEU'
repository: 'java-jscript' repository: 'java-jscript'
- name: "Build" - name: Build
run: | run: |
cd java-jscript; node ./build.js release ${{ github.ref }} cd java-jscript; gradle build
- uses: "marvinpinto/action-automatic-releases@latest" - uses: "marvinpinto/action-automatic-releases@latest"
with: with:
@@ -32,4 +34,4 @@ jobs:
prerelease: false prerelease: false
files: | files: |
java-jscript/LICENSE java-jscript/LICENSE
java-jscript/dst/*.jar java-jscript/build/libs/*.jar

10
.gitignore vendored
View File

@@ -3,7 +3,8 @@
!/src !/src
!/src/**/* !/src/**/*
/src/assets/js/ts.js !/doc
!/doc/**/*
!/tests !/tests
!/tests/**/* !/tests/**/*
@@ -13,6 +14,11 @@
!/.gitignore !/.gitignore
!/.gitattributes !/.gitattributes
!/build.js
!/LICENSE !/LICENSE
!/README.md !/README.md
!/settings.gradle
!/build.gradle
!/gradle.properties
!/gradle
!/gradle/wrapper
!/gradle/wrapper/gradle-wrapper.properties

View File

@@ -11,7 +11,7 @@ JScript is an engine, capable of running EcmaScript 5, written entirely in Java.
The following is going to execute a simple javascript statement: The following is going to execute a simple javascript statement:
```java ```java
var engine = new Engine(false); var engine = new Engine();
// Initialize a standard environment, with implementations of most basic standard libraries (Object, Array, Symbol, etc.) // Initialize a standard environment, with implementations of most basic standard libraries (Object, Array, Symbol, etc.)
var env = Internals.apply(new Environment()); var env = Internals.apply(new Environment());
@@ -23,7 +23,3 @@ engine.run(true);
// Get our result // Get our result
System.out.println(awaitable.await()); System.out.println(awaitable.await());
``` ```
## 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.

32
build.gradle Normal file
View File

@@ -0,0 +1,32 @@
plugins {
id "application"
}
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
toolchain.languageVersion = JavaLanguageVersion.of(11)
withSourcesJar()
}
jar {
manifest.attributes["Main-class"] = project.main_class
}
sourceSets {
main.java.srcDirs = [ "src/java" ]
main.resources.srcDirs = [ "src/assets" ]
}
processResources {
filesMatching "metadata.json", {
expand(
version: project.project_version,
name: project.project_name
)
}
}
base.archivesName = project.project_name
version = project.project_version
group = project.project_group

172
build.js
View File

@@ -1,172 +0,0 @@
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());
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);
}
})();

4
gradle.properties Normal file
View File

@@ -0,0 +1,4 @@
project_group = me.topchetoeu
project_name = jscript
project_version = 0.9.27-beta
main_class = me.topchetoeu.jscript.utils.JScriptRepl

View File

@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

5
settings.gradle Normal file
View File

@@ -0,0 +1,5 @@
plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0'
}
rootProject.name = properties.project_name

View File

@@ -1,113 +0,0 @@
(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
View File

@@ -1,623 +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 }
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: (key: KeyT, val: ValueT, 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, 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);
}

5
src/assets/metadata.json Normal file
View File

@@ -0,0 +1,5 @@
{
"version": "${version}",
"name": "${name}",
"author": "TopchetoEU"
}

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript; package me.topchetoeu.jscript.common;
public class Buffer { public class Buffer {
private byte[] data; private byte[] data;
@@ -24,6 +24,11 @@ public class Buffer {
return n; return n;
} }
public void clear() {
data = new byte[128];
length = 0;
}
public void append(byte b) { public void append(byte b) {
write(length, new byte[] { b }); write(length, new byte[] { b });
} }

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript; package me.topchetoeu.jscript.common;
import java.io.File; import java.io.File;
import java.nio.file.Path; import java.nio.file.Path;
@@ -7,22 +7,17 @@ public class Filename {
public final String protocol; public final String protocol;
public final String path; public final String path;
public String toString() { @Override public String toString() {
return protocol + "://" + path; return protocol + "://" + path;
} }
@Override public int hashCode() {
@Override
public int hashCode() {
final int prime = 31; final int prime = 31;
int result = 1; int result = 1;
result = prime * result + protocol.hashCode(); result = prime * result + protocol.hashCode();
result = prime * result + path.hashCode(); result = prime * result + path.hashCode();
return result; return result;
} }
@Override public boolean equals(Object obj) {
@Override
public boolean equals(Object obj) {
if (this == obj) return true; if (this == obj) return true;
if (obj == null) return false; if (obj == null) return false;
if (getClass() != obj.getClass()) return false; if (getClass() != obj.getClass()) return false;
@@ -41,9 +36,6 @@ public class Filename {
return true; return true;
} }
public Filename(String protocol, String path) { public Filename(String protocol, String path) {
path = path.trim(); path = path.trim();
protocol = protocol.trim(); protocol = protocol.trim();

View File

@@ -0,0 +1,14 @@
package me.topchetoeu.jscript.common;
public class FunctionBody {
public final FunctionBody[] children;
public final Instruction[] instructions;
public final int localsN, argsN;
public FunctionBody(int localsN, int argsN, Instruction[] instructions, FunctionBody[] children) {
this.children = children;
this.argsN = argsN;
this.localsN = localsN;
this.instructions = instructions;
}
}

View File

@@ -0,0 +1,369 @@
package me.topchetoeu.jscript.common;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.HashMap;
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
public class Instruction {
public static enum Type {
NOP(0),
RETURN(1),
THROW(2),
THROW_SYNTAX(3),
DELETE(4),
TRY_START(5),
TRY_END(6),
CALL(7),
CALL_NEW(8),
JMP_IF(9),
JMP_IFN(10),
JMP(11),
PUSH_UNDEFINED(12),
PUSH_NULL(13),
PUSH_BOOL(14),
PUSH_NUMBER(15),
PUSH_STRING(16),
LOAD_VAR(17),
LOAD_MEMBER(18),
LOAD_GLOB(20),
LOAD_FUNC(21),
LOAD_ARR(22),
LOAD_OBJ(23),
STORE_SELF_FUNC(24),
LOAD_REGEX(25),
DUP(26),
STORE_VAR(27),
STORE_MEMBER(28),
DISCARD(29),
MAKE_VAR(30),
DEF_PROP(31),
KEYS(32),
TYPEOF(33),
OPERATION(34);
private static final HashMap<Integer, Type> types = new HashMap<>();
public final int numeric;
static {
for (var val : Type.values()) types.put(val.numeric, val);
}
private Type(int numeric) {
this.numeric = numeric;
}
public static Type fromNumeric(int i) {
return types.get(i);
}
}
public static enum BreakpointType {
NONE,
STEP_OVER,
STEP_IN;
public boolean shouldStepIn() {
return this != NONE;
}
public boolean shouldStepOver() {
return this == STEP_OVER;
}
}
public final Type type;
public final Object[] params;
@SuppressWarnings("unchecked")
public <T> T get(int i) {
if (i >= params.length || i < 0) return null;
return (T)params[i];
}
@SuppressWarnings("unchecked")
public <T> T get(int i, T defaultVal) {
if (i >= params.length || i < 0) return defaultVal;
return (T)params[i];
}
public boolean match(Object ...args) {
if (args.length != params.length) return false;
for (int i = 0; i < args.length; i++) {
var a = params[i];
var b = args[i];
if (a == null || b == null) {
if (!(a == null && b == null)) return false;
}
if (!a.equals(b)) return false;
}
return true;
}
public boolean is(int i, Object arg) {
if (params.length <= i) return false;
return params[i].equals(arg);
}
public void write(DataOutputStream writer) throws IOException {
var rawType = type.numeric;
switch (type) {
case KEYS:
case PUSH_BOOL:
case STORE_MEMBER: rawType |= (boolean)get(0) ? 128 : 0; break;
case STORE_VAR: rawType |= (boolean)get(1) ? 128 : 0; break;
case TYPEOF: rawType |= params.length > 0 ? 128 : 0; break;
default:
}
writer.writeByte(rawType);
switch (type) {
case CALL: writer.writeInt(get(0)); break;
case CALL_NEW: writer.writeInt(get(0)); break;
case DUP: writer.writeInt(get(0)); break;
case JMP: writer.writeInt(get(0)); break;
case JMP_IF: writer.writeInt(get(0)); break;
case JMP_IFN: writer.writeInt(get(0)); break;
case LOAD_ARR: writer.writeInt(get(0)); break;
case LOAD_FUNC: {
writer.writeInt(params.length - 1);
for (var i = 0; i < params.length; i++) {
writer.writeInt(get(i + 1));
}
writer.writeInt(get(0));
break;
}
case LOAD_REGEX: writer.writeUTF(get(0)); break;
case LOAD_VAR: writer.writeInt(get(0)); break;
case MAKE_VAR: writer.writeUTF(get(0)); break;
case OPERATION: writer.writeByte(((Operation)get(0)).numeric); break;
case PUSH_NUMBER: writer.writeDouble(get(0)); break;
case PUSH_STRING: writer.writeUTF(get(0)); break;
case STORE_SELF_FUNC: writer.writeInt(get(0)); break;
case STORE_VAR: writer.writeInt(get(0)); break;
case THROW_SYNTAX: writer.writeUTF(get(0));
case TRY_START:
writer.writeInt(get(0));
writer.writeInt(get(1));
writer.writeInt(get(2));
break;
case TYPEOF:
if (params.length > 0) writer.writeUTF(get(0));
break;
default:
}
}
private Instruction(Type type, Object ...params) {
this.type = type;
this.params = params;
}
public static Instruction read(DataInputStream stream) throws IOException {
var rawType = stream.readUnsignedByte();
var type = Type.fromNumeric(rawType & 127);
var flag = (rawType & 128) != 0;
switch (type) {
case CALL: return call(stream.readInt());
case CALL_NEW: return callNew(stream.readInt());
case DEF_PROP: return defProp();
case DELETE: return delete();
case DISCARD: return discard();
case DUP: return dup(stream.readInt());
case JMP: return jmp(stream.readInt());
case JMP_IF: return jmpIf(stream.readInt());
case JMP_IFN: return jmpIfNot(stream.readInt());
case KEYS: return keys(flag);
case LOAD_ARR: return loadArr(stream.readInt());
case LOAD_FUNC: {
var captures = new int[stream.readInt()];
for (var i = 0; i < captures.length; i++) {
captures[i] = stream.readInt();
}
return loadFunc(stream.readInt(), captures);
}
case LOAD_GLOB: return loadGlob();
case LOAD_MEMBER: return loadMember();
case LOAD_OBJ: return loadObj();
case LOAD_REGEX: return loadRegex(stream.readUTF(), null);
case LOAD_VAR: return loadVar(stream.readInt());
case MAKE_VAR: return makeVar(stream.readUTF());
case OPERATION: return operation(Operation.fromNumeric(stream.readUnsignedByte()));
case PUSH_NULL: return pushNull();
case PUSH_UNDEFINED: return pushUndefined();
case PUSH_BOOL: return pushValue(flag);
case PUSH_NUMBER: return pushValue(stream.readDouble());
case PUSH_STRING: return pushValue(stream.readUTF());
case RETURN: return ret();
case STORE_MEMBER: return storeMember(flag);
case STORE_SELF_FUNC: return storeSelfFunc(stream.readInt());
case STORE_VAR: return storeVar(stream.readInt(), flag);
case THROW: return throwInstr();
case THROW_SYNTAX: return throwSyntax(stream.readUTF());
case TRY_END: return tryEnd();
case TRY_START: return tryStart(stream.readInt(), stream.readInt(), stream.readInt());
case TYPEOF: return flag ? typeof(stream.readUTF()) : typeof();
case NOP:
if (flag) return null;
else return nop();
default: return null;
}
}
public static Instruction tryStart(int catchStart, int finallyStart, int end) {
return new Instruction(Type.TRY_START, catchStart, finallyStart, end);
}
public static Instruction tryEnd() {
return new Instruction(Type.TRY_END);
}
public static Instruction throwInstr() {
return new Instruction(Type.THROW);
}
public static Instruction throwSyntax(SyntaxException err) {
return new Instruction(Type.THROW_SYNTAX, err.getMessage());
}
public static Instruction throwSyntax(String err) {
return new Instruction(Type.THROW_SYNTAX, err);
}
public static Instruction delete() {
return new Instruction(Type.DELETE);
}
public static Instruction ret() {
return new Instruction(Type.RETURN);
}
public static Instruction debug() {
return new Instruction(Type.NOP, "debug");
}
public static Instruction nop(Object ...params) {
return new Instruction(Type.NOP, params);
}
public static Instruction call(int argn) {
return new Instruction(Type.CALL, argn);
}
public static Instruction callNew(int argn) {
return new Instruction(Type.CALL_NEW, argn);
}
public static Instruction jmp(int offset) {
return new Instruction(Type.JMP, offset);
}
public static Instruction jmpIf(int offset) {
return new Instruction(Type.JMP_IF, offset);
}
public static Instruction jmpIfNot(int offset) {
return new Instruction(Type.JMP_IFN, offset);
}
public static Instruction pushUndefined() {
return new Instruction(Type.PUSH_UNDEFINED);
}
public static Instruction pushNull() {
return new Instruction(Type.PUSH_NULL);
}
public static Instruction pushValue(boolean val) {
return new Instruction(Type.PUSH_BOOL, val);
}
public static Instruction pushValue(double val) {
return new Instruction(Type.PUSH_NUMBER, val);
}
public static Instruction pushValue(String val) {
return new Instruction(Type.PUSH_STRING, val);
}
public static Instruction makeVar(String name) {
return new Instruction(Type.MAKE_VAR, name);
}
public static Instruction loadVar(Object i) {
return new Instruction(Type.LOAD_VAR, i);
}
public static Instruction loadGlob() {
return new Instruction(Type.LOAD_GLOB);
}
public static Instruction loadMember() {
return new Instruction(Type.LOAD_MEMBER);
}
public static Instruction loadRegex(String pattern, String flags) {
return new Instruction(Type.LOAD_REGEX, pattern, flags);
}
public static Instruction loadFunc(int id, int[] captures) {
var args = new Object[1 + captures.length];
args[0] = id;
for (var i = 0; i < captures.length; i++) args[i + 1] = captures[i];
return new Instruction(Type.LOAD_FUNC, args);
}
public static Instruction loadObj() {
return new Instruction(Type.LOAD_OBJ);
}
public static Instruction loadArr(int count) {
return new Instruction(Type.LOAD_ARR, count);
}
public static Instruction dup() {
return new Instruction(Type.DUP, 1);
}
public static Instruction dup(int count) {
return new Instruction(Type.DUP, count);
}
public static Instruction storeSelfFunc(int i) {
return new Instruction(Type.STORE_SELF_FUNC, i);
}
public static Instruction storeVar(Object i) {
return new Instruction(Type.STORE_VAR, i, false);
}
public static Instruction storeVar(Object i, boolean keep) {
return new Instruction(Type.STORE_VAR, i, keep);
}
public static Instruction storeMember() {
return new Instruction(Type.STORE_MEMBER, false);
}
public static Instruction storeMember(boolean keep) {
return new Instruction(Type.STORE_MEMBER, keep);
}
public static Instruction discard() {
return new Instruction(Type.DISCARD);
}
public static Instruction typeof() {
return new Instruction(Type.TYPEOF);
}
public static Instruction typeof(String varName) {
return new Instruction(Type.TYPEOF, varName);
}
public static Instruction keys(boolean forInFormat) {
return new Instruction(Type.KEYS, forInFormat);
}
public static Instruction defProp() {
return new Instruction(Type.DEF_PROP);
}
public static Instruction operation(Operation op) {
return new Instruction(Type.OPERATION, op);
}
@Override
public String toString() {
var res = type.toString();
for (int i = 0; i < params.length; i++) {
res += " " + params[i];
}
return res;
}
}

View File

@@ -1,7 +1,9 @@
package me.topchetoeu.jscript; package me.topchetoeu.jscript.common;
import java.util.ArrayList;
public class Location implements Comparable<Location> { public class Location implements Comparable<Location> {
public static final Location INTERNAL = new Location(0, 0, new Filename("jscript", "native")); public static final Location INTERNAL = new Location(-1, -1, new Filename("jscript", "native"));
private int line; private int line;
private int start; private int start;
private Filename filename; private Filename filename;
@@ -12,7 +14,13 @@ public class Location implements Comparable<Location> {
@Override @Override
public String toString() { public String toString() {
return filename.toString() + ":" + line + ":" + start; var res = new ArrayList<String>();
if (filename != null) res.add(filename.toString());
if (line >= 0) res.add(line + "");
if (start >= 0) res.add(start + "");
return String.join(":", res);
} }
public Location add(int n, boolean clone) { public Location add(int n, boolean clone) {

View File

@@ -1,9 +1,18 @@
package me.topchetoeu.jscript; package me.topchetoeu.jscript.common;
import me.topchetoeu.jscript.common.json.JSON;
public class Metadata { public class Metadata {
private static final String VERSION = "${VERSION}"; private static final String VERSION;
private static final String AUTHOR = "${AUTHOR}"; private static final String AUTHOR;
private static final String NAME = "${NAME}"; private static final String NAME;
static {
var data = JSON.parse(null, Reading.resourceToString("metadata.json")).map();
VERSION = data.string("version");
AUTHOR = data.string("author");
NAME = data.string("name");
}
public static String version() { public static String version() {
if (VERSION.equals("$" + "{VERSION}")) return "1337-devel"; if (VERSION.equals("$" + "{VERSION}")) return "1337-devel";

View File

@@ -0,0 +1,54 @@
package me.topchetoeu.jscript.common;
import java.util.HashMap;
public enum Operation {
INSTANCEOF(1, 2),
IN(2, 2),
MULTIPLY(3, 2),
DIVIDE(4, 2),
MODULO(5, 2),
ADD(6, 2),
SUBTRACT(7, 2),
USHIFT_RIGHT(8, 2),
SHIFT_RIGHT(9, 2),
SHIFT_LEFT(10, 2),
GREATER(11, 2),
LESS(12, 2),
GREATER_EQUALS(13, 2),
LESS_EQUALS(14, 2),
LOOSE_EQUALS(15, 2),
LOOSE_NOT_EQUALS(16, 2),
EQUALS(17, 2),
NOT_EQUALS(18, 2),
AND(19, 2),
OR(20, 2),
XOR(21, 2),
NEG(23, 1),
POS(24, 1),
NOT(25, 1),
INVERSE(26, 1);
private static final HashMap<Integer, Operation> operations = new HashMap<>();
static {
for (var val : Operation.values()) operations.put(val.numeric, val);
}
public final int numeric;
public final int operands;
private Operation(int numeric, int n) {
this.numeric = numeric;
this.operands = n;
}
public static Operation fromNumeric(int i) {
return operations.get(i);
}
}

View File

@@ -1,25 +1,26 @@
package me.topchetoeu.jscript; package me.topchetoeu.jscript.common;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.UncheckedIOException;
import me.topchetoeu.jscript.exceptions.UncheckedException;
public class Reading { public class Reading {
private static final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); private static final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
public static synchronized String read() throws IOException { public static synchronized String readline() throws IOException {
return reader.readLine(); return reader.readLine();
} }
public static String streamToString(InputStream in) { public static String streamToString(InputStream in) {
try { return new String(in.readAllBytes()); } try {
catch (Throwable e) { throw new UncheckedException(e); } return new String(in.readAllBytes());
}
catch (IOException e) { throw new UncheckedIOException(e); }
} }
public static InputStream resourceToStream(String name) { public static InputStream resourceToStream(String name) {
return Reading.class.getResourceAsStream("/assets/" + name); return Reading.class.getResourceAsStream("/" + name);
} }
public static String resourceToString(String name) { public static String resourceToString(String name) {
return streamToString(resourceToStream(name)); return streamToString(resourceToStream(name));

View File

@@ -0,0 +1,23 @@
package me.topchetoeu.jscript.common;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
public class RefTracker {
public static void onDestroy(Object obj, Runnable runnable) {
var queue = new ReferenceQueue<>();
var ref = new WeakReference<>(obj, queue);
obj = null;
var th = new Thread(() -> {
try {
queue.remove();
ref.get();
runnable.run();
}
catch (InterruptedException e) { return; }
});
th.setDaemon(true);
th.start();
}
}

View File

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

View File

@@ -1,6 +1,6 @@
package me.topchetoeu.jscript.events; package me.topchetoeu.jscript.common.events;
public class DataNotifier<T> implements Awaitable<T> { public class DataNotifier<T> {
private Notifier notifier = new Notifier(); private Notifier notifier = new Notifier();
private boolean isErr; private boolean isErr;
private T val; private T val;
@@ -11,9 +11,6 @@ public class DataNotifier<T> implements Awaitable<T> {
isErr = true; isErr = true;
notifier.next(); notifier.next();
} }
public void error(Throwable t) {
error(new RuntimeException(t));
}
public void next(T val) { public void next(T val) {
this.val = val; this.val = val;
isErr = false; isErr = false;

View File

@@ -1,6 +1,6 @@
package me.topchetoeu.jscript.events; package me.topchetoeu.jscript.common.events;
import me.topchetoeu.jscript.exceptions.InterruptException; import me.topchetoeu.jscript.runtime.exceptions.InterruptException;
public class Notifier { public class Notifier {
private boolean ok = false; private boolean ok = false;

View File

@@ -1,20 +1,20 @@
package me.topchetoeu.jscript.json; package me.topchetoeu.jscript.common.json;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import me.topchetoeu.jscript.Filename; import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.compilation.parsing.Operator;
import me.topchetoeu.jscript.engine.values.ArrayValue; import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.exceptions.EngineException; import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.exceptions.SyntaxException; import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.parsing.Operator; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
import me.topchetoeu.jscript.parsing.ParseRes; import me.topchetoeu.jscript.runtime.values.ArrayValue;
import me.topchetoeu.jscript.parsing.Parsing; import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.parsing.Token; import me.topchetoeu.jscript.runtime.values.Values;
public class JSON { public class JSON {
public static Object toJs(JSONElement val) { public static Object toJs(JSONElement val) {
@@ -32,7 +32,7 @@ public class JSON {
if (val.isNull()) return Values.NULL; if (val.isNull()) return Values.NULL;
return null; return null;
} }
private static JSONElement fromJs(Context ctx, Object val, HashSet<Object> prev) { private static JSONElement fromJs(Extensions ext, Object val, HashSet<Object> prev) {
if (val instanceof Boolean) return JSONElement.bool((boolean)val); if (val instanceof Boolean) return JSONElement.bool((boolean)val);
if (val instanceof Number) return JSONElement.number(((Number)val).doubleValue()); if (val instanceof Number) return JSONElement.number(((Number)val).doubleValue());
if (val instanceof String) return JSONElement.string((String)val); if (val instanceof String) return JSONElement.string((String)val);
@@ -44,7 +44,7 @@ public class JSON {
var res = new JSONList(); var res = new JSONList();
for (var el : ((ArrayValue)val).toArray()) { for (var el : ((ArrayValue)val).toArray()) {
var jsonEl = fromJs(ctx, el, prev); var jsonEl = fromJs(ext, el, prev);
if (jsonEl == null) jsonEl = JSONElement.NULL; if (jsonEl == null) jsonEl = JSONElement.NULL;
res.add(jsonEl); res.add(jsonEl);
} }
@@ -58,8 +58,8 @@ public class JSON {
var res = new JSONMap(); var res = new JSONMap();
for (var el : ((ObjectValue)val).keys(false)) { for (var el : Values.getMembers(ext, val, false, false)) {
var jsonEl = fromJs(ctx, ((ObjectValue)val).getMember(ctx, el), prev); var jsonEl = fromJs(ext, Values.getMember(ext, val, el), prev);
if (jsonEl == null) continue; if (jsonEl == null) continue;
if (el instanceof String || el instanceof Number) res.put(el.toString(), jsonEl); if (el instanceof String || el instanceof Number) res.put(el.toString(), jsonEl);
} }
@@ -70,8 +70,8 @@ public class JSON {
if (val == null) return null; if (val == null) return null;
return null; return null;
} }
public static JSONElement fromJs(Context ctx, Object val) { public static JSONElement fromJs(Extensions ext, Object val) {
return fromJs(ctx, val, new HashSet<>()); return fromJs(ext, val, new HashSet<>());
} }
public static ParseRes<String> parseIdentifier(List<Token> tokens, int i) { public static ParseRes<String> parseIdentifier(List<Token> tokens, int i) {

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript.json; package me.topchetoeu.jscript.common.json;
public class JSONElement { public class JSONElement {
public static enum Type { public static enum Type {

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript.json; package me.topchetoeu.jscript.common.json;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript.json; package me.topchetoeu.jscript.common.json;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;

View File

@@ -0,0 +1,8 @@
package me.topchetoeu.jscript.common.mapping;
public enum ConvertType {
Exact,
Lower,
Upper,
Both,
}

View File

@@ -0,0 +1,192 @@
package me.topchetoeu.jscript.common.mapping;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NavigableSet;
import java.util.Objects;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.scope.LocalScopeRecord;
import me.topchetoeu.jscript.utils.mapping.SourceMap;
public class FunctionMap {
public static class FunctionMapBuilder {
private final TreeMap<Integer, Location> sourceMap = new TreeMap<>();
private final HashMap<Location, BreakpointType> breakpoints = new HashMap<>();
public Location toLocation(int pc) {
return sourceMap.headMap(pc, true).firstEntry().getValue();
}
public FunctionMapBuilder setDebug(Location loc, BreakpointType type) {
if (loc == null || type == null || type == BreakpointType.NONE) return this;
breakpoints.put(loc, type);
return this;
}
public FunctionMapBuilder setLocation(int i, Location loc) {
if (loc == null || i < 0) return this;
sourceMap.put(i, loc);
return this;
}
public FunctionMapBuilder setLocationAndDebug(int i, Location loc, BreakpointType type) {
setDebug(loc, type);
setLocation(i, loc);
return this;
}
public Location first() {
if (sourceMap.size() == 0) return null;
return sourceMap.firstEntry().getValue();
}
public Location last() {
if (sourceMap.size() == 0) return null;
return sourceMap.lastEntry().getValue();
}
public FunctionMap build(String[] localNames, String[] captureNames) {
return new FunctionMap(sourceMap, breakpoints, localNames, captureNames);
}
public FunctionMap build(LocalScopeRecord scope) {
return new FunctionMap(sourceMap, breakpoints, scope.locals(), scope.captures());
}
public FunctionMap build() {
return new FunctionMap(sourceMap, breakpoints, new String[0], new String[0]);
}
private FunctionMapBuilder() { }
}
public static final FunctionMap EMPTY = new FunctionMap();
private final HashMap<Integer, BreakpointType> bps = new HashMap<>();
private final HashMap<Filename, TreeSet<Location>> bpLocs = new HashMap<>();
private final TreeMap<Integer, Location> pcToLoc = new TreeMap<>();
public final String[] localNames, captureNames;
public Location toLocation(int pc, boolean approxiamte) {
if (pcToLoc.size() == 0 || pc < 0 || pc > pcToLoc.lastKey()) return null;
var res = pcToLoc.get(pc);
if (!approxiamte || res != null) return res;
var entry = pcToLoc.headMap(pc, true).lastEntry();
if (entry == null) return null;
else return entry.getValue();
}
public Location toLocation(int pc) {
return toLocation(pc, false);
}
public BreakpointType getBreakpoint(int pc) {
return bps.getOrDefault(pc, BreakpointType.NONE);
}
public Location correctBreakpoint(Location loc) {
var set = bpLocs.get(loc.filename());
if (set == null) return null;
else return set.ceiling(loc);
}
public List<Location> correctBreakpoint(Pattern filename, int line, int column) {
var candidates = new HashMap<Filename, TreeSet<Location>>();
for (var name : bpLocs.keySet()) {
if (filename.matcher(name.toString()).matches()) {
candidates.put(name, bpLocs.get(name));
}
}
var res = new ArrayList<Location>(candidates.size());
for (var candidate : candidates.entrySet()) {
var val = correctBreakpoint(new Location(line, column, candidate.getKey()));
if (val == null) continue;
res.add(val);
}
return res;
}
public List<Location> breakpoints(Location start, Location end) {
if (!Objects.equals(start.filename(), end.filename())) return List.of();
NavigableSet<Location> set = bpLocs.get(start.filename());
if (set == null) return List.of();
if (start != null) set = set.tailSet(start, true);
if (end != null) set = set.headSet(end, true);
return set.stream().collect(Collectors.toList());
}
public Location start() {
if (pcToLoc.size() == 0) return null;
return pcToLoc.firstEntry().getValue();
}
public Location end() {
if (pcToLoc.size() == 0) return null;
return pcToLoc.lastEntry().getValue();
}
public FunctionMap apply(SourceMap map) {
var res = new FunctionMap(Map.of(), Map.of(), localNames, captureNames);
for (var el : pcToLoc.entrySet()) {
res.pcToLoc.put(el.getKey(), map.toCompiled(el.getValue()));
}
res.bps.putAll(bps);
for (var el : bpLocs.entrySet()) {
for (var loc : el.getValue()) {
loc = map.toCompiled(loc);
if (loc == null) continue;
if (!res.bpLocs.containsKey(loc.filename())) res.bpLocs.put(loc.filename(), new TreeSet<>());
res.bpLocs.get(loc.filename()).add(loc);
}
}
return res;
}
public FunctionMap clone() {
var res = new FunctionMap(Map.of(), Map.of(), localNames, captureNames);
res.pcToLoc.putAll(this.pcToLoc);
res.bps.putAll(bps);
res.bpLocs.putAll(bpLocs);
res.pcToLoc.putAll(pcToLoc);
return res;
}
public FunctionMap(Map<Integer, Location> map, Map<Location, BreakpointType> breakpoints, String[] localNames, String[] captureNames) {
var locToPc = new HashMap<Location, Integer>();
for (var el : map.entrySet()) {
pcToLoc.put(el.getKey(), el.getValue());
locToPc.putIfAbsent(el.getValue(), el.getKey());
}
for (var el : breakpoints.entrySet()) {
if (el.getValue() == null || el.getValue() == BreakpointType.NONE) continue;
bps.put(locToPc.get(el.getKey()), el.getValue());
if (!bpLocs.containsKey(el.getKey().filename())) bpLocs.put(el.getKey().filename(), new TreeSet<>());
bpLocs.get(el.getKey().filename()).add(el.getKey());
}
this.localNames = localNames;
this.captureNames = captureNames;
}
private FunctionMap() {
localNames = new String[0];
captureNames = new String[0];
}
public static FunctionMapBuilder builder() {
return new FunctionMapBuilder();
}
}

View File

@@ -1,7 +1,7 @@
package me.topchetoeu.jscript.compilation; package me.topchetoeu.jscript.compilation;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.engine.Operation; import me.topchetoeu.jscript.common.Operation;
public abstract class AssignableStatement extends Statement { public abstract class AssignableStatement extends Statement {
public abstract Statement toAssign(Statement val, Operation operation); public abstract Statement toAssign(Statement val, Operation operation);

View File

@@ -0,0 +1,78 @@
package me.topchetoeu.jscript.compilation;
import java.util.List;
import java.util.LinkedList;
import java.util.Vector;
import me.topchetoeu.jscript.common.FunctionBody;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.mapping.FunctionMap;
import me.topchetoeu.jscript.common.mapping.FunctionMap.FunctionMapBuilder;
import me.topchetoeu.jscript.compilation.scope.LocalScopeRecord;
public class CompileResult {
public final Vector<Instruction> instructions = new Vector<>();
public final List<CompileResult> children = new LinkedList<>();
public final FunctionMapBuilder map = FunctionMap.builder();
public final LocalScopeRecord scope;
public int length = 0;
public int temp() {
instructions.add(null);
return instructions.size() - 1;
}
public CompileResult add(Instruction instr) {
instructions.add(instr);
return this;
}
public CompileResult set(int i, Instruction instr) {
instructions.set(i, instr);
return this;
}
public Instruction get(int i) {
return instructions.get(i);
}
public int size() { return instructions.size(); }
public void setDebug(Location loc, BreakpointType type) {
map.setDebug(loc, type);
}
public void setLocation(int i, Location loc) {
map.setLocation(i, loc);
}
public void setLocationAndDebug(int i, Location loc, BreakpointType type) {
map.setLocationAndDebug(i, loc, type);
}
public void setDebug(BreakpointType type) {
setDebug(map.last(), type);
}
public void setLocation(Location type) {
setLocation(instructions.size() - 1, type);
}
public void setLocationAndDebug(Location loc, BreakpointType type) {
setLocationAndDebug(instructions.size() - 1, loc, type);
}
public CompileResult addChild(CompileResult child) {
this.children.add(child);
return child;
}
public FunctionMap map() {
return map.build(scope);
}
public FunctionBody body() {
var builtChildren = new FunctionBody[children.size()];
for (var i = 0; i < children.size(); i++) builtChildren[i] = children.get(i).body();
return new FunctionBody(scope.localsCount(), length, instructions.toArray(Instruction[]::new), builtChildren);
}
public CompileResult(LocalScopeRecord scope) {
this.scope = scope;
}
}

View File

@@ -3,10 +3,10 @@ package me.topchetoeu.jscript.compilation;
import java.util.List; import java.util.List;
import java.util.Vector; import java.util.Vector;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.values.FunctionStatement; import me.topchetoeu.jscript.compilation.values.FunctionStatement;
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;
@@ -22,16 +22,16 @@ public class CompoundStatement extends Statement {
} }
@Override @Override
public void declare(ScopeRecord varsScope) { public void declare(CompileResult target) {
for (var stm : statements) stm.declare(varsScope); for (var stm : statements) stm.declare(target);
} }
@Override @Override
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType type) { public void compile(CompileResult target, boolean pollute, BreakpointType type) {
List<Statement> statements = new Vector<Statement>(); List<Statement> statements = new Vector<Statement>();
if (separateFuncs) for (var stm : this.statements) { if (separateFuncs) for (var stm : this.statements) {
if (stm instanceof FunctionStatement && ((FunctionStatement)stm).statement) { if (stm instanceof FunctionStatement && ((FunctionStatement)stm).statement) {
stm.compile(target, scope, false); stm.compile(target, false);
} }
else statements.add(stm); else statements.add(stm);
} }
@@ -42,12 +42,12 @@ public class CompoundStatement extends Statement {
for (var i = 0; i < statements.size(); i++) { for (var i = 0; i < statements.size(); i++) {
var stm = statements.get(i); var stm = statements.get(i);
if (i != statements.size() - 1) stm.compile(target, scope, false, BreakpointType.STEP_OVER); if (i != statements.size() - 1) stm.compile(target, false, BreakpointType.STEP_OVER);
else stm.compile(target, scope, polluted = pollute, BreakpointType.STEP_OVER); else stm.compile(target, polluted = pollute, BreakpointType.STEP_OVER);
} }
if (!polluted && pollute) { if (!polluted && pollute) {
target.add(Instruction.loadValue(loc(), null)); target.add(Instruction.pushUndefined());
} }
} }

View File

@@ -0,0 +1,27 @@
package me.topchetoeu.jscript.compilation;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
public abstract class Statement {
private Location _loc;
public boolean pure() { return false; }
public void declare(CompileResult target) { }
public void compile(CompileResult target, boolean pollute, BreakpointType type) {
int start = target.size();
compile(target, pollute);
if (target.size() != start) target.setLocationAndDebug(start, loc(), type);
}
public void compile(CompileResult target, boolean pollute) {
compile(target, pollute, BreakpointType.NONE);
}
public Location loc() { return _loc; }
public void setLoc(Location loc) { _loc = loc; }
protected Statement(Location loc) {
this._loc = loc;
}
}

View File

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

View File

@@ -2,10 +2,10 @@ package me.topchetoeu.jscript.compilation;
import java.util.List; import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.values.FunctionStatement; import me.topchetoeu.jscript.compilation.values.FunctionStatement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class VariableDeclareStatement extends Statement { public class VariableDeclareStatement extends Statement {
public static class Pair { public static class Pair {
@@ -23,26 +23,26 @@ public class VariableDeclareStatement extends Statement {
public final List<Pair> values; public final List<Pair> values;
@Override @Override
public void declare(ScopeRecord varsScope) { public void declare(CompileResult target) {
for (var key : values) { for (var key : values) {
varsScope.define(key.name); target.scope.define(key.name);
} }
} }
@Override @Override
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) { public void compile(CompileResult target, 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 = target.scope.getKey(entry.name);
if (key instanceof String) target.add(Instruction.makeVar(entry.location, (String)key)); if (key instanceof String) target.add(Instruction.makeVar((String)key));
if (entry.value != null) { if (entry.value != null) {
FunctionStatement.compileWithName(entry.value, target, scope, true, entry.name, BreakpointType.STEP_OVER); FunctionStatement.compileWithName(entry.value, target, true, entry.name, BreakpointType.STEP_OVER);
target.add(Instruction.storeVar(entry.location, key)); target.add(Instruction.storeVar(key));
} }
} }
if (pollute) target.add(Instruction.loadValue(loc(), null)); if (pollute) target.add(Instruction.pushUndefined());
} }
public VariableDeclareStatement(Location loc, List<Pair> values) { public VariableDeclareStatement(Location loc, List<Pair> values) {

View File

@@ -0,0 +1,21 @@
package me.topchetoeu.jscript.compilation.control;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class BreakStatement extends Statement {
public final String label;
@Override
public void compile(CompileResult target, boolean pollute) {
target.add(Instruction.nop("break", label));
if (pollute) target.add(Instruction.pushUndefined());
}
public BreakStatement(Location loc, String label) {
super(loc);
this.label = label;
}
}

View File

@@ -1,18 +1,17 @@
package me.topchetoeu.jscript.compilation.control; package me.topchetoeu.jscript.compilation.control;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.compilation.CompileTarget; import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class ContinueStatement extends Statement { public class ContinueStatement extends Statement {
public final String label; public final String label;
@Override @Override
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) { public void compile(CompileResult target, boolean pollute) {
target.add(Instruction.nop(loc(), "cont", label)); target.add(Instruction.nop(loc(), "cont", label));
if (pollute) target.add(Instruction.loadValue(loc(), null)); if (pollute) target.add(Instruction.pushUndefined());
} }
public ContinueStatement(Location loc, String label) { public ContinueStatement(Location loc, String label) {

View File

@@ -0,0 +1,18 @@
package me.topchetoeu.jscript.compilation.control;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class DebugStatement extends Statement {
@Override
public void compile(CompileResult target, boolean pollute) {
target.add(Instruction.debug());
if (pollute) target.add(Instruction.pushUndefined());
}
public DebugStatement(Location loc) {
super(loc);
}
}

View File

@@ -0,0 +1,26 @@
package me.topchetoeu.jscript.compilation.control;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class DeleteStatement extends Statement {
public final Statement key;
public final Statement value;
@Override
public void compile(CompileResult target, boolean pollute) {
value.compile(target, true);
key.compile(target, true);
target.add(Instruction.delete());
if (pollute) target.add(Instruction.pushValue(true));
}
public DeleteStatement(Location loc, Statement key, Statement value) {
super(loc);
this.key = key;
this.value = value;
}
}

View File

@@ -1,31 +1,30 @@
package me.topchetoeu.jscript.compilation.control; package me.topchetoeu.jscript.compilation.control;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.compilation.CompileTarget; import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.CompileResult;
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;
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 @Override
public void declare(ScopeRecord globScope) { public void declare(CompileResult target) {
body.declare(globScope); body.declare(target);
} }
@Override @Override
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) { public void compile(CompileResult target, boolean pollute) {
int start = target.size(); int start = target.size();
body.compile(target, scope, false, BreakpointType.STEP_OVER); body.compile(target, false, BreakpointType.STEP_OVER);
int mid = target.size(); int mid = target.size();
condition.compile(target, scope, true, BreakpointType.STEP_OVER); condition.compile(target, 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(loc(), start - end)); target.add(Instruction.jmpIf(start - end));
} }
public DoWhileStatement(Location loc, String label, Statement condition, Statement body) { public DoWhileStatement(Location loc, String label, Statement condition, Statement body) {

View File

@@ -0,0 +1,69 @@
package me.topchetoeu.jscript.compilation.control;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class ForInStatement extends Statement {
public final String varName;
public final boolean isDeclaration;
public final Statement varValue, object, body;
public final String label;
public final Location varLocation;
@Override
public void declare(CompileResult target) {
body.declare(target);
if (isDeclaration) target.scope.define(varName);
}
@Override
public void compile(CompileResult target, boolean pollute) {
var key = target.scope.getKey(varName);
if (key instanceof String) target.add(Instruction.makeVar((String)key));
if (varValue != null) {
varValue.compile(target, true);
target.add(Instruction.storeVar(target.scope.getKey(varName)));
}
object.compile(target, true, BreakpointType.STEP_OVER);
target.add(Instruction.keys(true));
int start = target.size();
target.add(Instruction.dup());
target.add(Instruction.pushUndefined());
target.add(Instruction.operation(Operation.EQUALS));
int mid = target.temp();
target.add(Instruction.pushValue("value")).setLocation(varLocation);
target.add(Instruction.loadMember()).setLocation(varLocation);
target.add(Instruction.storeVar(key)).setLocationAndDebug(object.loc(), BreakpointType.STEP_OVER);
body.compile(target, false, BreakpointType.STEP_OVER);
int end = target.size();
WhileStatement.replaceBreaks(target, label, mid + 1, end, start, end + 1);
target.add(Instruction.jmp(start - end));
target.add(Instruction.discard());
target.set(mid, Instruction.jmpIf(end - mid + 1));
if (pollute) target.add(Instruction.pushUndefined());
}
public ForInStatement(Location loc, Location varLocation, String label, boolean isDecl, String varName, Statement varValue, Statement object, Statement body) {
super(loc);
this.varLocation = varLocation;
this.label = label;
this.isDeclaration = isDecl;
this.varName = varName;
this.varValue = varValue;
this.object = object;
this.body = body;
}
}

View File

@@ -0,0 +1,73 @@
package me.topchetoeu.jscript.compilation.control;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class ForOfStatement extends Statement {
public final String varName;
public final boolean isDeclaration;
public final Statement iterable, body;
public final String label;
public final Location varLocation;
@Override
public void declare(CompileResult target) {
body.declare(target);
if (isDeclaration) target.scope.define(varName);
}
@Override
public void compile(CompileResult target, boolean pollute) {
var key = target.scope.getKey(varName);
if (key instanceof String) target.add(Instruction.makeVar((String)key));
iterable.compile(target, true, BreakpointType.STEP_OVER);
target.add(Instruction.dup());
target.add(Instruction.loadVar("Symbol"));
target.add(Instruction.pushValue("iterator"));
target.add(Instruction.loadMember()).setLocation(iterable.loc());
target.add(Instruction.loadMember()).setLocation(iterable.loc());
target.add(Instruction.call(0)).setLocation(iterable.loc());
int start = target.size();
target.add(Instruction.dup());
target.add(Instruction.dup());
target.add(Instruction.pushValue("next"));
target.add(Instruction.loadMember()).setLocation(iterable.loc());
target.add(Instruction.call(0)).setLocation(iterable.loc());
target.add(Instruction.dup());
target.add(Instruction.pushValue("done"));
target.add(Instruction.loadMember()).setLocation(iterable.loc());
int mid = target.temp();
target.add(Instruction.pushValue("value"));
target.add(Instruction.loadMember()).setLocation(varLocation);
target.add(Instruction.storeVar(key)).setLocationAndDebug(iterable.loc(), BreakpointType.STEP_OVER);
body.compile(target, false, BreakpointType.STEP_OVER);
int end = target.size();
WhileStatement.replaceBreaks(target, label, mid + 1, end, start, end + 1);
target.add(Instruction.jmp(start - end));
target.add(Instruction.discard());
target.add(Instruction.discard());
target.set(mid, Instruction.jmpIf(end - mid + 1));
if (pollute) target.add(Instruction.pushUndefined());
}
public ForOfStatement(Location loc, Location varLocation, String label, boolean isDecl, String varName, Statement object, Statement body) {
super(loc);
this.varLocation = varLocation;
this.label = label;
this.isDeclaration = isDecl;
this.varName = varName;
this.iterable = object;
this.body = body;
}
}

View File

@@ -0,0 +1,45 @@
package me.topchetoeu.jscript.compilation.control;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class ForStatement extends Statement {
public final Statement declaration, assignment, condition, body;
public final String label;
@Override
public void declare(CompileResult target) {
declaration.declare(target);
body.declare(target);
}
@Override
public void compile(CompileResult target, boolean pollute) {
declaration.compile(target, false, BreakpointType.STEP_OVER);
int start = target.size();
condition.compile(target, true, BreakpointType.STEP_OVER);
int mid = target.temp();
body.compile(target, false, BreakpointType.STEP_OVER);
int beforeAssign = target.size();
assignment.compile(target, false, BreakpointType.STEP_OVER);
int end = target.size();
WhileStatement.replaceBreaks(target, label, mid + 1, end, beforeAssign, end + 1);
target.add(Instruction.jmp(start - end));
target.set(mid, Instruction.jmpIfNot(end - mid + 1));
if (pollute) target.add(Instruction.pushUndefined());
}
public ForStatement(Location loc, String label, Statement declaration, Statement condition, Statement assignment, Statement body) {
super(loc);
this.label = label;
this.declaration = declaration;
this.condition = condition;
this.assignment = assignment;
this.body = body;
}
}

View File

@@ -0,0 +1,48 @@
package me.topchetoeu.jscript.compilation.control;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class IfStatement extends Statement {
public final Statement condition, body, elseBody;
@Override
public void declare(CompileResult target) {
body.declare(target);
if (elseBody != null) elseBody.declare(target);
}
@Override public void compile(CompileResult target, boolean pollute, BreakpointType breakpoint) {
condition.compile(target, true, breakpoint);
if (elseBody == null) {
int i = target.temp();
body.compile(target, pollute, breakpoint);
int endI = target.size();
target.set(i, Instruction.jmpIfNot(endI - i));
}
else {
int start = target.temp();
body.compile(target, pollute, breakpoint);
int mid = target.temp();
elseBody.compile(target, pollute, breakpoint);
int end = target.size();
target.set(start, Instruction.jmpIfNot(mid - start + 1));
target.set(mid, Instruction.jmp(end - mid));
}
}
@Override public void compile(CompileResult target, boolean pollute) {
compile(target, pollute, BreakpointType.STEP_IN);
}
public IfStatement(Location loc, Statement condition, Statement body, Statement elseBody) {
super(loc);
this.condition = condition;
this.body = body;
this.elseBody = elseBody;
}
}

View File

@@ -0,0 +1,22 @@
package me.topchetoeu.jscript.compilation.control;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class ReturnStatement extends Statement {
public final Statement value;
@Override
public void compile(CompileResult target, boolean pollute) {
if (value == null) target.add(Instruction.pushUndefined());
else value.compile(target, true);
target.add(Instruction.ret()).setLocation(loc());
}
public ReturnStatement(Location loc, Statement value) {
super(loc);
this.value = value;
}
}

View File

@@ -2,14 +2,13 @@ package me.topchetoeu.jscript.compilation.control;
import java.util.HashMap; import java.util.HashMap;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.compilation.CompileTarget; import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.Instruction.Type;
import me.topchetoeu.jscript.compilation.CompileResult;
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.engine.Operation;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class SwitchStatement extends Statement { public class SwitchStatement extends Statement {
public static class SwitchCase { public static class SwitchCase {
@@ -28,51 +27,48 @@ public class SwitchStatement extends Statement {
public final int defaultI; public final int defaultI;
@Override @Override
public void declare(ScopeRecord varsScope) { public void declare(CompileResult target) {
for (var stm : body) stm.declare(varsScope); for (var stm : body) stm.declare(target);
} }
@Override @Override
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) { public void compile(CompileResult target, boolean pollute) {
var caseToStatement = new HashMap<Integer, Integer>(); var caseToStatement = new HashMap<Integer, Integer>();
var statementToIndex = new HashMap<Integer, Integer>(); var statementToIndex = new HashMap<Integer, Integer>();
value.compile(target, scope, true, BreakpointType.STEP_OVER); value.compile(target, true, BreakpointType.STEP_OVER);
for (var ccase : cases) { for (var ccase : cases) {
target.add(Instruction.dup(loc())); target.add(Instruction.dup());
ccase.value.compile(target, scope, true); ccase.value.compile(target, true);
target.add(Instruction.operation(loc(), Operation.EQUALS)); target.add(Instruction.operation(Operation.EQUALS));
caseToStatement.put(target.size(), ccase.statementI); caseToStatement.put(target.temp(), ccase.statementI);
target.add(Instruction.nop(null));
} }
int start = target.size(); int start = target.temp();
target.add(Instruction.nop(null));
for (var stm : body) { for (var stm : body) {
statementToIndex.put(statementToIndex.size(), target.size()); statementToIndex.put(statementToIndex.size(), target.size());
stm.compile(target, scope, false, BreakpointType.STEP_OVER); stm.compile(target, false, BreakpointType.STEP_OVER);
} }
int end = target.size(); int end = target.size();
target.add(Instruction.discard(loc())); target.add(Instruction.discard());
if (pollute) target.add(Instruction.loadValue(loc(), null)); if (pollute) target.add(Instruction.pushUndefined());
if (defaultI < 0 || defaultI >= body.length) target.set(start, Instruction.jmp(loc(), end - start)); if (defaultI < 0 || defaultI >= body.length) target.set(start, Instruction.jmp(end - start));
else target.set(start, Instruction.jmp(loc(), statementToIndex.get(defaultI) - start)); else target.set(start, Instruction.jmp(statementToIndex.get(defaultI) - start));
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, "break") && instr.get(1) == null) { if (instr.type == Type.NOP && instr.is(0, "break") && instr.get(1) == null) {
target.set(i, Instruction.jmp(loc(), end - i).locate(instr.location)); target.set(i, Instruction.jmp(end - i));
} }
} }
for (var el : caseToStatement.entrySet()) { for (var el : caseToStatement.entrySet()) {
var i = statementToIndex.get(el.getValue()); var i = statementToIndex.get(el.getValue());
if (i == null) i = end; if (i == null) i = end;
target.set(el.getKey(), Instruction.jmpIf(loc(), i - el.getKey())); target.set(el.getKey(), Instruction.jmpIf(i - el.getKey()));
} }
} }

View File

@@ -0,0 +1,21 @@
package me.topchetoeu.jscript.compilation.control;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class ThrowStatement extends Statement {
public final Statement value;
@Override
public void compile(CompileResult target, boolean pollute) {
value.compile(target, true);
target.add(Instruction.throwInstr()).setLocation(loc());
}
public ThrowStatement(Location loc, Statement value) {
super(loc);
this.value = value;
}
}

View File

@@ -0,0 +1,58 @@
package me.topchetoeu.jscript.compilation.control;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class TryStatement extends Statement {
public final Statement tryBody;
public final Statement catchBody;
public final Statement finallyBody;
public final String name;
@Override
public void declare(CompileResult target) {
tryBody.declare(target);
if (catchBody != null) catchBody.declare(target);
if (finallyBody != null) finallyBody.declare(target);
}
@Override
public void compile(CompileResult target, boolean pollute, BreakpointType bpt) {
int replace = target.temp();
int start = replace + 1, catchStart = -1, finallyStart = -1;
tryBody.compile(target, false);
target.add(Instruction.tryEnd());
if (catchBody != null) {
catchStart = target.size() - start;
target.scope.define(name, true);
catchBody.compile(target, false);
target.scope.undefine();
target.add(Instruction.tryEnd());
}
if (finallyBody != null) {
finallyStart = target.size() - start;
finallyBody.compile(target, false);
target.add(Instruction.tryEnd());
}
target.set(replace, Instruction.tryStart(catchStart, finallyStart, target.size() - start));
target.setLocationAndDebug(replace, loc(), BreakpointType.STEP_OVER);
if (pollute) target.add(Instruction.pushUndefined());
}
public TryStatement(Location loc, Statement tryBody, Statement catchBody, Statement finallyBody, String name) {
super(loc);
this.tryBody = tryBody;
this.catchBody = catchBody;
this.finallyBody = finallyBody;
this.name = name;
}
}

View File

@@ -0,0 +1,52 @@
package me.topchetoeu.jscript.compilation.control;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.Instruction.Type;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class WhileStatement extends Statement {
public final Statement condition, body;
public final String label;
@Override
public void declare(CompileResult target) {
body.declare(target);
}
@Override
public void compile(CompileResult target, boolean pollute) {
int start = target.size();
condition.compile(target, true);
int mid = target.temp();
body.compile(target, false, BreakpointType.STEP_OVER);
int end = target.size();
replaceBreaks(target, label, mid + 1, end, start, end + 1);
target.add(Instruction.jmp(start - end));
target.set(mid, Instruction.jmpIfNot(end - mid + 1));
if (pollute) target.add(Instruction.pushUndefined());
}
public WhileStatement(Location loc, String label, Statement condition, Statement body) {
super(loc);
this.label = label;
this.condition = condition;
this.body = body;
}
public static void replaceBreaks(CompileResult target, String label, int start, int end, int continuePoint, int breakPoint) {
for (int i = start; i < end; i++) {
var instr = target.get(i);
if (instr.type == Type.NOP && instr.is(0, "cont") && (instr.get(1) == null || instr.is(1, label))) {
target.set(i, Instruction.jmp(continuePoint - i));
}
if (instr.type == Type.NOP && instr.is(0, "break") && (instr.get(1) == null || instr.is(1, label))) {
target.set(i, Instruction.jmp(breakPoint - i));
}
}
}
}

View File

@@ -1,9 +1,9 @@
package me.topchetoeu.jscript.parsing; package me.topchetoeu.jscript.compilation.parsing;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import me.topchetoeu.jscript.engine.Operation; import me.topchetoeu.jscript.common.Operation;
public enum Operator { public enum Operator {
MULTIPLY("*", Operation.MULTIPLY, 13), MULTIPLY("*", Operation.MULTIPLY, 13),
@@ -55,7 +55,7 @@ public enum Operator {
INCREASE("++"), INCREASE("++"),
DECREASE("--"); DECREASE("--");
public final String value; public final String readable;
public final Operation operation; public final Operation operation;
public final int precedence; public final int precedence;
public final boolean reverse; public final boolean reverse;
@@ -63,7 +63,7 @@ public enum Operator {
static { static {
for (var el : Operator.values()) { for (var el : Operator.values()) {
ops.put(el.value, el); ops.put(el.readable, el);
} }
} }
@@ -74,38 +74,38 @@ public enum Operator {
} }
private Operator() { private Operator() {
this.value = null; this.readable = null;
this.operation = null; this.operation = null;
this.precedence = -1; this.precedence = -1;
this.reverse = false; this.reverse = false;
} }
private Operator(String value) { private Operator(String value) {
this. value = value; this.readable = value;
this.operation = null; this.operation = null;
this.precedence = -1; this.precedence = -1;
this.reverse = false; this.reverse = false;
} }
private Operator(String value, int precedence) { private Operator(String value, int precedence) {
this. value = value; this.readable = value;
this.operation = null; this.operation = null;
this.precedence = precedence; this.precedence = precedence;
this.reverse = false; this.reverse = false;
} }
private Operator(String value, int precedence, boolean reverse) { private Operator(String value, int precedence, boolean reverse) {
this. value = value; this.readable = value;
this.operation = null; this.operation = null;
this.precedence = precedence; this.precedence = precedence;
this.reverse = reverse; this.reverse = reverse;
} }
private Operator(String value, Operation funcName, int precedence) { private Operator(String value, Operation funcName, int precedence) {
this. value = value; this.readable = value;
this.operation = funcName; this.operation = funcName;
this.precedence = precedence; this.precedence = precedence;
this.reverse = false; this.reverse = false;
} }
private Operator(String value, Operation funcName, int precedence, boolean reverse) { private Operator(String value, Operation funcName, int precedence, boolean reverse) {
this.value = value; this.readable = value;
this.operation = funcName; this.operation = funcName;
this.precedence = precedence; this.precedence = precedence;
this.reverse = reverse; this.reverse = reverse;

View File

@@ -1,10 +1,10 @@
package me.topchetoeu.jscript.parsing; package me.topchetoeu.jscript.compilation.parsing;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.parsing.Parsing.Parser; import me.topchetoeu.jscript.compilation.parsing.Parsing.Parser;
public class ParseRes<T> { public class ParseRes<T> {
public static enum State { public static enum State {

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript.parsing; package me.topchetoeu.jscript.compilation.parsing;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@@ -6,21 +6,19 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.TreeSet;
import me.topchetoeu.jscript.Filename; import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.compilation.*; import me.topchetoeu.jscript.compilation.*;
import me.topchetoeu.jscript.compilation.VariableDeclareStatement.Pair; import me.topchetoeu.jscript.compilation.VariableDeclareStatement.Pair;
import me.topchetoeu.jscript.compilation.control.*; import me.topchetoeu.jscript.compilation.control.*;
import me.topchetoeu.jscript.compilation.control.SwitchStatement.SwitchCase; import me.topchetoeu.jscript.compilation.control.SwitchStatement.SwitchCase;
import me.topchetoeu.jscript.compilation.parsing.ParseRes.State;
import me.topchetoeu.jscript.compilation.scope.LocalScopeRecord;
import me.topchetoeu.jscript.compilation.values.*; import me.topchetoeu.jscript.compilation.values.*;
import me.topchetoeu.jscript.engine.Environment; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.scope.LocalScopeRecord;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.exceptions.SyntaxException;
import me.topchetoeu.jscript.parsing.ParseRes.State;
// TODO: this has to be rewritten // TODO: this has to be rewritten
public class Parsing { public class Parsing {
@@ -29,11 +27,11 @@ public class Parsing {
} }
private static class ObjProp { private static class ObjProp {
public final Object name; public final String name;
public final String access; public final String access;
public final FunctionStatement func; public final FunctionStatement func;
public ObjProp(Object name, String access, FunctionStatement func) { public ObjProp(String name, String access, FunctionStatement func) {
this.name = name; this.name = name;
this.access = access; this.access = access;
this.func = func; this.func = func;
@@ -403,6 +401,10 @@ public class Parsing {
return -1; return -1;
} }
private static boolean inBounds(List<Token> tokens, int i) {
return i >= 0 && i < tokens.size();
}
private static String parseString(Location loc, String literal) { private static String parseString(Location loc, String literal) {
var res = new StringBuilder(); var res = new StringBuilder();
@@ -579,9 +581,9 @@ public class Parsing {
var loc = new Location(el.line, el.start, filename); var loc = new Location(el.line, el.start, filename);
switch (el.type) { switch (el.type) {
case LITERAL: res.add(Token.identifier(el.line, el.start, el.value)); break; case LITERAL: res.add(Token.identifier(el.line, el.start, el.value)); break;
case NUMBER: res.add(Token.number(el.line, el.start, parseNumber(loc, el.value))); break; case NUMBER: res.add(Token.number(el.line, el.start, parseNumber(loc, el.value), el.value)); break;
case STRING: res.add(Token.string(el.line, el.start, parseString(loc, el.value))); break; case STRING: res.add(Token.string(el.line, el.start, parseString(loc, el.value), el.value)); break;
case REGEX: res.add(Token.regex(el.line, el.start, parseRegex(loc, el.value))); break; case REGEX: res.add(Token.regex(el.line, el.start, parseRegex(loc, el.value), el.value)); break;
case OPERATOR: case OPERATOR:
Operator op = Operator.parse(el.value); Operator op = Operator.parse(el.value);
if (op == null) throw new SyntaxException(loc, String.format("Unrecognized operator '%s'.", el.value)); if (op == null) throw new SyntaxException(loc, String.format("Unrecognized operator '%s'.", el.value));
@@ -608,49 +610,41 @@ public class Parsing {
} }
public static ParseRes<String> parseIdentifier(List<Token> tokens, int i) { public static ParseRes<String> parseIdentifier(List<Token> tokens, int i) {
try { if (inBounds(tokens, i)) {
if (tokens.get(i).isIdentifier()) { if (tokens.get(i).isIdentifier()) {
return ParseRes.res(tokens.get(i).identifier(), 1); return ParseRes.res(tokens.get(i).identifier(), 1);
} }
else return ParseRes.failed(); else return ParseRes.failed();
} }
catch (IndexOutOfBoundsException e) { else return ParseRes.failed();
return ParseRes.failed();
}
} }
public static ParseRes<Operator> parseOperator(List<Token> tokens, int i) { public static ParseRes<Operator> parseOperator(List<Token> tokens, int i) {
try { if (inBounds(tokens, i)) {
if (tokens.get(i).isOperator()) { if (tokens.get(i).isOperator()) {
return ParseRes.res(tokens.get(i).operator(), 1); return ParseRes.res(tokens.get(i).operator(), 1);
} }
else return ParseRes.failed(); else return ParseRes.failed();
} }
catch (IndexOutOfBoundsException e) { else return ParseRes.failed();
return ParseRes.failed();
}
} }
public static boolean isIdentifier(List<Token> tokens, int i, String lit) { public static boolean isIdentifier(List<Token> tokens, int i, String lit) {
try { if (inBounds(tokens, i)) {
if (tokens.get(i).isIdentifier(lit)) { if (tokens.get(i).isIdentifier(lit)) {
return true; return true;
} }
else return false; else return false;
} }
catch (IndexOutOfBoundsException e) { else return false;
return false;
}
} }
public static boolean isOperator(List<Token> tokens, int i, Operator op) { public static boolean isOperator(List<Token> tokens, int i, Operator op) {
try { if (inBounds(tokens, i)) {
if (tokens.get(i).isOperator(op)) { if (tokens.get(i).isOperator(op)) {
return true; return true;
} }
else return false; else return false;
} }
catch (IndexOutOfBoundsException e) { else return false;
return false;
}
} }
public static boolean isStatementEnd(List<Token> tokens, int i) { public static boolean isStatementEnd(List<Token> tokens, int i) {
if (isOperator(tokens, i, Operator.SEMICOLON)) return true; if (isOperator(tokens, i, Operator.SEMICOLON)) return true;
@@ -665,32 +659,27 @@ public class Parsing {
public static ParseRes<ConstantStatement> parseString(Filename filename, List<Token> tokens, int i) { public static ParseRes<ConstantStatement> parseString(Filename filename, List<Token> tokens, int i) {
var loc = getLoc(filename, tokens, i); var loc = getLoc(filename, tokens, i);
try { if (inBounds(tokens, i)) {
if (tokens.get(i).isString()) { if (tokens.get(i).isString()) {
return ParseRes.res(new ConstantStatement(loc, tokens.get(i).string()), 1); return ParseRes.res(new ConstantStatement(loc, tokens.get(i).string()), 1);
} }
else return ParseRes.failed(); else return ParseRes.failed();
} }
catch (IndexOutOfBoundsException e) { else return ParseRes.failed();
return ParseRes.failed();
}
} }
public static ParseRes<ConstantStatement> parseNumber(Filename filename, List<Token> tokens, int i) { public static ParseRes<ConstantStatement> parseNumber(Filename filename, List<Token> tokens, int i) {
var loc = getLoc(filename, tokens, i); var loc = getLoc(filename, tokens, i);
try { if (inBounds(tokens, i)) {
if (tokens.get(i).isNumber()) { if (tokens.get(i).isNumber()) {
return ParseRes.res(new ConstantStatement(loc, tokens.get(i).number()), 1); return ParseRes.res(new ConstantStatement(loc, tokens.get(i).number()), 1);
} }
else return ParseRes.failed(); else return ParseRes.failed();
} }
catch (IndexOutOfBoundsException e) { else return ParseRes.failed();
return ParseRes.failed();
}
} }
public static ParseRes<RegexStatement> parseRegex(Filename filename, List<Token> tokens, int i) { public static ParseRes<RegexStatement> parseRegex(Filename filename, List<Token> tokens, int i) {
var loc = getLoc(filename, tokens, i); var loc = getLoc(filename, tokens, i);
try { if (inBounds(tokens, i)) {
if (tokens.get(i).isRegex()) { if (tokens.get(i).isRegex()) {
var val = tokens.get(i).regex(); var val = tokens.get(i).regex();
var index = val.lastIndexOf('/'); var index = val.lastIndexOf('/');
@@ -700,9 +689,7 @@ public class Parsing {
} }
else return ParseRes.failed(); else return ParseRes.failed();
} }
catch (IndexOutOfBoundsException e) { return ParseRes.failed();
return ParseRes.failed();
}
} }
public static ParseRes<ArrayStatement> parseArray(Filename filename, List<Token> tokens, int i) { public static ParseRes<ArrayStatement> parseArray(Filename filename, List<Token> tokens, int i) {
@@ -776,15 +763,16 @@ public class Parsing {
return ParseRes.res(args, n); return ParseRes.res(args, n);
} }
public static ParseRes<? extends Object> parsePropName(Filename filename, List<Token> tokens, int i) { public static ParseRes<String> parsePropName(Filename filename, List<Token> tokens, int i) {
var idRes = parseIdentifier(tokens, i); var loc = getLoc(filename, tokens, i);
if (idRes.isSuccess()) return ParseRes.res(idRes.result, 1);
var strRes = parseString(null, tokens, i);
if (strRes.isSuccess()) return ParseRes.res(strRes.result.value, 1);
var numRes = parseNumber(null, tokens, i);
if (numRes.isSuccess()) return ParseRes.res(numRes.result.value, 1);
return ParseRes.failed(); if (inBounds(tokens, i)) {
var token = tokens.get(i);
if (token.isNumber() || token.isIdentifier() || token.isString()) return ParseRes.res(token.rawValue, 1);
else return ParseRes.error(loc, "Expected identifier, string or number literal.");
}
else return ParseRes.failed();
} }
public static ParseRes<ObjProp> parseObjectProp(Filename filename, List<Token> tokens, int i) { public static ParseRes<ObjProp> parseObjectProp(Filename filename, List<Token> tokens, int i) {
var loc = getLoc(filename, tokens, i); var loc = getLoc(filename, tokens, i);
@@ -820,9 +808,9 @@ public class Parsing {
int n = 0; int n = 0;
if (!isOperator(tokens, i + n++, Operator.BRACE_OPEN)) return ParseRes.failed(); if (!isOperator(tokens, i + n++, Operator.BRACE_OPEN)) return ParseRes.failed();
var values = new LinkedHashMap<Object, Statement>(); var values = new LinkedHashMap<String, Statement>();
var getters = new LinkedHashMap<Object, FunctionStatement>(); var getters = new LinkedHashMap<String, FunctionStatement>();
var setters = new LinkedHashMap<Object, FunctionStatement>(); var setters = new LinkedHashMap<String, FunctionStatement>();
if (isOperator(tokens, i + n, Operator.BRACE_CLOSE)) { if (isOperator(tokens, i + n, Operator.BRACE_CLOSE)) {
n++; n++;
@@ -994,7 +982,7 @@ public class Parsing {
var res = parseValue(filename, tokens, n + i, 14); var res = parseValue(filename, tokens, n + i, 14);
if (res.isSuccess()) return ParseRes.res(new OperationStatement(loc, operation, res.result), n + res.n); if (res.isSuccess()) return ParseRes.res(new OperationStatement(loc, operation, res.result), n + res.n);
else return ParseRes.error(loc, String.format("Expected a value after the unary operator '%s'.", op.value), res); else return ParseRes.error(loc, String.format("Expected a value after the unary operator '%s'.", op.readable), res);
} }
public static ParseRes<ChangeStatement> parsePrefixChange(Filename filename, List<Token> tokens, int i) { public static ParseRes<ChangeStatement> parsePrefixChange(Filename filename, List<Token> tokens, int i) {
var loc = getLoc(filename, tokens, i); var loc = getLoc(filename, tokens, i);
@@ -1079,10 +1067,10 @@ public class Parsing {
return ParseRes.res(new ConstantStatement(loc, false), 1); return ParseRes.res(new ConstantStatement(loc, false), 1);
} }
if (id.result.equals("undefined")) { if (id.result.equals("undefined")) {
return ParseRes.res(new ConstantStatement(loc, null), 1); return ParseRes.res(ConstantStatement.ofUndefined(loc), 1);
} }
if (id.result.equals("null")) { if (id.result.equals("null")) {
return ParseRes.res(new ConstantStatement(loc, Values.NULL), 1); return ParseRes.res(ConstantStatement.ofNull(loc), 1);
} }
if (id.result.equals("this")) { if (id.result.equals("this")) {
return ParseRes.res(new VariableIndexStatement(loc, 0), 1); return ParseRes.res(new VariableIndexStatement(loc, 0), 1);
@@ -1141,7 +1129,7 @@ public class Parsing {
} }
var res = parseValue(filename, tokens, i + n, 2); var res = parseValue(filename, tokens, i + n, 2);
if (!res.isSuccess()) return ParseRes.error(loc, String.format("Expected value after assignment operator '%s'.", op.value), res); if (!res.isSuccess()) return ParseRes.error(loc, String.format("Expected value after assignment operator '%s'.", op.readable), res);
n += res.n; n += res.n;
Operation operation = null; Operation operation = null;
@@ -1178,8 +1166,7 @@ public class Parsing {
prevArg = true; prevArg = true;
} }
else if (argRes.isError()) return argRes.transform(); else if (argRes.isError()) return argRes.transform();
else if (isOperator(tokens, i + n, Operator.COMMA)) { else if (prevArg && isOperator(tokens, i + n, Operator.COMMA)) {
if (!prevArg) args.add(null);
prevArg = false; prevArg = false;
n++; n++;
} }
@@ -1187,7 +1174,7 @@ public class Parsing {
n++; n++;
break; break;
} }
else return ParseRes.failed(); else return ParseRes.error(getLoc(filename, tokens, i + n), prevArg ? "Expected a comma or a closing paren." : "Expected an expression or a closing paren.");
} }
return ParseRes.res(new CallStatement(loc, false, prev, args.toArray(Statement[]::new)), n); return ParseRes.res(new CallStatement(loc, false, prev, args.toArray(Statement[]::new)), n);
@@ -1280,7 +1267,7 @@ public class Parsing {
if (op.isAssign()) return parseAssign(filename, tokens, i + n - 1, prev, precedence); if (op.isAssign()) return parseAssign(filename, tokens, i + n - 1, prev, precedence);
var res = parseValue(filename, tokens, i + n, op.precedence + (op.reverse ? 0 : 1)); var res = parseValue(filename, tokens, i + n, op.precedence + (op.reverse ? 0 : 1));
if (!res.isSuccess()) return ParseRes.error(loc, String.format("Expected a value after the '%s' operator.", op.value), res); if (!res.isSuccess()) return ParseRes.error(loc, String.format("Expected a value after the '%s' operator.", op.readable), res);
n += res.n; n += res.n;
if (op == Operator.LAZY_AND) { if (op == Operator.LAZY_AND) {
@@ -1339,11 +1326,10 @@ public class Parsing {
} }
public static ParseRes<? extends Statement> parseValueStatement(Filename filename, List<Token> tokens, int i) { public static ParseRes<? extends Statement> parseValueStatement(Filename filename, List<Token> tokens, int i) {
var loc = getLoc(filename, tokens, i);
var valRes = parseValue(filename, tokens, i, 0, true); var valRes = parseValue(filename, tokens, i, 0, true);
if (!valRes.isSuccess()) return valRes.transform(); if (!valRes.isSuccess()) return valRes.transform();
valRes.result.setLoc(loc); // valRes.result.setLoc(loc);
var res = ParseRes.res(valRes.result, valRes.n); var res = ParseRes.res(valRes.result, valRes.n);
if (isStatementEnd(tokens, i + res.n)) { if (isStatementEnd(tokens, i + res.n)) {
@@ -1791,6 +1777,46 @@ public class Parsing {
return ParseRes.res(new ForInStatement(loc, nameLoc, labelRes.result, isDecl, nameRes.result, varVal, objRes.result, bodyRes.result), n); return ParseRes.res(new ForInStatement(loc, nameLoc, labelRes.result, isDecl, nameRes.result, varVal, objRes.result, bodyRes.result), n);
} }
public static ParseRes<ForOfStatement> parseForOf(Filename filename, List<Token> tokens, int i) {
var loc = getLoc(filename, tokens, i);
int n = 0;
var labelRes = parseLabel(tokens, i + n);
var isDecl = false;
n += labelRes.n;
if (!isIdentifier(tokens, i + n++, "for")) return ParseRes.failed();
if (!isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'for'.");
if (isIdentifier(tokens, i + n, "var")) {
isDecl = true;
n++;
}
var nameRes = parseIdentifier(tokens, i + n);
if (!nameRes.isSuccess()) return ParseRes.error(loc, "Expected a variable name for 'for' loop.");
var nameLoc = getLoc(filename, tokens, i + n);
n += nameRes.n;
if (!isIdentifier(tokens, i + n++, "of")) {
if (nameRes.result.equals("const")) return ParseRes.error(loc, "'const' declarations are not supported.");
else if (nameRes.result.equals("let")) return ParseRes.error(loc, "'let' declarations are not supported.");
else return ParseRes.error(loc, "Expected 'of' keyword after variable declaration.");
}
var objRes = parseValue(filename, tokens, i + n, 0);
if (!objRes.isSuccess()) return ParseRes.error(loc, "Expected a value.", objRes);
n += objRes.n;
if (!isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after for.");
var bodyRes = parseStatement(filename, tokens, i + n);
if (!bodyRes.isSuccess()) return ParseRes.error(loc, "Expected a for body.", bodyRes);
n += bodyRes.n;
return ParseRes.res(new ForOfStatement(loc, nameLoc, labelRes.result, isDecl, nameRes.result, objRes.result, bodyRes.result), n);
}
public static ParseRes<TryStatement> parseCatch(Filename filename, List<Token> tokens, int i) { public static ParseRes<TryStatement> parseCatch(Filename filename, List<Token> tokens, int i) {
var loc = getLoc(filename, tokens, i); var loc = getLoc(filename, tokens, i);
int n = 0; int n = 0;
@@ -1847,6 +1873,7 @@ public class Parsing {
parseSwitch(filename, tokens, i), parseSwitch(filename, tokens, i),
parseFor(filename, tokens, i), parseFor(filename, tokens, i),
parseForIn(filename, tokens, i), parseForIn(filename, tokens, i),
parseForOf(filename, tokens, i),
parseDoWhile(filename, tokens, i), parseDoWhile(filename, tokens, i),
parseCatch(filename, tokens, i), parseCatch(filename, tokens, i),
parseCompound(filename, tokens, i), parseCompound(filename, tokens, i),
@@ -1876,30 +1903,31 @@ public class Parsing {
return list.toArray(Statement[]::new); return list.toArray(Statement[]::new);
} }
public static CompileTarget compile(Environment environment, Statement ...statements) { public static CompileResult compile(Statement ...statements) {
var subscope = new LocalScopeRecord(); var target = new CompileResult(new LocalScopeRecord());
var target = new CompileTarget(new HashMap<>(), new TreeSet<>());
var stm = new CompoundStatement(null, true, statements); var stm = new CompoundStatement(null, true, statements);
subscope.define("this"); target.scope.define("this");
subscope.define("arguments"); target.scope.define("arguments");
try { try {
stm.compile(target, subscope, true); stm.compile(target, true);
FunctionStatement.checkBreakAndCont(target, 0); FunctionStatement.checkBreakAndCont(target, 0);
} }
catch (SyntaxException e) { catch (SyntaxException e) {
target.target.clear(); target = new CompileResult(new LocalScopeRecord());
target.add(Instruction.throwSyntax(e.loc, e));
target.scope.define("this");
target.scope.define("arguments");
target.add(Instruction.throwSyntax(e)).setLocation(stm.loc());
} }
target.add(Instruction.ret(stm.loc())); target.add(Instruction.ret()).setLocation(stm.loc());
target.functions.put(0l, new FunctionBody(subscope.localsCount(), 0, target.array(), subscope.captures(), subscope.locals()));
return target; return target;
} }
public static CompileTarget compile(Environment environment, Filename filename, String raw) { public static CompileResult compile(Filename filename, String raw) {
try { return compile(environment, parse(filename, raw)); } return compile(parse(filename, raw));
catch (SyntaxException e) { return compile(environment, new ThrowSyntaxStatement(e)); }
} }
} }

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript.parsing; package me.topchetoeu.jscript.compilation.parsing;
public class RawToken { public class RawToken {
public final String value; public final String value;

View File

@@ -1,7 +1,7 @@
package me.topchetoeu.jscript.parsing; package me.topchetoeu.jscript.compilation.parsing;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.parsing.ParseRes.State; import me.topchetoeu.jscript.compilation.parsing.ParseRes.State;
public class TestRes { public class TestRes {
public final State state; public final State state;

View File

@@ -1,21 +1,24 @@
package me.topchetoeu.jscript.parsing; package me.topchetoeu.jscript.compilation.parsing;
public class Token { public class Token {
public final Object value; public final Object value;
public final String rawValue;
public final boolean isString; public final boolean isString;
public final boolean isRegex; public final boolean isRegex;
public final int line; public final int line;
public final int start; public final int start;
private Token(int line, int start, Object value, boolean isString, boolean isRegex) { private Token(int line, int start, Object value, String rawValue, boolean isString, boolean isRegex) {
this.value = value; this.value = value;
this.rawValue = rawValue;
this.line = line; this.line = line;
this.start = start; this.start = start;
this.isString = isString; this.isString = isString;
this.isRegex = isRegex; this.isRegex = isRegex;
} }
private Token(int line, int start, Object value) { private Token(int line, int start, Object value, String rawValue) {
this.value = value; this.value = value;
this.rawValue = rawValue;
this.line = line; this.line = line;
this.start = start; this.start = start;
this.isString = false; this.isString = false;
@@ -37,19 +40,19 @@ public class Token {
public String identifier() { return (String)value; } public String identifier() { return (String)value; }
public Operator operator() { return (Operator)value; } public Operator operator() { return (Operator)value; }
public static Token regex(int line, int start, String val) { public static Token regex(int line, int start, String val, String rawValue) {
return new Token(line, start, val, false, true); return new Token(line, start, val, rawValue, false, true);
} }
public static Token string(int line, int start, String val) { public static Token string(int line, int start, String val, String rawValue) {
return new Token(line, start, val, true, false); return new Token(line, start, val, rawValue, true, false);
} }
public static Token number(int line, int start, double val) { public static Token number(int line, int start, double val, String rawValue) {
return new Token(line, start, val); return new Token(line, start, val, rawValue);
} }
public static Token identifier(int line, int start, String val) { public static Token identifier(int line, int start, String val) {
return new Token(line, start, val); return new Token(line, start, val, val);
} }
public static Token operator(int line, int start, Operator val) { public static Token operator(int line, int start, Operator val) {
return new Token(line, start, val); return new Token(line, start, val, val.readable);
} }
} }

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript.parsing; package me.topchetoeu.jscript.compilation.parsing;
enum TokenType { enum TokenType {
REGEX, REGEX,

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript.engine.scope; package me.topchetoeu.jscript.compilation.scope;
import java.util.ArrayList; import java.util.ArrayList;

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript.engine.scope; package me.topchetoeu.jscript.compilation.scope;
public interface ScopeRecord { public interface ScopeRecord {
public Object getKey(String name); public Object getKey(String name);

View File

@@ -1,10 +1,9 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.compilation.CompileTarget; import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
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;
@@ -18,20 +17,20 @@ public class ArrayStatement extends Statement {
} }
@Override @Override
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) { public void compile(CompileResult target, boolean pollute) {
target.add(Instruction.loadArr(loc(), statements.length)); target.add(Instruction.loadArr(statements.length));
for (var i = 0; i < statements.length; i++) { for (var i = 0; i < statements.length; i++) {
var el = statements[i]; var el = statements[i];
if (el != null) { if (el != null) {
target.add(Instruction.dup(loc())); target.add(Instruction.dup());
target.add(Instruction.loadValue(loc(), i)); target.add(Instruction.pushValue(i));
el.compile(target, scope, true); el.compile(target, true);
target.add(Instruction.storeMember(loc())); target.add(Instruction.storeMember());
} }
} }
if (!pollute) target.add(Instruction.discard(loc())); if (!pollute) target.add(Instruction.discard());
} }
public ArrayStatement(Location loc, Statement[] statements) { public ArrayStatement(Location loc, Statement[] statements) {

View File

@@ -0,0 +1,43 @@
package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class CallStatement extends Statement {
public final Statement func;
public final Statement[] args;
public final boolean isNew;
@Override
public void compile(CompileResult target, boolean pollute, BreakpointType type) {
if (isNew) func.compile(target, true);
else if (func instanceof IndexStatement) {
((IndexStatement)func).compile(target, true, true);
}
else {
target.add(Instruction.pushUndefined());
func.compile(target, true);
}
for (var arg : args) arg.compile(target, true);
if (isNew) target.add(Instruction.callNew(args.length)).setLocationAndDebug(loc(), type);
else target.add(Instruction.call(args.length)).setLocationAndDebug(loc(), type);
if (!pollute) target.add(Instruction.discard());
}
@Override
public void compile(CompileResult target, boolean pollute) {
compile(target, pollute, BreakpointType.STEP_IN);
}
public CallStatement(Location loc, boolean isNew, Statement func, Statement ...args) {
super(loc);
this.isNew = isNew;
this.func = func;
this.args = args;
}
}

View File

@@ -1,12 +1,11 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.compilation.AssignableStatement; import me.topchetoeu.jscript.compilation.AssignableStatement;
import me.topchetoeu.jscript.compilation.CompileTarget; import me.topchetoeu.jscript.compilation.CompileResult;
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.scope.ScopeRecord;
public class ChangeStatement extends Statement { public class ChangeStatement extends Statement {
public final AssignableStatement value; public final AssignableStatement value;
@@ -14,12 +13,12 @@ public class ChangeStatement extends Statement {
public final boolean postfix; public final boolean postfix;
@Override @Override
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) { public void compile(CompileResult target, boolean pollute) {
value.toAssign(new ConstantStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, scope, true); value.toAssign(new ConstantStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, true);
if (!pollute) target.add(Instruction.discard(loc())); if (!pollute) target.add(Instruction.discard());
else if (postfix) { else if (postfix) {
target.add(Instruction.loadValue(loc(), addAmount)); target.add(Instruction.pushValue(addAmount));
target.add(Instruction.operation(loc(), Operation.SUBTRACT)); target.add(Instruction.operation(Operation.SUBTRACT));
} }
} }

View File

@@ -0,0 +1,47 @@
package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class ConstantStatement extends Statement {
public final Object value;
public final boolean isNull;
@Override public boolean pure() { return true; }
@Override
public void compile(CompileResult target, boolean pollute) {
if (pollute) {
if (isNull) target.add(Instruction.pushNull());
else if (value instanceof Double) target.add(Instruction.pushValue((Double)value));
else if (value instanceof String) target.add(Instruction.pushValue((String)value));
else if (value instanceof Boolean) target.add(Instruction.pushValue((Boolean)value));
else target.add(Instruction.pushUndefined());
}
}
private ConstantStatement(Location loc, Object val, boolean isNull) {
super(loc);
this.value = val;
this.isNull = isNull;
}
public ConstantStatement(Location loc, boolean val) {
this(loc, val, false);
}
public ConstantStatement(Location loc, String val) {
this(loc, val, false);
}
public ConstantStatement(Location loc, double val) {
this(loc, val, false);
}
public static ConstantStatement ofUndefined(Location loc) {
return new ConstantStatement(loc, null, false);
}
public static ConstantStatement ofNull(Location loc) {
return new ConstantStatement(loc, null, true);
}
}

View File

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

View File

@@ -0,0 +1,127 @@
package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.Instruction.Type;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.CompoundStatement;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
public class FunctionStatement extends Statement {
public final CompoundStatement body;
public final String varName;
public final String[] args;
public final boolean statement;
public final Location end;
@Override public boolean pure() { return varName == null && statement; }
@Override
public void declare(CompileResult target) {
if (varName != null && statement) target.scope.define(varName);
}
public static void checkBreakAndCont(CompileResult target, int start) {
for (int i = start; i < target.size(); i++) {
if (target.get(i).type == Type.NOP) {
if (target.get(i).is(0, "break") ) {
throw new SyntaxException(target.map.toLocation(i), "Break was placed outside a loop.");
}
if (target.get(i).is(0, "cont")) {
throw new SyntaxException(target.map.toLocation(i), "Continue was placed outside a loop.");
}
}
}
}
private CompileResult compileBody(CompileResult target, boolean pollute, BreakpointType bp) {
for (var i = 0; i < args.length; i++) {
for (var j = 0; j < i; j++) {
if (args[i].equals(args[j])) {
throw new SyntaxException(loc(), "Duplicate parameter '" + args[i] + "'.");
}
}
}
var subtarget = new CompileResult(target.scope.child());
subtarget.scope.define("this");
var argsVar = subtarget.scope.define("arguments");
if (args.length > 0) {
for (var i = 0; i < args.length; i++) {
subtarget.add(Instruction.loadVar(argsVar));
subtarget.add(Instruction.pushValue(i));
subtarget.add(Instruction.loadMember());
subtarget.add(Instruction.storeVar(subtarget.scope.define(args[i])));
}
}
if (!statement && this.varName != null) {
subtarget.add(Instruction.storeSelfFunc((int)subtarget.scope.define(this.varName))).setLocationAndDebug(loc(), bp);
}
body.declare(subtarget);
body.compile(subtarget, false);
subtarget.length = args.length;
subtarget.add(Instruction.ret()).setLocation(end);
checkBreakAndCont(subtarget, 0);
if (pollute) target.add(Instruction.loadFunc(target.children.size(), subtarget.scope.getCaptures()));
return target.addChild(subtarget);
}
public void compile(CompileResult target, 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, pollute || hasVar || hasName, bp);
if (hasName) {
if (pollute || hasVar) target.add(Instruction.dup());
target.add(Instruction.pushValue("name"));
target.add(Instruction.pushValue(name));
target.add(Instruction.storeMember());
}
if (hasVar) {
var key = target.scope.getKey(this.varName);
if (key instanceof String) target.add(Instruction.makeVar((String)key));
target.add(Instruction.storeVar(target.scope.getKey(this.varName), false));
}
}
public void compile(CompileResult target, boolean pollute, String name) {
compile(target, pollute, name, BreakpointType.NONE);
}
@Override public void compile(CompileResult target, boolean pollute, BreakpointType bp) {
compile(target, pollute, (String)null, bp);
}
@Override public void compile(CompileResult target, boolean pollute) {
compile(target, pollute, (String)null, BreakpointType.NONE);
}
public FunctionStatement(Location loc, Location end, String varName, String[] args, boolean statement, CompoundStatement body) {
super(loc);
this.end = end;
this.varName = varName;
this.statement = statement;
this.args = args;
this.body = body;
}
public static void compileWithName(Statement stm, CompileResult target, boolean pollute, String name) {
if (stm instanceof FunctionStatement) ((FunctionStatement)stm).compile(target, pollute, name);
else stm.compile(target, pollute);
}
public static void compileWithName(Statement stm, CompileResult target, boolean pollute, String name, BreakpointType bp) {
if (stm instanceof FunctionStatement) ((FunctionStatement)stm).compile(target, pollute, name, bp);
else stm.compile(target, pollute, bp);
}
}

View File

@@ -0,0 +1,19 @@
package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class GlobalThisStatement extends Statement {
@Override public boolean pure() { return true; }
@Override
public void compile(CompileResult target, boolean pollute) {
if (pollute) target.add(Instruction.loadGlob());
}
public GlobalThisStatement(Location loc) {
super(loc);
}
}

View File

@@ -0,0 +1,45 @@
package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class IndexAssignStatement extends Statement {
public final Statement object;
public final Statement index;
public final Statement value;
public final Operation operation;
@Override
public void compile(CompileResult target, boolean pollute) {
if (operation != null) {
object.compile(target, true);
index.compile(target, true);
target.add(Instruction.dup(2));
target.add(Instruction.loadMember());
value.compile(target, true);
target.add(Instruction.operation(operation));
target.add(Instruction.storeMember(pollute)).setLocationAndDebug(loc(), BreakpointType.STEP_IN);
}
else {
object.compile(target, true);
index.compile(target, true);
value.compile(target, true);
target.add(Instruction.storeMember(pollute)).setLocationAndDebug(loc(), BreakpointType.STEP_IN);;
}
}
public IndexAssignStatement(Location loc, Statement object, Statement index, Statement value, Operation operation) {
super(loc);
this.object = object;
this.index = index;
this.value = value;
this.operation = operation;
}
}

View File

@@ -0,0 +1,37 @@
package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.AssignableStatement;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class IndexStatement extends AssignableStatement {
public final Statement object;
public final Statement index;
@Override
public Statement toAssign(Statement val, Operation operation) {
return new IndexAssignStatement(loc(), object, index, val, operation);
}
public void compile(CompileResult target, boolean dupObj, boolean pollute) {
object.compile(target, true);
if (dupObj) target.add(Instruction.dup());
index.compile(target, true);
target.add(Instruction.loadMember()).setLocationAndDebug(loc(), BreakpointType.STEP_IN);
if (!pollute) target.add(Instruction.discard());
}
@Override
public void compile(CompileResult target, boolean pollute) {
compile(target, false, pollute);
}
public IndexStatement(Location loc, Statement object, Statement index) {
super(loc);
this.object = object;
this.index = index;
}
}

View File

@@ -0,0 +1,28 @@
package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class LazyAndStatement extends Statement {
public final Statement first, second;
@Override public boolean pure() { return first.pure() && second.pure(); }
@Override
public void compile(CompileResult target, boolean pollute) {
first.compile(target, true);
if (pollute) target.add(Instruction.dup());
int start = target.temp();
if (pollute) target.add(Instruction.discard());
second.compile(target, pollute);
target.set(start, Instruction.jmpIfNot(target.size() - start));
}
public LazyAndStatement(Location loc, Statement first, Statement second) {
super(loc);
this.first = first;
this.second = second;
}
}

View File

@@ -0,0 +1,28 @@
package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class LazyOrStatement extends Statement {
public final Statement first, second;
@Override public boolean pure() { return first.pure() && second.pure(); }
@Override
public void compile(CompileResult target, boolean pollute) {
first.compile(target, true);
if (pollute) target.add(Instruction.dup());
int start = target.temp();
if (pollute) target.add(Instruction.discard());
second.compile(target, pollute);
target.set(start, Instruction.jmpIf(target.size() - start));
}
public LazyOrStatement(Location loc, Statement first, Statement second) {
super(loc);
this.first = first;
this.second = second;
}
}

View File

@@ -0,0 +1,61 @@
package me.topchetoeu.jscript.compilation.values;
import java.util.ArrayList;
import java.util.Map;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class ObjectStatement extends Statement {
public final Map<String, Statement> map;
public final Map<String, FunctionStatement> getters;
public final Map<String, FunctionStatement> setters;
@Override public boolean pure() {
for (var el : map.values()) {
if (!el.pure()) return false;
}
return true;
}
@Override
public void compile(CompileResult target, boolean pollute) {
target.add(Instruction.loadObj());
for (var el : map.entrySet()) {
target.add(Instruction.dup());
target.add(Instruction.pushValue(el.getKey()));
var val = el.getValue();
FunctionStatement.compileWithName(val, target, true, el.getKey().toString());
target.add(Instruction.storeMember());
}
var keys = new ArrayList<Object>();
keys.addAll(getters.keySet());
keys.addAll(setters.keySet());
for (var key : keys) {
target.add(Instruction.pushValue((String)key));
if (getters.containsKey(key)) getters.get(key).compile(target, true);
else target.add(Instruction.pushUndefined());
if (setters.containsKey(key)) setters.get(key).compile(target, true);
else target.add(Instruction.pushUndefined());
target.add(Instruction.defProp());
}
if (!pollute) target.add(Instruction.discard());
}
public ObjectStatement(Location loc, Map<String, Statement> map, Map<String, FunctionStatement> getters, Map<String, FunctionStatement> setters) {
super(loc);
this.map = map;
this.getters = getters;
this.setters = setters;
}
}

View File

@@ -1,11 +1,10 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.compilation.CompileTarget; import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class OperationStatement extends Statement { public class OperationStatement extends Statement {
public final Statement[] args; public final Statement[] args;
@@ -20,13 +19,13 @@ public class OperationStatement extends Statement {
} }
@Override @Override
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) { public void compile(CompileResult target, boolean pollute) {
for (var arg : args) { for (var arg : args) {
arg.compile(target, scope, true); arg.compile(target, true);
} }
if (pollute) target.add(Instruction.operation(loc(), operation)); if (pollute) target.add(Instruction.operation(operation));
else target.add(Instruction.discard(loc())); else target.add(Instruction.discard());
} }
public OperationStatement(Location loc, Operation operation, Statement ...args) { public OperationStatement(Location loc, Operation operation, Statement ...args) {

View File

@@ -1,10 +1,9 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.compilation.CompileTarget; import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
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;
@@ -13,9 +12,9 @@ public class RegexStatement extends Statement {
@Override public boolean pure() { return true; } @Override public boolean pure() { return true; }
@Override @Override
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) { public void compile(CompileResult target, boolean pollute) {
target.add(Instruction.loadRegex(loc(), pattern, flags)); target.add(Instruction.loadRegex(pattern, flags));
if (!pollute) target.add(Instruction.discard(loc())); if (!pollute) target.add(Instruction.discard());
} }
public RegexStatement(Location loc, String pattern, String flags) { public RegexStatement(Location loc, String pattern, String flags) {

View File

@@ -1,10 +1,9 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.compilation.CompileTarget; import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class TypeofStatement extends Statement { public class TypeofStatement extends Statement {
public final Statement value; public final Statement value;
@@ -14,16 +13,16 @@ public class TypeofStatement extends Statement {
@Override public boolean pure() { return true; } @Override public boolean pure() { return true; }
@Override @Override
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) { public void compile(CompileResult target, boolean pollute) {
if (value instanceof VariableStatement) { if (value instanceof VariableStatement) {
var i = scope.getKey(((VariableStatement)value).name); var i = target.scope.getKey(((VariableStatement)value).name);
if (i instanceof String) { if (i instanceof String) {
target.add(Instruction.typeof(loc(), (String)i)); target.add(Instruction.typeof((String)i));
return; return;
} }
} }
value.compile(target, scope, pollute); value.compile(target, pollute);
target.add(Instruction.typeof(loc())); target.add(Instruction.typeof());
} }
public TypeofStatement(Location loc, Statement value) { public TypeofStatement(Location loc, Statement value) {

View File

@@ -0,0 +1,37 @@
package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class VariableAssignStatement extends Statement {
public final String name;
public final Statement value;
public final Operation operation;
@Override public boolean pure() { return false; }
@Override
public void compile(CompileResult target, boolean pollute) {
var i = target.scope.getKey(name);
if (operation != null) {
target.add(Instruction.loadVar(i));
FunctionStatement.compileWithName(value, target, true, name);
target.add(Instruction.operation(operation));
target.add(Instruction.storeVar(i, pollute));
}
else {
FunctionStatement.compileWithName(value, target, true, name);
target.add(Instruction.storeVar(i, pollute));
}
}
public VariableAssignStatement(Location loc, String name, Statement val, Operation operation) {
super(loc);
this.name = name;
this.value = val;
this.operation = operation;
}
}

View File

@@ -1,10 +1,9 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.CompileResult;
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.engine.scope.ScopeRecord;
public class VariableIndexStatement extends Statement { public class VariableIndexStatement extends Statement {
public final int index; public final int index;
@@ -12,8 +11,8 @@ public class VariableIndexStatement extends Statement {
@Override public boolean pure() { return true; } @Override public boolean pure() { return true; }
@Override @Override
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) { public void compile(CompileResult target, boolean pollute) {
if (pollute) target.add(Instruction.loadVar(loc(), index)); if (pollute) target.add(Instruction.loadVar(index));
} }
public VariableIndexStatement(Location loc, int i) { public VariableIndexStatement(Location loc, int i) {

View File

@@ -1,12 +1,11 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.compilation.AssignableStatement; import me.topchetoeu.jscript.compilation.AssignableStatement;
import me.topchetoeu.jscript.compilation.CompileTarget; import me.topchetoeu.jscript.compilation.CompileResult;
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.scope.ScopeRecord;
public class VariableStatement extends AssignableStatement { public class VariableStatement extends AssignableStatement {
public final String name; public final String name;
@@ -19,10 +18,10 @@ public class VariableStatement extends AssignableStatement {
} }
@Override @Override
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) { public void compile(CompileResult target, boolean pollute) {
var i = scope.getKey(name); var i = target.scope.getKey(name);
target.add(Instruction.loadVar(loc(), i)); target.add(Instruction.loadVar(i));
if (!pollute) target.add(Instruction.discard(loc())); if (!pollute) target.add(Instruction.discard());
} }
public VariableStatement(Location loc, String name) { public VariableStatement(Location loc, String name) {

View File

@@ -0,0 +1,456 @@
package me.topchetoeu.jscript.lib;
import java.util.Iterator;
import java.util.Stack;
import me.topchetoeu.jscript.runtime.values.ArrayValue;
import me.topchetoeu.jscript.runtime.values.FunctionValue;
import me.topchetoeu.jscript.runtime.values.NativeFunction;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.runtime.values.Values;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.ExposeConstructor;
import me.topchetoeu.jscript.utils.interop.ExposeTarget;
import me.topchetoeu.jscript.utils.interop.ExposeType;
import me.topchetoeu.jscript.utils.interop.WrapperName;
@WrapperName("Array")
public class ArrayLib {
private static int normalizeI(int len, int i, boolean clamp) {
if (i < 0) i += len;
if (clamp) {
if (i < 0) i = 0;
if (i > len) i = len;
}
return i;
}
@Expose(value = "length", type = ExposeType.GETTER)
public static int __getLength(Arguments args) {
return args.self(ArrayValue.class).size();
}
@Expose(value = "length", type = ExposeType.SETTER)
public static void __setLength(Arguments args) {
args.self(ArrayValue.class).setSize(args.getInt(0));
}
@Expose public static ObjectValue __values(Arguments args) {
return __iterator(args);
}
@Expose public static ObjectValue __keys(Arguments args) {
return Values.toJSIterator(args.ctx, () -> new Iterator<Object>() {
private int i = 0;
@Override
public boolean hasNext() {
return i < args.self(ArrayValue.class).size();
}
@Override
public Object next() {
if (!hasNext()) return null;
return i++;
}
});
}
@Expose public static ObjectValue __entries(Arguments args) {
return Values.toJSIterator(args.ctx, () -> new Iterator<Object>() {
private int i = 0;
@Override
public boolean hasNext() {
return i < args.self(ArrayValue.class).size();
}
@Override
public Object next() {
if (!hasNext()) return null;
return new ArrayValue(args.ctx, i, args.self(ArrayValue.class).get(i++));
}
});
}
@Expose(value = "@@Symbol.iterator")
public static ObjectValue __iterator(Arguments args) {
return Values.toJSIterator(args.ctx, args.self(ArrayValue.class));
}
@Expose(value = "@@Symbol.asyncIterator")
public static ObjectValue __asyncIterator(Arguments args) {
return Values.toJSAsyncIterator(args.ctx, args.self(ArrayValue.class).iterator());
}
@Expose public static ArrayValue __concat(Arguments args) {
// TODO: Fully implement with non-array spreadable objects
var arrs = args.slice(-1);
var size = 0;
for (int i = 0; i < arrs.n(); i++) {
if (arrs.get(i) instanceof ArrayValue) size += arrs.convert(i, ArrayValue.class).size();
else i++;
}
var res = new ArrayValue(size);
for (int i = 0, j = 0; i < arrs.n(); i++) {
if (arrs.get(i) instanceof ArrayValue) {
var arrEl = arrs.convert(i, ArrayValue.class);
int n = arrEl.size();
arrEl.copyTo(res, 0, j, n);
j += n;
}
else {
res.set(args.ctx, j++, arrs.get(i));
}
}
return res;
}
@Expose public static ArrayValue __sort(Arguments args) {
var arr = args.self(ArrayValue.class);
var cmp = args.convert(0, FunctionValue.class);
var defaultCmp = new NativeFunction("", _args -> {
return _args.getString(0).compareTo(_args.getString(1));
});
arr.sort((a, b) -> {
var res = Values.toNumber(args.ctx, (cmp == null ? defaultCmp : cmp).call(args.ctx, null, a, b));
if (res < 0) return -1;
if (res > 0) return 1;
return 0;
});
return arr;
}
@Expose public static ArrayValue __fill(Arguments args) {
var arr = args.self(ArrayValue.class);
var val = args.get(0);
var start = normalizeI(arr.size(), args.getInt(1, 0), true);
var end = normalizeI(arr.size(), args.getInt(2, arr.size()), true);
for (; start < end; start++) arr.set(args.ctx, start, val);
return arr;
}
@Expose public static boolean __every(Arguments args) {
var arr = args.self(ArrayValue.class);
for (var i = 0; i < arr.size(); i++) {
if (arr.has(i) && !Values.toBoolean(Values.call(
args.ctx, args.get(0), args.get(1),
arr.get(i), i, arr
))) return false;
}
return true;
}
@Expose public static boolean __some(Arguments args) {
var arr = args.self(ArrayValue.class);
for (var i = 0; i < arr.size(); i++) {
if (arr.has(i) && Values.toBoolean(Values.call(
args.ctx, args.get(0), args.get(1),
arr.get(i), i, arr
))) return true;
}
return false;
}
@Expose public static ArrayValue __filter(Arguments args) {
var arr = args.self(ArrayValue.class);
var res = new ArrayValue(arr.size());
for (int i = 0, j = 0; i < arr.size(); i++) {
if (arr.has(i) && Values.toBoolean(Values.call(
args.ctx, args.get(0), args.get(1),
arr.get(i), i, arr
))) res.set(args.ctx, j++, arr.get(i));
}
return res;
}
@Expose public static ArrayValue __map(Arguments args) {
var arr = args.self(ArrayValue.class);
var res = new ArrayValue(arr.size());
res.setSize(arr.size());
for (int i = 0; i < arr.size(); i++) {
if (arr.has(i)) res.set(args.ctx, i, Values.call(args.ctx, args.get(0), args.get(1), arr.get(i), i, arr));
}
return res;
}
@Expose public static void __forEach(Arguments args) {
var arr = args.self(ArrayValue.class);
var func = args.convert(0, FunctionValue.class);
var thisArg = args.get(1);
for (int i = 0; i < arr.size(); i++) {
if (arr.has(i)) func.call(args.ctx, thisArg, arr.get(i), i, arr);
}
}
@Expose public static Object __reduce(Arguments args) {
var arr = args.self(ArrayValue.class);
var func = args.convert(0, FunctionValue.class);
var res = args.get(1);
var i = 0;
if (args.n() < 2) {
for (; i < arr.size(); i++) {
if (arr.has(i)){
res = arr.get(i++);
break;
}
}
}
for (; i < arr.size(); i++) {
if (arr.has(i)) {
res = func.call(args.ctx, null, res, arr.get(i), i, arr);
}
}
return res;
}
@Expose public static Object __reduceRight(Arguments args) {
var arr = args.self(ArrayValue.class);
var func = args.convert(0, FunctionValue.class);
var res = args.get(1);
var i = arr.size();
if (args.n() < 2) {
while (!arr.has(i--) && i >= 0) {
res = arr.get(i);
}
}
else i--;
for (; i >= 0; i--) {
if (arr.has(i)) {
res = func.call(args.ctx, null, res, arr.get(i), i, arr);
}
}
return res;
}
@Expose public static ArrayValue __flat(Arguments args) {
var arr = args.self(ArrayValue.class);
var depth = args.getInt(0, 1);
var res = new ArrayValue(arr.size());
var stack = new Stack<Object>();
var depths = new Stack<Integer>();
stack.push(arr);
depths.push(-1);
while (!stack.empty()) {
var el = stack.pop();
int d = depths.pop();
if ((d == -1 || d < depth) && el instanceof ArrayValue) {
var arrEl = (ArrayValue)el;
for (int i = arrEl.size() - 1; i >= 0; i--) {
if (!arrEl.has(i)) continue;
stack.push(arrEl.get(i));
depths.push(d + 1);
}
}
else res.set(args.ctx, res.size(), el);
}
return res;
}
@Expose public static ArrayValue __flatMap(Arguments args) {
return __flat(new Arguments(args.ctx, __map(args), 1));
}
@Expose public static Object __find(Arguments args) {
var arr = args.self(ArrayValue.class);
for (int i = 0; i < arr.size(); i++) {
if (arr.has(i) && Values.toBoolean(Values.call(
args.ctx, args.get(0), args.get(1),
arr.get(i), i, args.self
))) return arr.get(i);
}
return null;
}
@Expose public static Object __findLast(Arguments args) {
var arr = args.self(ArrayValue.class);
for (var i = arr.size() - 1; i >= 0; i--) {
if (arr.has(i) && Values.toBoolean(Values.call(
args.ctx, args.get(0), args.get(1),
arr.get(i), i, args.self
))) return arr.get(i);
}
return null;
}
@Expose public static int __findIndex(Arguments args) {
var arr = args.self(ArrayValue.class);
for (int i = 0; i < arr.size(); i++) {
if (arr.has(i) && Values.toBoolean(Values.call(
args.ctx, args.get(0), args.get(1),
arr.get(i), i, args.self
))) return i;
}
return -1;
}
@Expose public static int __findLastIndex(Arguments args) {
var arr = args.self(ArrayValue.class);
for (var i = arr.size() - 1; i >= 0; i--) {
if (arr.has(i) && Values.toBoolean(Values.call(
args.ctx, args.get(0), args.get(1),
arr.get(i), i, args.self
))) return i;
}
return -1;
}
@Expose public static int __indexOf(Arguments args) {
var arr = args.self(ArrayValue.class);
var val = args.get(0);
var start = normalizeI(arr.size(), args.getInt(1), true);
for (int i = start; i < arr.size(); i++) {
if (Values.strictEquals(args.ctx, arr.get(i), val)) return i;
}
return -1;
}
@Expose public static int __lastIndexOf(Arguments args) {
var arr = args.self(ArrayValue.class);
var val = args.get(0);
var start = normalizeI(arr.size(), args.getInt(1), true);
for (int i = arr.size(); i >= start; i--) {
if (Values.strictEquals(args.ctx, arr.get(i), val)) return i;
}
return -1;
}
@Expose public static boolean __includes(Arguments args) {
return __indexOf(args) >= 0;
}
@Expose public static Object __pop(Arguments args) {
var arr = args.self(ArrayValue.class);
if (arr.size() == 0) return null;
var val = arr.get(arr.size() - 1);
arr.shrink(1);
return val;
}
@Expose public static int __push(Arguments args) {
var arr = args.self(ArrayValue.class);
var values = args.args;
arr.copyFrom(args.ctx, values, 0, arr.size(), values.length);
return arr.size();
}
@Expose public static Object __shift(Arguments args) {
var arr = args.self(ArrayValue.class);
if (arr.size() == 0) return null;
var val = arr.get(0);
arr.move(1, 0, arr.size());
arr.shrink(1);
return val;
}
@Expose public static int __unshift(Arguments args) {
var arr = args.self(ArrayValue.class);
var values = args.slice(0).args;
arr.move(0, values.length, arr.size());
arr.copyFrom(args.ctx, values, 0, 0, values.length);
return arr.size();
}
@Expose public static ArrayValue __slice(Arguments args) {
var arr = args.self(ArrayValue.class);
var start = normalizeI(arr.size(), args.getInt(0), true);
var end = normalizeI(arr.size(), args.getInt(1, arr.size()), true);
var res = new ArrayValue(end - start);
arr.copyTo(res, start, 0, end - start);
return res;
}
@Expose public static ArrayValue __splice(Arguments args) {
var arr = args.self(ArrayValue.class);
var start = normalizeI(arr.size(), args.getInt(0), true);
var deleteCount = normalizeI(arr.size(), args.getInt(1, arr.size()), true);
var items = args.slice(2).args;
if (start + deleteCount >= arr.size()) deleteCount = arr.size() - start;
var size = arr.size() - deleteCount + items.length;
var res = new ArrayValue(deleteCount);
arr.copyTo(res, start, 0, deleteCount);
arr.move(start + deleteCount, start + items.length, arr.size() - start - deleteCount);
arr.copyFrom(args.ctx, items, 0, start, items.length);
arr.setSize(size);
return res;
}
@Expose public static String __toString(Arguments args) {
return __join(new Arguments(args.ctx, args.self, ","));
}
@Expose public static String __join(Arguments args) {
var arr = args.self(ArrayValue.class);
var sep = args.getString(0, ", ");
var res = new StringBuilder();
var comma = false;
for (int i = 0; i < arr.size(); i++) {
if (!arr.has(i)) continue;
if (comma) res.append(sep);
comma = true;
var el = arr.get(i);
if (el == null || el == Values.NULL) continue;
res.append(Values.toString(args.ctx, el));
}
return res.toString();
}
@Expose(target = ExposeTarget.STATIC)
public static boolean __isArray(Arguments args) {
return args.get(0) instanceof ArrayValue;
}
@Expose(target = ExposeTarget.STATIC)
public static ArrayValue __of(Arguments args) {
return new ArrayValue(args.ctx, args.slice(0).args);
}
@ExposeConstructor public static ArrayValue __constructor(Arguments args) {
ArrayValue res;
if (args.n() == 1 && args.get(0) instanceof Number) {
var len = args.getInt(0);
res = new ArrayValue(len);
res.setSize(len);
}
else {
var val = args.args;
res = new ArrayValue(val.length);
res.copyFrom(args.ctx, val, 0, 0, val.length);
}
return res;
}
}

View File

@@ -0,0 +1,87 @@
package me.topchetoeu.jscript.lib;
import me.topchetoeu.jscript.lib.PromiseLib.Handle;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.Frame;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.CodeFunction;
import me.topchetoeu.jscript.runtime.values.FunctionValue;
import me.topchetoeu.jscript.runtime.values.NativeFunction;
import me.topchetoeu.jscript.runtime.values.Values;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.WrapperName;
@WrapperName("AsyncFunction")
public class AsyncFunctionLib extends FunctionValue {
public final CodeFunction func;
private static class AsyncHelper {
public PromiseLib promise = new PromiseLib();
public Frame frame;
private boolean awaiting = false;
private void next(Context ctx, Object inducedValue, EngineException inducedError) {
Object res = null;
frame.onPush();
awaiting = false;
while (!awaiting) {
try {
res = frame.next(inducedValue, Values.NO_RETURN, inducedError);
inducedValue = Values.NO_RETURN;
inducedError = null;
if (res != Values.NO_RETURN) {
promise.fulfill(ctx, res);
break;
}
}
catch (EngineException e) {
promise.reject(ctx, e);
break;
}
}
frame.onPop();
if (awaiting) {
PromiseLib.handle(ctx, frame.pop(), new Handle() {
@Override
public void onFulfil(Object val) {
next(ctx, val, null);
}
@Override
public void onReject(EngineException err) {
next(ctx, Values.NO_RETURN, err);
}
}.defer(ctx));
}
}
public Object await(Arguments args) {
this.awaiting = true;
return args.get(0);
}
}
@Override
public Object call(Extensions ext, Object thisArg, Object ...args) {
var handler = new AsyncHelper();
var ctx = Context.of(ext);
var newArgs = new Object[args.length + 1];
newArgs[0] = new NativeFunction("await", handler::await);
System.arraycopy(args, 0, newArgs, 1, args.length);
handler.frame = new Frame(ctx, thisArg, newArgs, (CodeFunction)func);
handler.next(ctx, Values.NO_RETURN, null);
return handler.promise;
}
public AsyncFunctionLib(FunctionValue func) {
super(func.name, func.length);
if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function.");
this.func = (CodeFunction)func;
}
}

View File

@@ -0,0 +1,34 @@
package me.topchetoeu.jscript.lib;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.Frame;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.CodeFunction;
import me.topchetoeu.jscript.runtime.values.FunctionValue;
import me.topchetoeu.jscript.runtime.values.NativeFunction;
import me.topchetoeu.jscript.utils.interop.WrapperName;
@WrapperName("AsyncGeneratorFunction")
public class AsyncGeneratorFunctionLib extends FunctionValue {
public final CodeFunction func;
@Override
public Object call(Extensions ext, Object thisArg, Object ...args) {
var handler = new AsyncGeneratorLib();
var newArgs = new Object[args.length + 2];
newArgs[0] = new NativeFunction("await", handler::await);
newArgs[1] = new NativeFunction("yield", handler::yield);
System.arraycopy(args, 0, newArgs, 2, args.length);
handler.frame = new Frame(Context.of(ext), thisArg, newArgs, func);
return handler;
}
public AsyncGeneratorFunctionLib(CodeFunction func) {
super(func.name, func.length);
if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function.");
this.func = func;
}
}

View File

@@ -0,0 +1,107 @@
package me.topchetoeu.jscript.lib;
import java.util.Map;
import me.topchetoeu.jscript.lib.PromiseLib.Handle;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.Frame;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.runtime.values.Values;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.WrapperName;
@WrapperName("AsyncGenerator")
public class AsyncGeneratorLib {
private int state = 0;
private boolean done = false;
private PromiseLib currPromise;
public Frame frame;
private void next(Context ctx, Object inducedValue, Object inducedReturn, EngineException inducedError) {
if (done) {
if (inducedError != null) throw inducedError;
currPromise.fulfill(ctx, new ObjectValue(ctx, Map.of(
"done", true,
"value", inducedReturn == Values.NO_RETURN ? null : inducedReturn
)));
return;
}
Object res = null;
state = 0;
frame.onPush();
while (state == 0) {
try {
res = frame.next(inducedValue, inducedReturn, inducedError);
inducedValue = inducedReturn = Values.NO_RETURN;
inducedError = null;
if (res != Values.NO_RETURN) {
var obj = new ObjectValue();
obj.defineProperty(ctx, "done", true);
obj.defineProperty(ctx, "value", res);
currPromise.fulfill(ctx, obj);
break;
}
}
catch (EngineException e) {
currPromise.reject(ctx, e);
break;
}
}
frame.onPop();
if (state == 1) {
PromiseLib.handle(ctx, frame.pop(), new Handle() {
@Override public void onFulfil(Object val) {
next(ctx, val, Values.NO_RETURN, null);
}
@Override public void onReject(EngineException err) {
next(ctx, Values.NO_RETURN, Values.NO_RETURN, err);
}
}.defer(ctx));
}
else if (state == 2) {
var obj = new ObjectValue();
obj.defineProperty(ctx, "done", false);
obj.defineProperty(ctx, "value", frame.pop());
currPromise.fulfill(ctx, obj);
}
}
@Override
public String toString() {
if (done) return "Generator [closed]";
if (state != 0) return "Generator [suspended]";
return "Generator [running]";
}
public Object await(Arguments args) {
this.state = 1;
return args.get(0);
}
public Object yield(Arguments args) {
this.state = 2;
return args.get(0);
}
@Expose public PromiseLib __next(Arguments args) {
this.currPromise = new PromiseLib();
if (args.has(0)) next(args.ctx, args.get(0), Values.NO_RETURN, null);
else next(args.ctx, Values.NO_RETURN, Values.NO_RETURN, null);
return this.currPromise;
}
@Expose public PromiseLib __return(Arguments args) {
this.currPromise = new PromiseLib();
next(args.ctx, Values.NO_RETURN, args.get(0), null);
return this.currPromise;
}
@Expose public PromiseLib __throw(Arguments args) {
this.currPromise = new PromiseLib();
next(args.ctx, Values.NO_RETURN, Values.NO_RETURN, new EngineException(args.get(0)).setExtensions(args.ctx));
return this.currPromise;
}
}

View File

@@ -0,0 +1,37 @@
package me.topchetoeu.jscript.lib;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.runtime.values.Values;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.ExposeConstructor;
import me.topchetoeu.jscript.utils.interop.WrapperName;
@WrapperName("Boolean")
public class BooleanLib {
public static final BooleanLib TRUE = new BooleanLib(true);
public static final BooleanLib FALSE = new BooleanLib(false);
public final boolean value;
@Override public String toString() {
return value + "";
}
public BooleanLib(boolean val) {
this.value = val;
}
@ExposeConstructor public static Object __constructor(Arguments args) {
var val = args.getBoolean(0);
if (args.self instanceof ObjectValue) return val ? TRUE : FALSE;
else return val;
}
@Expose public static String __toString(Arguments args) {
return args.self(Boolean.class) ? "true" : "false";
}
@Expose public static boolean __valueOf(Arguments args) {
if (Values.isWrapper(args.self, BooleanLib.class)) return Values.wrapper(args.self, BooleanLib.class).value;
return args.self(Boolean.class);
}
}

View File

@@ -0,0 +1,38 @@
package me.topchetoeu.jscript.lib;
import java.io.IOException;
import me.topchetoeu.jscript.runtime.values.Values;
import me.topchetoeu.jscript.utils.filesystem.File;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.WrapperName;
@WrapperName("Console")
public class ConsoleLib {
public static interface Writer {
void writeLine(String val) throws IOException;
}
private File file;
@Expose
public void __log(Arguments args) {
var res = new StringBuilder();
var first = true;
for (var el : args.args) {
if (!first) res.append(" ");
first = false;
res.append(Values.toReadable(args.ctx, el).getBytes());
}
for (var line : res.toString().split("\n", -1)) {
file.write(line.getBytes());
}
}
public ConsoleLib(File file) {
this.file = file;
}
}

View File

@@ -1,12 +1,17 @@
package me.topchetoeu.jscript.lib; package me.topchetoeu.jscript.lib;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone; import java.util.TimeZone;
import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.interop.Native; import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.ExposeConstructor;
import me.topchetoeu.jscript.utils.interop.ExposeTarget;
import me.topchetoeu.jscript.utils.interop.WrapperName;
@Native("Date") public class DateLib { @WrapperName("Date")
public class DateLib {
private Calendar normal; private Calendar normal;
private Calendar utc; private Calendar utc;
@@ -22,244 +27,221 @@ import me.topchetoeu.jscript.interop.Native;
normal = utc = null; normal = utc = null;
} }
@Native @Expose public double __getYear() {
public static double now() {
return new DateLib().getTime();
}
@Native
public double getYear() {
if (normal == null) return Double.NaN; if (normal == null) return Double.NaN;
return normal.get(Calendar.YEAR) - 1900; return normal.get(Calendar.YEAR) - 1900;
} }
@Native @Expose public double __setYeard(Arguments args) {
public double setYear(Context ctx, double real) { var real = args.getDouble(0);
if (real >= 0 && real <= 99) real = real + 1900; if (real >= 0 && real <= 99) real = real + 1900;
if (Double.isNaN(real)) invalidate(); if (Double.isNaN(real)) invalidate();
else normal.set(Calendar.YEAR, (int)real); else normal.set(Calendar.YEAR, (int)real);
updateUTC(); updateUTC();
return getTime(); return __getTime();
} }
@Native @Expose public double __getFullYear() {
public double getFullYear() {
if (normal == null) return Double.NaN; if (normal == null) return Double.NaN;
return normal.get(Calendar.YEAR); return normal.get(Calendar.YEAR);
} }
@Native @Expose public double __getMonth() {
public double getMonth() {
if (normal == null) return Double.NaN; if (normal == null) return Double.NaN;
return normal.get(Calendar.MONTH); return normal.get(Calendar.MONTH);
} }
@Native @Expose public double __getDate() {
public double getDate() {
if (normal == null) return Double.NaN; if (normal == null) return Double.NaN;
return normal.get(Calendar.DAY_OF_MONTH); return normal.get(Calendar.DAY_OF_MONTH);
} }
@Native @Expose public double __getDay() {
public double getDay() {
if (normal == null) return Double.NaN; if (normal == null) return Double.NaN;
return normal.get(Calendar.DAY_OF_WEEK); return normal.get(Calendar.DAY_OF_WEEK);
} }
@Native @Expose public double __getHours() {
public double getHours() {
if (normal == null) return Double.NaN; if (normal == null) return Double.NaN;
return normal.get(Calendar.HOUR_OF_DAY); return normal.get(Calendar.HOUR_OF_DAY);
} }
@Native @Expose public double __getMinutes() {
public double getMinutes() {
if (normal == null) return Double.NaN; if (normal == null) return Double.NaN;
return normal.get(Calendar.MINUTE); return normal.get(Calendar.MINUTE);
} }
@Native @Expose public double __getSeconds() {
public double getSeconds() {
if (normal == null) return Double.NaN; if (normal == null) return Double.NaN;
return normal.get(Calendar.SECOND); return normal.get(Calendar.SECOND);
} }
@Native @Expose public double __getMilliseconds() {
public double getMilliseconds() {
if (normal == null) return Double.NaN; if (normal == null) return Double.NaN;
return normal.get(Calendar.MILLISECOND); return normal.get(Calendar.MILLISECOND);
} }
@Native @Expose public double __getUTCFullYear() {
public double getUTCFullYear() {
if (utc == null) return Double.NaN; if (utc == null) return Double.NaN;
return utc.get(Calendar.YEAR); return utc.get(Calendar.YEAR);
} }
@Native @Expose public double __getUTCMonth() {
public double getUTCMonth() {
if (utc == null) return Double.NaN; if (utc == null) return Double.NaN;
return utc.get(Calendar.MONTH); return utc.get(Calendar.MONTH);
} }
@Native @Expose public double __getUTCDate() {
public double getUTCDate() {
if (utc == null) return Double.NaN; if (utc == null) return Double.NaN;
return utc.get(Calendar.DAY_OF_MONTH); return utc.get(Calendar.DAY_OF_MONTH);
} }
@Native @Expose public double __getUTCDay() {
public double getUTCDay() {
if (utc == null) return Double.NaN; if (utc == null) return Double.NaN;
return utc.get(Calendar.DAY_OF_WEEK); return utc.get(Calendar.DAY_OF_WEEK);
} }
@Native @Expose public double __getUTCHours() {
public double getUTCHours() {
if (utc == null) return Double.NaN; if (utc == null) return Double.NaN;
return utc.get(Calendar.HOUR_OF_DAY); return utc.get(Calendar.HOUR_OF_DAY);
} }
@Native @Expose public double __getUTCMinutes() {
public double getUTCMinutes() {
if (utc == null) return Double.NaN; if (utc == null) return Double.NaN;
return utc.get(Calendar.MINUTE); return utc.get(Calendar.MINUTE);
} }
@Native @Expose public double __getUTCSeconds() {
public double getUTCSeconds() {
if (utc == null) return Double.NaN; if (utc == null) return Double.NaN;
return utc.get(Calendar.SECOND); return utc.get(Calendar.SECOND);
} }
@Native @Expose public double __getUTCMilliseconds() {
public double getUTCMilliseconds() {
if (utc == null) return Double.NaN; if (utc == null) return Double.NaN;
return utc.get(Calendar.MILLISECOND); return utc.get(Calendar.MILLISECOND);
} }
@Native @Expose public double __setFullYear(Arguments args) {
public double setFullYear(Context ctx, double real) { var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate(); if (Double.isNaN(real)) invalidate();
else normal.set(Calendar.YEAR, (int)real); else normal.set(Calendar.YEAR, (int)real);
updateUTC(); updateUTC();
return getTime(); return __getTime();
} }
@Native @Expose public double __setMonthd(Arguments args) {
public double setMonth(Context ctx, double real) { var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate(); if (Double.isNaN(real)) invalidate();
else normal.set(Calendar.MONTH, (int)real); else normal.set(Calendar.MONTH, (int)real);
updateUTC(); updateUTC();
return getTime(); return __getTime();
} }
@Native @Expose public double __setDated(Arguments args) {
public double setDate(Context ctx, double real) { var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate(); if (Double.isNaN(real)) invalidate();
else normal.set(Calendar.DAY_OF_MONTH, (int)real); else normal.set(Calendar.DAY_OF_MONTH, (int)real);
updateUTC(); updateUTC();
return getTime(); return __getTime();
} }
@Native @Expose public double __setDayd(Arguments args) {
public double setDay(Context ctx, double real) { var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate(); if (Double.isNaN(real)) invalidate();
else normal.set(Calendar.DAY_OF_WEEK, (int)real); else normal.set(Calendar.DAY_OF_WEEK, (int)real);
updateUTC(); updateUTC();
return getTime(); return __getTime();
} }
@Native @Expose public double __setHoursd(Arguments args) {
public double setHours(Context ctx, double real) { var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate(); if (Double.isNaN(real)) invalidate();
else normal.set(Calendar.HOUR_OF_DAY, (int)real); else normal.set(Calendar.HOUR_OF_DAY, (int)real);
updateUTC(); updateUTC();
return getTime(); return __getTime();
} }
@Native @Expose public double __setMinutesd(Arguments args) {
public double setMinutes(Context ctx, double real) { var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate(); if (Double.isNaN(real)) invalidate();
else normal.set(Calendar.MINUTE, (int)real); else normal.set(Calendar.MINUTE, (int)real);
updateUTC(); updateUTC();
return getTime(); return __getTime();
} }
@Native @Expose public double __setSecondsd(Arguments args) {
public double setSeconds(Context ctx, double real) { var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate(); if (Double.isNaN(real)) invalidate();
else normal.set(Calendar.SECOND, (int)real); else normal.set(Calendar.SECOND, (int)real);
updateUTC(); updateUTC();
return getTime(); return __getTime();
} }
@Native @Expose public double __setMillisecondsd(Arguments args) {
public double setMilliseconds(Context ctx, double real) { var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate(); if (Double.isNaN(real)) invalidate();
else normal.set(Calendar.MILLISECOND, (int)real); else normal.set(Calendar.MILLISECOND, (int)real);
updateUTC(); updateUTC();
return getTime(); return __getTime();
} }
@Native @Expose public double __setUTCFullYeard(Arguments args) {
public double setUTCFullYear(Context ctx, double real) { var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate(); if (Double.isNaN(real)) invalidate();
else utc.set(Calendar.YEAR, (int)real); else utc.set(Calendar.YEAR, (int)real);
updateNormal(); updateNormal();
return getTime(); return __getTime();
} }
@Native @Expose public double __setUTCMonthd(Arguments args) {
public double setUTCMonth(Context ctx, double real) { var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate(); if (Double.isNaN(real)) invalidate();
else utc.set(Calendar.MONTH, (int)real); else utc.set(Calendar.MONTH, (int)real);
updateNormal(); updateNormal();
return getTime(); return __getTime();
} }
@Native @Expose public double __setUTCDated(Arguments args) {
public double setUTCDate(Context ctx, double real) { var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate(); if (Double.isNaN(real)) invalidate();
else utc.set(Calendar.DAY_OF_MONTH, (int)real); else utc.set(Calendar.DAY_OF_MONTH, (int)real);
updateNormal(); updateNormal();
return getTime(); return __getTime();
} }
@Native @Expose public double __setUTCDayd(Arguments args) {
public double setUTCDay(Context ctx, double real) { var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate(); if (Double.isNaN(real)) invalidate();
else utc.set(Calendar.DAY_OF_WEEK, (int)real); else utc.set(Calendar.DAY_OF_WEEK, (int)real);
updateNormal(); updateNormal();
return getTime(); return __getTime();
} }
@Native @Expose public double __setUTCHoursd(Arguments args) {
public double setUTCHours(Context ctx, double real) { var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate(); if (Double.isNaN(real)) invalidate();
else utc.set(Calendar.HOUR_OF_DAY, (int)real); else utc.set(Calendar.HOUR_OF_DAY, (int)real);
updateNormal(); updateNormal();
return getTime(); return __getTime();
} }
@Native @Expose public double __setUTCMinutesd(Arguments args) {
public double setUTCMinutes(Context ctx, double real) { var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate(); if (Double.isNaN(real)) invalidate();
else utc.set(Calendar.MINUTE, (int)real); else utc.set(Calendar.MINUTE, (int)real);
updateNormal(); updateNormal();
return getTime(); return __getTime();
} }
@Native @Expose public double __setUTCSecondsd(Arguments args) {
public double setUTCSeconds(Context ctx, double real) { var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate(); if (Double.isNaN(real)) invalidate();
else utc.set(Calendar.SECOND, (int)real); else utc.set(Calendar.SECOND, (int)real);
updateNormal(); updateNormal();
return getTime(); return __getTime();
} }
@Native @Expose public double __setUTCMillisecondsd(Arguments args) {
public double setUTCMilliseconds(Context ctx, double real) { var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate(); if (Double.isNaN(real)) invalidate();
else utc.set(Calendar.MILLISECOND, (int)real); else utc.set(Calendar.MILLISECOND, (int)real);
updateNormal(); updateNormal();
return getTime(); return __getTime();
} }
@Native @Expose public double __getTime() {
public double getTime() {
if (utc == null) return Double.NaN; if (utc == null) return Double.NaN;
return utc.getTimeInMillis(); return utc.getTimeInMillis();
} }
@Native @Expose public double __getTimezoneOffset() {
public double getTimezoneOffset() {
if (normal == null) return Double.NaN; if (normal == null) return Double.NaN;
return normal.getTimeZone().getRawOffset() / 60000; return normal.getTimeZone().getRawOffset() / 60000;
} }
@Native @Expose public double __valueOf() {
public double valueOf() {
if (normal == null) return Double.NaN; if (normal == null) return Double.NaN;
else return normal.getTimeInMillis(); else return normal.getTimeInMillis();
} }
@Native @Expose public String __toString() {
public String toString() {
return normal.getTime().toString(); return normal.getTime().toString();
} }
@Native @Override public String toString() {
return __toString();
}
public DateLib(long timestamp) { public DateLib(long timestamp) {
normal = Calendar.getInstance(); normal = Calendar.getInstance();
utc = Calendar.getInstance(); utc = Calendar.getInstance();
@@ -268,8 +250,17 @@ import me.topchetoeu.jscript.interop.Native;
utc.setTimeInMillis(timestamp); utc.setTimeInMillis(timestamp);
} }
@Native
public DateLib() { public DateLib() {
this(new java.util.Date().getTime()); this(new Date().getTime());
}
@ExposeConstructor public static DateLib init(Arguments args) {
if (args.has(0)) return new DateLib(args.getLong(0));
else return new DateLib();
}
@Expose(target = ExposeTarget.STATIC)
public static double __now() {
return new DateLib().__getTime();
} }
} }

View File

@@ -0,0 +1,89 @@
package me.topchetoeu.jscript.lib;
import java.util.ArrayList;
import me.topchetoeu.jscript.common.Buffer;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.ArrayValue;
import me.topchetoeu.jscript.runtime.values.Values;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.ExposeTarget;
import me.topchetoeu.jscript.utils.interop.WrapperName;
@WrapperName("Encoding")
public class EncodingLib {
private static final String HEX = "0123456789ABCDEF";
public static String encodeUriAny(String str, String keepAlphabet) {
if (str == null) str = "undefined";
var bytes = str.getBytes();
var sb = new StringBuilder(bytes.length);
for (byte c : bytes) {
if (Parsing.isAlphanumeric((char)c) || Parsing.isAny((char)c, keepAlphabet)) sb.append((char)c);
else {
sb.append('%');
sb.append(HEX.charAt(c / 16));
sb.append(HEX.charAt(c % 16));
}
}
return sb.toString();
}
public static String decodeUriAny(String str, String keepAlphabet) {
if (str == null) str = "undefined";
var res = new Buffer();
var bytes = str.getBytes();
for (var i = 0; i < bytes.length; i++) {
var c = bytes[i];
if (c == '%') {
if (i >= bytes.length - 2) throw EngineException.ofError("URIError", "URI malformed.");
var b = Parsing.fromHex((char)bytes[i + 1]) * 16 | Parsing.fromHex((char)bytes[i + 2]);
if (!Parsing.isAny((char)b, keepAlphabet)) {
i += 2;
res.append((byte)b);
continue;
}
}
res.append(c);
}
return new String(res.data());
}
@Expose(target = ExposeTarget.STATIC)
public static ArrayValue __encode(Arguments args) {
var res = new ArrayValue();
for (var el : args.getString(0).getBytes()) res.set(null, res.size(), (int)el);
return res;
}
@Expose(target = ExposeTarget.STATIC)
public static String __decode(Arguments args) {
var raw = args.convert(0, ArrayList.class);
var res = new byte[raw.size()];
for (var i = 0; i < raw.size(); i++) res[i] = (byte)Values.toNumber(args.ctx, raw.get(i));
return new String(res);
}
@Expose(target = ExposeTarget.STATIC)
public static String __encodeURIComponent(Arguments args) {
return EncodingLib.encodeUriAny(args.getString(0), ".-_!~*'()");
}
@Expose(target = ExposeTarget.STATIC)
public static String __decodeURIComponent(Arguments args) {
return decodeUriAny(args.getString(0), "");
}
@Expose(target = ExposeTarget.STATIC)
public static String __encodeURI(Arguments args) {
return encodeUriAny(args.getString(0), ";,/?:@&=+$#.-_!~*'()");
}
@Expose(target = ExposeTarget.STATIC)
public static String __decodeURI(Arguments args) {
return decodeUriAny(args.getString(0), ",/?:@&=+$#.");
}
}

View File

@@ -0,0 +1,54 @@
package me.topchetoeu.jscript.lib;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.exceptions.ConvertException;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.runtime.values.Values;
import me.topchetoeu.jscript.runtime.values.ObjectValue.PlaceholderProto;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.ExposeConstructor;
import me.topchetoeu.jscript.utils.interop.ExposeField;
import me.topchetoeu.jscript.utils.interop.WrapperName;
@WrapperName("Error")
public class ErrorLib {
private static String toString(Context ctx, Object name, Object message) {
if (name == null) name = "";
else name = Values.toString(ctx, name).trim();
if (message == null) message = "";
else message = Values.toString(ctx, message).trim();
StringBuilder res = new StringBuilder();
if (!name.equals("")) res.append(name);
if (!message.equals("") && !name.equals("")) res.append(": ");
if (!message.equals("")) res.append(message);
return res.toString();
}
@ExposeField public static final String __name = "Error";
@Expose public static String __toString(Arguments args) {
if (args.self instanceof ObjectValue) return toString(args.ctx,
Values.getMember(args.ctx, args.self, "name"),
Values.getMember(args.ctx, args.self, "message")
);
else return "[Invalid error]";
}
@ExposeConstructor public static ObjectValue __constructor(Arguments args) {
var target = new ObjectValue();
var message = args.getString(0, "");
try {
target = args.self(ObjectValue.class);
}
catch (ConvertException e) {}
target.setPrototype(PlaceholderProto.ERROR);
target.defineProperty(args.ctx, "message", Values.toString(args.ctx, message));
return target;
}
}

View File

@@ -0,0 +1,84 @@
package me.topchetoeu.jscript.lib;
import me.topchetoeu.jscript.runtime.values.ArrayValue;
import me.topchetoeu.jscript.runtime.values.Values;
import me.topchetoeu.jscript.utils.filesystem.File;
import me.topchetoeu.jscript.utils.filesystem.FilesystemException;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.WrapperName;
@WrapperName("File")
public class FileLib {
public final File fd;
@Expose public PromiseLib __pointer(Arguments args) {
return PromiseLib.await(args.ctx, () -> {
try {
return fd.seek(0, 1);
}
catch (FilesystemException e) { throw e.toEngineException(); }
});
}
@Expose public PromiseLib __length(Arguments args) {
return PromiseLib.await(args.ctx, () -> {
try {
long curr = fd.seek(0, 1);
long res = fd.seek(0, 2);
fd.seek(curr, 0);
return res;
}
catch (FilesystemException e) { throw e.toEngineException(); }
});
}
@Expose public PromiseLib __read(Arguments args) {
return PromiseLib.await(args.ctx, () -> {
var n = args.getInt(0);
try {
var buff = new byte[n];
var res = new ArrayValue();
int resI = fd.read(buff);
for (var i = resI - 1; i >= 0; i--) res.set(args.ctx, i, (int)buff[i]);
return res;
}
catch (FilesystemException e) { throw e.toEngineException(); }
});
}
@Expose public PromiseLib __write(Arguments args) {
return PromiseLib.await(args.ctx, () -> {
var val = args.convert(0, ArrayValue.class);
try {
var res = new byte[val.size()];
for (var i = 0; i < val.size(); i++) res[i] = (byte)Values.toNumber(args.ctx, val.get(i));
fd.write(res);
return null;
}
catch (FilesystemException e) { throw e.toEngineException(); }
});
}
@Expose public PromiseLib __close(Arguments args) {
return PromiseLib.await(args.ctx, () -> {
fd.close();
return null;
});
}
@Expose public PromiseLib __seek(Arguments args) {
return PromiseLib.await(args.ctx, () -> {
var ptr = args.getLong(0);
var whence = args.getInt(1);
try {
return fd.seek(ptr, whence);
}
catch (FilesystemException e) { throw e.toEngineException(); }
});
}
public FileLib(File fd) {
this.fd = fd;
}
}

View File

@@ -0,0 +1,193 @@
package me.topchetoeu.jscript.lib;
import java.io.IOException;
import java.util.Iterator;
import java.util.Stack;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.runtime.values.Values;
import me.topchetoeu.jscript.utils.filesystem.ActionType;
import me.topchetoeu.jscript.utils.filesystem.EntryType;
import me.topchetoeu.jscript.utils.filesystem.ErrorReason;
import me.topchetoeu.jscript.utils.filesystem.File;
import me.topchetoeu.jscript.utils.filesystem.FileStat;
import me.topchetoeu.jscript.utils.filesystem.Filesystem;
import me.topchetoeu.jscript.utils.filesystem.FilesystemException;
import me.topchetoeu.jscript.utils.filesystem.Mode;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.ExposeField;
import me.topchetoeu.jscript.utils.interop.ExposeTarget;
import me.topchetoeu.jscript.utils.interop.WrapperName;
@WrapperName("Filesystem")
public class FilesystemLib {
@ExposeField(target = ExposeTarget.STATIC)
public static final int __SEEK_SET = 0;
@ExposeField(target = ExposeTarget.STATIC)
public static final int __SEEK_CUR = 1;
@ExposeField(target = ExposeTarget.STATIC)
public static final int __SEEK_END = 2;
private static Filesystem fs(Context ctx) {
var fs = Filesystem.get(ctx);
if (fs != null) return fs;
throw EngineException.ofError("Current environment doesn't have a file system.");
}
@Expose(target = ExposeTarget.STATIC)
public static String __normalize(Arguments args) {
return fs(args.ctx).normalize(args.convert(String.class));
}
@Expose(target = ExposeTarget.STATIC)
public static PromiseLib __open(Arguments args) {
return PromiseLib.await(args.ctx, () -> {
var fs = fs(args.ctx);
var path = fs.normalize(args.getString(0));
var _mode = Mode.parse(args.getString(1));
try {
if (fs.stat(path).type != EntryType.FILE) {
throw new FilesystemException(ErrorReason.DOESNT_EXIST, "Not a file").setAction(ActionType.OPEN);
}
return new FileLib(fs.open(path, _mode));
}
catch (FilesystemException e) { throw e.toEngineException(); }
});
}
@Expose(target = ExposeTarget.STATIC)
public static ObjectValue __ls(Arguments args) {
return Values.toJSAsyncIterator(args.ctx, new Iterator<>() {
private boolean failed, done;
private File file;
private String nextLine;
private void update() {
if (done) return;
if (!failed) {
if (file == null) {
var fs = fs(args.ctx);
var path = fs.normalize(args.getString(0));
if (fs.stat(path).type != EntryType.FOLDER) {
throw new FilesystemException(ErrorReason.DOESNT_EXIST, "Not a directory").setAction(ActionType.OPEN);
}
file = fs.open(path, Mode.READ);
}
if (nextLine == null) {
while (true) {
nextLine = file.readLine();
if (nextLine == null) {
done = true;
return;
}
nextLine = nextLine.trim();
if (!nextLine.equals("")) break;
}
}
}
}
@Override
public boolean hasNext() {
try {
update();
return !done && !failed;
}
catch (FilesystemException e) { throw e.toEngineException(); }
}
@Override
public String next() {
try {
update();
var res = nextLine;
nextLine = null;
return res;
}
catch (FilesystemException e) { throw e.toEngineException(); }
}
});
}
@Expose(target = ExposeTarget.STATIC)
public static PromiseLib __mkdir(Arguments args) throws IOException {
return PromiseLib.await(args.ctx, () -> {
try {
fs(args.ctx).create(args.getString(0), EntryType.FOLDER);
return null;
}
catch (FilesystemException e) { throw e.toEngineException(); }
});
}
@Expose(target = ExposeTarget.STATIC)
public static PromiseLib __mkfile(Arguments args) throws IOException {
return PromiseLib.await(args.ctx, () -> {
try {
fs(args.ctx).create(args.getString(0), EntryType.FILE);
return null;
}
catch (FilesystemException e) { throw e.toEngineException(); }
});
}
@Expose(target = ExposeTarget.STATIC)
public static PromiseLib __rm(Arguments args) throws IOException {
return PromiseLib.await(args.ctx, () -> {
try {
var fs = fs(args.ctx);
var path = fs.normalize(args.getString(0));
var recursive = args.getBoolean(1);
if (!recursive) fs.create(path, EntryType.NONE);
else {
var stack = new Stack<String>();
stack.push(path);
while (!stack.empty()) {
var currPath = stack.pop();
FileStat stat;
try { stat = fs.stat(currPath); }
catch (FilesystemException e) { continue; }
if (stat.type == EntryType.FOLDER) {
for (var el : fs.open(currPath, Mode.READ).readToString().split("\n")) stack.push(el);
}
else fs.create(currPath, EntryType.NONE);
}
}
return null;
}
catch (FilesystemException e) { throw e.toEngineException(); }
});
}
@Expose(target = ExposeTarget.STATIC)
public static PromiseLib __stat(Arguments args) throws IOException {
return PromiseLib.await(args.ctx, () -> {
try {
var fs = fs(args.ctx);
var path = fs.normalize(args.getString(0));
var stat = fs.stat(path);
var res = new ObjectValue();
res.defineProperty(args.ctx, "type", stat.type.name);
res.defineProperty(args.ctx, "mode", stat.mode.name);
return res;
}
catch (FilesystemException e) { throw e.toEngineException(); }
});
}
@Expose(target = ExposeTarget.STATIC)
public static PromiseLib __exists(Arguments args) throws IOException {
return PromiseLib.await(args.ctx, () -> {
try { fs(args.ctx).stat(args.getString(0)); return true; }
catch (FilesystemException e) { return false; }
});
}
}

View File

@@ -0,0 +1,54 @@
package me.topchetoeu.jscript.lib;
import me.topchetoeu.jscript.runtime.values.ArrayValue;
import me.topchetoeu.jscript.runtime.values.CodeFunction;
import me.topchetoeu.jscript.runtime.values.FunctionValue;
import me.topchetoeu.jscript.runtime.values.NativeFunction;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.ExposeTarget;
import me.topchetoeu.jscript.utils.interop.WrapperName;
@WrapperName("Function")
public class FunctionLib {
@Expose public static Object __apply(Arguments args) {
return args.self(FunctionValue.class).call(args.ctx, args.get(0), args.convert(1, ArrayValue.class).toArray());
}
@Expose public static Object __call(Arguments args) {
return args.self(FunctionValue.class).call(args.ctx, args.get(0), args.slice(1).args);
}
@Expose public static FunctionValue __bind(Arguments args) {
var self = args.self(FunctionValue.class);
var thisArg = args.get(0);
var bindArgs = args.slice(1).args;
return new NativeFunction(self.name + " (bound)", callArgs -> {
Object[] resArgs;
if (args.n() == 0) resArgs = bindArgs;
else {
resArgs = new Object[bindArgs.length + callArgs.n()];
System.arraycopy(bindArgs, 0, resArgs, 0, bindArgs.length);
System.arraycopy(callArgs.args, 0, resArgs, bindArgs.length, callArgs.n());
}
return self.call(callArgs.ctx, thisArg, resArgs);
});
}
@Expose public static String __toString(Arguments args) {
return args.self.toString();
}
@Expose(target = ExposeTarget.STATIC)
public static FunctionValue __async(Arguments args) {
return new AsyncFunctionLib(args.convert(0, FunctionValue.class));
}
@Expose(target = ExposeTarget.STATIC)
public static FunctionValue __asyncGenerator(Arguments args) {
return new AsyncGeneratorFunctionLib(args.convert(0, CodeFunction.class));
}
@Expose(target = ExposeTarget.STATIC)
public static FunctionValue __generator(Arguments args) {
return new GeneratorFunctionLib(args.convert(0, CodeFunction.class));
}
}

View File

@@ -0,0 +1,32 @@
package me.topchetoeu.jscript.lib;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.Frame;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.CodeFunction;
import me.topchetoeu.jscript.runtime.values.FunctionValue;
import me.topchetoeu.jscript.runtime.values.NativeFunction;
import me.topchetoeu.jscript.utils.interop.WrapperName;
@WrapperName("GeneratorFunction")
public class GeneratorFunctionLib extends FunctionValue {
public final CodeFunction func;
@Override public Object call(Extensions ext, Object thisArg, Object ...args) {
var handler = new GeneratorLib();
var newArgs = new Object[args.length + 1];
newArgs[0] = new NativeFunction("yield", handler::yield);
System.arraycopy(args, 0, newArgs, 1, args.length);
handler.frame = new Frame(Context.of(ext), thisArg, newArgs, func);
return handler;
}
public GeneratorFunctionLib(CodeFunction func) {
super(func.name, func.length);
if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function.");
this.func = func;
}
}

View File

@@ -0,0 +1,78 @@
package me.topchetoeu.jscript.lib;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.Frame;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.runtime.values.Values;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.WrapperName;
@WrapperName("Generator")
public class GeneratorLib {
private boolean yielding = true;
private boolean done = false;
public Frame frame;
private ObjectValue next(Context ctx, Object inducedValue, Object inducedReturn, EngineException inducedError) {
if (done) {
if (inducedError != Values.NO_RETURN) throw inducedError;
var res = new ObjectValue();
res.defineProperty(ctx, "done", true);
res.defineProperty(ctx, "value", inducedReturn == Values.NO_RETURN ? null : inducedReturn);
return res;
}
Object res = null;
yielding = false;
frame.onPush();
while (!yielding) {
try {
res = frame.next(inducedValue, inducedReturn, inducedError);
inducedReturn = Values.NO_RETURN;
inducedError = null;
if (res != Values.NO_RETURN) {
done = true;
break;
}
}
catch (EngineException e) {
done = true;
throw e;
}
}
frame.onPop();
if (done) frame = null;
else res = frame.pop();
var obj = new ObjectValue();
obj.defineProperty(ctx, "done", done);
obj.defineProperty(ctx, "value", res);
return obj;
}
@Expose public ObjectValue __next(Arguments args) {
if (args.n() == 0) return next(args.ctx, Values.NO_RETURN, Values.NO_RETURN, null);
else return next(args.ctx, args.get(0), Values.NO_RETURN, null);
}
@Expose public ObjectValue __throw(Arguments args) {
return next(args.ctx, Values.NO_RETURN, Values.NO_RETURN, new EngineException(args.get(0)).setExtensions(args.ctx));
}
@Expose public ObjectValue __return(Arguments args) {
return next(args.ctx, Values.NO_RETURN, args.get(0), null);
}
@Override public String toString() {
if (done) return "Generator [closed]";
if (yielding) return "Generator [suspended]";
return "Generator [running]";
}
public Object yield(Arguments args) {
this.yielding = true;
return args.get(0);
}
}

View File

@@ -0,0 +1,222 @@
package me.topchetoeu.jscript.lib;
import java.util.HashMap;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.Environment;
import me.topchetoeu.jscript.runtime.EventLoop;
import me.topchetoeu.jscript.runtime.Key;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.scope.GlobalScope;
import me.topchetoeu.jscript.runtime.values.FunctionValue;
import me.topchetoeu.jscript.runtime.values.Values;
import me.topchetoeu.jscript.utils.filesystem.Filesystem;
import me.topchetoeu.jscript.utils.filesystem.Mode;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.ExposeField;
import me.topchetoeu.jscript.utils.interop.ExposeTarget;
import me.topchetoeu.jscript.utils.interop.ExposeType;
import me.topchetoeu.jscript.utils.interop.NativeWrapperProvider;
import me.topchetoeu.jscript.utils.modules.ModuleRepo;
public class Internals {
private static final Key<HashMap<Integer, Thread>> THREADS = new Key<>();
private static final Key<Integer> I = new Key<>();
@Expose(target = ExposeTarget.STATIC)
public static Object __require(Arguments args) {
var repo = ModuleRepo.get(args.ctx);
if (repo != null) {
var res = repo.getModule(args.ctx, ModuleRepo.cwd(args.ctx), args.getString(0));
res.load(args.ctx);
return res.value();
}
else throw EngineException.ofError("Modules are not supported.");
}
@Expose(target = ExposeTarget.STATIC)
public static Thread __setTimeout(Arguments args) {
var func = args.convert(0, FunctionValue.class);
var delay = args.getDouble(1);
var arguments = args.slice(2).args;
if (!args.ctx.hasNotNull(EventLoop.KEY)) throw EngineException.ofError("No event loop");
var thread = new Thread(() -> {
var ms = (long)delay;
var ns = (int)((delay - ms) * 10000000);
try { Thread.sleep(ms, ns); }
catch (InterruptedException e) { return; }
args.ctx.get(EventLoop.KEY).pushMsg(() -> func.call(new Context(args.ctx.extensions), null, arguments), false);
});
thread.start();
var i = args.ctx.init(I, 1);
args.ctx.add(I, i + 1);
args.ctx.init(THREADS, new HashMap<Integer, Thread>()).put(i, thread);
return thread;
}
@Expose(target = ExposeTarget.STATIC)
public static Thread __setInterval(Arguments args) {
var func = args.convert(0, FunctionValue.class);
var delay = args.getDouble(1);
var arguments = args.slice(2).args;
if (!args.ctx.hasNotNull(EventLoop.KEY)) throw EngineException.ofError("No event loop");
var thread = new Thread(() -> {
var ms = (long)delay;
var ns = (int)((delay - ms) * 10000000);
while (true) {
try {
Thread.sleep(ms, ns);
}
catch (InterruptedException e) { return; }
args.ctx.get(EventLoop.KEY).pushMsg(() -> func.call(new Context(args.ctx.extensions), null, arguments), false);
}
});
thread.start();
var i = args.ctx.init(I, 1);
args.ctx.add(I, i + 1);
args.ctx.init(THREADS, new HashMap<Integer, Thread>()).put(i, thread);
return thread;
}
@Expose(target = ExposeTarget.STATIC)
public static void __clearTimeout(Arguments args) {
var i = args.getInt(0);
HashMap<Integer, Thread> map = args.ctx.get(THREADS);
if (map == null) return;
var thread = map.get(i);
if (thread == null) return;
thread.interrupt();
map.remove(i);
}
@Expose(target = ExposeTarget.STATIC)
public static void __clearInterval(Arguments args) {
__clearTimeout(args);
}
@Expose(target = ExposeTarget.STATIC)
public static double __parseInt(Arguments args) {
return NumberLib.__parseInt(args);
}
@Expose(target = ExposeTarget.STATIC)
public static double __parseFloat(Arguments args) {
return NumberLib.__parseFloat(args);
}
@Expose(target = ExposeTarget.STATIC)
public static boolean __isNaN(Arguments args) {
return NumberLib.__isNaN(args);
}
@Expose(target = ExposeTarget.STATIC)
public static boolean __isFinite(Arguments args) {
return NumberLib.__isFinite(args);
}
@Expose(target = ExposeTarget.STATIC)
public static boolean __isInfinite(Arguments args) {
return NumberLib.__isInfinite(args);
}
@Expose(target = ExposeTarget.STATIC, type = ExposeType.GETTER)
public static FileLib __stdin(Arguments args) {
return new FileLib(Filesystem.get(args.ctx).open("std://in", Mode.READ));
}
@Expose(target = ExposeTarget.STATIC, type = ExposeType.GETTER)
public static FileLib __stdout(Arguments args) {
return new FileLib(Filesystem.get(args.ctx).open("std://out", Mode.READ));
}
@Expose(target = ExposeTarget.STATIC, type = ExposeType.GETTER)
public static FileLib __stderr(Arguments args) {
return new FileLib(Filesystem.get(args.ctx).open("std://err", Mode.READ));
}
@ExposeField(target = ExposeTarget.STATIC)
public static double __NaN = Double.NaN;
@ExposeField(target = ExposeTarget.STATIC)
public static double __Infinity = Double.POSITIVE_INFINITY;
@Expose(target = ExposeTarget.STATIC)
public static String __encodeURIComponent(Arguments args) {
return EncodingLib.__encodeURIComponent(args);
}
@Expose(target = ExposeTarget.STATIC)
public static String __decodeURIComponent(Arguments args) {
return EncodingLib.__decodeURIComponent(args);
}
@Expose(target = ExposeTarget.STATIC)
public static String __encodeURI(Arguments args) {
return EncodingLib.__encodeURI(args);
}
@Expose(target = ExposeTarget.STATIC)
public static String __decodeURI(Arguments args) {
return EncodingLib.__decodeURI(args);
}
public static Environment apply(Environment env) {
var wp = new NativeWrapperProvider();
var glob = new GlobalScope(wp.getNamespace(Internals.class));
glob.define(null, "Math", false, wp.getNamespace(MathLib.class));
glob.define(null, "JSON", false, wp.getNamespace(JSONLib.class));
glob.define(null, "Encoding", false, wp.getNamespace(EncodingLib.class));
glob.define(null, "Filesystem", false, wp.getNamespace(FilesystemLib.class));
glob.define(null, false, wp.getConstr(FileLib.class));
glob.define(null, false, wp.getConstr(DateLib.class));
glob.define(null, false, wp.getConstr(ObjectLib.class));
glob.define(null, false, wp.getConstr(FunctionLib.class));
glob.define(null, false, wp.getConstr(ArrayLib.class));
glob.define(null, false, wp.getConstr(BooleanLib.class));
glob.define(null, false, wp.getConstr(NumberLib.class));
glob.define(null, false, wp.getConstr(StringLib.class));
glob.define(null, false, wp.getConstr(SymbolLib.class));
glob.define(null, false, wp.getConstr(PromiseLib.class));
glob.define(null, false, wp.getConstr(RegExpLib.class));
glob.define(null, false, wp.getConstr(MapLib.class));
glob.define(null, false, wp.getConstr(SetLib.class));
glob.define(null, false, wp.getConstr(ErrorLib.class));
glob.define(null, false, wp.getConstr(SyntaxErrorLib.class));
glob.define(null, false, wp.getConstr(TypeErrorLib.class));
glob.define(null, false, wp.getConstr(RangeErrorLib.class));
env.add(Environment.OBJECT_PROTO, wp.getProto(ObjectLib.class));
env.add(Environment.FUNCTION_PROTO, wp.getProto(FunctionLib.class));
env.add(Environment.ARRAY_PROTO, wp.getProto(ArrayLib.class));
env.add(Environment.BOOL_PROTO, wp.getProto(BooleanLib.class));
env.add(Environment.NUMBER_PROTO, wp.getProto(NumberLib.class));
env.add(Environment.STRING_PROTO, wp.getProto(StringLib.class));
env.add(Environment.SYMBOL_PROTO, wp.getProto(SymbolLib.class));
env.add(Environment.ERROR_PROTO, wp.getProto(ErrorLib.class));
env.add(Environment.SYNTAX_ERR_PROTO, wp.getProto(SyntaxErrorLib.class));
env.add(Environment.TYPE_ERR_PROTO, wp.getProto(TypeErrorLib.class));
env.add(Environment.RANGE_ERR_PROTO, wp.getProto(RangeErrorLib.class));
env.add(Environment.REGEX_CONSTR, wp.getConstr(RegExpLib.class));
Values.setPrototype(new Context(), wp.getProto(ObjectLib.class), null);
env.add(NativeWrapperProvider.KEY, wp);
env.add(GlobalScope.KEY, glob);
return env;
}
}

View File

@@ -0,0 +1,24 @@
package me.topchetoeu.jscript.lib;
import me.topchetoeu.jscript.common.json.JSON;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.ExposeTarget;
import me.topchetoeu.jscript.utils.interop.WrapperName;
@WrapperName("JSON")
public class JSONLib {
@Expose(target = ExposeTarget.STATIC)
public static Object __parse(Arguments args) {
try {
return JSON.toJs(JSON.parse(null, args.getString(0)));
}
catch (SyntaxException e) { throw EngineException.ofSyntax(e.msg); }
}
@Expose(target = ExposeTarget.STATIC)
public static String __stringify(Arguments args) {
return JSON.stringify(JSON.fromJs(args.ctx, args.get(0)));
}
}

View File

@@ -0,0 +1,87 @@
package me.topchetoeu.jscript.lib;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.stream.Collectors;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.values.ArrayValue;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.runtime.values.Values;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.ExposeConstructor;
import me.topchetoeu.jscript.utils.interop.ExposeType;
import me.topchetoeu.jscript.utils.interop.WrapperName;
@WrapperName("Map")
public class MapLib {
private LinkedHashMap<Object, Object> map = new LinkedHashMap<>();
@Expose("@@Symbol.iterator")
public ObjectValue __iterator(Arguments args) {
return this.__entries(args);
}
@Expose public void __clear() {
map.clear();
}
@Expose public boolean __delete(Arguments args) {
var key = args.get(0);
if (map.containsKey(key)) {
map.remove(key);
return true;
}
return false;
}
@Expose public ObjectValue __entries(Arguments args) {
return Values.toJSIterator(args.ctx, map
.entrySet()
.stream()
.map(v -> new ArrayValue(args.ctx, v.getKey(), v.getValue()))
.collect(Collectors.toList())
);
}
@Expose public ObjectValue __keys(Arguments args) {
return Values.toJSIterator(args.ctx, map.keySet());
}
@Expose public ObjectValue __values(Arguments args) {
return Values.toJSIterator(args.ctx, map.values());
}
@Expose public Object __get(Arguments args) {
return map.get(args.get(0));
}
@Expose public MapLib __set(Arguments args) {
map.put(args.get(0), args.get(1));
return this;
}
@Expose public boolean __has(Arguments args) {
return map.containsKey(args.get(0));
}
@Expose(type = ExposeType.GETTER)
public int __size() {
return map.size();
}
@Expose public void __forEach(Arguments args) {
var keys = new ArrayList<>(map.keySet());
for (var el : keys) Values.call(args.ctx, args.get(0), args.get(1), map.get(el), el, args.self);
}
public MapLib(Context ctx, Object iterable) {
for (var el : Values.fromJSIterator(ctx, iterable)) {
try {
map.put(Values.getMember(ctx, el, 0), Values.getMember(ctx, el, 1));
}
catch (IllegalArgumentException e) { }
}
}
@ExposeConstructor public static MapLib __constructor(Arguments args) {
return new MapLib(args.ctx, args.get(0));
}
}

View File

@@ -0,0 +1,211 @@
package me.topchetoeu.jscript.lib;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.ExposeField;
import me.topchetoeu.jscript.utils.interop.ExposeTarget;
import me.topchetoeu.jscript.utils.interop.WrapperName;
@WrapperName("Math")
public class MathLib {
@ExposeField(target = ExposeTarget.STATIC)
public static final double __E = Math.E;
@ExposeField(target = ExposeTarget.STATIC)
public static final double __PI = Math.PI;
@ExposeField(target = ExposeTarget.STATIC)
public static final double __SQRT2 = Math.sqrt(2);
@ExposeField(target = ExposeTarget.STATIC)
public static final double __SQRT1_2 = Math.sqrt(.5);
@ExposeField(target = ExposeTarget.STATIC)
public static final double __LN2 = Math.log(2);
@ExposeField(target = ExposeTarget.STATIC)
public static final double __LN10 = Math.log(10);
@ExposeField(target = ExposeTarget.STATIC)
public static final double __LOG2E = Math.log(Math.E) / __LN2;
@ExposeField(target = ExposeTarget.STATIC)
public static final double __LOG10E = Math.log10(Math.E);
@Expose(target = ExposeTarget.STATIC)
public static double __asin(Arguments args) {
return Math.asin(args.getDouble(0));
}
@Expose(target = ExposeTarget.STATIC)
public static double __acos(Arguments args) {
return Math.acos(args.getDouble(0));
}
@Expose(target = ExposeTarget.STATIC)
public static double __atan(Arguments args) {
return Math.atan(args.getDouble(0));
}
@Expose(target = ExposeTarget.STATIC)
public static double __atan2(Arguments args) {
var x = args.getDouble(1);
var y = args.getDouble(0);
if (x == 0) {
if (y == 0) return Double.NaN;
return Math.signum(y) * Math.PI / 2;
}
else {
var val = Math.atan(y / x);
if (x > 0) return val;
else if (y < 0) return val - Math.PI;
else return val + Math.PI;
}
}
@Expose(target = ExposeTarget.STATIC)
public static double __asinh(Arguments args) {
var x = args.getDouble(0);
return Math.log(x + Math.sqrt(x * x + 1));
}
@Expose(target = ExposeTarget.STATIC)
public static double __acosh(Arguments args) {
var x = args.getDouble(0);
return Math.log(x + Math.sqrt(x * x - 1));
}
@Expose(target = ExposeTarget.STATIC)
public static double __atanh(Arguments args) {
var x = args.getDouble(0);
if (x <= -1 || x >= 1) return Double.NaN;
return .5 * Math.log((1 + x) / (1 - x));
}
@Expose(target = ExposeTarget.STATIC)
public static double __sin(Arguments args) {
return Math.sin(args.getDouble(0));
}
@Expose(target = ExposeTarget.STATIC)
public static double __cos(Arguments args) {
return Math.cos(args.getDouble(0));
}
@Expose(target = ExposeTarget.STATIC)
public static double __tan(Arguments args) {
return Math.tan(args.getDouble(0));
}
@Expose(target = ExposeTarget.STATIC)
public static double __sinh(Arguments args) {
return Math.sinh(args.getDouble(0));
}
@Expose(target = ExposeTarget.STATIC)
public static double __cosh(Arguments args) {
return Math.cosh(args.getDouble(0));
}
@Expose(target = ExposeTarget.STATIC)
public static double __tanh(Arguments args) {
return Math.tanh(args.getDouble(0));
}
@Expose(target = ExposeTarget.STATIC)
public static double __sqrt(Arguments args) {
return Math.sqrt(args.getDouble(0));
}
@Expose(target = ExposeTarget.STATIC)
public static double __cbrt(Arguments args) {
return Math.cbrt(args.getDouble(0));
}
@Expose(target = ExposeTarget.STATIC)
public static double __hypot(Arguments args) {
var res = 0.;
for (var i = 0; i < args.n(); i++) {
var val = args.getDouble(i);
res += val * val;
}
return Math.sqrt(res);
}
@Expose(target = ExposeTarget.STATIC)
public static int __imul(Arguments args) { return args.getInt(0) * args.getInt(1); }
@Expose(target = ExposeTarget.STATIC)
public static double __exp(Arguments args) {
return Math.exp(args.getDouble(0));
}
@Expose(target = ExposeTarget.STATIC)
public static double __expm1(Arguments args) {
return Math.expm1(args.getDouble(0));
}
@Expose(target = ExposeTarget.STATIC)
public static double __pow(Arguments args) { return Math.pow(args.getDouble(0), args.getDouble(1)); }
@Expose(target = ExposeTarget.STATIC)
public static double __log(Arguments args) {
return Math.log(args.getDouble(0));
}
@Expose(target = ExposeTarget.STATIC)
public static double __log10(Arguments args) {
return Math.log10(args.getDouble(0));
}
@Expose(target = ExposeTarget.STATIC)
public static double __log1p(Arguments args) {
return Math.log1p(args.getDouble(0));
}
@Expose(target = ExposeTarget.STATIC)
public static double __log2(Arguments args) {
return Math.log(args.getDouble(0)) / __LN2;
}
@Expose(target = ExposeTarget.STATIC)
public static double __ceil(Arguments args) {
return Math.ceil(args.getDouble(0));
}
@Expose(target = ExposeTarget.STATIC)
public static double __floor(Arguments args) {
return Math.floor(args.getDouble(0));
}
@Expose(target = ExposeTarget.STATIC)
public static double __round(Arguments args) {
return Math.round(args.getDouble(0));
}
@Expose(target = ExposeTarget.STATIC)
public static float __fround(Arguments args) {
return (float)args.getDouble(0);
}
@Expose(target = ExposeTarget.STATIC)
public static double __trunc(Arguments args) {
var x = args.getDouble(0);
return Math.floor(Math.abs(x)) * Math.signum(x);
}
@Expose(target = ExposeTarget.STATIC)
public static double __abs(Arguments args) {
return Math.abs(args.getDouble(0));
}
@Expose(target = ExposeTarget.STATIC)
public static double __max(Arguments args) {
var res = Double.NEGATIVE_INFINITY;
for (var i = 0; i < args.n(); i++) {
var el = args.getDouble(i);
if (el > res) res = el;
}
return res;
}
@Expose(target = ExposeTarget.STATIC)
public static double __min(Arguments args) {
var res = Double.POSITIVE_INFINITY;
for (var i = 0; i < args.n(); i++) {
var el = args.getDouble(i);
if (el < res) res = el;
}
return res;
}
@Expose(target = ExposeTarget.STATIC)
public static double __sign(Arguments args) {
return Math.signum(args.getDouble(0));
}
@Expose(target = ExposeTarget.STATIC)
public static double __random() { return Math.random(); }
@Expose(target = ExposeTarget.STATIC)
public static int __clz32(Arguments args) {
return Integer.numberOfLeadingZeros(args.getInt(0));
}
}

View File

@@ -0,0 +1,92 @@
package me.topchetoeu.jscript.lib;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.runtime.values.Values;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.ExposeConstructor;
import me.topchetoeu.jscript.utils.interop.ExposeField;
import me.topchetoeu.jscript.utils.interop.ExposeTarget;
import me.topchetoeu.jscript.utils.interop.WrapperName;
@WrapperName("Number")
public class NumberLib {
@ExposeField(target = ExposeTarget.STATIC)
public static final double __EPSILON = Math.ulp(1.0);
@ExposeField(target = ExposeTarget.STATIC)
public static final double __MAX_SAFE_INTEGER = 9007199254740991.;
@ExposeField(target = ExposeTarget.STATIC)
public static final double __MIN_SAFE_INTEGER = -__MAX_SAFE_INTEGER;
// lmao big number go brrr
@ExposeField(target = ExposeTarget.STATIC)
public static final double __MAX_VALUE = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.;
@ExposeField(target = ExposeTarget.STATIC)
public static final double __MIN_VALUE = -__MAX_VALUE;
@ExposeField(target = ExposeTarget.STATIC)
public static final double __NaN = 0. / 0;
@ExposeField(target = ExposeTarget.STATIC)
public static final double __NEGATIVE_INFINITY = -1. / 0;
@ExposeField(target = ExposeTarget.STATIC)
public static final double __POSITIVE_INFINITY = 1. / 0;
public final double value;
@Override public String toString() { return value + ""; }
public NumberLib(double val) {
this.value = val;
}
@Expose(target = ExposeTarget.STATIC)
public static boolean __isFinite(Arguments args) { return Double.isFinite(args.getDouble(0)); }
@Expose(target = ExposeTarget.STATIC)
public static boolean __isInfinite(Arguments args) { return Double.isInfinite(args.getDouble(0)); }
@Expose(target = ExposeTarget.STATIC)
public static boolean __isNaN(Arguments args) { return Double.isNaN(args.getDouble(0)); }
@Expose(target = ExposeTarget.STATIC)
public static boolean __isSafeInteger(Arguments args) {
return args.getDouble(0) > __MIN_SAFE_INTEGER && args.getDouble(0) < __MAX_SAFE_INTEGER;
}
@Expose(target = ExposeTarget.STATIC)
public static double __parseFloat(Arguments args) {
return args.getDouble(0);
}
@Expose(target = ExposeTarget.STATIC)
public static double __parseInt(Arguments args) {
var radix = args.getInt(1, 10);
if (radix < 2 || radix > 36) return Double.NaN;
else {
long res = 0;
for (var c : args.getString(0).toCharArray()) {
var digit = 0;
if (c >= '0' && c <= '9') digit = c - '0';
else if (c >= 'a' && c <= 'z') digit = c - 'a' + 10;
else if (c >= 'A' && c <= 'Z') digit = c - 'A' + 10;
else break;
if (digit > radix) break;
res *= radix;
res += digit;
}
return res;
}
}
@ExposeConstructor public static Object __constructor(Arguments args) {
if (args.self instanceof ObjectValue) return new NumberLib(args.getDouble(0));
else return args.getDouble(0);
}
@Expose public static String __toString(Arguments args) {
return Values.toString(args.ctx, args.self);
}
@Expose public static double __valueOf(Arguments args) {
if (Values.isWrapper(args.self, NumberLib.class)) return Values.wrapper(args.self, NumberLib.class).value;
else return Values.toNumber(args.ctx, args.self);
}
}

View File

@@ -0,0 +1,273 @@
package me.topchetoeu.jscript.lib;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.ArrayValue;
import me.topchetoeu.jscript.runtime.values.FunctionValue;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.runtime.values.Symbol;
import me.topchetoeu.jscript.runtime.values.Values;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.ExposeConstructor;
import me.topchetoeu.jscript.utils.interop.ExposeTarget;
import me.topchetoeu.jscript.utils.interop.WrapperName;
@WrapperName("Object")
public class ObjectLib {
@Expose(target = ExposeTarget.STATIC)
public static Object __assign(Arguments args) {
for (var obj : args.slice(1).args) {
for (var key : Values.getMembers(args.ctx, obj, true, true)) {
Values.setMember(args.ctx, args.get(0), key, Values.getMember(args.ctx, obj, key));
}
}
return args.get(0);
}
@Expose(target = ExposeTarget.STATIC)
public static ObjectValue __create(Arguments args) {
var obj = new ObjectValue();
Values.setPrototype(args.ctx, obj, args.get(0));
if (args.n() >= 1) {
var newArgs = new Object[args.n()];
System.arraycopy(args.args, 1, args, 1, args.n() - 1);
newArgs[0] = obj;
__defineProperties(new Arguments(args.ctx, null, newArgs));
}
return obj;
}
@Expose(target = ExposeTarget.STATIC)
public static ObjectValue __defineProperty(Arguments args) {
var obj = args.convert(0, ObjectValue.class);
var key = args.get(1);
var attrib = args.convert(2, ObjectValue.class);
var hasVal = Values.hasMember(args.ctx, attrib, "value", false);
var hasGet = Values.hasMember(args.ctx, attrib, "get", false);
var hasSet = Values.hasMember(args.ctx, attrib, "set", false);
if (hasVal) {
if (hasGet || hasSet) throw EngineException.ofType("Cannot specify a value and accessors for a property.");
if (!obj.defineProperty(
args.ctx, key,
Values.getMember(args.ctx, attrib, "value"),
Values.toBoolean(Values.getMember(args.ctx, attrib, "writable")),
Values.toBoolean(Values.getMember(args.ctx, attrib, "configurable")),
Values.toBoolean(Values.getMember(args.ctx, attrib, "enumerable"))
)) throw EngineException.ofType("Can't define property '" + key + "'.");
}
else {
var get = Values.getMember(args.ctx, attrib, "get");
var set = Values.getMember(args.ctx, attrib, "set");
if (get != null && !(get instanceof FunctionValue)) throw EngineException.ofType("Get accessor must be a function.");
if (set != null && !(set instanceof FunctionValue)) throw EngineException.ofType("Set accessor must be a function.");
if (!obj.defineProperty(
args.ctx, key,
(FunctionValue)get, (FunctionValue)set,
Values.toBoolean(Values.getMember(args.ctx, attrib, "configurable")),
Values.toBoolean(Values.getMember(args.ctx, attrib, "enumerable"))
)) throw EngineException.ofType("Can't define property '" + key + "'.");
}
return obj;
}
@Expose(target = ExposeTarget.STATIC)
public static ObjectValue __defineProperties(Arguments args) {
var obj = args.convert(0, ObjectValue.class);
var attrib = args.get(1);
for (var key : Values.getMembers(null, attrib, false, false)) {
__defineProperty(new Arguments(args.ctx, null, obj, key, Values.getMember(args.ctx, attrib, key)));
}
return obj;
}
@Expose(target = ExposeTarget.STATIC)
public static ArrayValue __keys(Arguments args) {
var obj = args.get(0);
var all = args.getBoolean(1);
var res = new ArrayValue();
for (var key : Values.getMembers(args.ctx, obj, true, false)) {
if (all || !(key instanceof Symbol)) res.set(args.ctx, res.size(), key);
}
return res;
}
@Expose(target = ExposeTarget.STATIC)
public static ArrayValue __entries(Arguments args) {
var res = new ArrayValue();
var obj = args.get(0);
var all = args.getBoolean(1);
for (var key : Values.getMembers(args.ctx, obj, true, false)) {
if (all || !(key instanceof Symbol)) res.set(args.ctx, res.size(), new ArrayValue(args.ctx, key, Values.getMember(args.ctx, obj, key)));
}
return res;
}
@Expose(target = ExposeTarget.STATIC)
public static ArrayValue __values(Arguments args) {
var res = new ArrayValue();
var obj = args.get(0);
var all = args.getBoolean(1);
for (var key : Values.getMembers(args.ctx, obj, true, false)) {
if (all || !(key instanceof Symbol)) res.set(args.ctx, res.size(), Values.getMember(args.ctx, obj, key));
}
return res;
}
@Expose(target = ExposeTarget.STATIC)
public static ObjectValue __getOwnPropertyDescriptor(Arguments args) {
return Values.getMemberDescriptor(args.ctx, args.get(0), args.get(1));
}
@Expose(target = ExposeTarget.STATIC)
public static ObjectValue __getOwnPropertyDescriptors(Arguments args) {
var res = new ObjectValue();
var obj = args.get(0);
for (var key : Values.getMembers(args.ctx, obj, true, true)) {
res.defineProperty(args.ctx, key, Values.getMemberDescriptor(args.ctx, obj, key));
}
return res;
}
@Expose(target = ExposeTarget.STATIC)
public static ArrayValue __getOwnPropertyNames(Arguments args) {
var res = new ArrayValue();
var obj = args.get(0);
var all = args.getBoolean(1);
for (var key : Values.getMembers(args.ctx, obj, true, true)) {
if (all || !(key instanceof Symbol)) res.set(args.ctx, res.size(), key);
}
return res;
}
@Expose(target = ExposeTarget.STATIC)
public static ArrayValue __getOwnPropertySymbols(Arguments args) {
var obj = args.get(0);
var res = new ArrayValue();
for (var key : Values.getMembers(args.ctx, obj, true, true)) {
if (key instanceof Symbol) res.set(args.ctx, res.size(), key);
}
return res;
}
@Expose(target = ExposeTarget.STATIC)
public static boolean __hasOwn(Arguments args) {
return Values.hasMember(args.ctx, args.get(0), args.get(1), true);
}
@Expose(target = ExposeTarget.STATIC)
public static ObjectValue __getPrototypeOf(Arguments args) {
return Values.getPrototype(args.ctx, args.get(0));
}
@Expose(target = ExposeTarget.STATIC)
public static Object __setPrototypeOf(Arguments args) {
Values.setPrototype(args.ctx, args.get(0), args.get(1));
return args.get(0);
}
@Expose(target = ExposeTarget.STATIC)
public static ObjectValue __fromEntries(Arguments args) {
var res = new ObjectValue();
for (var el : Values.fromJSIterator(args.ctx, args.get(0))) {
if (el instanceof ArrayValue) {
res.defineProperty(args.ctx, ((ArrayValue)el).get(0), ((ArrayValue)el).get(1));
}
}
return res;
}
@Expose(target = ExposeTarget.STATIC)
public static Object __preventExtensions(Arguments args) {
if (args.get(0) instanceof ObjectValue) args.convert(0, ObjectValue.class).preventExtensions();
return args.get(0);
}
@Expose(target = ExposeTarget.STATIC)
public static Object __seal(Arguments args) {
if (args.get(0) instanceof ObjectValue) args.convert(0, ObjectValue.class).seal();
return args.get(0);
}
@Expose(target = ExposeTarget.STATIC)
public static Object __freeze(Arguments args) {
if (args.get(0) instanceof ObjectValue) args.convert(0, ObjectValue.class).freeze();
return args.get(0);
}
@Expose(target = ExposeTarget.STATIC)
public static boolean __isExtensible(Arguments args) {
var obj = args.get(0);
if (!(obj instanceof ObjectValue)) return false;
return ((ObjectValue)obj).extensible();
}
@Expose(target = ExposeTarget.STATIC)
public static boolean __isSealed(Arguments args) {
var obj = args.get(0);
if (!(obj instanceof ObjectValue)) return true;
var _obj = (ObjectValue)obj;
if (_obj.extensible()) return false;
for (var key : _obj.keys(true)) {
if (_obj.memberConfigurable(key)) return false;
}
return true;
}
@Expose(target = ExposeTarget.STATIC)
public static boolean __isFrozen(Arguments args) {
var obj = args.get(0);
if (!(obj instanceof ObjectValue)) return true;
var _obj = (ObjectValue)obj;
if (_obj.extensible()) return false;
for (var key : _obj.keys(true)) {
if (_obj.memberConfigurable(key)) return false;
if (_obj.memberWritable(key)) return false;
}
return true;
}
@Expose
public static Object __valueOf(Arguments args) {
return args.self;
}
@Expose
public static String __toString(Arguments args) {
var name = Values.getMember(args.ctx, args.self, Symbol.get("Symbol.typeName"));
if (name == null) name = "Unknown";
else name = Values.toString(args.ctx, name);
return "[object " + name + "]";
}
@Expose
public static boolean __hasOwnProperty(Arguments args) {
return Values.hasMember(args.ctx, args.self, args.get(0), true);
}
@ExposeConstructor
public static Object __constructor(Arguments args) {
var arg = args.get(0);
if (arg == null || arg == Values.NULL) return new ObjectValue();
else if (arg instanceof Boolean) return new BooleanLib((boolean)arg);
else if (arg instanceof Number) return new NumberLib(((Number)arg).doubleValue());
else if (arg instanceof String) return new StringLib((String)arg);
else if (arg instanceof Symbol) return new SymbolLib((Symbol)arg);
else return arg;
}
}

View File

@@ -0,0 +1,404 @@
package me.topchetoeu.jscript.lib;
import java.util.ArrayList;
import java.util.List;
import me.topchetoeu.jscript.common.ResultRunnable;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.EventLoop;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.exceptions.InterruptException;
import me.topchetoeu.jscript.runtime.values.ArrayValue;
import me.topchetoeu.jscript.runtime.values.FunctionValue;
import me.topchetoeu.jscript.runtime.values.NativeFunction;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.runtime.values.Values;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.ExposeConstructor;
import me.topchetoeu.jscript.utils.interop.ExposeTarget;
import me.topchetoeu.jscript.utils.interop.WrapperName;
@WrapperName("Promise")
public class PromiseLib {
public static interface Handle {
void onFulfil(Object val);
void onReject(EngineException err);
default Handle defer(Extensions loop) {
var self = this;
return new Handle() {
@Override public void onFulfil(Object val) {
if (!loop.hasNotNull(EventLoop.KEY)) throw EngineException.ofError("No event loop");
loop.get(EventLoop.KEY).pushMsg(() -> self.onFulfil(val), true);
}
@Override public void onReject(EngineException val) {
if (!loop.hasNotNull(EventLoop.KEY)) throw EngineException.ofError("No event loop");
loop.get(EventLoop.KEY).pushMsg(() -> self.onReject(val), true);
}
};
}
}
private static final int STATE_PENDING = 0;
private static final int STATE_FULFILLED = 1;
private static final int STATE_REJECTED = 2;
private List<Handle> handles = new ArrayList<>();
private int state = STATE_PENDING;
private boolean handled = false;
private Object val;
private void resolveSynchronized(Context ctx, Object val, int newState) {
this.val = val;
this.state = newState;
for (var handle : handles) {
if (newState == STATE_FULFILLED) handle.onFulfil(val);
if (newState == STATE_REJECTED) {
handle.onReject((EngineException)val);
handled = true;
}
}
if (state == STATE_REJECTED && !handled) {
Values.printError(((EngineException)val).setExtensions(ctx), "(in promise)");
}
handles = null;
// ctx.get(EventLoop.KEY).pushMsg(() -> {
// if (!ctx.hasNotNull(EventLoop.KEY)) throw EngineException.ofError("No event loop");
// handles = null;
// }, true);
}
private synchronized void resolve(Context ctx, Object val, int newState) {
if (this.state != STATE_PENDING || newState == STATE_PENDING) return;
handle(ctx, val, new Handle() {
@Override public void onFulfil(Object val) {
resolveSynchronized(ctx, val, newState);
}
@Override public void onReject(EngineException err) {
resolveSynchronized(ctx, val, STATE_REJECTED);
}
});
}
public synchronized void fulfill(Context ctx, Object val) {
resolve(ctx, val, STATE_FULFILLED);
}
public synchronized void reject(Context ctx, EngineException val) {
resolve(ctx, val, STATE_REJECTED);
}
private void handle(Handle handle) {
if (state == STATE_FULFILLED) handle.onFulfil(val);
else if (state == STATE_REJECTED) {
handle.onReject((EngineException)val);
handled = true;
}
else handles.add(handle);
}
@Override public String toString() {
if (state == STATE_PENDING) return "Promise (pending)";
else if (state == STATE_FULFILLED) return "Promise (fulfilled)";
else return "Promise (rejected)";
}
public PromiseLib() {
this.state = STATE_PENDING;
this.val = null;
}
public static PromiseLib await(Context ctx, ResultRunnable<Object> runner) {
var res = new PromiseLib();
new Thread(() -> {
try {
res.fulfill(ctx, runner.run());
}
catch (EngineException e) {
res.reject(ctx, e);
}
catch (Exception e) {
if (e instanceof InterruptException) throw e;
else {
res.reject(ctx, EngineException.ofError("Native code failed with " + e.getMessage()));
}
}
}, "Promisifier").start();
return res;
}
public static PromiseLib await(Context ctx, Runnable runner) {
return await(ctx, () -> {
runner.run();
return null;
});
}
public static void handle(Context ctx, Object obj, Handle handle) {
if (Values.isWrapper(obj, PromiseLib.class)) {
var promise = Values.wrapper(obj, PromiseLib.class);
handle(ctx, promise, handle);
return;
}
if (obj instanceof PromiseLib) {
((PromiseLib)obj).handle(handle);
return;
}
var rethrow = new boolean[1];
try {
var then = Values.getMember(ctx, obj, "then");
if (then instanceof FunctionValue) Values.call(ctx, then, obj,
new NativeFunction(args -> {
try { handle.onFulfil(args.get(0)); }
catch (Exception e) {
rethrow[0] = true;
throw e;
}
return null;
}),
new NativeFunction(args -> {
try { handle.onReject(new EngineException(args.get(0))); }
catch (Exception e) {
rethrow[0] = true;
throw e;
}
return null;
})
);
else handle.onFulfil(obj);
return;
}
catch (Exception e) {
if (rethrow[0]) throw e;
}
handle.onFulfil(obj);
}
public static PromiseLib ofResolved(Context ctx, Object value) {
var res = new PromiseLib();
res.fulfill(ctx, value);
return res;
}
public static PromiseLib ofRejected(Context ctx, EngineException value) {
var res = new PromiseLib();
res.reject(ctx, value);
return res;
}
@Expose(value = "resolve", target = ExposeTarget.STATIC)
public static PromiseLib __ofResolved(Arguments args) {
return ofResolved(args.ctx, args.get(0));
}
@Expose(value = "reject", target = ExposeTarget.STATIC)
public static PromiseLib __ofRejected(Arguments args) {
return ofRejected(args.ctx, new EngineException(args.get(0)).setExtensions(args.ctx));
}
@Expose(target = ExposeTarget.STATIC)
public static PromiseLib __any(Arguments args) {
if (!(args.get(0) instanceof ArrayValue)) throw EngineException.ofType("Expected argument for any to be an array.");
var promises = args.convert(0, ArrayValue.class);
if (promises.size() == 0) return ofRejected(args.ctx, EngineException.ofError("No promises passed to 'Promise.any'.").setExtensions(args.ctx));
var n = new int[] { promises.size() };
var res = new PromiseLib();
var errors = new ArrayValue();
for (var i = 0; i < promises.size(); i++) {
var index = i;
var val = promises.get(i);
if (res.state != STATE_PENDING) break;
handle(args.ctx, val, new Handle() {
public void onFulfil(Object val) { res.fulfill(args.ctx, val); }
public void onReject(EngineException err) {
errors.set(args.ctx, index, err.value);
n[0]--;
if (n[0] <= 0) res.reject(args.ctx, new EngineException(errors).setExtensions(args.ctx));
}
});
}
return res;
}
@Expose(target = ExposeTarget.STATIC)
public static PromiseLib __race(Arguments args) {
if (!(args.get(0) instanceof ArrayValue)) throw EngineException.ofType("Expected argument for any to be an array.");
var promises = args.convert(0, ArrayValue.class);
var res = new PromiseLib();
for (var i = 0; i < promises.size(); i++) {
var val = promises.get(i);
if (res.state != STATE_PENDING) break;
handle(args.ctx, val, new Handle() {
@Override public void onFulfil(Object val) { res.fulfill(args.ctx, val); }
@Override public void onReject(EngineException err) { res.reject(args.ctx, err); }
});
}
return res;
}
@Expose(target = ExposeTarget.STATIC)
public static PromiseLib __all(Arguments args) {
if (!(args.get(0) instanceof ArrayValue)) throw EngineException.ofType("Expected argument for any to be an array.");
var promises = args.convert(0, ArrayValue.class);
var n = new int[] { promises.size() };
var res = new PromiseLib();
var result = new ArrayValue();
for (var i = 0; i < promises.size(); i++) {
if (res.state != STATE_PENDING) break;
var index = i;
var val = promises.get(i);
handle(args.ctx, val, new Handle() {
@Override public void onFulfil(Object val) {
result.set(args.ctx, index, val);
n[0]--;
if (n[0] <= 0) res.fulfill(args.ctx, result);
}
@Override public void onReject(EngineException err) {
res.reject(args.ctx, err);
}
});
}
if (n[0] <= 0) res.fulfill(args.ctx, result);
return res;
}
@Expose(target = ExposeTarget.STATIC)
public static PromiseLib __allSettled(Arguments args) {
if (!(args.get(0) instanceof ArrayValue)) throw EngineException.ofType("Expected argument for any to be an array.");
var promises = args.convert(0, ArrayValue.class);
var n = new int[] { promises.size() };
var res = new PromiseLib();
var result = new ArrayValue();
for (var i = 0; i < promises.size(); i++) {
if (res.state != STATE_PENDING) break;
var index = i;
handle(args.ctx, promises.get(i), new Handle() {
@Override public void onFulfil(Object val) {
var desc = new ObjectValue();
desc.defineProperty(args.ctx, "status", "fulfilled");
desc.defineProperty(args.ctx, "value", val);
result.set(args.ctx, index, desc);
n[0]--;
if (n[0] <= 0) res.fulfill(args.ctx, res);
}
@Override public void onReject(EngineException err) {
var desc = new ObjectValue();
desc.defineProperty(args.ctx, "status", "reject");
desc.defineProperty(args.ctx, "value", err.value);
result.set(args.ctx, index, desc);
n[0]--;
if (n[0] <= 0) res.fulfill(args.ctx, res);
}
});
}
if (n[0] <= 0) res.fulfill(args.ctx, result);
return res;
}
@Expose
public static Object __then(Arguments args) {
var onFulfill = args.get(0) instanceof FunctionValue ? args.convert(0, FunctionValue.class) : null;
var onReject = args.get(1) instanceof FunctionValue ? args.convert(1, FunctionValue.class) : null;
var res = new PromiseLib();
handle(args.ctx, args.self, new Handle() {
@Override public void onFulfil(Object val) {
try { res.fulfill(args.ctx, onFulfill.call(args.ctx, null, val)); }
catch (EngineException e) { res.reject(args.ctx, e); }
}
@Override public void onReject(EngineException err) {
try { res.fulfill(args.ctx, onReject.call(args.ctx, null, err.value)); }
catch (EngineException e) { res.reject(args.ctx, e); }
}
}.defer(args.ctx));
return res;
}
@Expose
public static Object __catch(Arguments args) {
return __then(new Arguments(args.ctx, args.self, null, args.get(0)));
}
@Expose
public static Object __finally(Arguments args) {
var func = args.get(0) instanceof FunctionValue ? args.convert(0, FunctionValue.class) : null;
var res = new PromiseLib();
handle(args.ctx, args.self, new Handle() {
@Override public void onFulfil(Object val) {
try {
func.call(args.ctx);
res.fulfill(args.ctx, val);
}
catch (EngineException e) { res.reject(args.ctx, e); }
}
@Override public void onReject(EngineException err) {
try {
func.call(args.ctx);
res.reject(args.ctx, err);
}
catch (EngineException e) { res.reject(args.ctx, e); }
}
}.defer(args.ctx));
return res;
}
@ExposeConstructor
public static PromiseLib __constructor(Arguments args) {
var func = args.convert(0, FunctionValue.class);
var res = new PromiseLib();
try {
func.call(
args.ctx, null,
new NativeFunction(null, _args -> {
res.fulfill(_args.ctx, _args.get(0));
return null;
}),
new NativeFunction(null, _args -> {
res.reject(_args.ctx, new EngineException(_args.get(0)).setExtensions(_args.ctx));
return null;
})
);
}
catch (EngineException e) {
res.reject(args.ctx, e);
}
return res;
}
}

View File

@@ -0,0 +1,19 @@
package me.topchetoeu.jscript.lib;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.runtime.values.ObjectValue.PlaceholderProto;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.ExposeConstructor;
import me.topchetoeu.jscript.utils.interop.ExposeField;
import me.topchetoeu.jscript.utils.interop.WrapperName;
@WrapperName("RangeError")
public class RangeErrorLib extends ErrorLib {
@ExposeField public static final String __name = "RangeError";
@ExposeConstructor public static ObjectValue constructor(Arguments args) {
var target = ErrorLib.__constructor(args);
target.setPrototype(PlaceholderProto.RANGE_ERROR);
return target;
}
}

View File

@@ -4,85 +4,69 @@ import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.engine.values.ArrayValue; import me.topchetoeu.jscript.runtime.values.ArrayValue;
import me.topchetoeu.jscript.engine.values.FunctionValue; import me.topchetoeu.jscript.runtime.values.FunctionValue;
import me.topchetoeu.jscript.engine.values.NativeWrapper; import me.topchetoeu.jscript.runtime.values.NativeWrapper;
import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.runtime.values.Values;
import me.topchetoeu.jscript.interop.Native; import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.interop.NativeGetter; import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.ExposeConstructor;
import me.topchetoeu.jscript.utils.interop.ExposeTarget;
import me.topchetoeu.jscript.utils.interop.ExposeType;
import me.topchetoeu.jscript.utils.interop.WrapperName;
@Native("RegExp") public class RegExpLib { @WrapperName("RegExp")
public class RegExpLib {
// I used Regex to analyze Regex // I used Regex to analyze Regex
private static final Pattern NAMED_PATTERN = Pattern.compile("\\(\\?<([^=!].*?)>", Pattern.DOTALL); private static final Pattern NAMED_PATTERN = Pattern.compile("\\(\\?<([^=!].*?)>", Pattern.DOTALL);
private static final Pattern ESCAPE_PATTERN = Pattern.compile("[/\\-\\\\^$*+?.()|\\[\\]{}]"); private static final Pattern ESCAPE_PATTERN = Pattern.compile("[/\\-\\\\^$*+?.()|\\[\\]{}]");
private static String cleanupPattern(Context ctx, Object val) {
if (val == null) return "(?:)";
if (val instanceof RegExpLib) return ((RegExpLib)val).source;
if (val instanceof NativeWrapper && ((NativeWrapper)val).wrapped instanceof RegExpLib) {
return ((RegExpLib)((NativeWrapper)val).wrapped).source;
}
var res = Values.toString(ctx, val);
if (res.equals("")) return "(?:)";
return res;
}
private static String cleanupFlags(Context ctx, Object val) {
if (val == null) return "";
return Values.toString(ctx, val);
}
private static boolean checkEscaped(String s, int pos) {
int n = 0;
while (true) {
if (pos <= 0) break;
if (s.charAt(pos) != '\\') break;
n++;
pos--;
}
return (n % 2) != 0;
}
@Native
public static RegExpLib escape(Context ctx, Object raw, Object flags) {
return escape(Values.toString(ctx, raw), cleanupFlags(ctx, flags));
}
public static RegExpLib escape(String raw, String flags) {
return new RegExpLib(ESCAPE_PATTERN.matcher(raw).replaceAll("\\\\$0"), flags);
}
private Pattern pattern; private Pattern pattern;
private String[] namedGroups; private String[] namedGroups;
private int flags; private int flags;
@Native public int lastI = 0;
@Native public final String source;
@Native public final boolean hasIndices;
@Native public final boolean global;
@Native public final boolean sticky;
@Native("@@Symbol.typeName") public final String name = "RegExp";
@NativeGetter public boolean ignoreCase() { return (flags & Pattern.CASE_INSENSITIVE) != 0; } public int lastI = 0;
@NativeGetter public boolean multiline() { return (flags & Pattern.MULTILINE) != 0; } public final String source;
@NativeGetter public boolean unicode() { return (flags & Pattern.UNICODE_CHARACTER_CLASS) != 0; } public final boolean hasIndices;
@NativeGetter public boolean dotAll() { return (flags & Pattern.DOTALL) != 0; } public final boolean global;
public final boolean sticky;
@NativeGetter("flags") public final String flags() { @Expose(type = ExposeType.GETTER)
public int __lastIndex() { return lastI; }
@Expose(type = ExposeType.SETTER)
public void __setLastIndex(Arguments args) { lastI = args.getInt(0); }
@Expose(type = ExposeType.GETTER)
public String __source() { return source; }
@Expose(type = ExposeType.GETTER)
public boolean __ignoreCase() { return (flags & Pattern.CASE_INSENSITIVE) != 0; }
@Expose(type = ExposeType.GETTER)
public boolean __multiline() { return (flags & Pattern.MULTILINE) != 0; }
@Expose(type = ExposeType.GETTER)
public boolean __unicode() { return (flags & Pattern.UNICODE_CHARACTER_CLASS) != 0; }
@Expose(type = ExposeType.GETTER)
public boolean __dotAll() { return (flags & Pattern.DOTALL) != 0; }
@Expose(type = ExposeType.GETTER)
public boolean __global() { return global; }
@Expose(type = ExposeType.GETTER)
public boolean __sticky() { return sticky; }
@Expose(type = ExposeType.GETTER)
public final String __flags() {
String res = ""; String res = "";
if (hasIndices) res += 'd'; if (hasIndices) res += 'd';
if (global) res += 'g'; if (global) res += 'g';
if (ignoreCase()) res += 'i'; if (__ignoreCase()) res += 'i';
if (multiline()) res += 'm'; if (__multiline()) res += 'm';
if (dotAll()) res += 's'; if (__dotAll()) res += 's';
if (unicode()) res += 'u'; if (__unicode()) res += 'u';
if (sticky) res += 'y'; if (sticky) res += 'y';
return res; return res;
} }
@Native public Object exec(String str) { @Expose public Object __exec(Arguments args) {
var str = args.getString(0);
var matcher = pattern.matcher(str); var matcher = pattern.matcher(str);
if (lastI > str.length() || !matcher.find(lastI) || sticky && matcher.start() != lastI) { if (lastI > str.length() || !matcher.find(lastI) || sticky && matcher.start() != lastI) {
lastI = 0; lastI = 0;
@@ -94,13 +78,13 @@ import me.topchetoeu.jscript.interop.NativeGetter;
} }
var obj = new ArrayValue(); var obj = new ArrayValue();
var groups = new ObjectValue(); ObjectValue groups = null;
for (var el : namedGroups) { for (var el : namedGroups) {
if (groups == null) groups = new ObjectValue();
try { groups.defineProperty(null, el, matcher.group(el)); } try { groups.defineProperty(null, el, matcher.group(el)); }
catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) { }
} }
if (groups.values.size() == 0) groups = null;
for (int i = 0; i < matcher.groupCount() + 1; i++) { for (int i = 0; i < matcher.groupCount() + 1; i++) {
@@ -126,39 +110,36 @@ import me.topchetoeu.jscript.interop.NativeGetter;
return obj; return obj;
} }
@Native public boolean test(String str) { @Expose public boolean __test(Arguments args) {
return this.exec(str) != Values.NULL; return this.__exec(args) != Values.NULL;
}
@Native public String toString() {
return "/" + source + "/" + flags();
} }
@Native("@@Symbol.match") public Object match(Context ctx, String target) { @Expose("@@Symbol.match") public Object __match(Arguments args) {
if (this.global) { if (this.global) {
var res = new ArrayValue(); var res = new ArrayValue();
Object val; Object val;
while ((val = this.exec(target)) != Values.NULL) { while ((val = this.__exec(args)) != Values.NULL) {
res.set(ctx, res.size(), Values.getMember(ctx, val, 0)); res.set(args.ctx, res.size(), Values.getMember(args.ctx, val, 0));
} }
lastI = 0; lastI = 0;
return res; return res;
} }
else { else {
var res = this.exec(target); var res = this.__exec(args);
if (!this.sticky) this.lastI = 0; if (!this.sticky) this.lastI = 0;
return res; return res;
} }
} }
@Native("@@Symbol.matchAll") public Object matchAll(Context ctx, String target) { @Expose("@@Symbol.matchAll") public Object __matchAll(Arguments args) {
var pattern = new RegExpLib(this.source, this.flags() + "g"); var pattern = this.toGlobal();
return Values.toJSIterator(ctx, new Iterator<Object>() { return Values.toJSIterator(args.ctx, new Iterator<Object>() {
private Object val = null; private Object val = null;
private boolean updated = false; private boolean updated = false;
private void update() { private void update() {
if (!updated) val = pattern.exec(target); if (!updated) val = pattern.__exec(args);
} }
@Override public boolean hasNext() { @Override public boolean hasNext() {
update(); update();
@@ -172,17 +153,21 @@ import me.topchetoeu.jscript.interop.NativeGetter;
}); });
} }
@Native("@@Symbol.split") public ArrayValue split(Context ctx, String target, Object limit, boolean sensible) { @Expose("@@Symbol.split") public ArrayValue __split(Arguments args) {
var pattern = new RegExpLib(this.source, this.flags() + "g"); var pattern = this.toGlobal();
var target = args.getString(0);
var hasLimit = args.get(1) != null;
var lim = args.getInt(1);
var sensible = args.getBoolean(2);
Object match; Object match;
int lastEnd = 0; int lastEnd = 0;
var res = new ArrayValue(); var res = new ArrayValue();
var lim = limit == null ? 0 : Values.toNumber(ctx, limit);
while ((match = pattern.exec(target)) != Values.NULL) { while ((match = pattern.__exec(args)) != Values.NULL) {
var added = new ArrayList<String>(); var added = new ArrayList<String>();
var arrMatch = (ArrayValue)match; var arrMatch = (ArrayValue)match;
int index = (int)Values.toNumber(ctx, Values.getMember(ctx, match, "index")); int index = (int)Values.toNumber(args.ctx, Values.getMember(args.ctx, match, "index"));
var matchVal = (String)arrMatch.get(0); var matchVal = (String)arrMatch.get(0);
if (index >= target.length()) break; if (index >= target.length()) break;
@@ -198,31 +183,33 @@ import me.topchetoeu.jscript.interop.NativeGetter;
} }
if (sensible) { if (sensible) {
if (limit != null && res.size() + added.size() >= lim) break; if (hasLimit && res.size() + added.size() >= lim) break;
else for (var i = 0; i < added.size(); i++) res.set(ctx, res.size(), added.get(i)); else for (var i = 0; i < added.size(); i++) res.set(args.ctx, res.size(), added.get(i));
} }
else { else {
for (var i = 0; i < added.size(); i++) { for (var i = 0; i < added.size(); i++) {
if (limit != null && res.size() >= lim) return res; if (hasLimit && res.size() >= lim) return res;
else res.set(ctx, res.size(), added.get(i)); else res.set(args.ctx, res.size(), added.get(i));
} }
} }
lastEnd = pattern.lastI; lastEnd = pattern.lastI;
} }
if (lastEnd < target.length()) { if (lastEnd < target.length()) {
res.set(ctx, res.size(), target.substring(lastEnd)); res.set(args.ctx, res.size(), target.substring(lastEnd));
} }
return res; return res;
} }
@Native("@@Symbol.replace") public String replace(Context ctx, String target, Object replacement) { @Expose("@@Symbol.replace") public String __replace(Arguments args) {
var pattern = new RegExpLib(this.source, this.flags() + "d"); var pattern = this.toIndexed();
var target = args.getString(0);
var replacement = args.get(1);
Object match; Object match;
var lastEnd = 0; var lastEnd = 0;
var res = new StringBuilder(); var res = new StringBuilder();
while ((match = pattern.exec(target)) != Values.NULL) { while ((match = pattern.__exec(args)) != Values.NULL) {
var indices = (ArrayValue)((ArrayValue)Values.getMember(ctx, match, "indices")).get(0); var indices = (ArrayValue)((ArrayValue)Values.getMember(args.ctx, match, "indices")).get(0);
var arrMatch = (ArrayValue)match; var arrMatch = (ArrayValue)match;
var start = ((Number)indices.get(0)).intValue(); var start = ((Number)indices.get(0)).intValue();
@@ -230,15 +217,15 @@ import me.topchetoeu.jscript.interop.NativeGetter;
res.append(target.substring(lastEnd, start)); res.append(target.substring(lastEnd, start));
if (replacement instanceof FunctionValue) { if (replacement instanceof FunctionValue) {
var args = new Object[arrMatch.size() + 2]; var callArgs = new Object[arrMatch.size() + 2];
args[0] = target.substring(start, end); callArgs[0] = target.substring(start, end);
arrMatch.copyTo(args, 1, 1, arrMatch.size() - 1); arrMatch.copyTo(callArgs, 1, 1, arrMatch.size() - 1);
args[args.length - 2] = start; callArgs[callArgs.length - 2] = start;
args[args.length - 1] = target; callArgs[callArgs.length - 1] = target;
res.append(Values.toString(ctx, ((FunctionValue)replacement).call(ctx, null, args))); res.append(Values.toString(args.ctx, ((FunctionValue)replacement).call(args.ctx, null, callArgs)));
} }
else { else {
res.append(Values.toString(ctx, replacement)); res.append(Values.toString(args.ctx, replacement));
} }
lastEnd = end; lastEnd = end;
if (!pattern.global) break; if (!pattern.global) break;
@@ -271,9 +258,17 @@ import me.topchetoeu.jscript.interop.NativeGetter;
// } // }
// }, // },
@Native public RegExpLib(Context ctx, Object pattern, Object flags) { public RegExpLib toGlobal() {
this(cleanupPattern(ctx, pattern), cleanupFlags(ctx, flags)); return new RegExpLib(pattern, namedGroups, flags, source, hasIndices, true, sticky);
} }
public RegExpLib toIndexed() {
return new RegExpLib(pattern, namedGroups, flags, source, true, global, sticky);
}
public String toString() {
return "/" + source + "/" + __flags();
}
public RegExpLib(String pattern, String flags) { public RegExpLib(String pattern, String flags) {
if (pattern == null || pattern.equals("")) pattern = "(?:)"; if (pattern == null || pattern.equals("")) pattern = "(?:)";
if (flags == null || flags.equals("")) flags = ""; if (flags == null || flags.equals("")) flags = "";
@@ -304,6 +299,56 @@ import me.topchetoeu.jscript.interop.NativeGetter;
namedGroups = groups.toArray(String[]::new); namedGroups = groups.toArray(String[]::new);
} }
private RegExpLib(Pattern pattern, String[] namedGroups, int flags, String source, boolean hasIndices, boolean global, boolean sticky) {
this.pattern = pattern;
this.namedGroups = namedGroups;
this.flags = flags;
this.source = source;
this.hasIndices = hasIndices;
this.global = global;
this.sticky = sticky;
}
public RegExpLib(String pattern) { this(pattern, null); } public RegExpLib(String pattern) { this(pattern, null); }
public RegExpLib() { this(null, null); } public RegExpLib() { this(null, null); }
@ExposeConstructor
public static RegExpLib __constructor(Arguments args) {
return new RegExpLib(cleanupPattern(args.ctx, args.get(0)), cleanupFlags(args.ctx, args.get(1)));
}
@Expose(target = ExposeTarget.STATIC)
public static RegExpLib __escape(Arguments args) {
return escape(Values.toString(args.ctx, args.get(0)), cleanupFlags(args.ctx, args.get(1)));
}
private static String cleanupPattern(Context ctx, Object val) {
if (val == null) return "(?:)";
if (val instanceof RegExpLib) return ((RegExpLib)val).source;
if (val instanceof NativeWrapper && ((NativeWrapper)val).wrapped instanceof RegExpLib) {
return ((RegExpLib)((NativeWrapper)val).wrapped).source;
}
var res = Values.toString(ctx, val);
if (res.equals("")) return "(?:)";
return res;
}
private static String cleanupFlags(Context ctx, Object val) {
if (val == null) return "";
return Values.toString(ctx, val);
}
private static boolean checkEscaped(String s, int pos) {
int n = 0;
while (true) {
if (pos <= 0) break;
if (s.charAt(pos) != '\\') break;
n++;
pos--;
}
return (n % 2) != 0;
}
public static RegExpLib escape(String raw, String flags) {
return new RegExpLib(ESCAPE_PATTERN.matcher(raw).replaceAll("\\\\$0"), flags);
}
} }

View File

@@ -0,0 +1,69 @@
package me.topchetoeu.jscript.lib;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.stream.Collectors;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.values.ArrayValue;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.runtime.values.Values;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.ExposeConstructor;
import me.topchetoeu.jscript.utils.interop.ExposeType;
import me.topchetoeu.jscript.utils.interop.WrapperName;
@WrapperName("Set")
public class SetLib {
private LinkedHashSet<Object> set = new LinkedHashSet<>();
@Expose("@@Symbol.iterator")
public ObjectValue __iterator(Arguments args) {
return this.__values(args);
}
@Expose public ObjectValue __entries(Arguments args) {
return Values.toJSIterator(args.ctx, set.stream().map(v -> new ArrayValue(args.ctx, v, v)).collect(Collectors.toList()));
}
@Expose public ObjectValue __keys(Arguments args) {
return Values.toJSIterator(args.ctx, set);
}
@Expose public ObjectValue __values(Arguments args) {
return Values.toJSIterator(args.ctx, set);
}
@Expose public Object __add(Arguments args) {
return set.add(args.get(0));
}
@Expose public boolean __delete(Arguments args) {
return set.remove(args.get(0));
}
@Expose public boolean __has(Arguments args) {
return set.contains(args.get(0));
}
@Expose public void __clear() {
set.clear();
}
@Expose(type = ExposeType.GETTER)
public int __size() {
return set.size();
}
@Expose public void __forEach(Arguments args) {
var keys = new ArrayList<>(set);
for (var el : keys) Values.call(args.ctx, args.get(0), args.get(1), el, el, args.self);
}
public SetLib(Context ctx, Object iterable) {
for (var el : Values.fromJSIterator(ctx, iterable)) set.add(el);
}
@ExposeConstructor
public static SetLib __constructor(Arguments args) {
return new SetLib(args.ctx, args.get(0));
}
}

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