Compare commits
126 Commits
v0.4.1-alp
...
TopchetoEU
| Author | SHA1 | Date | |
|---|---|---|---|
|
b791f3277e
|
|||
|
5e6e6fea61
|
|||
|
89eea7d62b
|
|||
| 18d22a1282 | |||
|
72a0d39d0b
|
|||
|
d8585a20bf
|
|||
|
e4c9a8756e
|
|||
|
c6e6425c7e
|
|||
|
292ca64cb9
|
|||
|
4572db5c46
|
|||
|
0251c4689d
|
|||
|
3173919b49
|
|||
|
45f133c6b0
|
|||
|
34276d720c
|
|||
|
2c634778c3
|
|||
|
4aa757e625
|
|||
|
918f2623cd
|
|||
|
a321fc14bc
|
|||
|
07a6f18b16
|
|||
|
5f4011aa0c
|
|||
|
71f735b812
|
|||
|
e575b3287e
|
|||
|
4fa5f5a815
|
|||
|
a61c6a494e
|
|||
|
978ee8db79
|
|||
|
e372941e99
|
|||
|
c36a0db860
|
|||
|
d6ee59363f
|
|||
|
d5fd6e650e
|
|||
|
c0b895e00a
|
|||
|
9ea5cd9277
|
|||
|
aaf9a6fa45
|
|||
|
579f09c837
|
|||
|
3343262e72
|
|||
|
153a1a9a49
|
|||
|
bf38587271
|
|||
|
21534efd60
|
|||
|
802f2f3f52
|
|||
|
38acc20a6f
|
|||
|
d7f6010319
|
|||
|
87f8975275
|
|||
| 09eb6507dc | |||
|
2f58f6b245
|
|||
|
4bc363485f
|
|||
|
8e01db637b
|
|||
|
1c64912786
|
|||
|
28265a8f44
|
|||
|
e9e020512e
|
|||
|
4b0bbf5190
|
|||
|
031f78ebf1
|
|||
|
562f1f9425
|
|||
|
82a09e8865
|
|||
|
90da2db1fb
|
|||
|
3d275c52c0
|
|||
|
797585f539
|
|||
|
7a301eba8f
|
|||
|
1b2068a274
|
|||
|
078d7ed95f
|
|||
|
93973c12b1
|
|||
|
cad4f34b51
|
|||
|
d3571d6ee2
|
|||
|
caf9131cde
|
|||
| 8c6379eb24 | |||
|
380a5c720a
|
|||
|
76c3d377af
|
|||
|
42f443572a
|
|||
|
773bc72f3e
|
|||
|
0b5178e9fd
|
|||
|
8cffcff7db
|
|||
|
60bbaaccd4
|
|||
|
60b1762462
|
|||
|
34434965d2
|
|||
|
fe86123f0f
|
|||
|
d5e6edfa8b
|
|||
|
73345062ca
|
|||
|
124341969c
|
|||
|
8defd93855
|
|||
|
6c57e0e9f2
|
|||
|
f1932914ee
|
|||
|
977701e601
|
|||
|
e8a7ac8da8
|
|||
|
6b1cb852c2
|
|||
|
b59a003086
|
|||
|
1902e41f61
|
|||
|
27162ef8ac
|
|||
|
4f22e76d2b
|
|||
|
8924e7aadc
|
|||
| 1d0e31a423 | |||
|
ab56908171
|
|||
|
eb14bb080c
|
|||
|
f52f47cdb4
|
|||
|
567eaa8514
|
|||
|
2cfdd8e335
|
|||
|
4b1ec671e2
|
|||
|
b127aadcf6
|
|||
|
b6eaff65ca
|
|||
|
443dc0ffa1
|
|||
|
e107dd3507
|
|||
|
6af3c70fce
|
|||
|
8b743f49d1
|
|||
|
e1ce384815
|
|||
|
86d205e521
|
|||
|
f0ad936e5b
|
|||
|
4a1473c5be
|
|||
|
4111dbf5c4
|
|||
|
1666682dc2
|
|||
|
f2b33d0233
|
|||
|
f5a0b6eaf7
|
|||
|
829bea755d
|
|||
|
4b0dcffd13
|
|||
|
987f8b8f00
|
|||
|
55e3d46bc2
|
|||
|
3e25068219
|
|||
|
7ecb8bfabb
|
|||
|
488deea164
|
|||
|
ed08041335
|
|||
|
0a4149ba81
|
|||
|
30f5d619c3
|
|||
|
e7dbe91374
|
|||
|
455f5a613e
|
|||
|
1eeac3ae97
|
|||
|
1acd78e119
|
|||
|
df9932874d
|
|||
|
b47d1a7576
|
|||
|
fdfa8d7713
|
|||
|
f5d1287948
|
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
* -text
|
||||||
2
.github/workflows/tagged-release.yml
vendored
2
.github/workflows/tagged-release.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
|||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: 'adopt'
|
distribution: 'adopt'
|
||||||
java-version: '17'
|
java-version: '11'
|
||||||
- name: Clone repository
|
- name: Clone repository
|
||||||
uses: GuillaumeFalourd/clone-github-repo-action@main
|
uses: GuillaumeFalourd/clone-github-repo-action@main
|
||||||
with:
|
with:
|
||||||
|
|||||||
30
.gitignore
vendored
30
.gitignore
vendored
@@ -1,11 +1,21 @@
|
|||||||
.vscode
|
*
|
||||||
.gradle
|
|
||||||
.ignore
|
!/src
|
||||||
/out
|
!/src/**/*
|
||||||
/build
|
/src/assets/js/ts.js
|
||||||
/bin
|
|
||||||
/dst
|
!/doc
|
||||||
/*.js
|
!/doc/**/*
|
||||||
|
|
||||||
|
!/tests
|
||||||
|
!/tests/**/*
|
||||||
|
|
||||||
|
|
||||||
|
!/.github
|
||||||
|
!/.github/**/*
|
||||||
|
|
||||||
|
!/.gitignore
|
||||||
|
!/.gitattributes
|
||||||
!/build.js
|
!/build.js
|
||||||
/dead-code
|
!/LICENSE
|
||||||
/Metadata.java
|
!/README.md
|
||||||
|
|||||||
37
README.md
37
README.md
@@ -2,33 +2,28 @@
|
|||||||
|
|
||||||
**NOTE: This had nothing to do with Microsoft's dialect of EcmaScript**
|
**NOTE: This had nothing to do with Microsoft's dialect of EcmaScript**
|
||||||
|
|
||||||
**WARNING: Currently, this code is mostly undocumented. Proceed with caution and a psychiatrist.**
|
**WARNING: Currently, this code is undocumented. Proceed with caution and a psychiatrist.**
|
||||||
|
|
||||||
JScript is an engine, capable of running EcmaScript 5, written entirely in Java. This engine has been developed with the goal of being easy to integrate with your preexisting codebase, **THE GOAL OF THIS ENGINE IS NOT PERFORMANCE**. My crude experiments show that this engine is 50x-100x slower than V8, which, although bad, is acceptable for most simple scripting purposes.
|
JScript is an engine, capable of running EcmaScript 5, written entirely in Java. This engine has been developed with the goal of being easy to integrate with your preexisting codebase, **THE GOAL OF THIS ENGINE IS NOT PERFORMANCE**. My crude experiments show that this engine is 50x-100x slower than V8, which, although bad, is acceptable for most simple scripting purposes. Note that although the codebase has a Main class, this isn't meant to be a standalone program, but instead a library for running JavaScript code.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
The following will create a REPL using the engine as a backend. Not that this won't properly log errors. I recommend checking out the implementation in `Main.main`:
|
The following is going to execute a simple javascript statement:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
var engine = new PolyfillEngine(new File("."));
|
var engine = new Engine(false);
|
||||||
var in = new BufferedReader(new InputStreamReader(System.in));
|
// Initialize a standard environment, with implementations of most basic standard libraries (Object, Array, Symbol, etc.)
|
||||||
engine.start();
|
var env = Internals.apply(new Environment());
|
||||||
|
|
||||||
while (true) {
|
// Queue code to load internal libraries and start engine
|
||||||
try {
|
var awaitable = engine.pushMsg(false, env, new Filename("tmp", "eval"), "10 + Math.sqrt(5 / 3)", null);
|
||||||
var raw = in.readLine();
|
// Run the engine on the same thread, until the event loop runs empty
|
||||||
|
engine.run(true);
|
||||||
|
|
||||||
var res = engine.pushMsg(false, engine.global(), Map.of(), "<stdio>", raw, null).await();
|
// Get our result
|
||||||
Values.printValue(engine.context(), res);
|
System.out.println(awaitable.await());
|
||||||
System.out.println();
|
|
||||||
}
|
|
||||||
catch (EngineException e) {
|
|
||||||
try {
|
|
||||||
System.out.println("Uncaught " + e.toString(engine.context()));
|
|
||||||
}
|
|
||||||
catch (InterruptedException _e) { return; }
|
|
||||||
}
|
|
||||||
catch (IOException | InterruptedException e) { return; }
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## NOTE:
|
||||||
|
|
||||||
|
To setup the typescript bundle in your sources, run `node build.js init-ts`. This will download the latest version of typescript, minify it, and add it to your src/assets folder. If you are going to work with the `node build.js debug|release` command, this is not a necessary step.
|
||||||
|
|||||||
150
build.js
150
build.js
@@ -1,17 +1,9 @@
|
|||||||
const { spawn } = require('child_process');
|
const { spawn } = require('child_process');
|
||||||
const fs = require('fs/promises');
|
const fs = require('fs/promises');
|
||||||
const pt = require('path');
|
const pt = require('path');
|
||||||
const { argv } = require('process');
|
const { argv, exit } = require('process');
|
||||||
|
const { Readable } = require('stream');
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
async function* find(src, dst, wildcard) {
|
async function* find(src, dst, wildcard) {
|
||||||
const stat = await fs.stat(src);
|
const stat = await fs.stat(src);
|
||||||
@@ -36,9 +28,9 @@ async function copy(src, dst, wildcard) {
|
|||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
function run(cmd, ...args) {
|
function run(suppressOutput, cmd, ...args) {
|
||||||
return new Promise((res, rej) => {
|
return new Promise((res, rej) => {
|
||||||
const proc = spawn(cmd, args, { stdio: 'inherit' });
|
const proc = spawn(cmd, args, { stdio: suppressOutput ? 'ignore' : 'inherit' });
|
||||||
proc.once('exit', code => {
|
proc.once('exit', code => {
|
||||||
if (code === 0) res(code);
|
if (code === 0) res(code);
|
||||||
else rej(new Error(`Process ${cmd} exited with code ${code}.`));
|
else rej(new Error(`Process ${cmd} exited with code ${code}.`));
|
||||||
@@ -46,9 +38,87 @@ function run(cmd, ...args) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function compileJava() {
|
async function downloadTypescript(outFile) {
|
||||||
try {
|
try {
|
||||||
await fs.writeFile('Metadata.java', (await fs.readFile('src/me/topchetoeu/jscript/Metadata.java')).toString()
|
// Import the required libraries, without the need of a package.json
|
||||||
|
console.log('Importing modules...');
|
||||||
|
await run(true, 'npm', 'i', 'tar', 'zlib', 'uglify-js');
|
||||||
|
await fs.mkdir(pt.dirname(outFile), { recursive: true });
|
||||||
|
await fs.mkdir('tmp', { recursive: true });
|
||||||
|
|
||||||
|
const tar = require('tar');
|
||||||
|
const zlib = require('zlib');
|
||||||
|
const { minify } = await import('uglify-js');
|
||||||
|
|
||||||
|
// Download the package.json file of typescript
|
||||||
|
const packageDesc = await (await fetch('https://registry.npmjs.org/typescript/latest')).json();
|
||||||
|
const url = packageDesc.dist.tarball;
|
||||||
|
|
||||||
|
console.log('Extracting typescript...');
|
||||||
|
await new Promise(async (res, rej) => Readable.fromWeb((await fetch(url)).body)
|
||||||
|
.pipe(zlib.createGunzip())
|
||||||
|
.pipe(tar.x({ cwd: 'tmp', filter: v => v === 'package/lib/typescript.js' }))
|
||||||
|
.on('end', res)
|
||||||
|
.on('error', rej)
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('Compiling typescript to ES5...');
|
||||||
|
|
||||||
|
const ts = require('./tmp/package/lib/typescript');
|
||||||
|
const program = ts.createProgram([ 'tmp/package/lib/typescript.js' ], {
|
||||||
|
outFile: "tmp/typescript-es5.js",
|
||||||
|
target: ts.ScriptTarget.ES5,
|
||||||
|
module: ts.ModuleKind.None,
|
||||||
|
downlevelIteration: true,
|
||||||
|
allowJs: true,
|
||||||
|
});
|
||||||
|
program.emit();
|
||||||
|
|
||||||
|
console.log('Minifying typescript...');
|
||||||
|
|
||||||
|
const minified = minify((await fs.readFile('tmp/typescript-es5.js')).toString());
|
||||||
|
// const minified = { code: (await fs.readFile('tmp/typescript-es5.js')).toString() };
|
||||||
|
if (minified.error) throw minified.error;
|
||||||
|
|
||||||
|
// Patch unsupported regex syntax
|
||||||
|
minified.code = minified.code.replaceAll('[-/\\\\^$*+?.()|[\\]{}]', '[-/\\\\^$*+?.()|\\[\\]{}]');
|
||||||
|
|
||||||
|
const stream = await fs.open(outFile, 'w');
|
||||||
|
|
||||||
|
// Write typescript's license
|
||||||
|
await stream.write(new TextEncoder().encode(`
|
||||||
|
/*! *****************************************************************************
|
||||||
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||||
|
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||||
|
MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||||
|
|
||||||
|
See the Apache Version 2.0 License for specific language governing permissions
|
||||||
|
and limitations under the License.
|
||||||
|
|
||||||
|
The following is a minified version of the unmodified Typescript 5.2
|
||||||
|
***************************************************************************** */
|
||||||
|
`));
|
||||||
|
|
||||||
|
await stream.write(minified.code);
|
||||||
|
console.log('Typescript bundling done!');
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
// Clean up all stuff left from typescript bundling
|
||||||
|
await fs.rm('tmp', { recursive: true, force: true });
|
||||||
|
await fs.rm('package.json');
|
||||||
|
await fs.rm('package-lock.json');
|
||||||
|
await fs.rm('node_modules', { recursive: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function compileJava(conf) {
|
||||||
|
try {
|
||||||
|
await fs.writeFile('Metadata.java', (await fs.readFile('src/me/topchetoeu/jscript/common/Metadata.java')).toString()
|
||||||
.replace('${VERSION}', conf.version)
|
.replace('${VERSION}', conf.version)
|
||||||
.replace('${NAME}', conf.name)
|
.replace('${NAME}', conf.name)
|
||||||
.replace('${AUTHOR}', conf.author)
|
.replace('${AUTHOR}', conf.author)
|
||||||
@@ -57,23 +127,67 @@ async function compileJava() {
|
|||||||
if (argv[2] === 'debug') args.push('-g');
|
if (argv[2] === 'debug') args.push('-g');
|
||||||
args.push('-d', 'dst/classes', 'Metadata.java');
|
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);
|
for await (const path of find('src', undefined, v => v.endsWith('.java') && !v.endsWith('Metadata.java'))) args.push(path);
|
||||||
await run(conf.javahome + 'javac', ...args);
|
await run(false, conf.javahome + 'javac', ...args);
|
||||||
|
console.log('Compiled java project!');
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
await fs.rm('Metadata.java');
|
await fs.rm('Metadata.java');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async function jar(conf, project, mainClass) {
|
||||||
|
const args = [
|
||||||
|
'jar', '-c',
|
||||||
|
'-f', `dst/${project}-v${conf.version}.jar`,
|
||||||
|
];
|
||||||
|
if (mainClass) args.push('-e', mainClass);
|
||||||
|
args.push('-C', 'dst/classes', project.replaceAll('.', '/'));
|
||||||
|
console.log(args.join(' '));
|
||||||
|
|
||||||
|
await run(true, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
|
if (argv[2] === 'init-ts') {
|
||||||
|
await downloadTypescript('src/me/topchetoeu/jscript/utils/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 {}
|
try { await fs.rm('dst', { recursive: true }); } catch {}
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
(async () => {
|
||||||
await copy('src', 'dst/classes', v => !v.endsWith('.java'));
|
await copy('src', 'dst/classes', v => !v.endsWith('.java'));
|
||||||
await compileJava();
|
// await downloadTypescript('dst/classes/me/topchetoeu/jscript/utils/assets/js/ts.js');
|
||||||
await run('jar', '-c', '-f', 'dst/jscript.jar', '-e', 'me.topchetoeu.jscript.Main', '-C', 'dst/classes', '.');
|
})(),
|
||||||
|
compileJava(conf),
|
||||||
|
]);
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
jar(conf, 'me.topchetoeu.jscript.common'),
|
||||||
|
jar(conf, 'me.topchetoeu.jscript.core'),
|
||||||
|
jar(conf, 'me.topchetoeu.jscript.lib'),
|
||||||
|
jar(conf, 'me.topchetoeu.jscript.utils'),
|
||||||
|
jar(conf, 'me.topchetoeu.jscript', 'me.topchetoeu.jscript.utils.JScriptRepl'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
console.log('Done!');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
if (argv[2] === 'debug') throw e;
|
if (argv[2] === 'debug') throw e;
|
||||||
else console.log(e.toString());
|
console.log(e.toString());
|
||||||
|
exit(-1);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,153 +0,0 @@
|
|||||||
package me.topchetoeu.jscript;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
|
||||||
import me.topchetoeu.jscript.engine.Engine;
|
|
||||||
import me.topchetoeu.jscript.engine.Environment;
|
|
||||||
import me.topchetoeu.jscript.engine.StackData;
|
|
||||||
import me.topchetoeu.jscript.engine.debug.DebugServer;
|
|
||||||
import me.topchetoeu.jscript.engine.debug.SimpleDebugger;
|
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
|
||||||
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
|
||||||
import me.topchetoeu.jscript.events.Observer;
|
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
|
||||||
import me.topchetoeu.jscript.exceptions.InterruptException;
|
|
||||||
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
|
||||||
import me.topchetoeu.jscript.lib.Internals;
|
|
||||||
|
|
||||||
public class Main {
|
|
||||||
static Thread engineTask, debugTask;
|
|
||||||
static Engine engine;
|
|
||||||
static Environment env;
|
|
||||||
static int j = 0;
|
|
||||||
|
|
||||||
private static Observer<Object> valuePrinter = new Observer<Object>() {
|
|
||||||
public void next(Object data) {
|
|
||||||
Values.printValue(null, data);
|
|
||||||
System.out.println();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void error(RuntimeException err) {
|
|
||||||
Values.printError(err, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void finish() {
|
|
||||||
engineTask.interrupt();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static void main(String args[]) {
|
|
||||||
System.out.println(String.format("Running %s v%s by %s", Metadata.NAME, Metadata.VERSION, Metadata.AUTHOR));
|
|
||||||
engine = new Engine(true);
|
|
||||||
|
|
||||||
env = new Environment(null, null, null);
|
|
||||||
var exited = new boolean[1];
|
|
||||||
var server = new DebugServer();
|
|
||||||
server.targets.put("target", (ws, req) -> new SimpleDebugger(ws, engine));
|
|
||||||
|
|
||||||
engineTask = engine.start();
|
|
||||||
debugTask = server.start(new InetSocketAddress("127.0.0.1", 9229), true);
|
|
||||||
|
|
||||||
engine.pushMsg(false, null, new NativeFunction((ctx, thisArg, _a) -> {
|
|
||||||
new Internals().apply(env);
|
|
||||||
|
|
||||||
env.global.define("exit", _ctx -> {
|
|
||||||
exited[0] = true;
|
|
||||||
throw new InterruptException();
|
|
||||||
});
|
|
||||||
env.global.define("go", _ctx -> {
|
|
||||||
try {
|
|
||||||
var f = Path.of("do.js");
|
|
||||||
var func = _ctx.compile(new Filename("do", "do/" + j++ + ".js"), new String(Files.readAllBytes(f)));
|
|
||||||
return func.call(_ctx);
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
throw new EngineException("Couldn't open do.js");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// TODO: make better API
|
|
||||||
env.global.define(true, new NativeFunction("include", (_ctx, th, __args) -> {
|
|
||||||
try {
|
|
||||||
var currFilename = StackData.peekFrame(_ctx).function.loc().filename();
|
|
||||||
var loc = Path.of("").toAbsolutePath();
|
|
||||||
if (currFilename.protocol.equals("file")) loc = Path.of(currFilename.path).getParent();
|
|
||||||
var path = loc.resolve(Path.of(__args.length >= 1 ? Values.toString(_ctx, __args[0]) : ""));
|
|
||||||
var src = Files.readString(path);
|
|
||||||
var func = _ctx.compile(Filename.fromFile(path.toFile()), src);
|
|
||||||
var callArgs = new ArrayValue();
|
|
||||||
if (__args.length >= 2 && __args[1] instanceof ArrayValue) callArgs = (ArrayValue)__args[1];
|
|
||||||
return func.call(_ctx, null, callArgs);
|
|
||||||
}
|
|
||||||
catch (IOException e) { throw EngineException.ofError("IOError", "Couldn't open file."); }
|
|
||||||
}));
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}), null).await();
|
|
||||||
|
|
||||||
try {
|
|
||||||
var tsEnv = env.child();
|
|
||||||
tsEnv.global.define(null, "module", false, new ObjectValue());
|
|
||||||
engine.pushMsg(
|
|
||||||
false, new Context(engine).pushEnv(tsEnv),
|
|
||||||
new Filename("jscript", "ts.js"),
|
|
||||||
Reading.resourceToString("js/ts.js"), null
|
|
||||||
).await();
|
|
||||||
System.out.println("Loaded typescript!");
|
|
||||||
|
|
||||||
var ctx = new Context(engine).pushEnv(env.child());
|
|
||||||
|
|
||||||
engine.pushMsg(
|
|
||||||
false, ctx,
|
|
||||||
new Filename("jscript", "internals/bootstrap.js"), Reading.resourceToString("js/bootstrap.js"), null,
|
|
||||||
tsEnv.global.get(ctx, "ts"), env, new ArrayValue(null, Reading.resourceToString("js/lib.d.ts"))
|
|
||||||
).await();
|
|
||||||
}
|
|
||||||
catch (EngineException e) {
|
|
||||||
Values.printError(e, "(while initializing TS)");
|
|
||||||
}
|
|
||||||
|
|
||||||
var reader = new Thread(() -> {
|
|
||||||
try {
|
|
||||||
for (var arg : args) {
|
|
||||||
try {
|
|
||||||
var file = Path.of(arg);
|
|
||||||
var raw = Files.readString(file);
|
|
||||||
valuePrinter.next(engine.pushMsg(false, new Context(engine).pushEnv(env), Filename.fromFile(file.toFile()), raw, null).await());
|
|
||||||
}
|
|
||||||
catch (EngineException e) { Values.printError(e, ""); }
|
|
||||||
}
|
|
||||||
for (var i = 0; ; i++) {
|
|
||||||
try {
|
|
||||||
var raw = Reading.read();
|
|
||||||
|
|
||||||
if (raw == null) break;
|
|
||||||
valuePrinter.next(engine.pushMsg(false, new Context(engine).pushEnv(env), new Filename("jscript", "repl/" + i + ".js"), raw, null).await());
|
|
||||||
}
|
|
||||||
catch (EngineException e) { Values.printError(e, ""); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException e) { exited[0] = true; }
|
|
||||||
catch (SyntaxException ex) {
|
|
||||||
if (exited[0]) return;
|
|
||||||
System.out.println("Syntax error:" + ex.msg);
|
|
||||||
}
|
|
||||||
catch (RuntimeException ex) {
|
|
||||||
if (!exited[0]) {
|
|
||||||
System.out.println("Internal error ocurred:");
|
|
||||||
ex.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (exited[0]) debugTask.interrupt();
|
|
||||||
});
|
|
||||||
reader.setDaemon(true);
|
|
||||||
reader.setName("STD Reader");
|
|
||||||
reader.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
package me.topchetoeu.jscript;
|
|
||||||
|
|
||||||
public interface MessageReceiver {
|
|
||||||
void sendMessage(String msg);
|
|
||||||
void sendError(String msg);
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package me.topchetoeu.jscript;
|
|
||||||
|
|
||||||
public class Metadata {
|
|
||||||
public static final String VERSION = "${VERSION}";
|
|
||||||
public static final String AUTHOR = "${AUTHOR}";
|
|
||||||
public static final String NAME = "${NAME}";
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
package me.topchetoeu.jscript;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.exceptions.UncheckedException;
|
|
||||||
|
|
||||||
public class Reading {
|
|
||||||
private static final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
|
|
||||||
|
|
||||||
public static synchronized String read() throws IOException {
|
|
||||||
return reader.readLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String streamToString(InputStream in) {
|
|
||||||
try {
|
|
||||||
StringBuilder out = new StringBuilder();
|
|
||||||
BufferedReader br = new BufferedReader(new InputStreamReader(in));
|
|
||||||
|
|
||||||
for(var line = br.readLine(); line != null; line = br.readLine()) {
|
|
||||||
out.append(line).append('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
br.close();
|
|
||||||
return out.toString();
|
|
||||||
}
|
|
||||||
catch (Throwable e) { throw new UncheckedException(e); }
|
|
||||||
}
|
|
||||||
public static String resourceToString(String name) {
|
|
||||||
var str = Main.class.getResourceAsStream("/me/topchetoeu/jscript/" + name);
|
|
||||||
if (str == null) return null;
|
|
||||||
return streamToString(str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
53
src/me/topchetoeu/jscript/common/Buffer.java
Normal file
53
src/me/topchetoeu/jscript/common/Buffer.java
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package me.topchetoeu.jscript.common;
|
||||||
|
|
||||||
|
public class Buffer {
|
||||||
|
private byte[] data;
|
||||||
|
private int length;
|
||||||
|
|
||||||
|
public void write(int i, byte[] val) {
|
||||||
|
if (i + val.length > data.length) {
|
||||||
|
var newCap = i + val.length + 1;
|
||||||
|
if (newCap < data.length * 2) newCap = data.length * 2;
|
||||||
|
|
||||||
|
var tmp = new byte[newCap];
|
||||||
|
System.arraycopy(this.data, 0, tmp, 0, length);
|
||||||
|
this.data = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.arraycopy(val, 0, data, i, val.length);
|
||||||
|
if (i + val.length > length) length = i + val.length;
|
||||||
|
}
|
||||||
|
public int read(int i, byte[] buff) {
|
||||||
|
int n = buff.length;
|
||||||
|
if (i + n > length) n = length - i;
|
||||||
|
System.arraycopy(data, i, buff, 0, n);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void append(byte b) {
|
||||||
|
write(length, new byte[] { b });
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] data() {
|
||||||
|
var res = new byte[length];
|
||||||
|
System.arraycopy(this.data, 0, res, 0, length);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
public int length() {
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Buffer(byte[] data) {
|
||||||
|
this.data = new byte[data.length];
|
||||||
|
this.length = data.length;
|
||||||
|
System.arraycopy(data, 0, this.data, 0, data.length);
|
||||||
|
}
|
||||||
|
public Buffer(int capacity) {
|
||||||
|
this.data = new byte[capacity];
|
||||||
|
this.length = 0;
|
||||||
|
}
|
||||||
|
public Buffer() {
|
||||||
|
this.data = new byte[128];
|
||||||
|
this.length = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package me.topchetoeu.jscript;
|
package me.topchetoeu.jscript.common;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
public class Filename {
|
public class Filename {
|
||||||
public final String protocol;
|
public final String protocol;
|
||||||
@@ -40,9 +41,7 @@ public class Filename {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Filename fromFile(File file) {
|
|
||||||
return new Filename("file", file.getAbsolutePath());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Filename(String protocol, String path) {
|
public Filename(String protocol, String path) {
|
||||||
@@ -51,4 +50,16 @@ public class Filename {
|
|||||||
this.protocol = protocol;
|
this.protocol = protocol;
|
||||||
this.path = path;
|
this.path = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Filename parse(String val) {
|
||||||
|
var i = val.indexOf("://");
|
||||||
|
if (i >= 0) return new Filename(val.substring(0, i).trim(), val.substring(i + 3).trim());
|
||||||
|
else return new Filename("file", val.trim());
|
||||||
|
}
|
||||||
|
public static Path normalize(String path) {
|
||||||
|
return Path.of(Path.of("/" + path.trim().replace("\\", "/")).normalize().toString().substring(1));
|
||||||
|
}
|
||||||
|
public static Filename fromFile(File file) {
|
||||||
|
return new Filename("file", file.getAbsolutePath());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package me.topchetoeu.jscript;
|
package me.topchetoeu.jscript.common;
|
||||||
|
|
||||||
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(0, 0, new Filename("jscript", "native"));
|
||||||
@@ -71,4 +71,23 @@ public class Location implements Comparable<Location> {
|
|||||||
this.start = start;
|
this.start = start;
|
||||||
this.filename = filename;
|
this.filename = filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Location parse(String raw) {
|
||||||
|
int i0 = -1, i1 = -1;
|
||||||
|
for (var i = raw.length() - 1; i >= 0; i--) {
|
||||||
|
if (raw.charAt(i) == ':') {
|
||||||
|
if (i1 == -1) i1 = i;
|
||||||
|
else if (i0 == -1) {
|
||||||
|
i0 = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Location(
|
||||||
|
Integer.parseInt(raw.substring(i0 + 1, i1)),
|
||||||
|
Integer.parseInt(raw.substring(i1 + 1)),
|
||||||
|
Filename.parse(raw.substring(0, i0))
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
20
src/me/topchetoeu/jscript/common/Metadata.java
Normal file
20
src/me/topchetoeu/jscript/common/Metadata.java
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package me.topchetoeu.jscript.common;
|
||||||
|
|
||||||
|
public class Metadata {
|
||||||
|
private static final String VERSION = "${VERSION}";
|
||||||
|
private static final String AUTHOR = "${AUTHOR}";
|
||||||
|
private static final String NAME = "${NAME}";
|
||||||
|
|
||||||
|
public static String version() {
|
||||||
|
if (VERSION.equals("$" + "{VERSION}")) return "1337-devel";
|
||||||
|
else return VERSION;
|
||||||
|
}
|
||||||
|
public static String author() {
|
||||||
|
if (AUTHOR.equals("$" + "{AUTHOR}")) return "anonymous";
|
||||||
|
else return AUTHOR;
|
||||||
|
}
|
||||||
|
public static String name() {
|
||||||
|
if (NAME.equals("$" + "{NAME}")) return "some-product";
|
||||||
|
else return NAME;
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/me/topchetoeu/jscript/common/Reading.java
Normal file
33
src/me/topchetoeu/jscript/common/Reading.java
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package me.topchetoeu.jscript.common;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
|
||||||
|
public class Reading {
|
||||||
|
private static final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
|
||||||
|
|
||||||
|
public static synchronized String readline() throws IOException {
|
||||||
|
return reader.readLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the given stream to a string
|
||||||
|
* @param in
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String streamToString(InputStream in) {
|
||||||
|
try {
|
||||||
|
return new String(in.readAllBytes());
|
||||||
|
}
|
||||||
|
catch (IOException e) { throw new UncheckedIOException(e); }
|
||||||
|
}
|
||||||
|
public static InputStream resourceToStream(String name) {
|
||||||
|
return Reading.class.getResourceAsStream("/" + name);
|
||||||
|
}
|
||||||
|
public static String resourceToString(String name) {
|
||||||
|
return streamToString(resourceToStream(name));
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/me/topchetoeu/jscript/common/ResultRunnable.java
Normal file
5
src/me/topchetoeu/jscript/common/ResultRunnable.java
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package me.topchetoeu.jscript.common;
|
||||||
|
|
||||||
|
public interface ResultRunnable<T> {
|
||||||
|
T run();
|
||||||
|
}
|
||||||
27
src/me/topchetoeu/jscript/common/events/Awaitable.java
Normal file
27
src/me/topchetoeu/jscript/common/events/Awaitable.java
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package me.topchetoeu.jscript.common.events;
|
||||||
|
|
||||||
|
public interface Awaitable<T> {
|
||||||
|
public static interface ResultHandler<T> {
|
||||||
|
public void onResult(T data);
|
||||||
|
}
|
||||||
|
public static interface ErrorHandler {
|
||||||
|
public void onError(RuntimeException error);
|
||||||
|
}
|
||||||
|
|
||||||
|
T await();
|
||||||
|
|
||||||
|
default void handle(ResultHandler<T> onResult, ErrorHandler onError) {
|
||||||
|
var thread = new Thread(() -> {
|
||||||
|
try {
|
||||||
|
onResult.onResult(await());
|
||||||
|
}
|
||||||
|
catch (RuntimeException e) {
|
||||||
|
onError.onError(e);
|
||||||
|
}
|
||||||
|
}, "Awaiter");
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
default void handle(ResultHandler<T> onResult) {
|
||||||
|
handle(onResult, err -> {});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package me.topchetoeu.jscript.events;
|
package me.topchetoeu.jscript.common.events;
|
||||||
|
|
||||||
public class DataNotifier<T> implements Awaitable<T> {
|
public class DataNotifier<T> implements Awaitable<T> {
|
||||||
private Notifier notifier = new Notifier();
|
private Notifier notifier = new Notifier();
|
||||||
@@ -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;
|
||||||
@@ -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.core.exceptions.InterruptException;
|
||||||
|
|
||||||
public class Notifier {
|
public class Notifier {
|
||||||
private boolean ok = false;
|
private boolean ok = false;
|
||||||
@@ -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.core.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.core.engine.values.ArrayValue;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.core.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.core.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.core.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
import me.topchetoeu.jscript.core.exceptions.SyntaxException;
|
||||||
import me.topchetoeu.jscript.parsing.Operator;
|
import me.topchetoeu.jscript.core.parsing.Operator;
|
||||||
import me.topchetoeu.jscript.parsing.ParseRes;
|
import me.topchetoeu.jscript.core.parsing.ParseRes;
|
||||||
import me.topchetoeu.jscript.parsing.Parsing;
|
import me.topchetoeu.jscript.core.parsing.Parsing;
|
||||||
import me.topchetoeu.jscript.parsing.Token;
|
import me.topchetoeu.jscript.core.parsing.Token;
|
||||||
|
|
||||||
public class JSON {
|
public class JSON {
|
||||||
public static Object toJs(JSONElement val) {
|
public static Object toJs(JSONElement val) {
|
||||||
@@ -37,21 +37,6 @@ public class JSON {
|
|||||||
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);
|
||||||
if (val == Values.NULL) return JSONElement.NULL;
|
if (val == Values.NULL) return JSONElement.NULL;
|
||||||
if (val instanceof ObjectValue) {
|
|
||||||
if (prev.contains(val)) throw new EngineException("Circular dependency in JSON.");
|
|
||||||
prev.add(val);
|
|
||||||
|
|
||||||
var res = new JSONMap();
|
|
||||||
|
|
||||||
for (var el : ((ObjectValue)val).keys(false)) {
|
|
||||||
var jsonEl = fromJs(ctx, ((ObjectValue)val).getMember(ctx, el), prev);
|
|
||||||
if (jsonEl == null) continue;
|
|
||||||
if (el instanceof String || el instanceof Number) res.put(el.toString(), jsonEl);
|
|
||||||
}
|
|
||||||
|
|
||||||
prev.remove(val);
|
|
||||||
return JSONElement.of(res);
|
|
||||||
}
|
|
||||||
if (val instanceof ArrayValue) {
|
if (val instanceof ArrayValue) {
|
||||||
if (prev.contains(val)) throw new EngineException("Circular dependency in JSON.");
|
if (prev.contains(val)) throw new EngineException("Circular dependency in JSON.");
|
||||||
prev.add(val);
|
prev.add(val);
|
||||||
@@ -67,6 +52,21 @@ public class JSON {
|
|||||||
prev.remove(val);
|
prev.remove(val);
|
||||||
return JSONElement.of(res);
|
return JSONElement.of(res);
|
||||||
}
|
}
|
||||||
|
if (val instanceof ObjectValue) {
|
||||||
|
if (prev.contains(val)) throw new EngineException("Circular dependency in JSON.");
|
||||||
|
prev.add(val);
|
||||||
|
|
||||||
|
var res = new JSONMap();
|
||||||
|
|
||||||
|
for (var el : Values.getMembers(ctx, val, false, false)) {
|
||||||
|
var jsonEl = fromJs(ctx, Values.getMember(ctx, val, el), prev);
|
||||||
|
if (jsonEl == null) continue;
|
||||||
|
if (el instanceof String || el instanceof Number) res.put(el.toString(), jsonEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
prev.remove(val);
|
||||||
|
return JSONElement.of(res);
|
||||||
|
}
|
||||||
if (val == null) return null;
|
if (val == null) return null;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -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 {
|
||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.TreeSet;
|
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
|
|
||||||
public class CompileTarget {
|
|
||||||
public final Vector<Instruction> target = new Vector<>();
|
|
||||||
public final Map<Long, FunctionBody> functions;
|
|
||||||
public final TreeSet<Location> breakpoints;
|
|
||||||
|
|
||||||
public Instruction add(Instruction instr) {
|
|
||||||
target.add(instr);
|
|
||||||
return instr;
|
|
||||||
}
|
|
||||||
public Instruction set(int i, Instruction instr) {
|
|
||||||
return target.set(i, instr);
|
|
||||||
}
|
|
||||||
public void setDebug(int i) {
|
|
||||||
breakpoints.add(target.get(i).location);
|
|
||||||
}
|
|
||||||
public void setDebug() {
|
|
||||||
setDebug(target.size() - 1);
|
|
||||||
}
|
|
||||||
public Instruction get(int i) {
|
|
||||||
return target.get(i);
|
|
||||||
}
|
|
||||||
public int size() { return target.size(); }
|
|
||||||
|
|
||||||
public Instruction[] array() { return target.toArray(Instruction[]::new); }
|
|
||||||
|
|
||||||
public CompileTarget(Map<Long, FunctionBody> functions, TreeSet<Location> breakpoints) {
|
|
||||||
this.functions = functions;
|
|
||||||
this.breakpoints = breakpoints;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
|
||||||
|
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.control.ContinueStatement;
|
|
||||||
import me.topchetoeu.jscript.compilation.control.ReturnStatement;
|
|
||||||
import me.topchetoeu.jscript.compilation.control.ThrowStatement;
|
|
||||||
import me.topchetoeu.jscript.compilation.values.FunctionStatement;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
|
|
||||||
public class CompoundStatement extends Statement {
|
|
||||||
public final Statement[] statements;
|
|
||||||
public Location end;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void declare(ScopeRecord varsScope) {
|
|
||||||
for (var stm : statements) {
|
|
||||||
stm.declare(varsScope);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
for (var stm : statements) {
|
|
||||||
if (stm instanceof FunctionStatement) {
|
|
||||||
int start = target.size();
|
|
||||||
((FunctionStatement)stm).compile(target, scope, null, true);
|
|
||||||
target.setDebug(start);
|
|
||||||
target.add(Instruction.discard());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < statements.length; i++) {
|
|
||||||
var stm = statements[i];
|
|
||||||
|
|
||||||
if (stm instanceof FunctionStatement) continue;
|
|
||||||
if (i != statements.length - 1) stm.compileWithDebug(target, scope, false);
|
|
||||||
else stm.compileWithDebug(target, scope, pollute);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (end != null) {
|
|
||||||
target.add(Instruction.nop().locate(end));
|
|
||||||
target.setDebug();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Statement optimize() {
|
|
||||||
var res = new Vector<Statement>(statements.length);
|
|
||||||
|
|
||||||
for (var i = 0; i < statements.length; i++) {
|
|
||||||
var stm = statements[i].optimize();
|
|
||||||
if (i < statements.length - 1 && stm.pure()) continue;
|
|
||||||
res.add(stm);
|
|
||||||
if (
|
|
||||||
stm instanceof ContinueStatement ||
|
|
||||||
stm instanceof ReturnStatement ||
|
|
||||||
stm instanceof ThrowStatement ||
|
|
||||||
stm instanceof ContinueStatement
|
|
||||||
) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.size() == 1) return res.get(0);
|
|
||||||
else return new CompoundStatement(loc(), res.toArray(Statement[]::new));
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompoundStatement setEnd(Location loc) {
|
|
||||||
this.end = loc;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompoundStatement(Location loc, Statement ...statements) {
|
|
||||||
super(loc);
|
|
||||||
this.statements = statements;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.values.ConstantStatement;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
|
|
||||||
public class DiscardStatement extends Statement {
|
|
||||||
public final Statement value;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
value.compile(target, scope, false);
|
|
||||||
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public Statement optimize() {
|
|
||||||
if (value == null) return this;
|
|
||||||
var val = value.optimize();
|
|
||||||
if (val.pure()) return new ConstantStatement(loc(), null);
|
|
||||||
else return new DiscardStatement(loc(), val);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DiscardStatement(Location loc, Statement val) {
|
|
||||||
super(loc);
|
|
||||||
this.value = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
|
||||||
|
|
||||||
public class FunctionBody {
|
|
||||||
public final Instruction[] instructions;
|
|
||||||
public final String[] captureNames, localNames;
|
|
||||||
|
|
||||||
public FunctionBody(Instruction[] instructions, String[] captureNames, String[] localNames) {
|
|
||||||
this.instructions = instructions;
|
|
||||||
this.captureNames = captureNames;
|
|
||||||
this.localNames = localNames;
|
|
||||||
}
|
|
||||||
public FunctionBody(Instruction[] instructions) {
|
|
||||||
this.instructions = instructions;
|
|
||||||
this.captureNames = new String[0];
|
|
||||||
this.localNames = new String[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,276 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
|
||||||
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
|
||||||
|
|
||||||
public class Instruction {
|
|
||||||
public static enum Type {
|
|
||||||
RETURN,
|
|
||||||
THROW,
|
|
||||||
THROW_SYNTAX,
|
|
||||||
DELETE,
|
|
||||||
TRY,
|
|
||||||
NOP,
|
|
||||||
|
|
||||||
CALL,
|
|
||||||
CALL_NEW,
|
|
||||||
JMP_IF,
|
|
||||||
JMP_IFN,
|
|
||||||
JMP,
|
|
||||||
|
|
||||||
LOAD_VALUE,
|
|
||||||
|
|
||||||
LOAD_VAR,
|
|
||||||
LOAD_MEMBER,
|
|
||||||
LOAD_VAL_MEMBER,
|
|
||||||
LOAD_GLOB,
|
|
||||||
|
|
||||||
LOAD_FUNC,
|
|
||||||
LOAD_ARR,
|
|
||||||
LOAD_OBJ,
|
|
||||||
STORE_SELF_FUNC,
|
|
||||||
LOAD_REGEX,
|
|
||||||
|
|
||||||
DUP,
|
|
||||||
MOVE,
|
|
||||||
|
|
||||||
STORE_VAR,
|
|
||||||
STORE_MEMBER,
|
|
||||||
DISCARD,
|
|
||||||
|
|
||||||
MAKE_VAR,
|
|
||||||
DEF_PROP,
|
|
||||||
KEYS,
|
|
||||||
|
|
||||||
TYPEOF,
|
|
||||||
OPERATION;
|
|
||||||
// TYPEOF,
|
|
||||||
// INSTANCEOF(true),
|
|
||||||
// IN(true),
|
|
||||||
|
|
||||||
// MULTIPLY(true),
|
|
||||||
// DIVIDE(true),
|
|
||||||
// MODULO(true),
|
|
||||||
// ADD(true),
|
|
||||||
// SUBTRACT(true),
|
|
||||||
|
|
||||||
// USHIFT_RIGHT(true),
|
|
||||||
// SHIFT_RIGHT(true),
|
|
||||||
// SHIFT_LEFT(true),
|
|
||||||
|
|
||||||
// GREATER(true),
|
|
||||||
// LESS(true),
|
|
||||||
// GREATER_EQUALS(true),
|
|
||||||
// LESS_EQUALS(true),
|
|
||||||
// LOOSE_EQUALS(true),
|
|
||||||
// LOOSE_NOT_EQUALS(true),
|
|
||||||
// EQUALS(true),
|
|
||||||
// NOT_EQUALS(true),
|
|
||||||
|
|
||||||
// AND(true),
|
|
||||||
// OR(true),
|
|
||||||
// XOR(true),
|
|
||||||
|
|
||||||
// NEG(true),
|
|
||||||
// POS(true),
|
|
||||||
// NOT(true),
|
|
||||||
// INVERSE(true);
|
|
||||||
|
|
||||||
// final boolean isOperation;
|
|
||||||
|
|
||||||
// private Type(boolean isOperation) {
|
|
||||||
// this.isOperation = isOperation;
|
|
||||||
// }
|
|
||||||
// private Type() {
|
|
||||||
// this(false);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
public final Type type;
|
|
||||||
public final Object[] params;
|
|
||||||
public Location location;
|
|
||||||
|
|
||||||
public Instruction locate(Location loc) {
|
|
||||||
this.location = loc;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@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);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Instruction(Location location, Type type, Object ...params) {
|
|
||||||
this.location = location;
|
|
||||||
this.type = type;
|
|
||||||
this.params = params;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction tryInstr(int n, int catchN, int finallyN) {
|
|
||||||
return new Instruction(null, Type.TRY, n, catchN, finallyN);
|
|
||||||
}
|
|
||||||
public static Instruction throwInstr() {
|
|
||||||
return new Instruction(null, Type.THROW);
|
|
||||||
}
|
|
||||||
public static Instruction throwSyntax(SyntaxException err) {
|
|
||||||
return new Instruction(null, Type.THROW_SYNTAX, err.getMessage());
|
|
||||||
}
|
|
||||||
public static Instruction delete() {
|
|
||||||
return new Instruction(null, Type.DELETE);
|
|
||||||
}
|
|
||||||
public static Instruction ret() {
|
|
||||||
return new Instruction(null, Type.RETURN);
|
|
||||||
}
|
|
||||||
public static Instruction debug() {
|
|
||||||
return new Instruction(null, Type.NOP, "debug");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction nop(Object ...params) {
|
|
||||||
for (var param : params) {
|
|
||||||
if (param instanceof String) continue;
|
|
||||||
if (param instanceof Boolean) continue;
|
|
||||||
if (param instanceof Double) continue;
|
|
||||||
if (param instanceof Integer) continue;
|
|
||||||
if (param == null) continue;
|
|
||||||
|
|
||||||
throw new RuntimeException("NOP params may contain only strings, booleans, doubles, integers and nulls.");
|
|
||||||
}
|
|
||||||
return new Instruction(null, Type.NOP, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction call(int argn) {
|
|
||||||
return new Instruction(null, Type.CALL, argn);
|
|
||||||
}
|
|
||||||
public static Instruction callNew(int argn) {
|
|
||||||
return new Instruction(null, Type.CALL_NEW, argn);
|
|
||||||
}
|
|
||||||
public static Instruction jmp(int offset) {
|
|
||||||
return new Instruction(null, Type.JMP, offset);
|
|
||||||
}
|
|
||||||
public static Instruction jmpIf(int offset) {
|
|
||||||
return new Instruction(null, Type.JMP_IF, offset);
|
|
||||||
}
|
|
||||||
public static Instruction jmpIfNot(int offset) {
|
|
||||||
return new Instruction(null, Type.JMP_IFN, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction loadValue(Object val) {
|
|
||||||
return new Instruction(null, Type.LOAD_VALUE, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction makeVar(String name) {
|
|
||||||
return new Instruction(null, Type.MAKE_VAR, name);
|
|
||||||
}
|
|
||||||
public static Instruction loadVar(Object i) {
|
|
||||||
return new Instruction(null, Type.LOAD_VAR, i);
|
|
||||||
}
|
|
||||||
public static Instruction loadGlob() {
|
|
||||||
return new Instruction(null, Type.LOAD_GLOB);
|
|
||||||
}
|
|
||||||
public static Instruction loadMember() {
|
|
||||||
return new Instruction(null, Type.LOAD_MEMBER);
|
|
||||||
}
|
|
||||||
public static Instruction loadMember(Object key) {
|
|
||||||
if (key instanceof Number) key = ((Number)key).doubleValue();
|
|
||||||
return new Instruction(null, Type.LOAD_VAL_MEMBER, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction loadRegex(String pattern, String flags) {
|
|
||||||
return new Instruction(null, Type.LOAD_REGEX, pattern, flags);
|
|
||||||
}
|
|
||||||
public static Instruction loadFunc(long id, int varN, int len, int[] captures) {
|
|
||||||
var args = new Object[3 + captures.length];
|
|
||||||
args[0] = id;
|
|
||||||
args[1] = varN;
|
|
||||||
args[2] = len;
|
|
||||||
for (var i = 0; i < captures.length; i++) args[i + 3] = captures[i];
|
|
||||||
return new Instruction(null, Type.LOAD_FUNC, args);
|
|
||||||
}
|
|
||||||
public static Instruction loadObj() {
|
|
||||||
return new Instruction(null, Type.LOAD_OBJ);
|
|
||||||
}
|
|
||||||
public static Instruction loadArr(int count) {
|
|
||||||
return new Instruction(null, Type.LOAD_ARR, count);
|
|
||||||
}
|
|
||||||
public static Instruction dup() {
|
|
||||||
return new Instruction(null, Type.DUP, 0, 1);
|
|
||||||
}
|
|
||||||
public static Instruction dup(int count, int offset) {
|
|
||||||
return new Instruction(null, Type.DUP, offset, count);
|
|
||||||
}
|
|
||||||
public static Instruction move(int count, int offset) {
|
|
||||||
return new Instruction(null, Type.MOVE, offset, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction storeSelfFunc(int i) {
|
|
||||||
return new Instruction(null, Type.STORE_SELF_FUNC, i);
|
|
||||||
}
|
|
||||||
public static Instruction storeVar(Object i) {
|
|
||||||
return new Instruction(null, Type.STORE_VAR, i, false);
|
|
||||||
}
|
|
||||||
public static Instruction storeVar(Object i, boolean keep) {
|
|
||||||
return new Instruction(null, Type.STORE_VAR, i, keep);
|
|
||||||
}
|
|
||||||
public static Instruction storeMember() {
|
|
||||||
return new Instruction(null, Type.STORE_MEMBER, false);
|
|
||||||
}
|
|
||||||
public static Instruction storeMember(boolean keep) {
|
|
||||||
return new Instruction(null, Type.STORE_MEMBER, keep);
|
|
||||||
}
|
|
||||||
public static Instruction discard() {
|
|
||||||
return new Instruction(null, Type.DISCARD);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction typeof() {
|
|
||||||
return new Instruction(null, Type.TYPEOF);
|
|
||||||
}
|
|
||||||
public static Instruction typeof(Object varName) {
|
|
||||||
return new Instruction(null, Type.TYPEOF, varName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction keys(boolean forInFormat) {
|
|
||||||
return new Instruction(null, Type.KEYS, forInFormat);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction defProp() {
|
|
||||||
return new Instruction(null, Type.DEF_PROP);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction operation(Operation op) {
|
|
||||||
return new Instruction(null, Type.OPERATION, op);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
var res = type.toString();
|
|
||||||
|
|
||||||
for (int i = 0; i < params.length; i++) {
|
|
||||||
res += " " + params[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
|
|
||||||
public abstract class Statement {
|
|
||||||
private Location _loc;
|
|
||||||
|
|
||||||
public boolean pure() { return false; }
|
|
||||||
public abstract void compile(CompileTarget target, ScopeRecord scope, boolean pollute);
|
|
||||||
public void declare(ScopeRecord varsScope) { }
|
|
||||||
public Statement optimize() { return this; }
|
|
||||||
|
|
||||||
public void compileWithDebug(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
int start = target.size();
|
|
||||||
compile(target, scope, pollute);
|
|
||||||
if (target.size() != start) target.setDebug(start);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Location loc() { return _loc; }
|
|
||||||
public void setLoc(Location loc) { _loc = loc; }
|
|
||||||
|
|
||||||
protected Statement(Location loc) {
|
|
||||||
this._loc = loc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
|
|
||||||
public class ArrayStatement extends Statement {
|
|
||||||
public final Statement[] statements;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean pure() { return true; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
target.add(Instruction.loadArr(statements.length).locate(loc()));
|
|
||||||
var i = 0;
|
|
||||||
for (var el : statements) {
|
|
||||||
if (el != null) {
|
|
||||||
target.add(Instruction.dup().locate(loc()));
|
|
||||||
target.add(Instruction.loadValue(i).locate(loc()));
|
|
||||||
el.compile(target, scope, true);
|
|
||||||
target.add(Instruction.storeMember().locate(loc()));
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
if (!pollute) target.add(Instruction.discard().locate(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArrayStatement(Location loc, Statement[] statements) {
|
|
||||||
super(loc);
|
|
||||||
this.statements = statements;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
|
|
||||||
public class BreakStatement extends Statement {
|
|
||||||
public final String label;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
target.add(Instruction.nop("break", label).locate(loc()));
|
|
||||||
if (pollute) target.add(Instruction.loadValue(null).locate(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public BreakStatement(Location loc, String label) {
|
|
||||||
super(loc);
|
|
||||||
this.label = label;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
|
|
||||||
public class ContinueStatement extends Statement {
|
|
||||||
public final String label;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
target.add(Instruction.nop("cont", label).locate(loc()));
|
|
||||||
if (pollute) target.add(Instruction.loadValue(null).locate(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public ContinueStatement(Location loc, String label) {
|
|
||||||
super(loc);
|
|
||||||
this.label = label;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
|
|
||||||
public class DebugStatement extends Statement {
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
target.add(Instruction.debug().locate(loc()));
|
|
||||||
if (pollute) target.add(Instruction.loadValue(null).locate(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public DebugStatement(Location loc) {
|
|
||||||
super(loc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompoundStatement;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
import me.topchetoeu.jscript.compilation.values.ConstantStatement;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
|
||||||
|
|
||||||
public class DoWhileStatement extends Statement {
|
|
||||||
public final Statement condition, body;
|
|
||||||
public final String label;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void declare(ScopeRecord globScope) {
|
|
||||||
body.declare(globScope);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
if (condition instanceof ConstantStatement) {
|
|
||||||
int start = target.size();
|
|
||||||
body.compile(target, scope, false);
|
|
||||||
int end = target.size();
|
|
||||||
if (Values.toBoolean(((ConstantStatement)condition).value)) {
|
|
||||||
WhileStatement.replaceBreaks(target, label, start, end, end + 1, end + 1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
target.add(Instruction.jmp(start - end).locate(loc()));
|
|
||||||
WhileStatement.replaceBreaks(target, label, start, end, start, end + 1);
|
|
||||||
}
|
|
||||||
if (pollute) target.add(Instruction.loadValue(null).locate(loc()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int start = target.size();
|
|
||||||
body.compileWithDebug(target, scope, false);
|
|
||||||
int mid = target.size();
|
|
||||||
condition.compile(target, scope, true);
|
|
||||||
int end = target.size();
|
|
||||||
|
|
||||||
WhileStatement.replaceBreaks(target, label, start, mid - 1, mid, end + 1);
|
|
||||||
target.add(Instruction.jmpIf(start - end).locate(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Statement optimize() {
|
|
||||||
var cond = condition.optimize();
|
|
||||||
var b = body.optimize();
|
|
||||||
|
|
||||||
if (b instanceof CompoundStatement) {
|
|
||||||
var comp = (CompoundStatement)b;
|
|
||||||
if (comp.statements.length > 0) {
|
|
||||||
var last = comp.statements[comp.statements.length - 1];
|
|
||||||
if (last instanceof ContinueStatement) comp.statements[comp.statements.length - 1] = new CompoundStatement(loc());
|
|
||||||
if (last instanceof BreakStatement) {
|
|
||||||
comp.statements[comp.statements.length - 1] = new CompoundStatement(loc());
|
|
||||||
return new CompoundStatement(loc());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (b instanceof ContinueStatement) {
|
|
||||||
b = new CompoundStatement(loc());
|
|
||||||
}
|
|
||||||
else if (b instanceof BreakStatement) return new CompoundStatement(loc());
|
|
||||||
|
|
||||||
if (b.pure()) return new DoWhileStatement(loc(), label, cond, new CompoundStatement(loc()));
|
|
||||||
else return new DoWhileStatement(loc(), label, cond, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DoWhileStatement(Location loc, String label, Statement condition, Statement body) {
|
|
||||||
super(loc);
|
|
||||||
this.label = label;
|
|
||||||
this.condition = condition;
|
|
||||||
this.body = body;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompoundStatement;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
|
||||||
import me.topchetoeu.jscript.compilation.values.ConstantStatement;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
|
||||||
|
|
||||||
public class ForStatement extends Statement {
|
|
||||||
public final Statement declaration, assignment, condition, body;
|
|
||||||
public final String label;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void declare(ScopeRecord globScope) {
|
|
||||||
declaration.declare(globScope);
|
|
||||||
body.declare(globScope);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
declaration.compile(target, scope, false);
|
|
||||||
|
|
||||||
if (condition instanceof ConstantStatement) {
|
|
||||||
if (Values.toBoolean(((ConstantStatement)condition).value)) {
|
|
||||||
int start = target.size();
|
|
||||||
body.compile(target, scope, false);
|
|
||||||
int mid = target.size();
|
|
||||||
assignment.compileWithDebug(target, scope, false);
|
|
||||||
int end = target.size();
|
|
||||||
WhileStatement.replaceBreaks(target, label, start, mid, mid, end + 1);
|
|
||||||
target.add(Instruction.jmp(start - target.size()).locate(loc()));
|
|
||||||
if (pollute) target.add(Instruction.loadValue(null).locate(loc()));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int start = target.size();
|
|
||||||
condition.compile(target, scope, true);
|
|
||||||
int mid = target.size();
|
|
||||||
target.add(Instruction.nop());
|
|
||||||
body.compile(target, scope, false);
|
|
||||||
int beforeAssign = target.size();
|
|
||||||
assignment.compileWithDebug(target, scope, false);
|
|
||||||
int end = target.size();
|
|
||||||
|
|
||||||
WhileStatement.replaceBreaks(target, label, mid + 1, end, beforeAssign, end + 1);
|
|
||||||
|
|
||||||
target.add(Instruction.jmp(start - end).locate(loc()));
|
|
||||||
target.set(mid, Instruction.jmpIfNot(end - mid + 1).locate(loc()));
|
|
||||||
if (pollute) target.add(Instruction.loadValue(null).locate(loc()));
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public Statement optimize() {
|
|
||||||
var decl = declaration.optimize();
|
|
||||||
var asgn = assignment.optimize();
|
|
||||||
var cond = condition.optimize();
|
|
||||||
var b = body.optimize();
|
|
||||||
|
|
||||||
if (asgn.pure()) {
|
|
||||||
if (decl.pure()) return new WhileStatement(loc(), label, cond, b).optimize();
|
|
||||||
else return new CompoundStatement(loc(),
|
|
||||||
decl, new WhileStatement(loc(), label, cond, b)
|
|
||||||
).optimize();
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (b instanceof ContinueStatement) return new CompoundStatement(loc(),
|
|
||||||
decl, new WhileStatement(loc(), label, cond, new CompoundStatement(loc(), b, asgn))
|
|
||||||
);
|
|
||||||
else if (b instanceof BreakStatement) return decl;
|
|
||||||
|
|
||||||
if (b.pure()) return new ForStatement(loc(), label, decl, cond, asgn, new CompoundStatement(null));
|
|
||||||
else return new ForStatement(loc(), label, decl, cond, asgn, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ForStatement(Location loc, String label, Statement declaration, Statement condition, Statement assignment, Statement body) {
|
|
||||||
super(loc);
|
|
||||||
this.label = label;
|
|
||||||
this.declaration = declaration;
|
|
||||||
this.condition = condition;
|
|
||||||
this.assignment = assignment;
|
|
||||||
this.body = body;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CompoundStatement ofFor(Location loc, String label, Statement declaration, Statement condition, Statement increment, Statement body) {
|
|
||||||
return new CompoundStatement(loc,
|
|
||||||
declaration,
|
|
||||||
new WhileStatement(loc, label, condition, new CompoundStatement(loc,
|
|
||||||
body,
|
|
||||||
increment
|
|
||||||
))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompoundStatement;
|
|
||||||
import me.topchetoeu.jscript.compilation.DiscardStatement;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
import me.topchetoeu.jscript.compilation.values.ConstantStatement;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
|
||||||
|
|
||||||
public class IfStatement extends Statement {
|
|
||||||
public final Statement condition, body, elseBody;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void declare(ScopeRecord globScope) {
|
|
||||||
body.declare(globScope);
|
|
||||||
if (elseBody != null) elseBody.declare(globScope);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
if (condition instanceof ConstantStatement) {
|
|
||||||
if (Values.not(((ConstantStatement)condition).value)) {
|
|
||||||
if (elseBody != null) elseBody.compileWithDebug(target, scope, pollute);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
body.compileWithDebug(target, scope, pollute);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
condition.compile(target, scope, true);
|
|
||||||
|
|
||||||
if (elseBody == null) {
|
|
||||||
int i = target.size();
|
|
||||||
target.add(Instruction.nop());
|
|
||||||
body.compileWithDebug(target, scope, pollute);
|
|
||||||
int endI = target.size();
|
|
||||||
target.set(i, Instruction.jmpIfNot(endI - i).locate(loc()));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
int start = target.size();
|
|
||||||
target.add(Instruction.nop());
|
|
||||||
body.compileWithDebug(target, scope, pollute);
|
|
||||||
target.add(Instruction.nop());
|
|
||||||
int mid = target.size();
|
|
||||||
elseBody.compileWithDebug(target, scope, pollute);
|
|
||||||
int end = target.size();
|
|
||||||
|
|
||||||
target.set(start, Instruction.jmpIfNot(mid - start).locate(loc()));
|
|
||||||
target.set(mid - 1, Instruction.jmp(end - mid + 1).locate(loc()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Statement optimize() {
|
|
||||||
var cond = condition.optimize();
|
|
||||||
var b = body.optimize();
|
|
||||||
var e = elseBody == null ? null : elseBody.optimize();
|
|
||||||
|
|
||||||
if (b.pure()) b = new CompoundStatement(null);
|
|
||||||
if (e != null && e.pure()) e = null;
|
|
||||||
|
|
||||||
if (b.pure() && e == null) return new DiscardStatement(loc(), cond).optimize();
|
|
||||||
else return new IfStatement(loc(), cond, b, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IfStatement(Location loc, Statement condition, Statement body, Statement elseBody) {
|
|
||||||
super(loc);
|
|
||||||
this.condition = condition;
|
|
||||||
this.body = body;
|
|
||||||
this.elseBody = elseBody;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
|
|
||||||
public class ReturnStatement extends Statement {
|
|
||||||
public final Statement value;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
if (value == null) target.add(Instruction.loadValue(null).locate(loc()));
|
|
||||||
else value.compile(target, scope, true);
|
|
||||||
target.add(Instruction.ret().locate(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReturnStatement(Location loc, Statement value) {
|
|
||||||
super(loc);
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.Type;
|
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
|
|
||||||
public class SwitchStatement extends Statement {
|
|
||||||
public static class SwitchCase {
|
|
||||||
public final Statement value;
|
|
||||||
public final int statementI;
|
|
||||||
|
|
||||||
public SwitchCase(Statement value, int statementI) {
|
|
||||||
this.value = value;
|
|
||||||
this.statementI = statementI;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public final Statement value;
|
|
||||||
public final SwitchCase[] cases;
|
|
||||||
public final Statement[] body;
|
|
||||||
public final int defaultI;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void declare(ScopeRecord varsScope) {
|
|
||||||
for (var stm : body) stm.declare(varsScope);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
var caseMap = new HashMap<Integer, Integer>();
|
|
||||||
var stmIndexMap = new HashMap<Integer, Integer>();
|
|
||||||
|
|
||||||
value.compile(target, scope, true);
|
|
||||||
|
|
||||||
for (var ccase : cases) {
|
|
||||||
target.add(Instruction.dup().locate(loc()));
|
|
||||||
ccase.value.compile(target, scope, true);
|
|
||||||
target.add(Instruction.operation(Operation.EQUALS).locate(loc()));
|
|
||||||
caseMap.put(target.size(), ccase.statementI);
|
|
||||||
target.add(Instruction.nop().locate(ccase.value.loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
int start = target.size();
|
|
||||||
|
|
||||||
target.add(Instruction.nop());
|
|
||||||
|
|
||||||
for (var stm : body) {
|
|
||||||
stmIndexMap.put(stmIndexMap.size(), target.size());
|
|
||||||
stm.compileWithDebug(target, scope, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (defaultI < 0 || defaultI >= body.length) target.set(start, Instruction.jmp(target.size() - start).locate(loc()));
|
|
||||||
else target.set(start, Instruction.jmp(stmIndexMap.get(defaultI) - start)).locate(loc());
|
|
||||||
|
|
||||||
for (int i = start; i < target.size(); i++) {
|
|
||||||
var instr = target.get(i);
|
|
||||||
if (instr.type == Type.NOP && instr.is(0, "break") && instr.get(1) == null) {
|
|
||||||
target.set(i, Instruction.jmp(target.size() - i).locate(instr.location));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (var el : caseMap.entrySet()) {
|
|
||||||
var loc = target.get(el.getKey()).location;
|
|
||||||
var i = stmIndexMap.get(el.getValue());
|
|
||||||
if (i == null) i = target.size();
|
|
||||||
target.set(el.getKey(), Instruction.jmpIf(i - el.getKey()).locate(loc));
|
|
||||||
target.setDebug(el.getKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
target.add(Instruction.discard().locate(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public SwitchStatement(Location loc, Statement value, int defaultI, SwitchCase[] cases, Statement[] body) {
|
|
||||||
super(loc);
|
|
||||||
this.value = value;
|
|
||||||
this.defaultI = defaultI;
|
|
||||||
this.cases = cases;
|
|
||||||
this.body = body;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
|
|
||||||
public class ThrowStatement extends Statement {
|
|
||||||
public final Statement value;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
value.compile(target, scope, true);
|
|
||||||
target.add(Instruction.throwInstr().locate(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public ThrowStatement(Location loc, Statement value) {
|
|
||||||
super(loc);
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompoundStatement;
|
|
||||||
import me.topchetoeu.jscript.compilation.DiscardStatement;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.Type;
|
|
||||||
import me.topchetoeu.jscript.compilation.values.ConstantStatement;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
|
||||||
|
|
||||||
public class WhileStatement extends Statement {
|
|
||||||
public final Statement condition, body;
|
|
||||||
public final String label;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void declare(ScopeRecord globScope) {
|
|
||||||
body.declare(globScope);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
if (condition instanceof ConstantStatement) {
|
|
||||||
if (Values.toBoolean(((ConstantStatement)condition).value)) {
|
|
||||||
int start = target.size();
|
|
||||||
body.compile(target, scope, false);
|
|
||||||
int end = target.size();
|
|
||||||
replaceBreaks(target, label, start, end, start, end + 1);
|
|
||||||
target.add(Instruction.jmp(start - target.size()).locate(loc()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int start = target.size();
|
|
||||||
condition.compile(target, scope, true);
|
|
||||||
int mid = target.size();
|
|
||||||
target.add(Instruction.nop());
|
|
||||||
body.compile(target, scope, false);
|
|
||||||
|
|
||||||
int end = target.size();
|
|
||||||
|
|
||||||
replaceBreaks(target, label, mid + 1, end, start, end + 1);
|
|
||||||
|
|
||||||
target.add(Instruction.jmp(start - end).locate(loc()));
|
|
||||||
target.set(mid, Instruction.jmpIfNot(end - mid + 1).locate(loc()));
|
|
||||||
if (pollute) target.add(Instruction.loadValue(null).locate(loc()));
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public Statement optimize() {
|
|
||||||
var cond = condition.optimize();
|
|
||||||
var b = body.optimize();
|
|
||||||
|
|
||||||
if (b instanceof ContinueStatement) {
|
|
||||||
b = new CompoundStatement(loc());
|
|
||||||
}
|
|
||||||
else if (b instanceof BreakStatement) return new DiscardStatement(loc(), cond).optimize();
|
|
||||||
|
|
||||||
if (b.pure()) return new WhileStatement(loc(), label, cond, new CompoundStatement(null));
|
|
||||||
else return new WhileStatement(loc(), label, cond, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
public WhileStatement(Location loc, String label, Statement condition, Statement body) {
|
|
||||||
super(loc);
|
|
||||||
this.label = label;
|
|
||||||
this.condition = condition;
|
|
||||||
this.body = body;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void replaceBreaks(CompileTarget 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));
|
|
||||||
target.get(i).location = instr.location;
|
|
||||||
}
|
|
||||||
if (instr.type == Type.NOP && instr.is(0, "break") && (instr.get(1) == null || instr.is(1, label))) {
|
|
||||||
target.set(i, Instruction.jmp(breakPoint - i));
|
|
||||||
target.get(i).location = instr.location;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// public static CompoundStatement ofFor(Location loc, String label, Statement declaration, Statement condition, Statement increment, Statement body) {
|
|
||||||
// return new CompoundStatement(loc,
|
|
||||||
// declaration,
|
|
||||||
// new WhileStatement(loc, label, condition, new CompoundStatement(loc,
|
|
||||||
// body,
|
|
||||||
// increment
|
|
||||||
// ))
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
|
|
||||||
public class CallStatement extends Statement {
|
|
||||||
public final Statement func;
|
|
||||||
public final Statement[] args;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
if (func instanceof IndexStatement) {
|
|
||||||
((IndexStatement)func).compile(target, scope, true, true);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
target.add(Instruction.loadValue(null).locate(loc()));
|
|
||||||
func.compile(target, scope, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var arg : args) arg.compile(target, scope, true);
|
|
||||||
|
|
||||||
target.add(Instruction.call(args.length).locate(loc()));
|
|
||||||
target.setDebug();
|
|
||||||
if (!pollute) target.add(Instruction.discard().locate(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public CallStatement(Location loc, Statement func, Statement ...args) {
|
|
||||||
super(loc);
|
|
||||||
this.func = func;
|
|
||||||
this.args = args;
|
|
||||||
}
|
|
||||||
public CallStatement(Location loc, Statement obj, Object key, Statement ...args) {
|
|
||||||
super(loc);
|
|
||||||
this.func = new IndexStatement(loc, obj, new ConstantStatement(loc, key));
|
|
||||||
this.args = args;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.AssignableStatement;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
|
||||||
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 final AssignableStatement value;
|
|
||||||
public final double addAmount;
|
|
||||||
public final boolean postfix;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
value.toAssign(new ConstantStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, scope, true);
|
|
||||||
if (!pollute) target.add(Instruction.discard().locate(loc()));
|
|
||||||
else if (postfix) {
|
|
||||||
target.add(Instruction.loadValue(addAmount));
|
|
||||||
target.add(Instruction.operation(Operation.SUBTRACT));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ChangeStatement(Location loc, AssignableStatement value, double addAmount, boolean postfix) {
|
|
||||||
super(loc);
|
|
||||||
this.value = value;
|
|
||||||
this.addAmount = addAmount;
|
|
||||||
this.postfix = postfix;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
|
|
||||||
public class CommaStatement extends Statement {
|
|
||||||
public final Statement[] values;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
for (var i = 0; i < values.length; i++) {
|
|
||||||
values[i].compile(target, scope, i == values.length - 1 && pollute);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Statement optimize() {
|
|
||||||
var res = new Vector<Statement>(values.length);
|
|
||||||
|
|
||||||
for (var i = 0; i < values.length; i++) {
|
|
||||||
var stm = values[i].optimize();
|
|
||||||
if (i < values.length - 1 && stm.pure()) continue;
|
|
||||||
res.add(stm);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.size() == 1) return res.get(0);
|
|
||||||
else return new CommaStatement(loc(), res.toArray(Statement[]::new));
|
|
||||||
}
|
|
||||||
|
|
||||||
public CommaStatement(Location loc, Statement ...args) {
|
|
||||||
super(loc);
|
|
||||||
this.values = args;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
|
|
||||||
public class ConstantStatement extends Statement {
|
|
||||||
public final Object value;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean pure() { return true; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
if (pollute) target.add(Instruction.loadValue(value).locate(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConstantStatement(Location loc, Object val) {
|
|
||||||
super(loc);
|
|
||||||
this.value = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompoundStatement;
|
|
||||||
import me.topchetoeu.jscript.compilation.FunctionBody;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.Type;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
|
||||||
|
|
||||||
public class FunctionStatement extends Statement {
|
|
||||||
public final CompoundStatement body;
|
|
||||||
public final String name;
|
|
||||||
public final String[] args;
|
|
||||||
|
|
||||||
private static Random rand = new Random();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean pure() { return name == null; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void declare(ScopeRecord scope) {
|
|
||||||
if (name != null) scope.define(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void checkBreakAndCont(CompileTarget 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.get(i).location, "Break was placed outside a loop.");
|
|
||||||
}
|
|
||||||
if (target.get(i).is(0, "cont")) {
|
|
||||||
throw new SyntaxException(target.get(i).location, "Continue was placed outside a loop.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, String name, boolean isStatement) {
|
|
||||||
for (var i = 0; i < args.length; i++) {
|
|
||||||
for (var j = 0; j < i; j++) {
|
|
||||||
if (args[i].equals(args[j])){
|
|
||||||
target.add(Instruction.throwSyntax(new SyntaxException(loc(), "Duplicate parameter '" + args[i] + "'.")));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var subscope = scope.child();
|
|
||||||
|
|
||||||
int start = target.size();
|
|
||||||
var funcTarget = new CompileTarget(target.functions, target.breakpoints);
|
|
||||||
|
|
||||||
subscope.define("this");
|
|
||||||
var argsVar = subscope.define("arguments");
|
|
||||||
|
|
||||||
if (args.length > 0) {
|
|
||||||
for (var i = 0; i < args.length; i++) {
|
|
||||||
funcTarget.add(Instruction.loadVar(argsVar).locate(loc()));
|
|
||||||
funcTarget.add(Instruction.loadMember(i).locate(loc()));
|
|
||||||
funcTarget.add(Instruction.storeVar(subscope.define(args[i])).locate(loc()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isStatement && this.name != null) {
|
|
||||||
funcTarget.add(Instruction.storeSelfFunc((int)subscope.define(this.name)));
|
|
||||||
}
|
|
||||||
|
|
||||||
body.declare(subscope);
|
|
||||||
body.compile(funcTarget, subscope, false);
|
|
||||||
funcTarget.add(Instruction.ret().locate(loc()));
|
|
||||||
checkBreakAndCont(funcTarget, start);
|
|
||||||
|
|
||||||
var id = rand.nextLong();
|
|
||||||
|
|
||||||
target.add(Instruction.loadFunc(id, subscope.localsCount(), args.length, subscope.getCaptures()).locate(loc()));
|
|
||||||
target.functions.put(id, new FunctionBody(funcTarget.array(), subscope.captures(), subscope.locals()));
|
|
||||||
|
|
||||||
if (name == null) name = this.name;
|
|
||||||
|
|
||||||
if (name != null) {
|
|
||||||
target.add(Instruction.dup().locate(loc()));
|
|
||||||
target.add(Instruction.loadValue("name").locate(loc()));
|
|
||||||
target.add(Instruction.loadValue(name).locate(loc()));
|
|
||||||
target.add(Instruction.storeMember().locate(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.name != null && isStatement) {
|
|
||||||
var key = scope.getKey(this.name);
|
|
||||||
|
|
||||||
if (key instanceof String) target.add(Instruction.makeVar((String)key).locate(loc()));
|
|
||||||
target.add(Instruction.storeVar(scope.getKey(this.name), false).locate(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
compile(target, scope, null, false);
|
|
||||||
if (!pollute) target.add(Instruction.discard().locate(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public FunctionStatement(Location loc, String name, String[] args, CompoundStatement body) {
|
|
||||||
super(loc);
|
|
||||||
this.name = name;
|
|
||||||
|
|
||||||
this.args = args;
|
|
||||||
this.body = body;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
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 GlobalThisStatement extends Statement {
|
|
||||||
@Override
|
|
||||||
public boolean pure() { return true; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
if (pollute) target.add(Instruction.loadGlob().locate(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public GlobalThisStatement(Location loc) {
|
|
||||||
super(loc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
|
|
||||||
public class NewStatement extends Statement {
|
|
||||||
public final Statement func;
|
|
||||||
public final Statement[] args;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
func.compile(target, scope, true);
|
|
||||||
|
|
||||||
for (var arg : args) arg.compile(target, scope, true);
|
|
||||||
|
|
||||||
target.add(Instruction.callNew(args.length).locate(loc()));
|
|
||||||
target.setDebug();
|
|
||||||
}
|
|
||||||
|
|
||||||
public NewStatement(Location loc, Statement func, Statement ...args) {
|
|
||||||
super(loc);
|
|
||||||
this.func = func;
|
|
||||||
this.args = args;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
|
|
||||||
public class ObjectStatement extends Statement {
|
|
||||||
public final Map<Object, Statement> map;
|
|
||||||
public final Map<Object, FunctionStatement> getters;
|
|
||||||
public final Map<Object, FunctionStatement> setters;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
target.add(Instruction.loadObj().locate(loc()));
|
|
||||||
|
|
||||||
for (var el : map.entrySet()) {
|
|
||||||
target.add(Instruction.dup().locate(loc()));
|
|
||||||
target.add(Instruction.loadValue(el.getKey()).locate(loc()));
|
|
||||||
var val = el.getValue();
|
|
||||||
if (val instanceof FunctionStatement) ((FunctionStatement)val).compile(target, scope, el.getKey().toString(), false);
|
|
||||||
else val.compile(target, scope, true);
|
|
||||||
target.add(Instruction.storeMember().locate(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
var keys = new ArrayList<Object>();
|
|
||||||
keys.addAll(getters.keySet());
|
|
||||||
keys.addAll(setters.keySet());
|
|
||||||
|
|
||||||
for (var key : keys) {
|
|
||||||
if (key instanceof String) target.add(Instruction.loadValue((String)key).locate(loc()));
|
|
||||||
else target.add(Instruction.loadValue((Double)key).locate(loc()));
|
|
||||||
|
|
||||||
if (getters.containsKey(key)) getters.get(key).compile(target, scope, true);
|
|
||||||
else target.add(Instruction.loadValue(null).locate(loc()));
|
|
||||||
|
|
||||||
if (setters.containsKey(key)) setters.get(key).compile(target, scope, true);
|
|
||||||
else target.add(Instruction.loadValue(null).locate(loc()));
|
|
||||||
|
|
||||||
target.add(Instruction.defProp().locate(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pollute) target.add(Instruction.discard().locate(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectStatement(Location loc, Map<Object, Statement> map, Map<Object, FunctionStatement> getters, Map<Object, FunctionStatement> setters) {
|
|
||||||
super(loc);
|
|
||||||
this.map = map;
|
|
||||||
this.getters = getters;
|
|
||||||
this.setters = setters;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
import me.topchetoeu.jscript.compilation.control.ThrowStatement;
|
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
|
||||||
|
|
||||||
public class OperationStatement extends Statement {
|
|
||||||
public final Statement[] args;
|
|
||||||
public final Operation operation;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
for (var arg : args) {
|
|
||||||
arg.compile(target, scope, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pollute) target.add(Instruction.operation(operation).locate(loc()));
|
|
||||||
else target.add(Instruction.discard().locate(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean pure() {
|
|
||||||
for (var arg : args) {
|
|
||||||
if (!arg.pure()) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Statement optimize() {
|
|
||||||
var args = new Statement[this.args.length];
|
|
||||||
var allConst = true;
|
|
||||||
|
|
||||||
for (var i = 0; i < this.args.length; i++) {
|
|
||||||
args[i] = this.args[i].optimize();
|
|
||||||
if (!(args[i] instanceof ConstantStatement)) allConst = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (allConst) {
|
|
||||||
var vals = new Object[this.args.length];
|
|
||||||
|
|
||||||
for (var i = 0; i < args.length; i++) {
|
|
||||||
vals[i] = ((ConstantStatement)args[i]).value;
|
|
||||||
}
|
|
||||||
|
|
||||||
try { return new ConstantStatement(loc(), Values.operation(null, operation, vals)); }
|
|
||||||
catch (EngineException e) { return new ThrowStatement(loc(), new ConstantStatement(loc(), e.value)); }
|
|
||||||
}
|
|
||||||
|
|
||||||
return new OperationStatement(loc(), operation, args);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public OperationStatement(Location loc, Operation operation, Statement ...args) {
|
|
||||||
super(loc);
|
|
||||||
this.operation = operation;
|
|
||||||
this.args = args;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
|
|
||||||
public class RegexStatement extends Statement {
|
|
||||||
public final String pattern, flags;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean pure() { return true; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
target.add(Instruction.loadRegex(pattern, flags).locate(loc()));
|
|
||||||
if (!pollute) target.add(Instruction.discard().locate(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public RegexStatement(Location loc, String pattern, String flags) {
|
|
||||||
super(loc);
|
|
||||||
this.pattern = pattern;
|
|
||||||
this.flags = flags;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
import me.topchetoeu.jscript.compilation.control.ArrayStatement;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
|
||||||
|
|
||||||
public class TypeofStatement extends Statement {
|
|
||||||
public final Statement value;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean pure() { return true; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
if (value instanceof VariableStatement) {
|
|
||||||
var i = scope.getKey(((VariableStatement)value).name);
|
|
||||||
if (i instanceof String) {
|
|
||||||
target.add(Instruction.typeof((String)i).locate(loc()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
value.compile(target, scope, pollute);
|
|
||||||
target.add(Instruction.typeof().locate(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Statement optimize() {
|
|
||||||
var val = value.optimize();
|
|
||||||
|
|
||||||
if (val instanceof ConstantStatement) {
|
|
||||||
return new ConstantStatement(loc(), Values.type(((ConstantStatement)val).value));
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
val instanceof ObjectStatement ||
|
|
||||||
val instanceof ArrayStatement ||
|
|
||||||
val instanceof GlobalThisStatement
|
|
||||||
) return new ConstantStatement(loc(), "object");
|
|
||||||
else if(val instanceof FunctionStatement) return new ConstantStatement(loc(), "function");
|
|
||||||
|
|
||||||
return new TypeofStatement(loc(), val);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TypeofStatement(Location loc, Statement value) {
|
|
||||||
super(loc);
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
|
|
||||||
public class VariableAssignStatement extends Statement {
|
|
||||||
public final String name;
|
|
||||||
public final Statement value;
|
|
||||||
public final Operation operation;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
var i = scope.getKey(name);
|
|
||||||
if (operation != null) {
|
|
||||||
target.add(Instruction.loadVar(i).locate(loc()));
|
|
||||||
if (value instanceof FunctionStatement) ((FunctionStatement)value).compile(target, scope, name, false);
|
|
||||||
else value.compile(target, scope, true);
|
|
||||||
target.add(Instruction.operation(operation).locate(loc()));
|
|
||||||
target.add(Instruction.storeVar(i, pollute).locate(loc()));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (value instanceof FunctionStatement) ((FunctionStatement)value).compile(target, scope, name, false);
|
|
||||||
else value.compile(target, scope, true);
|
|
||||||
target.add(Instruction.storeVar(i, pollute).locate(loc()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public VariableAssignStatement(Location loc, String name, Statement val, Operation operation) {
|
|
||||||
super(loc);
|
|
||||||
this.name = name;
|
|
||||||
this.value = val;
|
|
||||||
this.operation = operation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
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 final int index;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean pure() { return true; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
if (pollute) target.add(Instruction.loadVar(index).locate(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public VariableIndexStatement(Location loc, int i) {
|
|
||||||
super(loc);
|
|
||||||
this.index = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.AssignableStatement;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
|
||||||
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 final String name;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean pure() { return true; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Statement toAssign(Statement val, Operation operation) {
|
|
||||||
return new VariableAssignStatement(loc(), name, val, operation);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
var i = scope.getKey(name);
|
|
||||||
target.add(Instruction.loadVar(i).locate(loc()));
|
|
||||||
if (!pollute) target.add(Instruction.discard().locate(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public VariableStatement(Location loc, String name) {
|
|
||||||
super(loc);
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
|
||||||
|
|
||||||
public class VoidStatement extends Statement {
|
|
||||||
public final Statement value;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
if (value != null) value.compile(target, scope, false);
|
|
||||||
if (pollute) target.add(Instruction.loadValue(null).locate(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Statement optimize() {
|
|
||||||
if (value == null) return this;
|
|
||||||
var val = value.optimize();
|
|
||||||
if (val.pure()) return new ConstantStatement(loc(), null);
|
|
||||||
else return new VoidStatement(loc(), val);
|
|
||||||
}
|
|
||||||
|
|
||||||
public VoidStatement(Location loc, Statement value) {
|
|
||||||
super(loc);
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
package me.topchetoeu.jscript.core.compilation;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.common.Location;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.core.engine.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);
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.core.engine.values.Values;
|
||||||
|
|
||||||
|
public final class CalculateResult {
|
||||||
|
public final boolean exists;
|
||||||
|
public final Object value;
|
||||||
|
|
||||||
|
public final boolean isTruthy() {
|
||||||
|
return exists && Values.toBoolean(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CalculateResult(Object value) {
|
||||||
|
this.exists = true;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
public CalculateResult() {
|
||||||
|
this.exists = false;
|
||||||
|
this.value = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
|
||||||
|
import me.topchetoeu.jscript.core.engine.Environment;
|
||||||
|
import me.topchetoeu.jscript.core.engine.values.CodeFunction;
|
||||||
|
|
||||||
|
public class CompileTarget {
|
||||||
|
public final Vector<Instruction> target = new Vector<>();
|
||||||
|
public final Map<Long, FunctionBody> functions;
|
||||||
|
public final TreeSet<Location> breakpoints;
|
||||||
|
private final HashMap<Location, Instruction> bpToInstr = new HashMap<>();
|
||||||
|
|
||||||
|
public Instruction add(Instruction instr) {
|
||||||
|
target.add(instr);
|
||||||
|
return instr;
|
||||||
|
}
|
||||||
|
public Instruction set(int i, Instruction instr) {
|
||||||
|
return target.set(i, instr);
|
||||||
|
}
|
||||||
|
public void setDebug(int i, BreakpointType type) {
|
||||||
|
var instr = target.get(i);
|
||||||
|
instr.breakpoint = type;
|
||||||
|
|
||||||
|
if (type == BreakpointType.NONE) {
|
||||||
|
breakpoints.remove(target.get(i).location);
|
||||||
|
bpToInstr.remove(instr.location, instr);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
breakpoints.add(target.get(i).location);
|
||||||
|
|
||||||
|
var old = bpToInstr.put(instr.location, instr);
|
||||||
|
if (old != null) old.breakpoint = BreakpointType.NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void setDebug(BreakpointType type) {
|
||||||
|
setDebug(target.size() - 1, type);
|
||||||
|
}
|
||||||
|
public Instruction get(int i) {
|
||||||
|
return target.get(i);
|
||||||
|
}
|
||||||
|
public int size() { return target.size(); }
|
||||||
|
public Location lastLoc(Location fallback) {
|
||||||
|
if (target.size() == 0) return fallback;
|
||||||
|
else return target.get(target.size() - 1).location;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Instruction[] array() { return target.toArray(Instruction[]::new); }
|
||||||
|
|
||||||
|
public FunctionBody body() {
|
||||||
|
return functions.get(0l);
|
||||||
|
}
|
||||||
|
public CodeFunction func(Environment env) {
|
||||||
|
return new CodeFunction(env, "", body());
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompileTarget(Map<Long, FunctionBody> functions, TreeSet<Location> breakpoints) {
|
||||||
|
this.functions = functions;
|
||||||
|
this.breakpoints = breakpoints;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.values.FunctionStatement;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
|
public class CompoundStatement extends Statement {
|
||||||
|
public final Statement[] statements;
|
||||||
|
public final boolean separateFuncs;
|
||||||
|
public Location end;
|
||||||
|
|
||||||
|
@Override public boolean pure() {
|
||||||
|
for (var stm : statements) {
|
||||||
|
if (!stm.pure()) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void declare(ScopeRecord varsScope) {
|
||||||
|
for (var stm : statements) stm.declare(varsScope);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType type) {
|
||||||
|
List<Statement> statements = new Vector<Statement>();
|
||||||
|
if (separateFuncs) for (var stm : this.statements) {
|
||||||
|
if (stm instanceof FunctionStatement && ((FunctionStatement)stm).statement) {
|
||||||
|
stm.compile(target, scope, false);
|
||||||
|
}
|
||||||
|
else statements.add(stm);
|
||||||
|
}
|
||||||
|
else statements = List.of(this.statements);
|
||||||
|
|
||||||
|
var polluted = false;
|
||||||
|
|
||||||
|
for (var i = 0; i < statements.size(); i++) {
|
||||||
|
var stm = statements.get(i);
|
||||||
|
|
||||||
|
if (i != statements.size() - 1) stm.compile(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
|
else stm.compile(target, scope, polluted = pollute, BreakpointType.STEP_OVER);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!polluted && pollute) {
|
||||||
|
target.add(Instruction.loadValue(loc(), null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompoundStatement setEnd(Location loc) {
|
||||||
|
this.end = loc;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompoundStatement(Location loc, boolean separateFuncs, Statement ...statements) {
|
||||||
|
super(loc);
|
||||||
|
this.separateFuncs = separateFuncs;
|
||||||
|
this.statements = statements;
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/me/topchetoeu/jscript/core/compilation/FunctionBody.java
Normal file
29
src/me/topchetoeu/jscript/core/compilation/FunctionBody.java
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation;
|
||||||
|
|
||||||
|
public class FunctionBody {
|
||||||
|
public final Instruction[] instructions;
|
||||||
|
public final String[] captureNames, localNames;
|
||||||
|
public final int localsN, argsN;
|
||||||
|
|
||||||
|
public FunctionBody(int localsN, int argsN, Instruction[] instructions, String[] captureNames, String[] localNames) {
|
||||||
|
this.argsN = argsN;
|
||||||
|
this.localsN = localsN;
|
||||||
|
this.instructions = instructions;
|
||||||
|
this.captureNames = captureNames;
|
||||||
|
this.localNames = localNames;
|
||||||
|
}
|
||||||
|
public FunctionBody(int localsN, int argsN, Instruction[] instructions) {
|
||||||
|
this.argsN = argsN;
|
||||||
|
this.localsN = localsN;
|
||||||
|
this.instructions = instructions;
|
||||||
|
this.captureNames = new String[0];
|
||||||
|
this.localNames = new String[0];
|
||||||
|
}
|
||||||
|
public FunctionBody(Instruction... instructions) {
|
||||||
|
this.argsN = 0;
|
||||||
|
this.localsN = 2;
|
||||||
|
this.instructions = instructions;
|
||||||
|
this.captureNames = new String[0];
|
||||||
|
this.localNames = new String[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
256
src/me/topchetoeu/jscript/core/compilation/Instruction.java
Normal file
256
src/me/topchetoeu/jscript/core/compilation/Instruction.java
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.engine.Operation;
|
||||||
|
import me.topchetoeu.jscript.core.exceptions.SyntaxException;
|
||||||
|
|
||||||
|
public class Instruction {
|
||||||
|
public static enum Type {
|
||||||
|
RETURN,
|
||||||
|
THROW,
|
||||||
|
THROW_SYNTAX,
|
||||||
|
DELETE,
|
||||||
|
TRY_START,
|
||||||
|
TRY_END,
|
||||||
|
NOP,
|
||||||
|
|
||||||
|
CALL,
|
||||||
|
CALL_NEW,
|
||||||
|
JMP_IF,
|
||||||
|
JMP_IFN,
|
||||||
|
JMP,
|
||||||
|
|
||||||
|
LOAD_VALUE,
|
||||||
|
|
||||||
|
LOAD_VAR,
|
||||||
|
LOAD_MEMBER,
|
||||||
|
LOAD_VAL_MEMBER,
|
||||||
|
LOAD_GLOB,
|
||||||
|
|
||||||
|
LOAD_FUNC,
|
||||||
|
LOAD_ARR,
|
||||||
|
LOAD_OBJ,
|
||||||
|
STORE_SELF_FUNC,
|
||||||
|
LOAD_REGEX,
|
||||||
|
|
||||||
|
DUP,
|
||||||
|
|
||||||
|
STORE_VAR,
|
||||||
|
STORE_MEMBER,
|
||||||
|
DISCARD,
|
||||||
|
|
||||||
|
MAKE_VAR,
|
||||||
|
DEF_PROP,
|
||||||
|
KEYS,
|
||||||
|
|
||||||
|
TYPEOF,
|
||||||
|
OPERATION;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
public Location location;
|
||||||
|
public BreakpointType breakpoint = BreakpointType.NONE;
|
||||||
|
|
||||||
|
public Instruction setDbgData(Instruction other) {
|
||||||
|
this.location = other.location;
|
||||||
|
this.breakpoint = other.breakpoint;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Instruction locate(Location loc) {
|
||||||
|
this.location = loc;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Instruction(Location location, Type type, Object ...params) {
|
||||||
|
this.location = location;
|
||||||
|
this.type = type;
|
||||||
|
this.params = params;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Instruction tryStart(Location loc, int catchStart, int finallyStart, int end) {
|
||||||
|
return new Instruction(loc, Type.TRY_START, catchStart, finallyStart, end);
|
||||||
|
}
|
||||||
|
public static Instruction tryEnd(Location loc) {
|
||||||
|
return new Instruction(loc, Type.TRY_END);
|
||||||
|
}
|
||||||
|
public static Instruction throwInstr(Location loc) {
|
||||||
|
return new Instruction(loc, Type.THROW);
|
||||||
|
}
|
||||||
|
public static Instruction throwSyntax(Location loc, SyntaxException err) {
|
||||||
|
return new Instruction(loc, Type.THROW_SYNTAX, err.getMessage());
|
||||||
|
}
|
||||||
|
public static Instruction throwSyntax(Location loc, String err) {
|
||||||
|
return new Instruction(loc, Type.THROW_SYNTAX, err);
|
||||||
|
}
|
||||||
|
public static Instruction delete(Location loc) {
|
||||||
|
return new Instruction(loc, Type.DELETE);
|
||||||
|
}
|
||||||
|
public static Instruction ret(Location loc) {
|
||||||
|
return new Instruction(loc, Type.RETURN);
|
||||||
|
}
|
||||||
|
public static Instruction debug(Location loc) {
|
||||||
|
return new Instruction(loc, Type.NOP, "debug");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Instruction nop(Location loc, Object ...params) {
|
||||||
|
for (var param : params) {
|
||||||
|
if (param instanceof String) continue;
|
||||||
|
if (param instanceof Boolean) continue;
|
||||||
|
if (param instanceof Double) continue;
|
||||||
|
if (param instanceof Integer) continue;
|
||||||
|
if (param == null) continue;
|
||||||
|
|
||||||
|
throw new RuntimeException("NOP params may contain only strings, booleans, doubles, integers and nulls.");
|
||||||
|
}
|
||||||
|
return new Instruction(loc, Type.NOP, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Instruction call(Location loc, int argn) {
|
||||||
|
return new Instruction(loc, Type.CALL, argn);
|
||||||
|
}
|
||||||
|
public static Instruction callNew(Location loc, int argn) {
|
||||||
|
return new Instruction(loc, Type.CALL_NEW, argn);
|
||||||
|
}
|
||||||
|
public static Instruction jmp(Location loc, int offset) {
|
||||||
|
return new Instruction(loc, Type.JMP, offset);
|
||||||
|
}
|
||||||
|
public static Instruction jmpIf(Location loc, int offset) {
|
||||||
|
return new Instruction(loc, Type.JMP_IF, offset);
|
||||||
|
}
|
||||||
|
public static Instruction jmpIfNot(Location loc, int offset) {
|
||||||
|
return new Instruction(loc, Type.JMP_IFN, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Instruction loadValue(Location loc, Object val) {
|
||||||
|
return new Instruction(loc, Type.LOAD_VALUE, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Instruction makeVar(Location loc, String name) {
|
||||||
|
return new Instruction(loc, Type.MAKE_VAR, name);
|
||||||
|
}
|
||||||
|
public static Instruction loadVar(Location loc, Object i) {
|
||||||
|
return new Instruction(loc, Type.LOAD_VAR, i);
|
||||||
|
}
|
||||||
|
public static Instruction loadGlob(Location loc) {
|
||||||
|
return new Instruction(loc, Type.LOAD_GLOB);
|
||||||
|
}
|
||||||
|
public static Instruction loadMember(Location loc) {
|
||||||
|
return new Instruction(loc, Type.LOAD_MEMBER);
|
||||||
|
}
|
||||||
|
public static Instruction loadMember(Location loc, Object key) {
|
||||||
|
if (key instanceof Number) key = ((Number)key).doubleValue();
|
||||||
|
return new Instruction(loc, Type.LOAD_VAL_MEMBER, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Instruction loadRegex(Location loc, String pattern, String flags) {
|
||||||
|
return new Instruction(loc, Type.LOAD_REGEX, pattern, flags);
|
||||||
|
}
|
||||||
|
public static Instruction loadFunc(Location loc, long 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(loc, Type.LOAD_FUNC, args);
|
||||||
|
}
|
||||||
|
public static Instruction loadObj(Location loc) {
|
||||||
|
return new Instruction(loc, Type.LOAD_OBJ);
|
||||||
|
}
|
||||||
|
public static Instruction loadArr(Location loc, int count) {
|
||||||
|
return new Instruction(loc, Type.LOAD_ARR, count);
|
||||||
|
}
|
||||||
|
public static Instruction dup(Location loc) {
|
||||||
|
return new Instruction(loc, Type.DUP, 1);
|
||||||
|
}
|
||||||
|
public static Instruction dup(Location loc, int count) {
|
||||||
|
return new Instruction(loc, Type.DUP, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Instruction storeSelfFunc(Location loc, int i) {
|
||||||
|
return new Instruction(loc, Type.STORE_SELF_FUNC, i);
|
||||||
|
}
|
||||||
|
public static Instruction storeVar(Location loc, Object i) {
|
||||||
|
return new Instruction(loc, Type.STORE_VAR, i, false);
|
||||||
|
}
|
||||||
|
public static Instruction storeVar(Location loc, Object i, boolean keep) {
|
||||||
|
return new Instruction(loc, Type.STORE_VAR, i, keep);
|
||||||
|
}
|
||||||
|
public static Instruction storeMember(Location loc) {
|
||||||
|
return new Instruction(loc, Type.STORE_MEMBER, false);
|
||||||
|
}
|
||||||
|
public static Instruction storeMember(Location loc, boolean keep) {
|
||||||
|
return new Instruction(loc, Type.STORE_MEMBER, keep);
|
||||||
|
}
|
||||||
|
public static Instruction discard(Location loc) {
|
||||||
|
return new Instruction(loc, Type.DISCARD);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Instruction typeof(Location loc) {
|
||||||
|
return new Instruction(loc, Type.TYPEOF);
|
||||||
|
}
|
||||||
|
public static Instruction typeof(Location loc, Object varName) {
|
||||||
|
return new Instruction(loc, Type.TYPEOF, varName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Instruction keys(Location loc, boolean forInFormat) {
|
||||||
|
return new Instruction(loc, Type.KEYS, forInFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Instruction defProp(Location loc) {
|
||||||
|
return new Instruction(loc, Type.DEF_PROP);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Instruction operation(Location loc, Operation op) {
|
||||||
|
return new Instruction(loc, Type.OPERATION, op);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
var res = type.toString();
|
||||||
|
|
||||||
|
for (int i = 0; i < params.length; i++) {
|
||||||
|
res += " " + params[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
32
src/me/topchetoeu/jscript/core/compilation/Statement.java
Normal file
32
src/me/topchetoeu/jscript/core/compilation/Statement.java
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
|
public abstract class Statement {
|
||||||
|
private Location _loc;
|
||||||
|
|
||||||
|
public boolean pure() { return false; }
|
||||||
|
public void declare(ScopeRecord varsScope) { }
|
||||||
|
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType type) {
|
||||||
|
int start = target.size();
|
||||||
|
compile(target, scope, pollute);
|
||||||
|
|
||||||
|
if (target.size() != start) {
|
||||||
|
target.get(start).locate(loc());
|
||||||
|
target.setDebug(start, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
compile(target, scope, pollute, BreakpointType.NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Location loc() { return _loc; }
|
||||||
|
public void setLoc(Location loc) { _loc = loc; }
|
||||||
|
|
||||||
|
protected Statement(Location loc) {
|
||||||
|
this._loc = loc;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
import me.topchetoeu.jscript.core.exceptions.SyntaxException;
|
||||||
|
|
||||||
|
public class ThrowSyntaxStatement extends Statement {
|
||||||
|
public final String name;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
target.add(Instruction.throwSyntax(loc(), name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ThrowSyntaxStatement(SyntaxException e) {
|
||||||
|
super(e.loc);
|
||||||
|
this.name = e.msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
package me.topchetoeu.jscript.core.compilation;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.common.Location;
|
||||||
import me.topchetoeu.jscript.compilation.values.FunctionStatement;
|
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.core.compilation.values.FunctionStatement;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class VariableDeclareStatement extends Statement {
|
public class VariableDeclareStatement extends Statement {
|
||||||
public static class Pair {
|
public static class Pair {
|
||||||
@@ -32,23 +33,16 @@ public class VariableDeclareStatement extends Statement {
|
|||||||
for (var entry : values) {
|
for (var entry : values) {
|
||||||
if (entry.name == null) continue;
|
if (entry.name == null) continue;
|
||||||
var key = scope.getKey(entry.name);
|
var key = scope.getKey(entry.name);
|
||||||
int start = target.size();
|
|
||||||
|
|
||||||
if (key instanceof String) target.add(Instruction.makeVar((String)key).locate(entry.location));
|
if (key instanceof String) target.add(Instruction.makeVar(entry.location, (String)key));
|
||||||
|
|
||||||
if (entry.value instanceof FunctionStatement) {
|
if (entry.value != null) {
|
||||||
((FunctionStatement)entry.value).compile(target, scope, entry.name, false);
|
FunctionStatement.compileWithName(entry.value, target, scope, true, entry.name, BreakpointType.STEP_OVER);
|
||||||
target.add(Instruction.storeVar(key).locate(entry.location));
|
target.add(Instruction.storeVar(entry.location, key));
|
||||||
}
|
}
|
||||||
else if (entry.value != null) {
|
|
||||||
entry.value.compile(target, scope, true);
|
|
||||||
target.add(Instruction.storeVar(key).locate(entry.location));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target.size() != start) target.setDebug(start);
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
}
|
|
||||||
|
|
||||||
if (pollute) target.add(Instruction.loadValue(null).locate(loc()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public VariableDeclareStatement(Location loc, List<Pair> values) {
|
public VariableDeclareStatement(Location loc, List<Pair> values) {
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation.control;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
|
public class BreakStatement extends Statement {
|
||||||
|
public final String label;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
target.add(Instruction.nop(loc(), "break", label));
|
||||||
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public BreakStatement(Location loc, String label) {
|
||||||
|
super(loc);
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation.control;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
|
public class ContinueStatement extends Statement {
|
||||||
|
public final String label;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
target.add(Instruction.nop(loc(), "cont", label));
|
||||||
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContinueStatement(Location loc, String label) {
|
||||||
|
super(loc);
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation.control;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
|
public class DebugStatement extends Statement {
|
||||||
|
@Override
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
target.add(Instruction.debug(loc()));
|
||||||
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DebugStatement(Location loc) {
|
||||||
|
super(loc);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.core.compilation.control;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.common.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class DeleteStatement extends Statement {
|
public class DeleteStatement extends Statement {
|
||||||
public final Statement key;
|
public final Statement key;
|
||||||
@@ -15,8 +15,8 @@ public class DeleteStatement extends Statement {
|
|||||||
value.compile(target, scope, true);
|
value.compile(target, scope, true);
|
||||||
key.compile(target, scope, true);
|
key.compile(target, scope, true);
|
||||||
|
|
||||||
target.add(Instruction.delete().locate(loc()));
|
target.add(Instruction.delete(loc()));
|
||||||
if (!pollute) target.add(Instruction.discard().locate(loc()));
|
if (pollute) target.add(Instruction.loadValue(loc(), true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public DeleteStatement(Location loc, Statement key, Statement value) {
|
public DeleteStatement(Location loc, Statement key, Statement value) {
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation.control;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
|
public class DoWhileStatement extends Statement {
|
||||||
|
public final Statement condition, body;
|
||||||
|
public final String label;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void declare(ScopeRecord globScope) {
|
||||||
|
body.declare(globScope);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
int start = target.size();
|
||||||
|
body.compile(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
|
int mid = target.size();
|
||||||
|
condition.compile(target, scope, true, BreakpointType.STEP_OVER);
|
||||||
|
int end = target.size();
|
||||||
|
|
||||||
|
WhileStatement.replaceBreaks(target, label, start, mid - 1, mid, end + 1);
|
||||||
|
target.add(Instruction.jmpIf(loc(), start - end));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DoWhileStatement(Location loc, String label, Statement condition, Statement body) {
|
||||||
|
super(loc);
|
||||||
|
this.label = label;
|
||||||
|
this.condition = condition;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.core.compilation.control;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.common.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.core.engine.Operation;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class ForInStatement extends Statement {
|
public class ForInStatement extends Statement {
|
||||||
public final String varName;
|
public final String varName;
|
||||||
@@ -25,39 +26,38 @@ public class ForInStatement extends Statement {
|
|||||||
var key = scope.getKey(varName);
|
var key = scope.getKey(varName);
|
||||||
|
|
||||||
int first = target.size();
|
int first = target.size();
|
||||||
if (key instanceof String) target.add(Instruction.makeVar((String)key));
|
if (key instanceof String) target.add(Instruction.makeVar(loc(), (String)key));
|
||||||
|
|
||||||
if (varValue != null) {
|
if (varValue != null) {
|
||||||
varValue.compile(target, scope, true);
|
varValue.compile(target, scope, true);
|
||||||
target.add(Instruction.storeVar(scope.getKey(varName)));
|
target.add(Instruction.storeVar(loc(), scope.getKey(varName)));
|
||||||
}
|
}
|
||||||
|
|
||||||
object.compileWithDebug(target, scope, true);
|
object.compile(target, scope, true, BreakpointType.STEP_OVER);
|
||||||
target.add(Instruction.keys(true));
|
target.add(Instruction.keys(loc(), true));
|
||||||
|
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
target.add(Instruction.dup());
|
target.add(Instruction.dup(loc()));
|
||||||
target.add(Instruction.loadValue(null));
|
target.add(Instruction.loadValue(loc(), null));
|
||||||
target.add(Instruction.operation(Operation.EQUALS));
|
target.add(Instruction.operation(loc(), Operation.EQUALS));
|
||||||
int mid = target.size();
|
int mid = target.size();
|
||||||
target.add(Instruction.nop());
|
target.add(Instruction.nop(loc()));
|
||||||
|
|
||||||
target.add(Instruction.loadMember("value").locate(varLocation));
|
target.add(Instruction.loadMember(varLocation, "value"));
|
||||||
target.setDebug();
|
target.add(Instruction.storeVar(object.loc(), key));
|
||||||
target.add(Instruction.storeVar(key));
|
target.setDebug(BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
body.compileWithDebug(target, scope, false);
|
body.compile(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
int end = target.size();
|
int end = target.size();
|
||||||
|
|
||||||
WhileStatement.replaceBreaks(target, label, mid + 1, end, start, end + 1);
|
WhileStatement.replaceBreaks(target, label, mid + 1, end, start, end + 1);
|
||||||
|
|
||||||
target.add(Instruction.jmp(start - end));
|
target.add(Instruction.jmp(loc(), start - end));
|
||||||
target.add(Instruction.discard());
|
target.add(Instruction.discard(loc()));
|
||||||
target.set(mid, Instruction.jmpIf(end - mid + 1));
|
target.set(mid, Instruction.jmpIf(loc(), end - mid + 1));
|
||||||
if (pollute) target.add(Instruction.loadValue(null));
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
target.get(first).locate(loc());
|
target.get(first).locate(loc());
|
||||||
target.setDebug(first);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ForInStatement(Location loc, Location varLocation, String label, boolean isDecl, String varName, Statement varValue, Statement object, Statement body) {
|
public ForInStatement(Location loc, Location varLocation, String label, boolean isDecl, String varName, Statement varValue, Statement object, Statement body) {
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation.control;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
|
public class ForStatement extends Statement {
|
||||||
|
public final Statement declaration, assignment, condition, body;
|
||||||
|
public final String label;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void declare(ScopeRecord globScope) {
|
||||||
|
declaration.declare(globScope);
|
||||||
|
body.declare(globScope);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
declaration.compile(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
|
int start = target.size();
|
||||||
|
condition.compile(target, scope, true, BreakpointType.STEP_OVER);
|
||||||
|
int mid = target.size();
|
||||||
|
target.add(Instruction.nop(null));
|
||||||
|
body.compile(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
|
int beforeAssign = target.size();
|
||||||
|
assignment.compile(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
|
int end = target.size();
|
||||||
|
|
||||||
|
WhileStatement.replaceBreaks(target, label, mid + 1, end, beforeAssign, end + 1);
|
||||||
|
|
||||||
|
target.add(Instruction.jmp(loc(), start - end));
|
||||||
|
target.set(mid, Instruction.jmpIfNot(loc(), end - mid + 1));
|
||||||
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation.control;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
|
public class IfStatement extends Statement {
|
||||||
|
public final Statement condition, body, elseBody;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void declare(ScopeRecord globScope) {
|
||||||
|
body.declare(globScope);
|
||||||
|
if (elseBody != null) elseBody.declare(globScope);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType breakpoint) {
|
||||||
|
condition.compile(target, scope, true, breakpoint);
|
||||||
|
|
||||||
|
if (elseBody == null) {
|
||||||
|
int i = target.size();
|
||||||
|
target.add(Instruction.nop(null));
|
||||||
|
body.compile(target, scope, pollute, breakpoint);
|
||||||
|
int endI = target.size();
|
||||||
|
target.set(i, Instruction.jmpIfNot(loc(), endI - i));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int start = target.size();
|
||||||
|
target.add(Instruction.nop(null));
|
||||||
|
body.compile(target, scope, pollute, breakpoint);
|
||||||
|
target.add(Instruction.nop(null));
|
||||||
|
int mid = target.size();
|
||||||
|
elseBody.compile(target, scope, pollute, breakpoint);
|
||||||
|
int end = target.size();
|
||||||
|
|
||||||
|
target.set(start, Instruction.jmpIfNot(loc(), mid - start));
|
||||||
|
target.set(mid - 1, Instruction.jmp(loc(), end - mid + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
compile(target, scope, 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation.control;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
|
public class ReturnStatement extends Statement {
|
||||||
|
public final Statement value;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
if (value == null) target.add(Instruction.loadValue(loc(), null));
|
||||||
|
else value.compile(target, scope, true);
|
||||||
|
target.add(Instruction.ret(loc()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReturnStatement(Location loc, Statement value) {
|
||||||
|
super(loc);
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation.control;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction.Type;
|
||||||
|
import me.topchetoeu.jscript.core.engine.Operation;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
|
public class SwitchStatement extends Statement {
|
||||||
|
public static class SwitchCase {
|
||||||
|
public final Statement value;
|
||||||
|
public final int statementI;
|
||||||
|
|
||||||
|
public SwitchCase(Statement value, int statementI) {
|
||||||
|
this.value = value;
|
||||||
|
this.statementI = statementI;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final Statement value;
|
||||||
|
public final SwitchCase[] cases;
|
||||||
|
public final Statement[] body;
|
||||||
|
public final int defaultI;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void declare(ScopeRecord varsScope) {
|
||||||
|
for (var stm : body) stm.declare(varsScope);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
var caseToStatement = new HashMap<Integer, Integer>();
|
||||||
|
var statementToIndex = new HashMap<Integer, Integer>();
|
||||||
|
|
||||||
|
value.compile(target, scope, true, BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
|
for (var ccase : cases) {
|
||||||
|
target.add(Instruction.dup(loc()));
|
||||||
|
ccase.value.compile(target, scope, true);
|
||||||
|
target.add(Instruction.operation(loc(), Operation.EQUALS));
|
||||||
|
caseToStatement.put(target.size(), ccase.statementI);
|
||||||
|
target.add(Instruction.nop(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
int start = target.size();
|
||||||
|
|
||||||
|
target.add(Instruction.nop(null));
|
||||||
|
|
||||||
|
for (var stm : body) {
|
||||||
|
statementToIndex.put(statementToIndex.size(), target.size());
|
||||||
|
stm.compile(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
|
}
|
||||||
|
|
||||||
|
int end = target.size();
|
||||||
|
target.add(Instruction.discard(loc()));
|
||||||
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
|
|
||||||
|
if (defaultI < 0 || defaultI >= body.length) target.set(start, Instruction.jmp(loc(), end - start));
|
||||||
|
else target.set(start, Instruction.jmp(loc(), statementToIndex.get(defaultI) - start));
|
||||||
|
|
||||||
|
for (int i = start; i < end; i++) {
|
||||||
|
var instr = target.get(i);
|
||||||
|
if (instr.type == Type.NOP && instr.is(0, "break") && instr.get(1) == null) {
|
||||||
|
target.set(i, Instruction.jmp(loc(), end - i).locate(instr.location));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (var el : caseToStatement.entrySet()) {
|
||||||
|
var i = statementToIndex.get(el.getValue());
|
||||||
|
if (i == null) i = end;
|
||||||
|
target.set(el.getKey(), Instruction.jmpIf(loc(), i - el.getKey()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public SwitchStatement(Location loc, Statement value, int defaultI, SwitchCase[] cases, Statement[] body) {
|
||||||
|
super(loc);
|
||||||
|
this.value = value;
|
||||||
|
this.defaultI = defaultI;
|
||||||
|
this.cases = cases;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation.control;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
|
public class ThrowStatement extends Statement {
|
||||||
|
public final Statement value;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
value.compile(target, scope, true);
|
||||||
|
target.add(Instruction.throwInstr(loc()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ThrowStatement(Location loc, Statement value) {
|
||||||
|
super(loc);
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.core.compilation.control;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.common.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.GlobalScope;
|
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.engine.scope.LocalScopeRecord;
|
import me.topchetoeu.jscript.core.engine.scope.GlobalScope;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.core.engine.scope.LocalScopeRecord;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class TryStatement extends Statement {
|
public class TryStatement extends Statement {
|
||||||
public final Statement tryBody;
|
public final Statement tryBody;
|
||||||
@@ -22,31 +23,32 @@ public class TryStatement extends Statement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType bpt) {
|
||||||
target.add(Instruction.nop());
|
target.add(Instruction.nop(null));
|
||||||
|
|
||||||
int start = target.size(), tryN, catchN = -1, finN = -1;
|
int start = target.size(), catchStart = -1, finallyStart = -1;
|
||||||
|
|
||||||
tryBody.compile(target, scope, false);
|
tryBody.compile(target, scope, false);
|
||||||
tryN = target.size() - start;
|
target.add(Instruction.tryEnd(loc()));
|
||||||
|
|
||||||
if (catchBody != null) {
|
if (catchBody != null) {
|
||||||
int tmp = target.size();
|
catchStart = target.size() - start;
|
||||||
var local = scope instanceof GlobalScope ? scope.child() : (LocalScopeRecord)scope;
|
var local = scope instanceof GlobalScope ? scope.child() : (LocalScopeRecord)scope;
|
||||||
local.define(name, true);
|
local.define(name, true);
|
||||||
catchBody.compile(target, scope, false);
|
catchBody.compile(target, scope, false);
|
||||||
local.undefine();
|
local.undefine();
|
||||||
catchN = target.size() - tmp;
|
target.add(Instruction.tryEnd(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (finallyBody != null) {
|
if (finallyBody != null) {
|
||||||
int tmp = target.size();
|
finallyStart = target.size() - start;
|
||||||
finallyBody.compile(target, scope, false);
|
finallyBody.compile(target, scope, false);
|
||||||
finN = target.size() - tmp;
|
target.add(Instruction.tryEnd(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
target.set(start - 1, Instruction.tryInstr(tryN, catchN, finN).locate(loc()));
|
target.set(start - 1, Instruction.tryStart(loc(), catchStart, finallyStart, target.size() - start));
|
||||||
if (pollute) target.add(Instruction.loadValue(null).locate(loc()));
|
target.setDebug(start - 1, BreakpointType.STEP_OVER);
|
||||||
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public TryStatement(Location loc, Statement tryBody, Statement catchBody, Statement finallyBody, String name) {
|
public TryStatement(Location loc, Statement tryBody, Statement catchBody, Statement finallyBody, String name) {
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation.control;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction.Type;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
|
public class WhileStatement extends Statement {
|
||||||
|
public final Statement condition, body;
|
||||||
|
public final String label;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void declare(ScopeRecord globScope) {
|
||||||
|
body.declare(globScope);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
int start = target.size();
|
||||||
|
condition.compile(target, scope, true);
|
||||||
|
int mid = target.size();
|
||||||
|
target.add(Instruction.nop(null));
|
||||||
|
body.compile(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
|
int end = target.size();
|
||||||
|
|
||||||
|
replaceBreaks(target, label, mid + 1, end, start, end + 1);
|
||||||
|
|
||||||
|
target.add(Instruction.jmp(loc(), start - end));
|
||||||
|
target.set(mid, Instruction.jmpIfNot(loc(), end - mid + 1));
|
||||||
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
|
}
|
||||||
|
|
||||||
|
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(CompileTarget 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(instr.location, continuePoint - i).setDbgData(target.get(i)));
|
||||||
|
}
|
||||||
|
if (instr.type == Type.NOP && instr.is(0, "break") && (instr.get(1) == null || instr.is(1, label))) {
|
||||||
|
target.set(i, Instruction.jmp(instr.location, breakPoint - i).setDbgData(target.get(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation.values;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
|
public class ArrayStatement extends Statement {
|
||||||
|
public final Statement[] statements;
|
||||||
|
|
||||||
|
@Override public boolean pure() {
|
||||||
|
for (var stm : statements) {
|
||||||
|
if (!stm.pure()) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
target.add(Instruction.loadArr(loc(), statements.length));
|
||||||
|
|
||||||
|
for (var i = 0; i < statements.length; i++) {
|
||||||
|
var el = statements[i];
|
||||||
|
if (el != null) {
|
||||||
|
target.add(Instruction.dup(loc()));
|
||||||
|
target.add(Instruction.loadValue(loc(), i));
|
||||||
|
el.compile(target, scope, true);
|
||||||
|
target.add(Instruction.storeMember(loc()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pollute) target.add(Instruction.discard(loc()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayStatement(Location loc, Statement[] statements) {
|
||||||
|
super(loc);
|
||||||
|
this.statements = statements;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation.values;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
|
public class CallStatement extends Statement {
|
||||||
|
public final Statement func;
|
||||||
|
public final Statement[] args;
|
||||||
|
public final boolean isNew;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType type) {
|
||||||
|
if (isNew) func.compile(target, scope, true);
|
||||||
|
else if (func instanceof IndexStatement) {
|
||||||
|
((IndexStatement)func).compile(target, scope, true, true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
target.add(Instruction.loadValue(loc(), null));
|
||||||
|
func.compile(target, scope, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var arg : args) arg.compile(target, scope, true);
|
||||||
|
|
||||||
|
if (isNew) target.add(Instruction.callNew(loc(), args.length));
|
||||||
|
else target.add(Instruction.call(loc(), args.length));
|
||||||
|
target.setDebug(type);
|
||||||
|
|
||||||
|
if (!pollute) target.add(Instruction.discard(loc()));
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
compile(target, scope, pollute, BreakpointType.STEP_IN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CallStatement(Location loc, boolean isNew, Statement func, Statement ...args) {
|
||||||
|
super(loc);
|
||||||
|
this.isNew = isNew;
|
||||||
|
this.func = func;
|
||||||
|
this.args = args;
|
||||||
|
}
|
||||||
|
public CallStatement(Location loc, boolean isNew, Statement obj, Object key, Statement ...args) {
|
||||||
|
super(loc);
|
||||||
|
this.isNew = isNew;
|
||||||
|
this.func = new IndexStatement(loc, obj, new ConstantStatement(loc, key));
|
||||||
|
this.args = args;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation.values;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.AssignableStatement;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.core.engine.Operation;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
|
public class ChangeStatement extends Statement {
|
||||||
|
public final AssignableStatement value;
|
||||||
|
public final double addAmount;
|
||||||
|
public final boolean postfix;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
value.toAssign(new ConstantStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, scope, true);
|
||||||
|
if (!pollute) target.add(Instruction.discard(loc()));
|
||||||
|
else if (postfix) {
|
||||||
|
target.add(Instruction.loadValue(loc(), addAmount));
|
||||||
|
target.add(Instruction.operation(loc(), Operation.SUBTRACT));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChangeStatement(Location loc, AssignableStatement value, double addAmount, boolean postfix) {
|
||||||
|
super(loc);
|
||||||
|
this.value = value;
|
||||||
|
this.addAmount = addAmount;
|
||||||
|
this.postfix = postfix;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation.values;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
|
public class ConstantStatement extends Statement {
|
||||||
|
public final Object value;
|
||||||
|
|
||||||
|
@Override public boolean pure() { return true; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
if (pollute) target.add(Instruction.loadValue(loc(), value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConstantStatement(Location loc, Object val) {
|
||||||
|
super(loc);
|
||||||
|
this.value = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation.values;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
|
public class DiscardStatement extends Statement {
|
||||||
|
public final Statement value;
|
||||||
|
|
||||||
|
@Override public boolean pure() { return value.pure(); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
value.compile(target, scope, false);
|
||||||
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DiscardStatement(Location loc, Statement val) {
|
||||||
|
super(loc);
|
||||||
|
this.value = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation.values;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.CompoundStatement;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.FunctionBody;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction.Type;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
import me.topchetoeu.jscript.core.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;
|
||||||
|
|
||||||
|
private static Random rand = new Random();
|
||||||
|
|
||||||
|
@Override public boolean pure() { return varName == null && statement; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void declare(ScopeRecord scope) {
|
||||||
|
if (varName != null && statement) scope.define(varName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void checkBreakAndCont(CompileTarget 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.get(i).location, "Break was placed outside a loop.");
|
||||||
|
}
|
||||||
|
if (target.get(i).is(0, "cont")) {
|
||||||
|
throw new SyntaxException(target.get(i).location, "Continue was placed outside a loop.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private long compileBody(CompileTarget target, ScopeRecord scope, boolean polute, 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 id = rand.nextLong();
|
||||||
|
var subscope = scope.child();
|
||||||
|
var subtarget = new CompileTarget(target.functions, target.breakpoints);
|
||||||
|
|
||||||
|
subscope.define("this");
|
||||||
|
var argsVar = subscope.define("arguments");
|
||||||
|
|
||||||
|
if (args.length > 0) {
|
||||||
|
for (var i = 0; i < args.length; i++) {
|
||||||
|
subtarget.add(Instruction.loadVar(loc(), argsVar));
|
||||||
|
subtarget.add(Instruction.loadMember(loc(), i));
|
||||||
|
subtarget.add(Instruction.storeVar(loc(), subscope.define(args[i])));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!statement && this.varName != null) {
|
||||||
|
subtarget.add(Instruction.storeSelfFunc(loc(), (int)subscope.define(this.varName)));
|
||||||
|
subtarget.setDebug(bp);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.declare(subscope);
|
||||||
|
body.compile(subtarget, subscope, false);
|
||||||
|
subtarget.add(Instruction.ret(end));
|
||||||
|
checkBreakAndCont(subtarget, 0);
|
||||||
|
|
||||||
|
if (polute) target.add(Instruction.loadFunc(loc(), id, subscope.getCaptures()));
|
||||||
|
target.functions.put(id, new FunctionBody(
|
||||||
|
subscope.localsCount(), args.length,
|
||||||
|
subtarget.array(), subscope.captures(), subscope.locals()
|
||||||
|
));
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, String name, BreakpointType bp) {
|
||||||
|
if (this.varName != null) name = this.varName;
|
||||||
|
|
||||||
|
var hasVar = this.varName != null && statement;
|
||||||
|
var hasName = name != null;
|
||||||
|
|
||||||
|
compileBody(target, scope, pollute || hasVar || hasName, bp);
|
||||||
|
|
||||||
|
if (hasName) {
|
||||||
|
if (pollute || hasVar) target.add(Instruction.dup(loc()));
|
||||||
|
target.add(Instruction.loadValue(loc(), "name"));
|
||||||
|
target.add(Instruction.loadValue(loc(), name));
|
||||||
|
target.add(Instruction.storeMember(loc()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasVar) {
|
||||||
|
var key = scope.getKey(this.varName);
|
||||||
|
|
||||||
|
if (key instanceof String) target.add(Instruction.makeVar(loc(), (String)key));
|
||||||
|
target.add(Instruction.storeVar(loc(), scope.getKey(this.varName), false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, String name) {
|
||||||
|
compile(target, scope, pollute, name, BreakpointType.NONE);
|
||||||
|
}
|
||||||
|
@Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType bp) {
|
||||||
|
compile(target, scope, pollute, (String)null, bp);
|
||||||
|
}
|
||||||
|
@Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
compile(target, scope, pollute, (String)null, BreakpointType.NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FunctionStatement(Location loc, Location end, String varName, String[] args, boolean statement, CompoundStatement body) {
|
||||||
|
super(loc);
|
||||||
|
|
||||||
|
this.end = end;
|
||||||
|
this.varName = varName;
|
||||||
|
this.statement = statement;
|
||||||
|
|
||||||
|
this.args = args;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void compileWithName(Statement stm, CompileTarget target, ScopeRecord scope, boolean pollute, String name) {
|
||||||
|
if (stm instanceof FunctionStatement) ((FunctionStatement)stm).compile(target, scope, pollute, name);
|
||||||
|
else stm.compile(target, scope, pollute);
|
||||||
|
}
|
||||||
|
public static void compileWithName(Statement stm, CompileTarget target, ScopeRecord scope, boolean pollute, String name, BreakpointType bp) {
|
||||||
|
if (stm instanceof FunctionStatement) ((FunctionStatement)stm).compile(target, scope, pollute, name, bp);
|
||||||
|
else stm.compile(target, scope, pollute, bp);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation.values;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
|
public class GlobalThisStatement extends Statement {
|
||||||
|
@Override public boolean pure() { return true; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
if (pollute) target.add(Instruction.loadGlob(loc()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public GlobalThisStatement(Location loc) {
|
||||||
|
super(loc);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.core.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.common.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.core.engine.Operation;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class IndexAssignStatement extends Statement {
|
public class IndexAssignStatement extends Statement {
|
||||||
public final Statement object;
|
public final Statement object;
|
||||||
@@ -18,22 +19,22 @@ public class IndexAssignStatement extends Statement {
|
|||||||
if (operation != null) {
|
if (operation != null) {
|
||||||
object.compile(target, scope, true);
|
object.compile(target, scope, true);
|
||||||
index.compile(target, scope, true);
|
index.compile(target, scope, true);
|
||||||
target.add(Instruction.dup(2, 0).locate(loc()));
|
target.add(Instruction.dup(loc(), 2));
|
||||||
|
|
||||||
target.add(Instruction.loadMember().locate(loc()));
|
target.add(Instruction.loadMember(loc()));
|
||||||
value.compile(target, scope, true);
|
value.compile(target, scope, true);
|
||||||
target.add(Instruction.operation(operation).locate(loc()));
|
target.add(Instruction.operation(loc(), operation));
|
||||||
|
|
||||||
target.add(Instruction.storeMember(pollute).locate(loc()));
|
target.add(Instruction.storeMember(loc(), pollute));
|
||||||
target.setDebug();
|
target.setDebug(BreakpointType.STEP_IN);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
object.compile(target, scope, true);
|
object.compile(target, scope, true);
|
||||||
index.compile(target, scope, true);
|
index.compile(target, scope, true);
|
||||||
value.compile(target, scope, true);
|
value.compile(target, scope, true);
|
||||||
|
|
||||||
target.add(Instruction.storeMember(pollute).locate(loc()));
|
target.add(Instruction.storeMember(loc(), pollute));
|
||||||
target.setDebug();
|
target.setDebug(BreakpointType.STEP_IN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,37 +1,35 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.core.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.common.Location;
|
||||||
import me.topchetoeu.jscript.compilation.AssignableStatement;
|
import me.topchetoeu.jscript.core.compilation.AssignableStatement;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.core.engine.Operation;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class IndexStatement extends AssignableStatement {
|
public class IndexStatement extends AssignableStatement {
|
||||||
public final Statement object;
|
public final Statement object;
|
||||||
public final Statement index;
|
public final Statement index;
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean pure() { return true; }
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Statement toAssign(Statement val, Operation operation) {
|
public Statement toAssign(Statement val, Operation operation) {
|
||||||
return new IndexAssignStatement(loc(), object, index, val, operation);
|
return new IndexAssignStatement(loc(), object, index, val, operation);
|
||||||
}
|
}
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean dupObj, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean dupObj, boolean pollute) {
|
||||||
object.compile(target, scope, true);
|
object.compile(target, scope, true);
|
||||||
if (dupObj) target.add(Instruction.dup().locate(loc()));
|
if (dupObj) target.add(Instruction.dup(loc()));
|
||||||
if (index instanceof ConstantStatement) {
|
if (index instanceof ConstantStatement) {
|
||||||
target.add(Instruction.loadMember(((ConstantStatement)index).value).locate(loc()));
|
target.add(Instruction.loadMember(loc(), ((ConstantStatement)index).value));
|
||||||
target.setDebug();
|
target.setDebug(BreakpointType.STEP_IN);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
index.compile(target, scope, true);
|
index.compile(target, scope, true);
|
||||||
target.add(Instruction.loadMember().locate(loc()));
|
target.add(Instruction.loadMember(loc()));
|
||||||
target.setDebug();
|
target.setDebug(BreakpointType.STEP_IN);
|
||||||
if (!pollute) target.add(Instruction.discard().locate(loc()));
|
if (!pollute) target.add(Instruction.discard(loc()));
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
@@ -1,19 +1,16 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.core.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.common.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.core.engine.values.Values;
|
||||||
|
|
||||||
public class LazyAndStatement extends Statement {
|
public class LazyAndStatement extends Statement {
|
||||||
public final Statement first, second;
|
public final Statement first, second;
|
||||||
|
|
||||||
@Override
|
@Override public boolean pure() { return first.pure() && second.pure(); }
|
||||||
public boolean pure() {
|
|
||||||
return first.pure() && second.pure();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
@@ -26,12 +23,12 @@ public class LazyAndStatement extends Statement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
first.compile(target, scope, true);
|
first.compile(target, scope, true);
|
||||||
if (pollute) target.add(Instruction.dup().locate(loc()));
|
if (pollute) target.add(Instruction.dup(loc()));
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
target.add(Instruction.nop());
|
target.add(Instruction.nop(null));
|
||||||
if (pollute) target.add(Instruction.discard().locate(loc()));
|
if (pollute) target.add(Instruction.discard(loc()));
|
||||||
second.compile(target, scope, pollute);
|
second.compile(target, scope, pollute);
|
||||||
target.set(start, Instruction.jmpIfNot(target.size() - start).locate(loc()));
|
target.set(start, Instruction.jmpIfNot(loc(), target.size() - start));
|
||||||
}
|
}
|
||||||
|
|
||||||
public LazyAndStatement(Location loc, Statement first, Statement second) {
|
public LazyAndStatement(Location loc, Statement first, Statement second) {
|
||||||
@@ -1,19 +1,16 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.core.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.common.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.core.engine.values.Values;
|
||||||
|
|
||||||
public class LazyOrStatement extends Statement {
|
public class LazyOrStatement extends Statement {
|
||||||
public final Statement first, second;
|
public final Statement first, second;
|
||||||
|
|
||||||
@Override
|
@Override public boolean pure() { return first.pure() && second.pure(); }
|
||||||
public boolean pure() {
|
|
||||||
return first.pure() && second.pure();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
@@ -26,12 +23,12 @@ public class LazyOrStatement extends Statement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
first.compile(target, scope, true);
|
first.compile(target, scope, true);
|
||||||
if (pollute) target.add(Instruction.dup().locate(loc()));
|
if (pollute) target.add(Instruction.dup(loc()));
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
target.add(Instruction.nop());
|
target.add(Instruction.nop(null));
|
||||||
if (pollute) target.add(Instruction.discard().locate(loc()));
|
if (pollute) target.add(Instruction.discard(loc()));
|
||||||
second.compile(target, scope, pollute);
|
second.compile(target, scope, pollute);
|
||||||
target.set(start, Instruction.jmpIf(target.size() - start).locate(loc()));
|
target.set(start, Instruction.jmpIf(loc(), target.size() - start));
|
||||||
}
|
}
|
||||||
|
|
||||||
public LazyOrStatement(Location loc, Statement first, Statement second) {
|
public LazyOrStatement(Location loc, Statement first, Statement second) {
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation.values;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
|
public class ObjectStatement extends Statement {
|
||||||
|
public final Map<Object, Statement> map;
|
||||||
|
public final Map<Object, FunctionStatement> getters;
|
||||||
|
public final Map<Object, FunctionStatement> setters;
|
||||||
|
|
||||||
|
@Override public boolean pure() {
|
||||||
|
for (var el : map.values()) {
|
||||||
|
if (!el.pure()) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
target.add(Instruction.loadObj(loc()));
|
||||||
|
|
||||||
|
for (var el : map.entrySet()) {
|
||||||
|
target.add(Instruction.dup(loc()));
|
||||||
|
target.add(Instruction.loadValue(loc(), el.getKey()));
|
||||||
|
var val = el.getValue();
|
||||||
|
FunctionStatement.compileWithName(val, target, scope, true, el.getKey().toString());
|
||||||
|
target.add(Instruction.storeMember(loc()));
|
||||||
|
}
|
||||||
|
|
||||||
|
var keys = new ArrayList<Object>();
|
||||||
|
keys.addAll(getters.keySet());
|
||||||
|
keys.addAll(setters.keySet());
|
||||||
|
|
||||||
|
for (var key : keys) {
|
||||||
|
if (key instanceof String) target.add(Instruction.loadValue(loc(), (String)key));
|
||||||
|
else target.add(Instruction.loadValue(loc(), (Double)key));
|
||||||
|
|
||||||
|
if (getters.containsKey(key)) getters.get(key).compile(target, scope, true);
|
||||||
|
else target.add(Instruction.loadValue(loc(), null));
|
||||||
|
|
||||||
|
if (setters.containsKey(key)) setters.get(key).compile(target, scope, true);
|
||||||
|
else target.add(Instruction.loadValue(loc(), null));
|
||||||
|
|
||||||
|
target.add(Instruction.defProp(loc()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pollute) target.add(Instruction.discard(loc()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectStatement(Location loc, Map<Object, Statement> map, Map<Object, FunctionStatement> getters, Map<Object, FunctionStatement> setters) {
|
||||||
|
super(loc);
|
||||||
|
this.map = map;
|
||||||
|
this.getters = getters;
|
||||||
|
this.setters = setters;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation.values;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.core.engine.Operation;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
|
public class OperationStatement extends Statement {
|
||||||
|
public final Statement[] args;
|
||||||
|
public final Operation operation;
|
||||||
|
|
||||||
|
@Override public boolean pure() {
|
||||||
|
for (var el : args) {
|
||||||
|
if (!el.pure()) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
for (var arg : args) {
|
||||||
|
arg.compile(target, scope, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pollute) target.add(Instruction.operation(loc(), operation));
|
||||||
|
else target.add(Instruction.discard(loc()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public OperationStatement(Location loc, Operation operation, Statement ...args) {
|
||||||
|
super(loc);
|
||||||
|
this.operation = operation;
|
||||||
|
this.args = args;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation.values;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
|
public class RegexStatement extends Statement {
|
||||||
|
public final String pattern, flags;
|
||||||
|
|
||||||
|
// Not really pure, since a function is called, but can be ignored.
|
||||||
|
@Override public boolean pure() { return true; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
target.add(Instruction.loadRegex(loc(), pattern, flags));
|
||||||
|
if (!pollute) target.add(Instruction.discard(loc()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public RegexStatement(Location loc, String pattern, String flags) {
|
||||||
|
super(loc);
|
||||||
|
this.pattern = pattern;
|
||||||
|
this.flags = flags;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation.values;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
|
public class TypeofStatement extends Statement {
|
||||||
|
public final Statement value;
|
||||||
|
|
||||||
|
// Not really pure, since a variable from the global scope could be accessed,
|
||||||
|
// which could lead to code execution, that would get omitted
|
||||||
|
@Override public boolean pure() { return true; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
if (value instanceof VariableStatement) {
|
||||||
|
var i = scope.getKey(((VariableStatement)value).name);
|
||||||
|
if (i instanceof String) {
|
||||||
|
target.add(Instruction.typeof(loc(), (String)i));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value.compile(target, scope, pollute);
|
||||||
|
target.add(Instruction.typeof(loc()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypeofStatement(Location loc, Statement value) {
|
||||||
|
super(loc);
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation.values;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.core.engine.Operation;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
|
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(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
var i = scope.getKey(name);
|
||||||
|
if (operation != null) {
|
||||||
|
target.add(Instruction.loadVar(loc(), i));
|
||||||
|
FunctionStatement.compileWithName(value, target, scope, true, name);
|
||||||
|
target.add(Instruction.operation(loc(), operation));
|
||||||
|
target.add(Instruction.storeVar(loc(), i, pollute));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
FunctionStatement.compileWithName(value, target, scope, true, name);
|
||||||
|
target.add(Instruction.storeVar(loc(), i, pollute));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public VariableAssignStatement(Location loc, String name, Statement val, Operation operation) {
|
||||||
|
super(loc);
|
||||||
|
this.name = name;
|
||||||
|
this.value = val;
|
||||||
|
this.operation = operation;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation.values;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
|
public class VariableIndexStatement extends Statement {
|
||||||
|
public final int index;
|
||||||
|
|
||||||
|
@Override public boolean pure() { return true; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
if (pollute) target.add(Instruction.loadVar(loc(), index));
|
||||||
|
}
|
||||||
|
|
||||||
|
public VariableIndexStatement(Location loc, int i) {
|
||||||
|
super(loc);
|
||||||
|
this.index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package me.topchetoeu.jscript.core.compilation.values;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.AssignableStatement;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.core.engine.Operation;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
|
public class VariableStatement extends AssignableStatement {
|
||||||
|
public final String name;
|
||||||
|
|
||||||
|
@Override public boolean pure() { return false; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Statement toAssign(Statement val, Operation operation) {
|
||||||
|
return new VariableAssignStatement(loc(), name, val, operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
var i = scope.getKey(name);
|
||||||
|
target.add(Instruction.loadVar(loc(), i));
|
||||||
|
if (!pollute) target.add(Instruction.discard(loc()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public VariableStatement(Location loc, String name) {
|
||||||
|
super(loc);
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
166
src/me/topchetoeu/jscript/core/engine/Context.java
Normal file
166
src/me/topchetoeu/jscript/core/engine/Context.java
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
package me.topchetoeu.jscript.core.engine;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Filename;
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.engine.debug.DebugContext;
|
||||||
|
import me.topchetoeu.jscript.core.engine.frame.CodeFrame;
|
||||||
|
import me.topchetoeu.jscript.core.engine.values.ArrayValue;
|
||||||
|
import me.topchetoeu.jscript.core.engine.values.FunctionValue;
|
||||||
|
import me.topchetoeu.jscript.core.engine.values.Symbol;
|
||||||
|
import me.topchetoeu.jscript.core.engine.values.Values;
|
||||||
|
import me.topchetoeu.jscript.core.exceptions.EngineException;
|
||||||
|
import me.topchetoeu.jscript.lib.EnvironmentLib;
|
||||||
|
import me.topchetoeu.jscript.utils.mapping.SourceMap;
|
||||||
|
|
||||||
|
public class Context implements Extensions {
|
||||||
|
public static final Context NULL = new Context(null);
|
||||||
|
|
||||||
|
public final Context parent;
|
||||||
|
public final Environment environment;
|
||||||
|
public final CodeFrame frame;
|
||||||
|
public final Engine engine;
|
||||||
|
public final int stackSize;
|
||||||
|
|
||||||
|
@Override public <T> void add(Symbol key, T obj) {
|
||||||
|
if (environment != null) environment.add(key, obj);
|
||||||
|
else if (engine != null) engine.add(key, obj);
|
||||||
|
}
|
||||||
|
@Override public <T> T get(Symbol key) {
|
||||||
|
if (environment != null && environment.has(key)) return environment.get(key);
|
||||||
|
else if (engine != null && engine.has(key)) return engine.get(key);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
@Override public boolean has(Symbol key) {
|
||||||
|
return
|
||||||
|
environment != null && environment.has(key) ||
|
||||||
|
engine != null && engine.has(key);
|
||||||
|
}
|
||||||
|
@Override public boolean remove(Symbol key) {
|
||||||
|
var res = false;
|
||||||
|
|
||||||
|
if (environment != null) res |= environment.remove(key);
|
||||||
|
else if (engine != null) res |= engine.remove(key);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
@Override public Iterable<Symbol> keys() {
|
||||||
|
if (engine == null && environment == null) return List.of();
|
||||||
|
if (engine == null) return environment.keys();
|
||||||
|
if (environment == null) return engine.keys();
|
||||||
|
|
||||||
|
return () -> Stream.concat(
|
||||||
|
StreamSupport.stream(engine.keys().spliterator(), false),
|
||||||
|
StreamSupport.stream(environment.keys().spliterator(), false)
|
||||||
|
).distinct().iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FunctionValue compile(Filename filename, String raw) {
|
||||||
|
var env = environment;
|
||||||
|
var result = Environment.compileFunc(this).call(this, null, raw, filename.toString(), new EnvironmentLib(env));
|
||||||
|
|
||||||
|
var function = (FunctionValue)Values.getMember(this, result, "function");
|
||||||
|
if (!DebugContext.enabled(this)) return function;
|
||||||
|
|
||||||
|
var rawMapChain = ((ArrayValue)Values.getMember(this, result, "mapChain")).toArray();
|
||||||
|
var breakpoints = new TreeSet<>(
|
||||||
|
Arrays.stream(((ArrayValue)Values.getMember(this, result, "breakpoints")).toArray())
|
||||||
|
.map(v -> Location.parse(Values.toString(this, v)))
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
var maps = new SourceMap[rawMapChain.length];
|
||||||
|
|
||||||
|
for (var i = 0; i < maps.length; i++) maps[i] = SourceMap.parse(Values.toString(this, (String)rawMapChain[i]));
|
||||||
|
|
||||||
|
var map = SourceMap.chain(maps);
|
||||||
|
|
||||||
|
if (map != null) {
|
||||||
|
var newBreakpoints = new TreeSet<Location>();
|
||||||
|
for (var bp : breakpoints) {
|
||||||
|
bp = map.toCompiled(bp);
|
||||||
|
if (bp != null) newBreakpoints.add(bp);
|
||||||
|
}
|
||||||
|
breakpoints = newBreakpoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugContext.get(this).onSource(filename, raw, breakpoints, map);
|
||||||
|
|
||||||
|
return function;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Context pushFrame(CodeFrame frame) {
|
||||||
|
var res = new Context(this, frame.function.environment, frame, engine, stackSize + 1);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterable<CodeFrame> frames() {
|
||||||
|
var self = this;
|
||||||
|
return () -> new Iterator<CodeFrame>() {
|
||||||
|
private Context curr = self;
|
||||||
|
|
||||||
|
private void update() {
|
||||||
|
while (curr != null && curr.frame == null) curr = curr.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public boolean hasNext() {
|
||||||
|
update();
|
||||||
|
return curr != null;
|
||||||
|
}
|
||||||
|
@Override public CodeFrame next() {
|
||||||
|
update();
|
||||||
|
var res = curr.frame;
|
||||||
|
curr = curr.parent;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
public List<String> stackTrace() {
|
||||||
|
var res = new ArrayList<String>();
|
||||||
|
|
||||||
|
for (var el : frames()) {
|
||||||
|
var name = el.function.name;
|
||||||
|
Location loc = null;
|
||||||
|
|
||||||
|
for (var j = el.codePtr; j >= 0 && loc == null; j--) loc = el.function.body[j].location;
|
||||||
|
if (loc == null) loc = el.function.loc();
|
||||||
|
|
||||||
|
var trace = "";
|
||||||
|
|
||||||
|
if (loc != null) trace += "at " + loc.toString() + " ";
|
||||||
|
if (name != null && !name.equals("")) trace += "in " + name + " ";
|
||||||
|
|
||||||
|
trace = trace.trim();
|
||||||
|
|
||||||
|
if (!trace.equals("")) res.add(trace);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Context(Context parent, Environment environment, CodeFrame frame, Engine engine, int stackSize) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.environment = environment;
|
||||||
|
this.frame = frame;
|
||||||
|
this.engine = engine;
|
||||||
|
this.stackSize = stackSize;
|
||||||
|
|
||||||
|
if (hasNotNull(Environment.MAX_STACK_COUNT) && stackSize > (int)get(Environment.MAX_STACK_COUNT)) {
|
||||||
|
throw EngineException.ofRange("Stack overflow!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Context(Engine engine) {
|
||||||
|
this(null, null, null, engine, 0);
|
||||||
|
}
|
||||||
|
public Context(Engine engine, Environment env) {
|
||||||
|
this(null, env, null, engine, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
57
src/me/topchetoeu/jscript/core/engine/Engine.java
Normal file
57
src/me/topchetoeu/jscript/core/engine/Engine.java
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
package me.topchetoeu.jscript.core.engine;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Filename;
|
||||||
|
import me.topchetoeu.jscript.common.events.Awaitable;
|
||||||
|
import me.topchetoeu.jscript.core.compilation.FunctionBody;
|
||||||
|
import me.topchetoeu.jscript.core.engine.values.FunctionValue;
|
||||||
|
import me.topchetoeu.jscript.core.engine.values.Symbol;
|
||||||
|
|
||||||
|
public class Engine extends EventLoop implements Extensions {
|
||||||
|
public static final HashMap<Long, FunctionBody> functions = new HashMap<>();
|
||||||
|
|
||||||
|
private final Environment env = new Environment();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> void add(Symbol key, T obj) {
|
||||||
|
this.env.add(key, obj);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public <T> T get(Symbol key) {
|
||||||
|
return this.env.get(key);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean has(Symbol key) {
|
||||||
|
return this.env.has(key);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean remove(Symbol key) {
|
||||||
|
return this.env.remove(key);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public Iterable<Symbol> keys() {
|
||||||
|
return env.keys();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Engine copy() {
|
||||||
|
var res = new Engine();
|
||||||
|
res.env.addAll(env);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Awaitable<Object> pushMsg(boolean micro, Environment env, FunctionValue func, Object thisArg, Object ...args) {
|
||||||
|
return pushMsg(() -> {
|
||||||
|
return func.call(new Context(this, env), thisArg, args);
|
||||||
|
}, micro);
|
||||||
|
}
|
||||||
|
public Awaitable<Object> pushMsg(boolean micro, Environment env, Filename filename, String raw, Object thisArg, Object ...args) {
|
||||||
|
return pushMsg(() -> {
|
||||||
|
var ctx = new Context(this, env);
|
||||||
|
return ctx.compile(filename, raw).call(new Context(this, env), thisArg, args);
|
||||||
|
}, micro);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Engine() {
|
||||||
|
}
|
||||||
|
}
|
||||||
128
src/me/topchetoeu/jscript/core/engine/Environment.java
Normal file
128
src/me/topchetoeu/jscript/core/engine/Environment.java
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
package me.topchetoeu.jscript.core.engine;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Filename;
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.core.engine.debug.DebugContext;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.GlobalScope;
|
||||||
|
import me.topchetoeu.jscript.core.engine.values.ArrayValue;
|
||||||
|
import me.topchetoeu.jscript.core.engine.values.FunctionValue;
|
||||||
|
import me.topchetoeu.jscript.core.engine.values.NativeFunction;
|
||||||
|
import me.topchetoeu.jscript.core.engine.values.ObjectValue;
|
||||||
|
import me.topchetoeu.jscript.core.engine.values.Symbol;
|
||||||
|
import me.topchetoeu.jscript.core.engine.values.Values;
|
||||||
|
import me.topchetoeu.jscript.core.exceptions.EngineException;
|
||||||
|
import me.topchetoeu.jscript.core.parsing.Parsing;
|
||||||
|
import me.topchetoeu.jscript.utils.interop.NativeWrapperProvider;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public class Environment implements Extensions {
|
||||||
|
|
||||||
|
public static final HashMap<String, Symbol> symbols = new HashMap<>();
|
||||||
|
|
||||||
|
public static final Symbol WRAPPERS = Symbol.get("Environment.wrappers");
|
||||||
|
public static final Symbol COMPILE_FUNC = Symbol.get("Environment.compile");
|
||||||
|
|
||||||
|
public static final Symbol REGEX_CONSTR = Symbol.get("Environment.regexConstructor");
|
||||||
|
public static final Symbol STACK = Symbol.get("Environment.stack");
|
||||||
|
public static final Symbol MAX_STACK_COUNT = Symbol.get("Environment.maxStackCount");
|
||||||
|
public static final Symbol HIDE_STACK = Symbol.get("Environment.hideStack");
|
||||||
|
|
||||||
|
public static final Symbol OBJECT_PROTO = Symbol.get("Environment.objectPrototype");
|
||||||
|
public static final Symbol FUNCTION_PROTO = Symbol.get("Environment.functionPrototype");
|
||||||
|
public static final Symbol ARRAY_PROTO = Symbol.get("Environment.arrayPrototype");
|
||||||
|
public static final Symbol BOOL_PROTO = Symbol.get("Environment.boolPrototype");
|
||||||
|
public static final Symbol NUMBER_PROTO = Symbol.get("Environment.numberPrototype");
|
||||||
|
public static final Symbol STRING_PROTO = Symbol.get("Environment.stringPrototype");
|
||||||
|
public static final Symbol SYMBOL_PROTO = Symbol.get("Environment.symbolPrototype");
|
||||||
|
public static final Symbol ERROR_PROTO = Symbol.get("Environment.errorPrototype");
|
||||||
|
public static final Symbol SYNTAX_ERR_PROTO = Symbol.get("Environment.syntaxErrorPrototype");
|
||||||
|
public static final Symbol TYPE_ERR_PROTO = Symbol.get("Environment.typeErrorPrototype");
|
||||||
|
public static final Symbol RANGE_ERR_PROTO = Symbol.get("Environment.rangeErrorPrototype");
|
||||||
|
|
||||||
|
private HashMap<Symbol, Object> data = new HashMap<>();
|
||||||
|
|
||||||
|
public GlobalScope global;
|
||||||
|
public WrapperProvider wrappers;
|
||||||
|
|
||||||
|
@Override public <T> void add(Symbol key, T obj) {
|
||||||
|
data.put(key, obj);
|
||||||
|
}
|
||||||
|
@Override public <T> T get(Symbol key) {
|
||||||
|
return (T)data.get(key);
|
||||||
|
}
|
||||||
|
@Override public boolean remove(Symbol key) {
|
||||||
|
if (data.containsKey(key)) {
|
||||||
|
data.remove(key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@Override public boolean has(Symbol key) {
|
||||||
|
return data.containsKey(key);
|
||||||
|
}
|
||||||
|
@Override public Iterable<Symbol> keys() {
|
||||||
|
return data.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FunctionValue compileFunc(Extensions ext) {
|
||||||
|
return ext.init(COMPILE_FUNC, new NativeFunction("compile", args -> {
|
||||||
|
var source = args.getString(0);
|
||||||
|
var filename = args.getString(1);
|
||||||
|
var env = Values.wrapper(Values.getMember(args.ctx, args.get(2), Symbol.get("env")), Environment.class);
|
||||||
|
var isDebug = DebugContext.enabled(args.ctx);
|
||||||
|
var res = new ObjectValue();
|
||||||
|
|
||||||
|
var target = Parsing.compile(env, Filename.parse(filename), source);
|
||||||
|
Engine.functions.putAll(target.functions);
|
||||||
|
Engine.functions.remove(0l);
|
||||||
|
|
||||||
|
res.defineProperty(args.ctx, "function", target.func(env));
|
||||||
|
res.defineProperty(args.ctx, "mapChain", new ArrayValue());
|
||||||
|
|
||||||
|
if (isDebug) res.defineProperty(
|
||||||
|
args.ctx, "breakpoints",
|
||||||
|
ArrayValue.of(args.ctx, target.breakpoints.stream().map(Location::toString).collect(Collectors.toList()))
|
||||||
|
);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
public static FunctionValue regexConstructor(Extensions ext) {
|
||||||
|
return ext.init(COMPILE_FUNC, new NativeFunction("RegExp", args -> {
|
||||||
|
throw EngineException.ofError("Regular expressions not supported.").setCtx(args.ctx.environment, args.ctx.engine);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Environment copy() {
|
||||||
|
var res = new Environment(null, global);
|
||||||
|
|
||||||
|
res.wrappers = wrappers.fork(res);
|
||||||
|
res.global = global;
|
||||||
|
res.data.putAll(data);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
public Environment child() {
|
||||||
|
var res = copy();
|
||||||
|
res.global = res.global.globalChild();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Context context(Engine engine) {
|
||||||
|
return new Context(engine, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Environment(WrapperProvider nativeConverter, GlobalScope global) {
|
||||||
|
if (nativeConverter == null) nativeConverter = new NativeWrapperProvider(this);
|
||||||
|
if (global == null) global = new GlobalScope();
|
||||||
|
|
||||||
|
this.wrappers = nativeConverter;
|
||||||
|
this.global = global;
|
||||||
|
}
|
||||||
|
public Environment() {
|
||||||
|
this(null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
81
src/me/topchetoeu/jscript/core/engine/EventLoop.java
Normal file
81
src/me/topchetoeu/jscript/core/engine/EventLoop.java
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
package me.topchetoeu.jscript.core.engine;
|
||||||
|
|
||||||
|
import java.util.concurrent.PriorityBlockingQueue;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.ResultRunnable;
|
||||||
|
import me.topchetoeu.jscript.common.events.Awaitable;
|
||||||
|
import me.topchetoeu.jscript.common.events.DataNotifier;
|
||||||
|
import me.topchetoeu.jscript.core.exceptions.InterruptException;
|
||||||
|
|
||||||
|
public class EventLoop {
|
||||||
|
private static class Task implements Comparable<Task> {
|
||||||
|
public final ResultRunnable<?> runnable;
|
||||||
|
public final DataNotifier<Object> notifier = new DataNotifier<>();
|
||||||
|
public final boolean micro;
|
||||||
|
|
||||||
|
public Task(ResultRunnable<?> runnable, boolean micro) {
|
||||||
|
this.runnable = runnable;
|
||||||
|
this.micro = micro;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Task other) {
|
||||||
|
return Integer.compare(this.micro ? 0 : 1, other.micro ? 0 : 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PriorityBlockingQueue<Task> tasks = new PriorityBlockingQueue<>();
|
||||||
|
private Thread thread;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> Awaitable<T> pushMsg(ResultRunnable<T> runnable, boolean micro) {
|
||||||
|
var msg = new Task(runnable, micro);
|
||||||
|
tasks.add(msg);
|
||||||
|
return (Awaitable<T>)msg.notifier;
|
||||||
|
}
|
||||||
|
public Awaitable<Object> pushMsg(Runnable runnable, boolean micro) {
|
||||||
|
return pushMsg(() -> { runnable.run(); return null; }, micro);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run(boolean untilEmpty) {
|
||||||
|
while (!untilEmpty || !tasks.isEmpty()) {
|
||||||
|
try {
|
||||||
|
var task = tasks.take();
|
||||||
|
|
||||||
|
try {
|
||||||
|
task.notifier.next(task.runnable.run());
|
||||||
|
}
|
||||||
|
catch (RuntimeException e) {
|
||||||
|
if (e instanceof InterruptException) throw e;
|
||||||
|
task.notifier.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (InterruptedException | InterruptException e) {
|
||||||
|
for (var msg : tasks) msg.notifier.error(new InterruptException(e));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Thread thread() {
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
public Thread start() {
|
||||||
|
if (thread == null) {
|
||||||
|
thread = new Thread(() -> run(false), "Event loop #" + hashCode());
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
public void stop() {
|
||||||
|
if (thread != null) thread.interrupt();
|
||||||
|
thread = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean inLoopThread() {
|
||||||
|
return Thread.currentThread() == thread;
|
||||||
|
}
|
||||||
|
public boolean isRunning() {
|
||||||
|
return this.thread != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
34
src/me/topchetoeu/jscript/core/engine/Extensions.java
Normal file
34
src/me/topchetoeu/jscript/core/engine/Extensions.java
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package me.topchetoeu.jscript.core.engine;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.core.engine.values.Symbol;
|
||||||
|
|
||||||
|
public interface Extensions {
|
||||||
|
<T> T get(Symbol key);
|
||||||
|
<T> void add(Symbol key, T obj);
|
||||||
|
Iterable<Symbol> keys();
|
||||||
|
|
||||||
|
boolean has(Symbol key);
|
||||||
|
boolean remove(Symbol key);
|
||||||
|
|
||||||
|
default boolean hasNotNull(Symbol key) {
|
||||||
|
return has(key) && get(key) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
default <T> T get(Symbol key, T defaultVal) {
|
||||||
|
if (has(key)) return get(key);
|
||||||
|
else return defaultVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
default <T> T init(Symbol key, T val) {
|
||||||
|
if (has(key)) return get(key);
|
||||||
|
else {
|
||||||
|
add(key, val);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default void addAll(Extensions source) {
|
||||||
|
for (var key : source.keys()) {
|
||||||
|
add(key, source.get(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user