Compare commits
2 Commits
0.9.9-beta
...
TopchetoEU
| Author | SHA1 | Date | |
|---|---|---|---|
|
b791f3277e
|
|||
|
5e6e6fea61
|
10
.github/workflows/tagged-release.yml
vendored
10
.github/workflows/tagged-release.yml
vendored
@@ -16,17 +16,15 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
distribution: 'adopt'
|
distribution: 'adopt'
|
||||||
java-version: '11'
|
java-version: '11'
|
||||||
- name: Setup Gradle
|
|
||||||
uses: gradle/gradle-build-action@v2
|
|
||||||
- name: Clone repository
|
- name: Clone repository
|
||||||
uses: GuillaumeFalourd/clone-github-repo-action@main
|
uses: GuillaumeFalourd/clone-github-repo-action@main
|
||||||
with:
|
with:
|
||||||
branch: 'master'
|
branch: 'master' # fuck this political bullshitshit, took me an hour to fix this
|
||||||
owner: 'TopchetoEU'
|
owner: 'TopchetoEU'
|
||||||
repository: 'java-jscript'
|
repository: 'java-jscript'
|
||||||
- name: Build
|
- name: "Build"
|
||||||
run: |
|
run: |
|
||||||
cd java-jscript; gradle build
|
cd java-jscript; node ./build.js release ${{ github.ref }}
|
||||||
|
|
||||||
- uses: "marvinpinto/action-automatic-releases@latest"
|
- uses: "marvinpinto/action-automatic-releases@latest"
|
||||||
with:
|
with:
|
||||||
@@ -34,4 +32,4 @@ jobs:
|
|||||||
prerelease: false
|
prerelease: false
|
||||||
files: |
|
files: |
|
||||||
java-jscript/LICENSE
|
java-jscript/LICENSE
|
||||||
java-jscript/build/libs/*.jar
|
java-jscript/dst/*.jar
|
||||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
!/src
|
!/src
|
||||||
!/src/**/*
|
!/src/**/*
|
||||||
|
/src/assets/js/ts.js
|
||||||
|
|
||||||
!/doc
|
!/doc
|
||||||
!/doc/**/*
|
!/doc/**/*
|
||||||
@@ -9,16 +10,12 @@
|
|||||||
!/tests
|
!/tests
|
||||||
!/tests/**/*
|
!/tests/**/*
|
||||||
|
|
||||||
|
|
||||||
!/.github
|
!/.github
|
||||||
!/.github/**/*
|
!/.github/**/*
|
||||||
|
|
||||||
!/.gitignore
|
!/.gitignore
|
||||||
!/.gitattributes
|
!/.gitattributes
|
||||||
|
!/build.js
|
||||||
!/LICENSE
|
!/LICENSE
|
||||||
!/README.md
|
!/README.md
|
||||||
!/settings.gradle
|
|
||||||
!/build.gradle
|
|
||||||
!/gradle.properties
|
|
||||||
!/gradle
|
|
||||||
!/gradle/wrapper
|
|
||||||
!/gradle/wrapper/gradle-wrapper.properties
|
|
||||||
@@ -11,7 +11,7 @@ JScript is an engine, capable of running EcmaScript 5, written entirely in Java.
|
|||||||
The following is going to execute a simple javascript statement:
|
The following is going to execute a simple javascript statement:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
var engine = new Engine();
|
var engine = new Engine(false);
|
||||||
// Initialize a standard environment, with implementations of most basic standard libraries (Object, Array, Symbol, etc.)
|
// Initialize a standard environment, with implementations of most basic standard libraries (Object, Array, Symbol, etc.)
|
||||||
var env = Internals.apply(new Environment());
|
var env = Internals.apply(new Environment());
|
||||||
|
|
||||||
@@ -26,4 +26,4 @@ System.out.println(awaitable.await());
|
|||||||
|
|
||||||
## NOTE:
|
## 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 folder. If you are going to work with the `node build.js debug|release` command, this is not a necessary step.
|
To setup the typescript bundle in your sources, run `node build.js init-ts`. This will download the latest version of typescript, minify it, and add it to your src/assets folder. If you are going to work with the `node build.js debug|release` command, this is not a necessary step.
|
||||||
|
|||||||
32
build.gradle
32
build.gradle
@@ -1,32 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id "application"
|
|
||||||
}
|
|
||||||
|
|
||||||
java {
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_11
|
|
||||||
targetCompatibility = JavaVersion.VERSION_11
|
|
||||||
toolchain.languageVersion = JavaLanguageVersion.of(11)
|
|
||||||
withSourcesJar()
|
|
||||||
}
|
|
||||||
|
|
||||||
jar {
|
|
||||||
manifest.attributes["Main-class"] = project.main_class
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
|
||||||
main.java.srcDirs = [ "src/java" ]
|
|
||||||
main.resources.srcDirs = [ "src/assets" ]
|
|
||||||
}
|
|
||||||
|
|
||||||
processResources {
|
|
||||||
filesMatching "metadata.json", {
|
|
||||||
expand(
|
|
||||||
version: project.project_version,
|
|
||||||
name: project.project_name
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
base.archivesName = project.project_name
|
|
||||||
version = project.project_version
|
|
||||||
group = project.project_group
|
|
||||||
193
build.js
Normal file
193
build.js
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
const { spawn } = require('child_process');
|
||||||
|
const fs = require('fs/promises');
|
||||||
|
const pt = require('path');
|
||||||
|
const { argv, exit } = require('process');
|
||||||
|
const { Readable } = require('stream');
|
||||||
|
|
||||||
|
|
||||||
|
async function* find(src, dst, wildcard) {
|
||||||
|
const stat = await fs.stat(src);
|
||||||
|
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
for (const el of await fs.readdir(src)) {
|
||||||
|
for await (const res of find(pt.join(src, el), dst ? pt.join(dst, el) : undefined, wildcard)) yield res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (stat.isFile() && wildcard(src)) yield dst ? { src, dst } : src;
|
||||||
|
}
|
||||||
|
async function copy(src, dst, wildcard) {
|
||||||
|
const promises = [];
|
||||||
|
|
||||||
|
for await (const el of find(src, dst, wildcard)) {
|
||||||
|
promises.push((async () => {
|
||||||
|
await fs.mkdir(pt.dirname(el.dst), { recursive: true });
|
||||||
|
await fs.copyFile(el.src, el.dst);
|
||||||
|
})());
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
function run(suppressOutput, cmd, ...args) {
|
||||||
|
return new Promise((res, rej) => {
|
||||||
|
const proc = spawn(cmd, args, { stdio: suppressOutput ? 'ignore' : 'inherit' });
|
||||||
|
proc.once('exit', code => {
|
||||||
|
if (code === 0) res(code);
|
||||||
|
else rej(new Error(`Process ${cmd} exited with code ${code}.`));
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function downloadTypescript(outFile) {
|
||||||
|
try {
|
||||||
|
// Import the required libraries, without the need of a package.json
|
||||||
|
console.log('Importing modules...');
|
||||||
|
await run(true, 'npm', 'i', 'tar', 'zlib', 'uglify-js');
|
||||||
|
await fs.mkdir(pt.dirname(outFile), { recursive: true });
|
||||||
|
await fs.mkdir('tmp', { recursive: true });
|
||||||
|
|
||||||
|
const tar = require('tar');
|
||||||
|
const zlib = require('zlib');
|
||||||
|
const { minify } = await import('uglify-js');
|
||||||
|
|
||||||
|
// Download the package.json file of typescript
|
||||||
|
const packageDesc = await (await fetch('https://registry.npmjs.org/typescript/latest')).json();
|
||||||
|
const url = packageDesc.dist.tarball;
|
||||||
|
|
||||||
|
console.log('Extracting typescript...');
|
||||||
|
await new Promise(async (res, rej) => Readable.fromWeb((await fetch(url)).body)
|
||||||
|
.pipe(zlib.createGunzip())
|
||||||
|
.pipe(tar.x({ cwd: 'tmp', filter: v => v === 'package/lib/typescript.js' }))
|
||||||
|
.on('end', res)
|
||||||
|
.on('error', rej)
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('Compiling typescript to ES5...');
|
||||||
|
|
||||||
|
const ts = require('./tmp/package/lib/typescript');
|
||||||
|
const program = ts.createProgram([ 'tmp/package/lib/typescript.js' ], {
|
||||||
|
outFile: "tmp/typescript-es5.js",
|
||||||
|
target: ts.ScriptTarget.ES5,
|
||||||
|
module: ts.ModuleKind.None,
|
||||||
|
downlevelIteration: true,
|
||||||
|
allowJs: true,
|
||||||
|
});
|
||||||
|
program.emit();
|
||||||
|
|
||||||
|
console.log('Minifying typescript...');
|
||||||
|
|
||||||
|
const minified = minify((await fs.readFile('tmp/typescript-es5.js')).toString());
|
||||||
|
// const minified = { code: (await fs.readFile('tmp/typescript-es5.js')).toString() };
|
||||||
|
if (minified.error) throw minified.error;
|
||||||
|
|
||||||
|
// Patch unsupported regex syntax
|
||||||
|
minified.code = minified.code.replaceAll('[-/\\\\^$*+?.()|[\\]{}]', '[-/\\\\^$*+?.()|\\[\\]{}]');
|
||||||
|
|
||||||
|
const stream = await fs.open(outFile, 'w');
|
||||||
|
|
||||||
|
// Write typescript's license
|
||||||
|
await stream.write(new TextEncoder().encode(`
|
||||||
|
/*! *****************************************************************************
|
||||||
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||||
|
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||||
|
MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||||
|
|
||||||
|
See the Apache Version 2.0 License for specific language governing permissions
|
||||||
|
and limitations under the License.
|
||||||
|
|
||||||
|
The following is a minified version of the unmodified Typescript 5.2
|
||||||
|
***************************************************************************** */
|
||||||
|
`));
|
||||||
|
|
||||||
|
await stream.write(minified.code);
|
||||||
|
console.log('Typescript bundling done!');
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
// Clean up all stuff left from typescript bundling
|
||||||
|
await fs.rm('tmp', { recursive: true, force: true });
|
||||||
|
await fs.rm('package.json');
|
||||||
|
await fs.rm('package-lock.json');
|
||||||
|
await fs.rm('node_modules', { recursive: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function compileJava(conf) {
|
||||||
|
try {
|
||||||
|
await fs.writeFile('Metadata.java', (await fs.readFile('src/me/topchetoeu/jscript/common/Metadata.java')).toString()
|
||||||
|
.replace('${VERSION}', conf.version)
|
||||||
|
.replace('${NAME}', conf.name)
|
||||||
|
.replace('${AUTHOR}', conf.author)
|
||||||
|
);
|
||||||
|
const args = ['--release', '11', ];
|
||||||
|
if (argv[2] === 'debug') args.push('-g');
|
||||||
|
args.push('-d', 'dst/classes', 'Metadata.java');
|
||||||
|
|
||||||
|
console.log('Compiling java project...');
|
||||||
|
for await (const path of find('src', undefined, v => v.endsWith('.java') && !v.endsWith('Metadata.java'))) args.push(path);
|
||||||
|
await run(false, conf.javahome + 'javac', ...args);
|
||||||
|
console.log('Compiled java project!');
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
await fs.rm('Metadata.java');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async 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 () => {
|
||||||
|
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 {}
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
(async () => {
|
||||||
|
await copy('src', 'dst/classes', v => !v.endsWith('.java'));
|
||||||
|
// await downloadTypescript('dst/classes/me/topchetoeu/jscript/utils/assets/js/ts.js');
|
||||||
|
})(),
|
||||||
|
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) {
|
||||||
|
if (argv[2] === 'debug') throw e;
|
||||||
|
console.log(e.toString());
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
})();
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
project_group = me.topchetoeu
|
|
||||||
project_name = jscript
|
|
||||||
project_version = 0.9.9-beta
|
|
||||||
main_class = me.topchetoeu.jscript.utils.JScriptRepl
|
|
||||||
7
gradle/wrapper/gradle-wrapper.properties
vendored
7
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,7 +0,0 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
|
||||||
distributionPath=wrapper/dists
|
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
|
||||||
networkTimeout=10000
|
|
||||||
validateDistributionUrl=true
|
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
|
||||||
zipStorePath=wrapper/dists
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0'
|
|
||||||
}
|
|
||||||
|
|
||||||
rootProject.name = properties.project_name
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "${version}",
|
|
||||||
"name": "${name}",
|
|
||||||
"author": "TopchetoEU"
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.common;
|
|
||||||
|
|
||||||
public class FunctionBody {
|
|
||||||
public final FunctionBody[] children;
|
|
||||||
public final Instruction[] instructions;
|
|
||||||
public final int localsN, argsN;
|
|
||||||
|
|
||||||
public FunctionBody(int localsN, int argsN, Instruction[] instructions, FunctionBody[] children) {
|
|
||||||
this.children = children;
|
|
||||||
this.argsN = argsN;
|
|
||||||
this.localsN = localsN;
|
|
||||||
this.instructions = instructions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,369 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.common;
|
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
|
|
||||||
|
|
||||||
public class Instruction {
|
|
||||||
public static enum Type {
|
|
||||||
NOP(0),
|
|
||||||
RETURN(1),
|
|
||||||
THROW(2),
|
|
||||||
THROW_SYNTAX(3),
|
|
||||||
DELETE(4),
|
|
||||||
TRY_START(5),
|
|
||||||
TRY_END(6),
|
|
||||||
|
|
||||||
CALL(7),
|
|
||||||
CALL_NEW(8),
|
|
||||||
JMP_IF(9),
|
|
||||||
JMP_IFN(10),
|
|
||||||
JMP(11),
|
|
||||||
|
|
||||||
PUSH_UNDEFINED(12),
|
|
||||||
PUSH_NULL(13),
|
|
||||||
PUSH_BOOL(14),
|
|
||||||
PUSH_NUMBER(15),
|
|
||||||
PUSH_STRING(16),
|
|
||||||
|
|
||||||
LOAD_VAR(17),
|
|
||||||
LOAD_MEMBER(18),
|
|
||||||
LOAD_GLOB(20),
|
|
||||||
|
|
||||||
LOAD_FUNC(21),
|
|
||||||
LOAD_ARR(22),
|
|
||||||
LOAD_OBJ(23),
|
|
||||||
STORE_SELF_FUNC(24),
|
|
||||||
LOAD_REGEX(25),
|
|
||||||
|
|
||||||
DUP(26),
|
|
||||||
|
|
||||||
STORE_VAR(27),
|
|
||||||
STORE_MEMBER(28),
|
|
||||||
DISCARD(29),
|
|
||||||
|
|
||||||
MAKE_VAR(30),
|
|
||||||
DEF_PROP(31),
|
|
||||||
KEYS(32),
|
|
||||||
|
|
||||||
TYPEOF(33),
|
|
||||||
OPERATION(34);
|
|
||||||
|
|
||||||
private static final HashMap<Integer, Type> types = new HashMap<>();
|
|
||||||
public final int numeric;
|
|
||||||
|
|
||||||
static {
|
|
||||||
for (var val : Type.values()) types.put(val.numeric, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Type(int numeric) {
|
|
||||||
this.numeric = numeric;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Type fromNumeric(int i) {
|
|
||||||
return types.get(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public static enum BreakpointType {
|
|
||||||
NONE,
|
|
||||||
STEP_OVER,
|
|
||||||
STEP_IN;
|
|
||||||
|
|
||||||
public boolean shouldStepIn() {
|
|
||||||
return this != NONE;
|
|
||||||
}
|
|
||||||
public boolean shouldStepOver() {
|
|
||||||
return this == STEP_OVER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public final Type type;
|
|
||||||
public final Object[] params;
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <T> T get(int i) {
|
|
||||||
if (i >= params.length || i < 0) return null;
|
|
||||||
return (T)params[i];
|
|
||||||
}
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <T> T get(int i, T defaultVal) {
|
|
||||||
if (i >= params.length || i < 0) return defaultVal;
|
|
||||||
return (T)params[i];
|
|
||||||
}
|
|
||||||
public boolean match(Object ...args) {
|
|
||||||
if (args.length != params.length) return false;
|
|
||||||
for (int i = 0; i < args.length; i++) {
|
|
||||||
var a = params[i];
|
|
||||||
var b = args[i];
|
|
||||||
if (a == null || b == null) {
|
|
||||||
if (!(a == null && b == null)) return false;
|
|
||||||
}
|
|
||||||
if (!a.equals(b)) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
public boolean is(int i, Object arg) {
|
|
||||||
if (params.length <= i) return false;
|
|
||||||
return params[i].equals(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void write(DataOutputStream writer) throws IOException {
|
|
||||||
var rawType = type.numeric;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case KEYS:
|
|
||||||
case PUSH_BOOL:
|
|
||||||
case STORE_MEMBER: rawType |= (boolean)get(0) ? 128 : 0; break;
|
|
||||||
case STORE_VAR: rawType |= (boolean)get(1) ? 128 : 0; break;
|
|
||||||
case TYPEOF: rawType |= params.length > 0 ? 128 : 0; break;
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.writeByte(rawType);
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case CALL: writer.writeInt(get(0)); break;
|
|
||||||
case CALL_NEW: writer.writeInt(get(0)); break;
|
|
||||||
case DUP: writer.writeInt(get(0)); break;
|
|
||||||
case JMP: writer.writeInt(get(0)); break;
|
|
||||||
case JMP_IF: writer.writeInt(get(0)); break;
|
|
||||||
case JMP_IFN: writer.writeInt(get(0)); break;
|
|
||||||
case LOAD_ARR: writer.writeInt(get(0)); break;
|
|
||||||
case LOAD_FUNC: {
|
|
||||||
writer.writeInt(params.length - 1);
|
|
||||||
|
|
||||||
for (var i = 0; i < params.length; i++) {
|
|
||||||
writer.writeInt(get(i + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.writeInt(get(0));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case LOAD_REGEX: writer.writeUTF(get(0)); break;
|
|
||||||
case LOAD_VAR: writer.writeInt(get(0)); break;
|
|
||||||
case MAKE_VAR: writer.writeUTF(get(0)); break;
|
|
||||||
case OPERATION: writer.writeByte(((Operation)get(0)).numeric); break;
|
|
||||||
case PUSH_NUMBER: writer.writeDouble(get(0)); break;
|
|
||||||
case PUSH_STRING: writer.writeUTF(get(0)); break;
|
|
||||||
case STORE_SELF_FUNC: writer.writeInt(get(0)); break;
|
|
||||||
case STORE_VAR: writer.writeInt(get(0)); break;
|
|
||||||
case THROW_SYNTAX: writer.writeUTF(get(0));
|
|
||||||
case TRY_START:
|
|
||||||
writer.writeInt(get(0));
|
|
||||||
writer.writeInt(get(1));
|
|
||||||
writer.writeInt(get(2));
|
|
||||||
break;
|
|
||||||
case TYPEOF:
|
|
||||||
if (params.length > 0) writer.writeUTF(get(0));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Instruction(Type type, Object ...params) {
|
|
||||||
this.type = type;
|
|
||||||
this.params = params;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction read(DataInputStream stream) throws IOException {
|
|
||||||
var rawType = stream.readUnsignedByte();
|
|
||||||
var type = Type.fromNumeric(rawType & 127);
|
|
||||||
var flag = (rawType & 128) != 0;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case CALL: return call(stream.readInt());
|
|
||||||
case CALL_NEW: return callNew(stream.readInt());
|
|
||||||
case DEF_PROP: return defProp();
|
|
||||||
case DELETE: return delete();
|
|
||||||
case DISCARD: return discard();
|
|
||||||
case DUP: return dup(stream.readInt());
|
|
||||||
case JMP: return jmp(stream.readInt());
|
|
||||||
case JMP_IF: return jmpIf(stream.readInt());
|
|
||||||
case JMP_IFN: return jmpIfNot(stream.readInt());
|
|
||||||
case KEYS: return keys(flag);
|
|
||||||
case LOAD_ARR: return loadArr(stream.readInt());
|
|
||||||
case LOAD_FUNC: {
|
|
||||||
var captures = new int[stream.readInt()];
|
|
||||||
|
|
||||||
for (var i = 0; i < captures.length; i++) {
|
|
||||||
captures[i] = stream.readInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
return loadFunc(stream.readInt(), captures);
|
|
||||||
}
|
|
||||||
case LOAD_GLOB: return loadGlob();
|
|
||||||
case LOAD_MEMBER: return loadMember();
|
|
||||||
case LOAD_OBJ: return loadObj();
|
|
||||||
case LOAD_REGEX: return loadRegex(stream.readUTF(), null);
|
|
||||||
case LOAD_VAR: return loadVar(stream.readInt());
|
|
||||||
case MAKE_VAR: return makeVar(stream.readUTF());
|
|
||||||
case OPERATION: return operation(Operation.fromNumeric(stream.readUnsignedByte()));
|
|
||||||
case PUSH_NULL: return pushNull();
|
|
||||||
case PUSH_UNDEFINED: return pushUndefined();
|
|
||||||
case PUSH_BOOL: return pushValue(flag);
|
|
||||||
case PUSH_NUMBER: return pushValue(stream.readDouble());
|
|
||||||
case PUSH_STRING: return pushValue(stream.readUTF());
|
|
||||||
case RETURN: return ret();
|
|
||||||
case STORE_MEMBER: return storeMember(flag);
|
|
||||||
case STORE_SELF_FUNC: return storeSelfFunc(stream.readInt());
|
|
||||||
case STORE_VAR: return storeVar(stream.readInt(), flag);
|
|
||||||
case THROW: return throwInstr();
|
|
||||||
case THROW_SYNTAX: return throwSyntax(stream.readUTF());
|
|
||||||
case TRY_END: return tryEnd();
|
|
||||||
case TRY_START: return tryStart(stream.readInt(), stream.readInt(), stream.readInt());
|
|
||||||
case TYPEOF: return flag ? typeof(stream.readUTF()) : typeof();
|
|
||||||
case NOP:
|
|
||||||
if (flag) return null;
|
|
||||||
else return nop();
|
|
||||||
default: return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction tryStart(int catchStart, int finallyStart, int end) {
|
|
||||||
return new Instruction(Type.TRY_START, catchStart, finallyStart, end);
|
|
||||||
}
|
|
||||||
public static Instruction tryEnd() {
|
|
||||||
return new Instruction(Type.TRY_END);
|
|
||||||
}
|
|
||||||
public static Instruction throwInstr() {
|
|
||||||
return new Instruction(Type.THROW);
|
|
||||||
}
|
|
||||||
public static Instruction throwSyntax(SyntaxException err) {
|
|
||||||
return new Instruction(Type.THROW_SYNTAX, err.getMessage());
|
|
||||||
}
|
|
||||||
public static Instruction throwSyntax(String err) {
|
|
||||||
return new Instruction(Type.THROW_SYNTAX, err);
|
|
||||||
}
|
|
||||||
public static Instruction delete() {
|
|
||||||
return new Instruction(Type.DELETE);
|
|
||||||
}
|
|
||||||
public static Instruction ret() {
|
|
||||||
return new Instruction(Type.RETURN);
|
|
||||||
}
|
|
||||||
public static Instruction debug() {
|
|
||||||
return new Instruction(Type.NOP, "debug");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction nop(Object ...params) {
|
|
||||||
return new Instruction(Type.NOP, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction call(int argn) {
|
|
||||||
return new Instruction(Type.CALL, argn);
|
|
||||||
}
|
|
||||||
public static Instruction callNew(int argn) {
|
|
||||||
return new Instruction(Type.CALL_NEW, argn);
|
|
||||||
}
|
|
||||||
public static Instruction jmp(int offset) {
|
|
||||||
return new Instruction(Type.JMP, offset);
|
|
||||||
}
|
|
||||||
public static Instruction jmpIf(int offset) {
|
|
||||||
return new Instruction(Type.JMP_IF, offset);
|
|
||||||
}
|
|
||||||
public static Instruction jmpIfNot(int offset) {
|
|
||||||
return new Instruction(Type.JMP_IFN, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction pushUndefined() {
|
|
||||||
return new Instruction(Type.PUSH_UNDEFINED);
|
|
||||||
}
|
|
||||||
public static Instruction pushNull() {
|
|
||||||
return new Instruction(Type.PUSH_NULL);
|
|
||||||
}
|
|
||||||
public static Instruction pushValue(boolean val) {
|
|
||||||
return new Instruction(Type.PUSH_BOOL, val);
|
|
||||||
}
|
|
||||||
public static Instruction pushValue(double val) {
|
|
||||||
return new Instruction(Type.PUSH_NUMBER, val);
|
|
||||||
}
|
|
||||||
public static Instruction pushValue(String val) {
|
|
||||||
return new Instruction(Type.PUSH_STRING, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction makeVar(String name) {
|
|
||||||
return new Instruction(Type.MAKE_VAR, name);
|
|
||||||
}
|
|
||||||
public static Instruction loadVar(Object i) {
|
|
||||||
return new Instruction(Type.LOAD_VAR, i);
|
|
||||||
}
|
|
||||||
public static Instruction loadGlob() {
|
|
||||||
return new Instruction(Type.LOAD_GLOB);
|
|
||||||
}
|
|
||||||
public static Instruction loadMember() {
|
|
||||||
return new Instruction(Type.LOAD_MEMBER);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction loadRegex(String pattern, String flags) {
|
|
||||||
return new Instruction(Type.LOAD_REGEX, pattern, flags);
|
|
||||||
}
|
|
||||||
public static Instruction loadFunc(int id, int[] captures) {
|
|
||||||
var args = new Object[1 + captures.length];
|
|
||||||
args[0] = id;
|
|
||||||
for (var i = 0; i < captures.length; i++) args[i + 1] = captures[i];
|
|
||||||
return new Instruction(Type.LOAD_FUNC, args);
|
|
||||||
}
|
|
||||||
public static Instruction loadObj() {
|
|
||||||
return new Instruction(Type.LOAD_OBJ);
|
|
||||||
}
|
|
||||||
public static Instruction loadArr(int count) {
|
|
||||||
return new Instruction(Type.LOAD_ARR, count);
|
|
||||||
}
|
|
||||||
public static Instruction dup() {
|
|
||||||
return new Instruction(Type.DUP, 1);
|
|
||||||
}
|
|
||||||
public static Instruction dup(int count) {
|
|
||||||
return new Instruction(Type.DUP, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction storeSelfFunc(int i) {
|
|
||||||
return new Instruction(Type.STORE_SELF_FUNC, i);
|
|
||||||
}
|
|
||||||
public static Instruction storeVar(Object i) {
|
|
||||||
return new Instruction(Type.STORE_VAR, i, false);
|
|
||||||
}
|
|
||||||
public static Instruction storeVar(Object i, boolean keep) {
|
|
||||||
return new Instruction(Type.STORE_VAR, i, keep);
|
|
||||||
}
|
|
||||||
public static Instruction storeMember() {
|
|
||||||
return new Instruction(Type.STORE_MEMBER, false);
|
|
||||||
}
|
|
||||||
public static Instruction storeMember(boolean keep) {
|
|
||||||
return new Instruction(Type.STORE_MEMBER, keep);
|
|
||||||
}
|
|
||||||
public static Instruction discard() {
|
|
||||||
return new Instruction(Type.DISCARD);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction typeof() {
|
|
||||||
return new Instruction(Type.TYPEOF);
|
|
||||||
}
|
|
||||||
public static Instruction typeof(String varName) {
|
|
||||||
return new Instruction(Type.TYPEOF, varName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction keys(boolean forInFormat) {
|
|
||||||
return new Instruction(Type.KEYS, forInFormat);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction defProp() {
|
|
||||||
return new Instruction(Type.DEF_PROP);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction operation(Operation op) {
|
|
||||||
return new Instruction(Type.OPERATION, op);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
var res = type.toString();
|
|
||||||
|
|
||||||
for (int i = 0; i < params.length; i++) {
|
|
||||||
res += " " + params[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.common;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
public enum Operation {
|
|
||||||
INSTANCEOF(1, 2),
|
|
||||||
IN(2, 2),
|
|
||||||
|
|
||||||
MULTIPLY(3, 2),
|
|
||||||
DIVIDE(4, 2),
|
|
||||||
MODULO(5, 2),
|
|
||||||
ADD(6, 2),
|
|
||||||
SUBTRACT(7, 2),
|
|
||||||
|
|
||||||
USHIFT_RIGHT(8, 2),
|
|
||||||
SHIFT_RIGHT(9, 2),
|
|
||||||
SHIFT_LEFT(10, 2),
|
|
||||||
|
|
||||||
GREATER(11, 2),
|
|
||||||
LESS(12, 2),
|
|
||||||
GREATER_EQUALS(13, 2),
|
|
||||||
LESS_EQUALS(14, 2),
|
|
||||||
LOOSE_EQUALS(15, 2),
|
|
||||||
LOOSE_NOT_EQUALS(16, 2),
|
|
||||||
EQUALS(17, 2),
|
|
||||||
NOT_EQUALS(18, 2),
|
|
||||||
|
|
||||||
AND(19, 2),
|
|
||||||
OR(20, 2),
|
|
||||||
XOR(21, 2),
|
|
||||||
|
|
||||||
NEG(23, 1),
|
|
||||||
POS(24, 1),
|
|
||||||
NOT(25, 1),
|
|
||||||
INVERSE(26, 1);
|
|
||||||
|
|
||||||
private static final HashMap<Integer, Operation> operations = new HashMap<>();
|
|
||||||
|
|
||||||
static {
|
|
||||||
for (var val : Operation.values()) operations.put(val.numeric, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final int numeric;
|
|
||||||
public final int operands;
|
|
||||||
|
|
||||||
private Operation(int numeric, int n) {
|
|
||||||
this.numeric = numeric;
|
|
||||||
this.operands = n;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operation fromNumeric(int i) {
|
|
||||||
return operations.get(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.common;
|
|
||||||
|
|
||||||
import java.lang.ref.ReferenceQueue;
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
|
|
||||||
public class RefTracker {
|
|
||||||
public static void onDestroy(Object obj, Runnable runnable) {
|
|
||||||
var queue = new ReferenceQueue<>();
|
|
||||||
var ref = new WeakReference<>(obj, queue);
|
|
||||||
obj = null;
|
|
||||||
|
|
||||||
var th = new Thread(() -> {
|
|
||||||
try {
|
|
||||||
queue.remove();
|
|
||||||
ref.get();
|
|
||||||
runnable.run();
|
|
||||||
}
|
|
||||||
catch (InterruptedException e) { return; }
|
|
||||||
});
|
|
||||||
th.setDaemon(true);
|
|
||||||
th.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.common.mapping;
|
|
||||||
|
|
||||||
public enum ConvertType {
|
|
||||||
Exact,
|
|
||||||
Lower,
|
|
||||||
Upper,
|
|
||||||
Both,
|
|
||||||
}
|
|
||||||
@@ -1,190 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.common.mapping;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.NavigableSet;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.TreeMap;
|
|
||||||
import java.util.TreeSet;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Filename;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
|
||||||
import me.topchetoeu.jscript.compilation.scope.LocalScopeRecord;
|
|
||||||
import me.topchetoeu.jscript.utils.mapping.SourceMap;
|
|
||||||
|
|
||||||
public class FunctionMap {
|
|
||||||
public static class FunctionMapBuilder {
|
|
||||||
private final TreeMap<Integer, Location> sourceMap = new TreeMap<>();
|
|
||||||
private final HashMap<Location, BreakpointType> breakpoints = new HashMap<>();
|
|
||||||
|
|
||||||
public Location toLocation(int pc) {
|
|
||||||
return sourceMap.headMap(pc, true).firstEntry().getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public FunctionMapBuilder setDebug(Location loc, BreakpointType type) {
|
|
||||||
if (loc == null || type == null || type == BreakpointType.NONE) return this;
|
|
||||||
breakpoints.put(loc, type);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
public FunctionMapBuilder setLocation(int i, Location loc) {
|
|
||||||
if (loc == null || i < 0) return this;
|
|
||||||
sourceMap.put(i, loc);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
public FunctionMapBuilder setLocationAndDebug(int i, Location loc, BreakpointType type) {
|
|
||||||
setDebug(loc, type);
|
|
||||||
setLocation(i, loc);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Location first() {
|
|
||||||
if (sourceMap.size() == 0) return null;
|
|
||||||
return sourceMap.firstEntry().getValue();
|
|
||||||
}
|
|
||||||
public Location last() {
|
|
||||||
if (sourceMap.size() == 0) return null;
|
|
||||||
return sourceMap.lastEntry().getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public FunctionMap build(String[] localNames, String[] captureNames) {
|
|
||||||
return new FunctionMap(sourceMap, breakpoints, localNames, captureNames);
|
|
||||||
}
|
|
||||||
public FunctionMap build(LocalScopeRecord scope) {
|
|
||||||
return new FunctionMap(sourceMap, breakpoints, scope.locals(), scope.captures());
|
|
||||||
}
|
|
||||||
public FunctionMap build() {
|
|
||||||
return new FunctionMap(sourceMap, breakpoints, new String[0], new String[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private FunctionMapBuilder() { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final FunctionMap EMPTY = new FunctionMap();
|
|
||||||
|
|
||||||
private final HashMap<Integer, BreakpointType> bps = new HashMap<>();
|
|
||||||
private final HashMap<Filename, TreeSet<Location>> bpLocs = new HashMap<>();
|
|
||||||
|
|
||||||
private final TreeMap<Integer, Location> pcToLoc = new TreeMap<>();
|
|
||||||
|
|
||||||
public final String[] localNames, captureNames;
|
|
||||||
|
|
||||||
public Location toLocation(int pc, boolean approxiamte) {
|
|
||||||
if (pcToLoc.size() == 0 || pc < 0 || pc > pcToLoc.lastKey()) return null;
|
|
||||||
var res = pcToLoc.get(pc);
|
|
||||||
if (!approxiamte || res != null) return res;
|
|
||||||
var entry = pcToLoc.headMap(pc, true).lastEntry();
|
|
||||||
if (entry == null) return null;
|
|
||||||
else return entry.getValue();
|
|
||||||
}
|
|
||||||
public Location toLocation(int pc) {
|
|
||||||
return toLocation(pc, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BreakpointType getBreakpoint(int pc) {
|
|
||||||
return bps.getOrDefault(pc, BreakpointType.NONE);
|
|
||||||
}
|
|
||||||
public Location correctBreakpoint(Location loc) {
|
|
||||||
var set = bpLocs.get(loc.filename());
|
|
||||||
if (set == null) return null;
|
|
||||||
else return set.ceiling(loc);
|
|
||||||
}
|
|
||||||
public List<Location> correctBreakpoint(Pattern filename, int line, int column) {
|
|
||||||
var candidates = new HashMap<Filename, TreeSet<Location>>();
|
|
||||||
|
|
||||||
for (var name : bpLocs.keySet()) {
|
|
||||||
if (filename.matcher(name.toString()).matches()) {
|
|
||||||
candidates.put(name, bpLocs.get(name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var res = new ArrayList<Location>(candidates.size());
|
|
||||||
for (var candidate : candidates.entrySet()) {
|
|
||||||
res.add(candidate.getValue().ceiling(new Location(line, column, candidate.getKey())));
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
public List<Location> breakpoints(Location start, Location end) {
|
|
||||||
if (!Objects.equals(start.filename(), end.filename())) return List.of();
|
|
||||||
NavigableSet<Location> set = bpLocs.get(start.filename());
|
|
||||||
if (set == null) return List.of();
|
|
||||||
|
|
||||||
if (start != null) set = set.tailSet(start, true);
|
|
||||||
if (end != null) set = set.headSet(end, true);
|
|
||||||
|
|
||||||
return set.stream().collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Location start() {
|
|
||||||
if (pcToLoc.size() == 0) return null;
|
|
||||||
return pcToLoc.firstEntry().getValue();
|
|
||||||
}
|
|
||||||
public Location end() {
|
|
||||||
if (pcToLoc.size() == 0) return null;
|
|
||||||
return pcToLoc.lastEntry().getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public FunctionMap apply(SourceMap map) {
|
|
||||||
var res = new FunctionMap(Map.of(), Map.of(), localNames, captureNames);
|
|
||||||
|
|
||||||
for (var el : pcToLoc.entrySet()) {
|
|
||||||
res.pcToLoc.put(el.getKey(), map.toCompiled(el.getValue()));
|
|
||||||
}
|
|
||||||
|
|
||||||
res.bps.putAll(bps);
|
|
||||||
|
|
||||||
for (var el : bpLocs.entrySet()) {
|
|
||||||
for (var loc : el.getValue()) {
|
|
||||||
loc = map.toCompiled(loc);
|
|
||||||
if (loc == null) continue;
|
|
||||||
|
|
||||||
if (!res.bpLocs.containsKey(loc.filename())) res.bpLocs.put(loc.filename(), new TreeSet<>());
|
|
||||||
res.bpLocs.get(loc.filename()).add(loc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FunctionMap clone() {
|
|
||||||
var res = new FunctionMap(Map.of(), Map.of(), localNames, captureNames);
|
|
||||||
res.pcToLoc.putAll(this.pcToLoc);
|
|
||||||
res.bps.putAll(bps);
|
|
||||||
res.bpLocs.putAll(bpLocs);
|
|
||||||
res.pcToLoc.putAll(pcToLoc);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FunctionMap(Map<Integer, Location> map, Map<Location, BreakpointType> breakpoints, String[] localNames, String[] captureNames) {
|
|
||||||
var locToPc = new HashMap<Location, Integer>();
|
|
||||||
|
|
||||||
for (var el : map.entrySet()) {
|
|
||||||
pcToLoc.put(el.getKey(), el.getValue());
|
|
||||||
locToPc.putIfAbsent(el.getValue(), el.getKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var el : breakpoints.entrySet()) {
|
|
||||||
if (el.getValue() == null || el.getValue() == BreakpointType.NONE) continue;
|
|
||||||
bps.put(locToPc.get(el.getKey()), el.getValue());
|
|
||||||
|
|
||||||
if (!bpLocs.containsKey(el.getKey().filename())) bpLocs.put(el.getKey().filename(), new TreeSet<>());
|
|
||||||
bpLocs.get(el.getKey().filename()).add(el.getKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
this.localNames = localNames;
|
|
||||||
this.captureNames = captureNames;
|
|
||||||
}
|
|
||||||
private FunctionMap() {
|
|
||||||
localNames = new String[0];
|
|
||||||
captureNames = new String[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FunctionMapBuilder builder() {
|
|
||||||
return new FunctionMapBuilder();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.FunctionBody;
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
|
||||||
import me.topchetoeu.jscript.common.mapping.FunctionMap;
|
|
||||||
import me.topchetoeu.jscript.common.mapping.FunctionMap.FunctionMapBuilder;
|
|
||||||
import me.topchetoeu.jscript.compilation.scope.LocalScopeRecord;
|
|
||||||
|
|
||||||
public class CompileResult {
|
|
||||||
public final Vector<Instruction> instructions = new Vector<>();
|
|
||||||
public final List<CompileResult> children = new LinkedList<>();
|
|
||||||
public final FunctionMapBuilder map = FunctionMap.builder();
|
|
||||||
public final LocalScopeRecord scope;
|
|
||||||
public int length = 0;
|
|
||||||
|
|
||||||
public int temp() {
|
|
||||||
instructions.add(null);
|
|
||||||
return instructions.size() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompileResult add(Instruction instr) {
|
|
||||||
instructions.add(instr);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
public CompileResult set(int i, Instruction instr) {
|
|
||||||
instructions.set(i, instr);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
public Instruction get(int i) {
|
|
||||||
return instructions.get(i);
|
|
||||||
}
|
|
||||||
public int size() { return instructions.size(); }
|
|
||||||
|
|
||||||
public void setDebug(Location loc, BreakpointType type) {
|
|
||||||
map.setDebug(loc, type);
|
|
||||||
}
|
|
||||||
public void setLocation(int i, Location loc) {
|
|
||||||
map.setLocation(i, loc);
|
|
||||||
}
|
|
||||||
public void setLocationAndDebug(int i, Location loc, BreakpointType type) {
|
|
||||||
map.setLocationAndDebug(i, loc, type);
|
|
||||||
}
|
|
||||||
public void setDebug(BreakpointType type) {
|
|
||||||
setDebug(map.last(), type);
|
|
||||||
}
|
|
||||||
public void setLocation(Location type) {
|
|
||||||
setLocation(instructions.size() - 1, type);
|
|
||||||
}
|
|
||||||
public void setLocationAndDebug(Location loc, BreakpointType type) {
|
|
||||||
setLocationAndDebug(instructions.size() - 1, loc, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompileResult addChild(CompileResult child) {
|
|
||||||
this.children.add(child);
|
|
||||||
return child;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FunctionMap map() {
|
|
||||||
return map.build(scope);
|
|
||||||
}
|
|
||||||
public FunctionBody body() {
|
|
||||||
var builtChildren = new FunctionBody[children.size()];
|
|
||||||
|
|
||||||
for (var i = 0; i < children.size(); i++) builtChildren[i] = children.get(i).body();
|
|
||||||
|
|
||||||
return new FunctionBody(scope.localsCount(), length, instructions.toArray(Instruction[]::new), builtChildren);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompileResult(LocalScopeRecord scope) {
|
|
||||||
this.scope = scope;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
|
||||||
|
|
||||||
public abstract class Statement {
|
|
||||||
private Location _loc;
|
|
||||||
|
|
||||||
public boolean pure() { return false; }
|
|
||||||
public void declare(CompileResult target) { }
|
|
||||||
|
|
||||||
public void compile(CompileResult target, boolean pollute, BreakpointType type) {
|
|
||||||
int start = target.size();
|
|
||||||
compile(target, pollute);
|
|
||||||
if (target.size() != start) target.setLocationAndDebug(start, loc(), type);
|
|
||||||
}
|
|
||||||
public void compile(CompileResult target, boolean pollute) {
|
|
||||||
compile(target, pollute, BreakpointType.NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Location loc() { return _loc; }
|
|
||||||
public void setLoc(Location loc) { _loc = loc; }
|
|
||||||
|
|
||||||
protected Statement(Location loc) {
|
|
||||||
this._loc = loc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
|
|
||||||
|
|
||||||
public class ThrowSyntaxStatement extends Statement {
|
|
||||||
public final String name;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileResult target, boolean pollute) {
|
|
||||||
target.add(Instruction.throwSyntax(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
public ThrowSyntaxStatement(SyntaxException e) {
|
|
||||||
super(e.loc);
|
|
||||||
this.name = e.msg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
public class BreakStatement extends Statement {
|
|
||||||
public final String label;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileResult target, boolean pollute) {
|
|
||||||
target.add(Instruction.nop("break", label));
|
|
||||||
if (pollute) target.add(Instruction.pushUndefined());
|
|
||||||
}
|
|
||||||
|
|
||||||
public BreakStatement(Location loc, String label) {
|
|
||||||
super(loc);
|
|
||||||
this.label = label;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
public class ContinueStatement extends Statement {
|
|
||||||
public final String label;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileResult target, boolean pollute) {
|
|
||||||
target.add(Instruction.nop(loc(), "cont", label));
|
|
||||||
if (pollute) target.add(Instruction.pushUndefined());
|
|
||||||
}
|
|
||||||
|
|
||||||
public ContinueStatement(Location loc, String label) {
|
|
||||||
super(loc);
|
|
||||||
this.label = label;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
public class DebugStatement extends Statement {
|
|
||||||
@Override
|
|
||||||
public void compile(CompileResult target, boolean pollute) {
|
|
||||||
target.add(Instruction.debug());
|
|
||||||
if (pollute) target.add(Instruction.pushUndefined());
|
|
||||||
}
|
|
||||||
|
|
||||||
public DebugStatement(Location loc) {
|
|
||||||
super(loc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
public class DeleteStatement extends Statement {
|
|
||||||
public final Statement key;
|
|
||||||
public final Statement value;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileResult target, boolean pollute) {
|
|
||||||
value.compile(target, true);
|
|
||||||
key.compile(target, true);
|
|
||||||
|
|
||||||
target.add(Instruction.delete());
|
|
||||||
if (pollute) target.add(Instruction.pushValue(true));
|
|
||||||
}
|
|
||||||
|
|
||||||
public DeleteStatement(Location loc, Statement key, Statement value) {
|
|
||||||
super(loc);
|
|
||||||
this.key = key;
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
public class DoWhileStatement extends Statement {
|
|
||||||
public final Statement condition, body;
|
|
||||||
public final String label;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void declare(CompileResult target) {
|
|
||||||
body.declare(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileResult target, boolean pollute) {
|
|
||||||
int start = target.size();
|
|
||||||
body.compile(target, false, BreakpointType.STEP_OVER);
|
|
||||||
int mid = target.size();
|
|
||||||
condition.compile(target, true, BreakpointType.STEP_OVER);
|
|
||||||
int end = target.size();
|
|
||||||
|
|
||||||
WhileStatement.replaceBreaks(target, label, start, mid - 1, mid, end + 1);
|
|
||||||
target.add(Instruction.jmpIf(start - end));
|
|
||||||
}
|
|
||||||
|
|
||||||
public DoWhileStatement(Location loc, String label, Statement condition, Statement body) {
|
|
||||||
super(loc);
|
|
||||||
this.label = label;
|
|
||||||
this.condition = condition;
|
|
||||||
this.body = body;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.common.Operation;
|
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
public class ForInStatement extends Statement {
|
|
||||||
public final String varName;
|
|
||||||
public final boolean isDeclaration;
|
|
||||||
public final Statement varValue, object, body;
|
|
||||||
public final String label;
|
|
||||||
public final Location varLocation;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void declare(CompileResult target) {
|
|
||||||
body.declare(target);
|
|
||||||
if (isDeclaration) target.scope.define(varName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileResult target, boolean pollute) {
|
|
||||||
var key = target.scope.getKey(varName);
|
|
||||||
|
|
||||||
if (key instanceof String) target.add(Instruction.makeVar((String)key));
|
|
||||||
|
|
||||||
if (varValue != null) {
|
|
||||||
varValue.compile(target, true);
|
|
||||||
target.add(Instruction.storeVar(target.scope.getKey(varName)));
|
|
||||||
}
|
|
||||||
|
|
||||||
object.compile(target, true, BreakpointType.STEP_OVER);
|
|
||||||
target.add(Instruction.keys(true));
|
|
||||||
|
|
||||||
int start = target.size();
|
|
||||||
target.add(Instruction.dup());
|
|
||||||
target.add(Instruction.pushUndefined());
|
|
||||||
target.add(Instruction.operation(Operation.EQUALS));
|
|
||||||
int mid = target.temp();
|
|
||||||
|
|
||||||
target.add(Instruction.pushValue("value")).setLocation(varLocation);
|
|
||||||
target.add(Instruction.loadMember()).setLocation(varLocation);
|
|
||||||
target.add(Instruction.storeVar(key)).setLocationAndDebug(object.loc(), BreakpointType.STEP_OVER);
|
|
||||||
|
|
||||||
body.compile(target, false, BreakpointType.STEP_OVER);
|
|
||||||
|
|
||||||
int end = target.size();
|
|
||||||
|
|
||||||
WhileStatement.replaceBreaks(target, label, mid + 1, end, start, end + 1);
|
|
||||||
|
|
||||||
target.add(Instruction.jmp(start - end));
|
|
||||||
target.add(Instruction.discard());
|
|
||||||
target.set(mid, Instruction.jmpIf(end - mid + 1));
|
|
||||||
if (pollute) target.add(Instruction.pushUndefined());
|
|
||||||
}
|
|
||||||
|
|
||||||
public ForInStatement(Location loc, Location varLocation, String label, boolean isDecl, String varName, Statement varValue, Statement object, Statement body) {
|
|
||||||
super(loc);
|
|
||||||
this.varLocation = varLocation;
|
|
||||||
this.label = label;
|
|
||||||
this.isDeclaration = isDecl;
|
|
||||||
this.varName = varName;
|
|
||||||
this.varValue = varValue;
|
|
||||||
this.object = object;
|
|
||||||
this.body = body;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
public class ForStatement extends Statement {
|
|
||||||
public final Statement declaration, assignment, condition, body;
|
|
||||||
public final String label;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void declare(CompileResult target) {
|
|
||||||
declaration.declare(target);
|
|
||||||
body.declare(target);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void compile(CompileResult target, boolean pollute) {
|
|
||||||
declaration.compile(target, false, BreakpointType.STEP_OVER);
|
|
||||||
|
|
||||||
int start = target.size();
|
|
||||||
condition.compile(target, true, BreakpointType.STEP_OVER);
|
|
||||||
int mid = target.temp();
|
|
||||||
body.compile(target, false, BreakpointType.STEP_OVER);
|
|
||||||
int beforeAssign = target.size();
|
|
||||||
assignment.compile(target, false, BreakpointType.STEP_OVER);
|
|
||||||
int end = target.size();
|
|
||||||
|
|
||||||
WhileStatement.replaceBreaks(target, label, mid + 1, end, beforeAssign, end + 1);
|
|
||||||
|
|
||||||
target.add(Instruction.jmp(start - end));
|
|
||||||
target.set(mid, Instruction.jmpIfNot(end - mid + 1));
|
|
||||||
if (pollute) target.add(Instruction.pushUndefined());
|
|
||||||
}
|
|
||||||
|
|
||||||
public ForStatement(Location loc, String label, Statement declaration, Statement condition, Statement assignment, Statement body) {
|
|
||||||
super(loc);
|
|
||||||
this.label = label;
|
|
||||||
this.declaration = declaration;
|
|
||||||
this.condition = condition;
|
|
||||||
this.assignment = assignment;
|
|
||||||
this.body = body;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
public class IfStatement extends Statement {
|
|
||||||
public final Statement condition, body, elseBody;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void declare(CompileResult target) {
|
|
||||||
body.declare(target);
|
|
||||||
if (elseBody != null) elseBody.declare(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute, BreakpointType breakpoint) {
|
|
||||||
condition.compile(target, true, breakpoint);
|
|
||||||
|
|
||||||
if (elseBody == null) {
|
|
||||||
int i = target.temp();
|
|
||||||
body.compile(target, pollute, breakpoint);
|
|
||||||
int endI = target.size();
|
|
||||||
target.set(i, Instruction.jmpIfNot(endI - i));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
int start = target.temp();
|
|
||||||
body.compile(target, pollute, breakpoint);
|
|
||||||
int mid = target.temp();
|
|
||||||
elseBody.compile(target, pollute, breakpoint);
|
|
||||||
int end = target.size();
|
|
||||||
|
|
||||||
target.set(start, Instruction.jmpIfNot(mid - start + 1));
|
|
||||||
target.set(mid, Instruction.jmp(end - mid));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute) {
|
|
||||||
compile(target, pollute, BreakpointType.STEP_IN);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IfStatement(Location loc, Statement condition, Statement body, Statement elseBody) {
|
|
||||||
super(loc);
|
|
||||||
this.condition = condition;
|
|
||||||
this.body = body;
|
|
||||||
this.elseBody = elseBody;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
public class ReturnStatement extends Statement {
|
|
||||||
public final Statement value;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileResult target, boolean pollute) {
|
|
||||||
if (value == null) target.add(Instruction.pushUndefined());
|
|
||||||
else value.compile(target, true);
|
|
||||||
target.add(Instruction.ret()).setLocation(loc());
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReturnStatement(Location loc, Statement value) {
|
|
||||||
super(loc);
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.common.Operation;
|
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
|
||||||
import me.topchetoeu.jscript.common.Instruction.Type;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
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(CompileResult target) {
|
|
||||||
for (var stm : body) stm.declare(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileResult target, boolean pollute) {
|
|
||||||
var caseToStatement = new HashMap<Integer, Integer>();
|
|
||||||
var statementToIndex = new HashMap<Integer, Integer>();
|
|
||||||
|
|
||||||
value.compile(target, true, BreakpointType.STEP_OVER);
|
|
||||||
|
|
||||||
for (var ccase : cases) {
|
|
||||||
target.add(Instruction.dup());
|
|
||||||
ccase.value.compile(target, true);
|
|
||||||
target.add(Instruction.operation(Operation.EQUALS));
|
|
||||||
caseToStatement.put(target.temp(), ccase.statementI);
|
|
||||||
}
|
|
||||||
|
|
||||||
int start = target.temp();
|
|
||||||
|
|
||||||
for (var stm : body) {
|
|
||||||
statementToIndex.put(statementToIndex.size(), target.size());
|
|
||||||
stm.compile(target, false, BreakpointType.STEP_OVER);
|
|
||||||
}
|
|
||||||
|
|
||||||
int end = target.size();
|
|
||||||
target.add(Instruction.discard());
|
|
||||||
if (pollute) target.add(Instruction.pushUndefined());
|
|
||||||
|
|
||||||
if (defaultI < 0 || defaultI >= body.length) target.set(start, Instruction.jmp(end - start));
|
|
||||||
else target.set(start, Instruction.jmp(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(end - i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (var el : caseToStatement.entrySet()) {
|
|
||||||
var i = statementToIndex.get(el.getValue());
|
|
||||||
if (i == null) i = end;
|
|
||||||
target.set(el.getKey(), Instruction.jmpIf(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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
public class ThrowStatement extends Statement {
|
|
||||||
public final Statement value;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileResult target, boolean pollute) {
|
|
||||||
value.compile(target, true);
|
|
||||||
target.add(Instruction.throwInstr()).setLocation(loc());
|
|
||||||
}
|
|
||||||
|
|
||||||
public ThrowStatement(Location loc, Statement value) {
|
|
||||||
super(loc);
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
public class TryStatement extends Statement {
|
|
||||||
public final Statement tryBody;
|
|
||||||
public final Statement catchBody;
|
|
||||||
public final Statement finallyBody;
|
|
||||||
public final String name;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void declare(CompileResult target) {
|
|
||||||
tryBody.declare(target);
|
|
||||||
if (catchBody != null) catchBody.declare(target);
|
|
||||||
if (finallyBody != null) finallyBody.declare(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileResult target, boolean pollute, BreakpointType bpt) {
|
|
||||||
int replace = target.temp();
|
|
||||||
|
|
||||||
int start = replace + 1, catchStart = -1, finallyStart = -1;
|
|
||||||
|
|
||||||
tryBody.compile(target, false);
|
|
||||||
target.add(Instruction.tryEnd());
|
|
||||||
|
|
||||||
if (catchBody != null) {
|
|
||||||
catchStart = target.size() - start;
|
|
||||||
target.scope.define(name, true);
|
|
||||||
catchBody.compile(target, false);
|
|
||||||
target.scope.undefine();
|
|
||||||
target.add(Instruction.tryEnd());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (finallyBody != null) {
|
|
||||||
finallyStart = target.size() - start;
|
|
||||||
finallyBody.compile(target, false);
|
|
||||||
target.add(Instruction.tryEnd());
|
|
||||||
}
|
|
||||||
|
|
||||||
target.set(replace, Instruction.tryStart(catchStart, finallyStart, target.size() - start));
|
|
||||||
target.setLocationAndDebug(replace, loc(), BreakpointType.STEP_OVER);
|
|
||||||
|
|
||||||
if (pollute) target.add(Instruction.pushUndefined());
|
|
||||||
}
|
|
||||||
|
|
||||||
public TryStatement(Location loc, Statement tryBody, Statement catchBody, Statement finallyBody, String name) {
|
|
||||||
super(loc);
|
|
||||||
this.tryBody = tryBody;
|
|
||||||
this.catchBody = catchBody;
|
|
||||||
this.finallyBody = finallyBody;
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
|
||||||
import me.topchetoeu.jscript.common.Instruction.Type;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
public class WhileStatement extends Statement {
|
|
||||||
public final Statement condition, body;
|
|
||||||
public final String label;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void declare(CompileResult target) {
|
|
||||||
body.declare(target);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void compile(CompileResult target, boolean pollute) {
|
|
||||||
int start = target.size();
|
|
||||||
condition.compile(target, true);
|
|
||||||
int mid = target.temp();
|
|
||||||
body.compile(target, false, BreakpointType.STEP_OVER);
|
|
||||||
|
|
||||||
int end = target.size();
|
|
||||||
|
|
||||||
replaceBreaks(target, label, mid + 1, end, start, end + 1);
|
|
||||||
|
|
||||||
target.add(Instruction.jmp(start - end));
|
|
||||||
target.set(mid, Instruction.jmpIfNot(end - mid + 1));
|
|
||||||
if (pollute) target.add(Instruction.pushUndefined());
|
|
||||||
}
|
|
||||||
|
|
||||||
public WhileStatement(Location loc, String label, Statement condition, Statement body) {
|
|
||||||
super(loc);
|
|
||||||
this.label = label;
|
|
||||||
this.condition = condition;
|
|
||||||
this.body = body;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void replaceBreaks(CompileResult target, String label, int start, int end, int continuePoint, int breakPoint) {
|
|
||||||
for (int i = start; i < end; i++) {
|
|
||||||
var instr = target.get(i);
|
|
||||||
if (instr.type == Type.NOP && instr.is(0, "cont") && (instr.get(1) == null || instr.is(1, label))) {
|
|
||||||
target.set(i, Instruction.jmp(continuePoint - i));
|
|
||||||
}
|
|
||||||
if (instr.type == Type.NOP && instr.is(0, "break") && (instr.get(1) == null || instr.is(1, label))) {
|
|
||||||
target.set(i, Instruction.jmp(breakPoint - i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
public class 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(CompileResult target, boolean pollute) {
|
|
||||||
target.add(Instruction.loadArr(statements.length));
|
|
||||||
|
|
||||||
for (var i = 0; i < statements.length; i++) {
|
|
||||||
var el = statements[i];
|
|
||||||
if (el != null) {
|
|
||||||
target.add(Instruction.dup());
|
|
||||||
target.add(Instruction.pushValue(i));
|
|
||||||
el.compile(target, true);
|
|
||||||
target.add(Instruction.storeMember());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pollute) target.add(Instruction.discard());
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArrayStatement(Location loc, Statement[] statements) {
|
|
||||||
super(loc);
|
|
||||||
this.statements = statements;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
public class CallStatement extends Statement {
|
|
||||||
public final Statement func;
|
|
||||||
public final Statement[] args;
|
|
||||||
public final boolean isNew;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileResult target, boolean pollute, BreakpointType type) {
|
|
||||||
if (isNew) func.compile(target, true);
|
|
||||||
else if (func instanceof IndexStatement) {
|
|
||||||
((IndexStatement)func).compile(target, true, true);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
target.add(Instruction.pushUndefined());
|
|
||||||
func.compile(target, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var arg : args) arg.compile(target, true);
|
|
||||||
|
|
||||||
if (isNew) target.add(Instruction.callNew(args.length)).setLocationAndDebug(loc(), type);
|
|
||||||
else target.add(Instruction.call(args.length)).setLocationAndDebug(loc(), type);
|
|
||||||
|
|
||||||
if (!pollute) target.add(Instruction.discard());
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void compile(CompileResult target, boolean pollute) {
|
|
||||||
compile(target, pollute, BreakpointType.STEP_IN);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CallStatement(Location loc, boolean isNew, Statement func, Statement ...args) {
|
|
||||||
super(loc);
|
|
||||||
this.isNew = isNew;
|
|
||||||
this.func = func;
|
|
||||||
this.args = args;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.common.Operation;
|
|
||||||
import me.topchetoeu.jscript.compilation.AssignableStatement;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
public class ChangeStatement extends Statement {
|
|
||||||
public final AssignableStatement value;
|
|
||||||
public final double addAmount;
|
|
||||||
public final boolean postfix;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileResult target, boolean pollute) {
|
|
||||||
value.toAssign(new ConstantStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, true);
|
|
||||||
if (!pollute) target.add(Instruction.discard());
|
|
||||||
else if (postfix) {
|
|
||||||
target.add(Instruction.pushValue(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,47 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
public class ConstantStatement extends Statement {
|
|
||||||
public final Object value;
|
|
||||||
public final boolean isNull;
|
|
||||||
|
|
||||||
@Override public boolean pure() { return true; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileResult target, boolean pollute) {
|
|
||||||
if (pollute) {
|
|
||||||
if (isNull) target.add(Instruction.pushNull());
|
|
||||||
else if (value instanceof Double) target.add(Instruction.pushValue((Double)value));
|
|
||||||
else if (value instanceof String) target.add(Instruction.pushValue((String)value));
|
|
||||||
else if (value instanceof Boolean) target.add(Instruction.pushValue((Boolean)value));
|
|
||||||
else target.add(Instruction.pushUndefined());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ConstantStatement(Location loc, Object val, boolean isNull) {
|
|
||||||
super(loc);
|
|
||||||
this.value = val;
|
|
||||||
this.isNull = isNull;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConstantStatement(Location loc, boolean val) {
|
|
||||||
this(loc, val, false);
|
|
||||||
}
|
|
||||||
public ConstantStatement(Location loc, String val) {
|
|
||||||
this(loc, val, false);
|
|
||||||
}
|
|
||||||
public ConstantStatement(Location loc, double val) {
|
|
||||||
this(loc, val, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ConstantStatement ofUndefined(Location loc) {
|
|
||||||
return new ConstantStatement(loc, null, false);
|
|
||||||
}
|
|
||||||
public static ConstantStatement ofNull(Location loc) {
|
|
||||||
return new ConstantStatement(loc, null, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
public class DiscardStatement extends Statement {
|
|
||||||
public final Statement value;
|
|
||||||
|
|
||||||
@Override public boolean pure() { return value.pure(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileResult target, boolean pollute) {
|
|
||||||
value.compile(target, false);
|
|
||||||
if (pollute) target.add(Instruction.pushUndefined());
|
|
||||||
}
|
|
||||||
|
|
||||||
public DiscardStatement(Location loc, Statement val) {
|
|
||||||
super(loc);
|
|
||||||
this.value = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
|
||||||
import me.topchetoeu.jscript.common.Instruction.Type;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompoundStatement;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
|
|
||||||
|
|
||||||
public class FunctionStatement extends Statement {
|
|
||||||
public final CompoundStatement body;
|
|
||||||
public final String varName;
|
|
||||||
public final String[] args;
|
|
||||||
public final boolean statement;
|
|
||||||
public final Location end;
|
|
||||||
|
|
||||||
@Override public boolean pure() { return varName == null && statement; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void declare(CompileResult target) {
|
|
||||||
if (varName != null && statement) target.scope.define(varName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void checkBreakAndCont(CompileResult target, int start) {
|
|
||||||
for (int i = start; i < target.size(); i++) {
|
|
||||||
if (target.get(i).type == Type.NOP) {
|
|
||||||
if (target.get(i).is(0, "break") ) {
|
|
||||||
throw new SyntaxException(target.map.toLocation(i), "Break was placed outside a loop.");
|
|
||||||
}
|
|
||||||
if (target.get(i).is(0, "cont")) {
|
|
||||||
throw new SyntaxException(target.map.toLocation(i), "Continue was placed outside a loop.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private CompileResult compileBody(CompileResult target, boolean pollute, BreakpointType bp) {
|
|
||||||
for (var i = 0; i < args.length; i++) {
|
|
||||||
for (var j = 0; j < i; j++) {
|
|
||||||
if (args[i].equals(args[j])) {
|
|
||||||
throw new SyntaxException(loc(), "Duplicate parameter '" + args[i] + "'.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var subtarget = new CompileResult(target.scope.child());
|
|
||||||
|
|
||||||
subtarget.scope.define("this");
|
|
||||||
var argsVar = subtarget.scope.define("arguments");
|
|
||||||
|
|
||||||
if (args.length > 0) {
|
|
||||||
for (var i = 0; i < args.length; i++) {
|
|
||||||
subtarget.add(Instruction.loadVar(argsVar));
|
|
||||||
subtarget.add(Instruction.pushValue(i));
|
|
||||||
subtarget.add(Instruction.loadMember());
|
|
||||||
subtarget.add(Instruction.storeVar(subtarget.scope.define(args[i])));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!statement && this.varName != null) {
|
|
||||||
subtarget.add(Instruction.storeSelfFunc((int)subtarget.scope.define(this.varName))).setLocationAndDebug(loc(), bp);
|
|
||||||
}
|
|
||||||
|
|
||||||
body.declare(subtarget);
|
|
||||||
body.compile(subtarget, false);
|
|
||||||
subtarget.length = args.length;
|
|
||||||
subtarget.add(Instruction.ret()).setLocation(end);
|
|
||||||
checkBreakAndCont(subtarget, 0);
|
|
||||||
|
|
||||||
if (pollute) target.add(Instruction.loadFunc(target.children.size(), subtarget.scope.getCaptures()));
|
|
||||||
return target.addChild(subtarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
|
||||||
if (this.varName != null) name = this.varName;
|
|
||||||
|
|
||||||
var hasVar = this.varName != null && statement;
|
|
||||||
var hasName = name != null;
|
|
||||||
|
|
||||||
compileBody(target, pollute || hasVar || hasName, bp);
|
|
||||||
|
|
||||||
if (hasName) {
|
|
||||||
if (pollute || hasVar) target.add(Instruction.dup());
|
|
||||||
target.add(Instruction.pushValue("name"));
|
|
||||||
target.add(Instruction.pushValue(name));
|
|
||||||
target.add(Instruction.storeMember());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasVar) {
|
|
||||||
var key = target.scope.getKey(this.varName);
|
|
||||||
|
|
||||||
if (key instanceof String) target.add(Instruction.makeVar((String)key));
|
|
||||||
target.add(Instruction.storeVar(target.scope.getKey(this.varName), false));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void compile(CompileResult target, boolean pollute, String name) {
|
|
||||||
compile(target, pollute, name, BreakpointType.NONE);
|
|
||||||
}
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute, BreakpointType bp) {
|
|
||||||
compile(target, pollute, (String)null, bp);
|
|
||||||
}
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute) {
|
|
||||||
compile(target, pollute, (String)null, BreakpointType.NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public FunctionStatement(Location loc, Location end, String varName, String[] args, boolean statement, CompoundStatement body) {
|
|
||||||
super(loc);
|
|
||||||
|
|
||||||
this.end = end;
|
|
||||||
this.varName = varName;
|
|
||||||
this.statement = statement;
|
|
||||||
|
|
||||||
this.args = args;
|
|
||||||
this.body = body;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void compileWithName(Statement stm, CompileResult target, boolean pollute, String name) {
|
|
||||||
if (stm instanceof FunctionStatement) ((FunctionStatement)stm).compile(target, pollute, name);
|
|
||||||
else stm.compile(target, pollute);
|
|
||||||
}
|
|
||||||
public static void compileWithName(Statement stm, CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
|
||||||
if (stm instanceof FunctionStatement) ((FunctionStatement)stm).compile(target, pollute, name, bp);
|
|
||||||
else stm.compile(target, pollute, bp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
public class GlobalThisStatement extends Statement {
|
|
||||||
@Override public boolean pure() { return true; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileResult target, boolean pollute) {
|
|
||||||
if (pollute) target.add(Instruction.loadGlob());
|
|
||||||
}
|
|
||||||
|
|
||||||
public GlobalThisStatement(Location loc) {
|
|
||||||
super(loc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.common.Operation;
|
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
public class IndexAssignStatement extends Statement {
|
|
||||||
public final Statement object;
|
|
||||||
public final Statement index;
|
|
||||||
public final Statement value;
|
|
||||||
public final Operation operation;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileResult target, boolean pollute) {
|
|
||||||
if (operation != null) {
|
|
||||||
object.compile(target, true);
|
|
||||||
index.compile(target, true);
|
|
||||||
target.add(Instruction.dup(2));
|
|
||||||
|
|
||||||
target.add(Instruction.loadMember());
|
|
||||||
value.compile(target, true);
|
|
||||||
target.add(Instruction.operation(operation));
|
|
||||||
|
|
||||||
target.add(Instruction.storeMember(pollute)).setLocationAndDebug(loc(), BreakpointType.STEP_IN);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
object.compile(target, true);
|
|
||||||
index.compile(target, true);
|
|
||||||
value.compile(target, true);
|
|
||||||
|
|
||||||
target.add(Instruction.storeMember(pollute)).setLocationAndDebug(loc(), BreakpointType.STEP_IN);;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IndexAssignStatement(Location loc, Statement object, Statement index, Statement value, Operation operation) {
|
|
||||||
super(loc);
|
|
||||||
this.object = object;
|
|
||||||
this.index = index;
|
|
||||||
this.value = value;
|
|
||||||
this.operation = operation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.common.Operation;
|
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
|
||||||
import me.topchetoeu.jscript.compilation.AssignableStatement;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
public class IndexStatement extends AssignableStatement {
|
|
||||||
public final Statement object;
|
|
||||||
public final Statement index;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Statement toAssign(Statement val, Operation operation) {
|
|
||||||
return new IndexAssignStatement(loc(), object, index, val, operation);
|
|
||||||
}
|
|
||||||
public void compile(CompileResult target, boolean dupObj, boolean pollute) {
|
|
||||||
object.compile(target, true);
|
|
||||||
if (dupObj) target.add(Instruction.dup());
|
|
||||||
|
|
||||||
index.compile(target, true);
|
|
||||||
target.add(Instruction.loadMember()).setLocationAndDebug(loc(), BreakpointType.STEP_IN);
|
|
||||||
if (!pollute) target.add(Instruction.discard());
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void compile(CompileResult target, boolean pollute) {
|
|
||||||
compile(target, false, pollute);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IndexStatement(Location loc, Statement object, Statement index) {
|
|
||||||
super(loc);
|
|
||||||
this.object = object;
|
|
||||||
this.index = index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
public class LazyAndStatement extends Statement {
|
|
||||||
public final Statement first, second;
|
|
||||||
|
|
||||||
@Override public boolean pure() { return first.pure() && second.pure(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileResult target, boolean pollute) {
|
|
||||||
first.compile(target, true);
|
|
||||||
if (pollute) target.add(Instruction.dup());
|
|
||||||
int start = target.temp();
|
|
||||||
if (pollute) target.add(Instruction.discard());
|
|
||||||
second.compile(target, pollute);
|
|
||||||
target.set(start, Instruction.jmpIfNot(target.size() - start));
|
|
||||||
}
|
|
||||||
|
|
||||||
public LazyAndStatement(Location loc, Statement first, Statement second) {
|
|
||||||
super(loc);
|
|
||||||
this.first = first;
|
|
||||||
this.second = second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
public class LazyOrStatement extends Statement {
|
|
||||||
public final Statement first, second;
|
|
||||||
|
|
||||||
@Override public boolean pure() { return first.pure() && second.pure(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileResult target, boolean pollute) {
|
|
||||||
first.compile(target, true);
|
|
||||||
if (pollute) target.add(Instruction.dup());
|
|
||||||
int start = target.temp();
|
|
||||||
if (pollute) target.add(Instruction.discard());
|
|
||||||
second.compile(target, pollute);
|
|
||||||
target.set(start, Instruction.jmpIf(target.size() - start));
|
|
||||||
}
|
|
||||||
|
|
||||||
public LazyOrStatement(Location loc, Statement first, Statement second) {
|
|
||||||
super(loc);
|
|
||||||
this.first = first;
|
|
||||||
this.second = second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
public class ObjectStatement extends Statement {
|
|
||||||
public final Map<String, Statement> map;
|
|
||||||
public final Map<String, FunctionStatement> getters;
|
|
||||||
public final Map<String, FunctionStatement> setters;
|
|
||||||
|
|
||||||
@Override public boolean pure() {
|
|
||||||
for (var el : map.values()) {
|
|
||||||
if (!el.pure()) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileResult target, boolean pollute) {
|
|
||||||
target.add(Instruction.loadObj());
|
|
||||||
|
|
||||||
for (var el : map.entrySet()) {
|
|
||||||
target.add(Instruction.dup());
|
|
||||||
target.add(Instruction.pushValue(el.getKey()));
|
|
||||||
var val = el.getValue();
|
|
||||||
FunctionStatement.compileWithName(val, target, true, el.getKey().toString());
|
|
||||||
target.add(Instruction.storeMember());
|
|
||||||
}
|
|
||||||
|
|
||||||
var keys = new ArrayList<Object>();
|
|
||||||
keys.addAll(getters.keySet());
|
|
||||||
keys.addAll(setters.keySet());
|
|
||||||
|
|
||||||
for (var key : keys) {
|
|
||||||
target.add(Instruction.pushValue((String)key));
|
|
||||||
|
|
||||||
if (getters.containsKey(key)) getters.get(key).compile(target, true);
|
|
||||||
else target.add(Instruction.pushUndefined());
|
|
||||||
|
|
||||||
if (setters.containsKey(key)) setters.get(key).compile(target, true);
|
|
||||||
else target.add(Instruction.pushUndefined());
|
|
||||||
|
|
||||||
target.add(Instruction.defProp());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pollute) target.add(Instruction.discard());
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectStatement(Location loc, Map<String, Statement> map, Map<String, FunctionStatement> getters, Map<String, FunctionStatement> setters) {
|
|
||||||
super(loc);
|
|
||||||
this.map = map;
|
|
||||||
this.getters = getters;
|
|
||||||
this.setters = setters;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.common.Operation;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
public class 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(CompileResult target, boolean pollute) {
|
|
||||||
for (var arg : args) {
|
|
||||||
arg.compile(target, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pollute) target.add(Instruction.operation(operation));
|
|
||||||
else target.add(Instruction.discard());
|
|
||||||
}
|
|
||||||
|
|
||||||
public OperationStatement(Location loc, Operation operation, Statement ...args) {
|
|
||||||
super(loc);
|
|
||||||
this.operation = operation;
|
|
||||||
this.args = args;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
public class 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(CompileResult target, boolean pollute) {
|
|
||||||
target.add(Instruction.loadRegex(pattern, flags));
|
|
||||||
if (!pollute) target.add(Instruction.discard());
|
|
||||||
}
|
|
||||||
|
|
||||||
public RegexStatement(Location loc, String pattern, String flags) {
|
|
||||||
super(loc);
|
|
||||||
this.pattern = pattern;
|
|
||||||
this.flags = flags;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.common.Operation;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
public class VariableAssignStatement extends Statement {
|
|
||||||
public final String name;
|
|
||||||
public final Statement value;
|
|
||||||
public final Operation operation;
|
|
||||||
|
|
||||||
@Override public boolean pure() { return false; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileResult target, boolean pollute) {
|
|
||||||
var i = target.scope.getKey(name);
|
|
||||||
if (operation != null) {
|
|
||||||
target.add(Instruction.loadVar(i));
|
|
||||||
FunctionStatement.compileWithName(value, target, true, name);
|
|
||||||
target.add(Instruction.operation(operation));
|
|
||||||
target.add(Instruction.storeVar(i, pollute));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
FunctionStatement.compileWithName(value, target, true, name);
|
|
||||||
target.add(Instruction.storeVar(i, pollute));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public VariableAssignStatement(Location loc, String name, Statement val, Operation operation) {
|
|
||||||
super(loc);
|
|
||||||
this.name = name;
|
|
||||||
this.value = val;
|
|
||||||
this.operation = operation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
public class VariableIndexStatement extends Statement {
|
|
||||||
public final int index;
|
|
||||||
|
|
||||||
@Override public boolean pure() { return true; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileResult target, boolean pollute) {
|
|
||||||
if (pollute) target.add(Instruction.loadVar(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
public VariableIndexStatement(Location loc, int i) {
|
|
||||||
super(loc);
|
|
||||||
this.index = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.common.Operation;
|
|
||||||
import me.topchetoeu.jscript.compilation.AssignableStatement;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
|
|
||||||
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(CompileResult target, boolean pollute) {
|
|
||||||
var i = target.scope.getKey(name);
|
|
||||||
target.add(Instruction.loadVar(i));
|
|
||||||
if (!pollute) target.add(Instruction.discard());
|
|
||||||
}
|
|
||||||
|
|
||||||
public VariableStatement(Location loc, String name) {
|
|
||||||
super(loc);
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.lib;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.runtime.Context;
|
|
||||||
import me.topchetoeu.jscript.runtime.Frame;
|
|
||||||
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
|
|
||||||
import me.topchetoeu.jscript.runtime.values.CodeFunction;
|
|
||||||
import me.topchetoeu.jscript.runtime.values.FunctionValue;
|
|
||||||
import me.topchetoeu.jscript.runtime.values.NativeFunction;
|
|
||||||
import me.topchetoeu.jscript.utils.interop.WrapperName;
|
|
||||||
|
|
||||||
@WrapperName("AsyncGeneratorFunction")
|
|
||||||
public class AsyncGeneratorFunctionLib extends FunctionValue {
|
|
||||||
public final CodeFunction func;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object call(Context ctx, Object thisArg, Object ...args) {
|
|
||||||
var handler = new AsyncGeneratorLib();
|
|
||||||
|
|
||||||
var newArgs = new Object[args.length + 2];
|
|
||||||
newArgs[0] = new NativeFunction("await", handler::await);
|
|
||||||
newArgs[1] = new NativeFunction("yield", handler::yield);
|
|
||||||
System.arraycopy(args, 0, newArgs, 2, args.length);
|
|
||||||
|
|
||||||
handler.frame = new Frame(ctx, thisArg, newArgs, func);
|
|
||||||
return handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AsyncGeneratorFunctionLib(CodeFunction func) {
|
|
||||||
super(func.name, func.length);
|
|
||||||
if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function.");
|
|
||||||
this.func = func;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.lib;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.runtime.values.Values;
|
|
||||||
import me.topchetoeu.jscript.utils.filesystem.File;
|
|
||||||
import me.topchetoeu.jscript.utils.interop.Arguments;
|
|
||||||
import me.topchetoeu.jscript.utils.interop.Expose;
|
|
||||||
import me.topchetoeu.jscript.utils.interop.WrapperName;
|
|
||||||
|
|
||||||
@WrapperName("Console")
|
|
||||||
public class ConsoleLib {
|
|
||||||
public static interface Writer {
|
|
||||||
void writeLine(String val) throws IOException;
|
|
||||||
}
|
|
||||||
|
|
||||||
private File file;
|
|
||||||
|
|
||||||
@Expose
|
|
||||||
public void __log(Arguments args) {
|
|
||||||
var res = new StringBuilder();
|
|
||||||
var first = true;
|
|
||||||
|
|
||||||
for (var el : args.args) {
|
|
||||||
if (!first) res.append(" ");
|
|
||||||
first = false;
|
|
||||||
res.append(Values.toReadable(args.ctx, el).getBytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var line : res.toString().split("\n", -1)) {
|
|
||||||
file.write(line.getBytes());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConsoleLib(File file) {
|
|
||||||
this.file = file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.lib;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.runtime.Context;
|
|
||||||
import me.topchetoeu.jscript.runtime.Frame;
|
|
||||||
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
|
|
||||||
import me.topchetoeu.jscript.runtime.values.CodeFunction;
|
|
||||||
import me.topchetoeu.jscript.runtime.values.FunctionValue;
|
|
||||||
import me.topchetoeu.jscript.runtime.values.NativeFunction;
|
|
||||||
import me.topchetoeu.jscript.utils.interop.WrapperName;
|
|
||||||
|
|
||||||
@WrapperName("GeneratorFunction")
|
|
||||||
public class GeneratorFunctionLib extends FunctionValue {
|
|
||||||
public final CodeFunction func;
|
|
||||||
|
|
||||||
@Override public Object call(Context ctx, Object thisArg, Object ...args) {
|
|
||||||
var handler = new GeneratorLib();
|
|
||||||
|
|
||||||
var newArgs = new Object[args.length + 1];
|
|
||||||
newArgs[0] = new NativeFunction("yield", handler::yield);
|
|
||||||
System.arraycopy(args, 0, newArgs, 1, args.length);
|
|
||||||
|
|
||||||
handler.frame = new Frame(ctx, thisArg, newArgs, func);
|
|
||||||
return handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GeneratorFunctionLib(CodeFunction func) {
|
|
||||||
super(func.name, func.length);
|
|
||||||
if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function.");
|
|
||||||
this.func = func;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.runtime;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Filename;
|
|
||||||
import me.topchetoeu.jscript.common.FunctionBody;
|
|
||||||
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
|
|
||||||
import me.topchetoeu.jscript.runtime.scope.ValueVariable;
|
|
||||||
import me.topchetoeu.jscript.runtime.values.CodeFunction;
|
|
||||||
|
|
||||||
public interface Compiler {
|
|
||||||
public Key<Compiler> KEY = new Key<>();
|
|
||||||
|
|
||||||
public FunctionBody compile(Filename filename, String source);
|
|
||||||
|
|
||||||
public static Compiler get(Extensions ext) {
|
|
||||||
return ext.get(KEY, (filename, src) -> {
|
|
||||||
throw EngineException.ofError("No compiler attached to engine.");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CodeFunction compile(Environment env, Filename filename, String raw) {
|
|
||||||
return new CodeFunction(env, filename.toString(), Compiler.get(env).compile(filename, raw), new ValueVariable[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.runtime;
|
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Filename;
|
|
||||||
import me.topchetoeu.jscript.runtime.debug.DebugContext;
|
|
||||||
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
|
|
||||||
import me.topchetoeu.jscript.runtime.scope.ValueVariable;
|
|
||||||
import me.topchetoeu.jscript.runtime.values.CodeFunction;
|
|
||||||
import me.topchetoeu.jscript.runtime.values.FunctionValue;
|
|
||||||
|
|
||||||
public class Context implements Extensions {
|
|
||||||
public static final Context NULL = new Context();
|
|
||||||
|
|
||||||
public final Context parent;
|
|
||||||
public final Environment environment;
|
|
||||||
public final Frame frame;
|
|
||||||
// public final Engine engine;
|
|
||||||
public final int stackSize;
|
|
||||||
|
|
||||||
@Override public <T> void add(Key<T> key, T obj) {
|
|
||||||
if (environment != null) environment.add(key, obj);
|
|
||||||
// else if (engine != null) engine.add(key, obj);
|
|
||||||
}
|
|
||||||
@Override public <T> T get(Key<T> 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(Key<?> key) {
|
|
||||||
return
|
|
||||||
environment != null && environment.has(key);
|
|
||||||
// engine != null && engine.has(key);
|
|
||||||
}
|
|
||||||
@Override public boolean remove(Key<?> key) {
|
|
||||||
var res = false;
|
|
||||||
|
|
||||||
if (environment != null) res |= environment.remove(key);
|
|
||||||
// else if (engine != null) res |= engine.remove(key);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
@Override public Iterable<Key<?>> keys() {
|
|
||||||
if (environment == null) return List.of();
|
|
||||||
else return environment.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) {
|
|
||||||
DebugContext.get(this).onSource(filename, raw);
|
|
||||||
var result = new CodeFunction(environment, filename.toString(), Compiler.get(this).compile(filename, raw), new ValueVariable[0]);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Context pushFrame(Frame frame) {
|
|
||||||
var res = new Context(this, frame.function.environment, frame, stackSize + 1);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Iterable<Frame> frames() {
|
|
||||||
var self = this;
|
|
||||||
return () -> new Iterator<Frame>() {
|
|
||||||
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 Frame next() {
|
|
||||||
update();
|
|
||||||
var res = curr.frame;
|
|
||||||
curr = curr.parent;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private Context(Context parent, Environment environment, Frame frame, int stackSize) {
|
|
||||||
this.parent = parent;
|
|
||||||
this.environment = environment;
|
|
||||||
this.frame = frame;
|
|
||||||
this.stackSize = stackSize;
|
|
||||||
|
|
||||||
if (hasNotNull(Environment.MAX_STACK_COUNT) && stackSize > (int)get(Environment.MAX_STACK_COUNT)) {
|
|
||||||
throw EngineException.ofRange("Stack overflow!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Context() {
|
|
||||||
this(null, null, null, 0);
|
|
||||||
}
|
|
||||||
public Context(Environment env) {
|
|
||||||
this(null, env, null, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.runtime;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
|
|
||||||
import me.topchetoeu.jscript.runtime.scope.GlobalScope;
|
|
||||||
import me.topchetoeu.jscript.runtime.values.FunctionValue;
|
|
||||||
import me.topchetoeu.jscript.runtime.values.NativeFunction;
|
|
||||||
import me.topchetoeu.jscript.runtime.values.ObjectValue;
|
|
||||||
import me.topchetoeu.jscript.utils.interop.NativeWrapperProvider;
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public class Environment implements Extensions {
|
|
||||||
public static final Key<Compiler> COMPILE_FUNC = new Key<>();
|
|
||||||
|
|
||||||
public static final Key<FunctionValue> REGEX_CONSTR = new Key<>();
|
|
||||||
public static final Key<Integer> MAX_STACK_COUNT = new Key<>();
|
|
||||||
public static final Key<Boolean> HIDE_STACK = new Key<>();
|
|
||||||
|
|
||||||
public static final Key<ObjectValue> OBJECT_PROTO = new Key<>();
|
|
||||||
public static final Key<ObjectValue> FUNCTION_PROTO = new Key<>();
|
|
||||||
public static final Key<ObjectValue> ARRAY_PROTO = new Key<>();
|
|
||||||
public static final Key<ObjectValue> BOOL_PROTO = new Key<>();
|
|
||||||
public static final Key<ObjectValue> NUMBER_PROTO = new Key<>();
|
|
||||||
public static final Key<ObjectValue> STRING_PROTO = new Key<>();
|
|
||||||
public static final Key<ObjectValue> SYMBOL_PROTO = new Key<>();
|
|
||||||
public static final Key<ObjectValue> ERROR_PROTO = new Key<>();
|
|
||||||
public static final Key<ObjectValue> SYNTAX_ERR_PROTO = new Key<>();
|
|
||||||
public static final Key<ObjectValue> TYPE_ERR_PROTO = new Key<>();
|
|
||||||
public static final Key<ObjectValue> RANGE_ERR_PROTO = new Key<>();
|
|
||||||
|
|
||||||
private HashMap<Key<?>, Object> data = new HashMap<>();
|
|
||||||
|
|
||||||
public GlobalScope global;
|
|
||||||
public WrapperProvider wrappers;
|
|
||||||
|
|
||||||
@Override public <T> void add(Key<T> key, T obj) {
|
|
||||||
data.put(key, obj);
|
|
||||||
}
|
|
||||||
@Override public <T> T get(Key<T> key) {
|
|
||||||
return (T)data.get(key);
|
|
||||||
}
|
|
||||||
@Override public boolean remove(Key<?> key) {
|
|
||||||
if (data.containsKey(key)) {
|
|
||||||
data.remove(key);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
@Override public boolean has(Key<?> key) {
|
|
||||||
return data.containsKey(key);
|
|
||||||
}
|
|
||||||
@Override public Iterable<Key<?>> keys() {
|
|
||||||
return data.keySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FunctionValue regexConstructor(Extensions ext) {
|
|
||||||
return ext.init(REGEX_CONSTR, new NativeFunction("RegExp", args -> {
|
|
||||||
throw EngineException.ofError("Regular expressions not supported.").setCtx(args.ctx);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
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() {
|
|
||||||
return new Context(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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.runtime;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Filename;
|
|
||||||
import me.topchetoeu.jscript.common.ResultRunnable;
|
|
||||||
import me.topchetoeu.jscript.common.events.DataNotifier;
|
|
||||||
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
|
|
||||||
import me.topchetoeu.jscript.runtime.values.FunctionValue;
|
|
||||||
|
|
||||||
public interface EventLoop {
|
|
||||||
public static final Key<EventLoop> KEY = new Key<>();
|
|
||||||
|
|
||||||
public static EventLoop get(Extensions ext) {
|
|
||||||
if (ext.hasNotNull(KEY)) return ext.get(KEY);
|
|
||||||
else return new EventLoop() {
|
|
||||||
@Override public <T> DataNotifier<T> pushMsg(ResultRunnable<T> runnable, boolean micro) {
|
|
||||||
throw EngineException.ofError("No event loop attached to environment.");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> DataNotifier<T> pushMsg(ResultRunnable<T> runnable, boolean micro);
|
|
||||||
public default DataNotifier<Void> pushMsg(Runnable runnable, boolean micro) {
|
|
||||||
return pushMsg(() -> { runnable.run(); return null; }, micro);
|
|
||||||
}
|
|
||||||
|
|
||||||
public default DataNotifier<Object> pushMsg(boolean micro, Environment env, FunctionValue func, Object thisArg, Object ...args) {
|
|
||||||
return pushMsg(() -> {
|
|
||||||
return func.call(new Context(env), thisArg, args);
|
|
||||||
}, micro);
|
|
||||||
}
|
|
||||||
public default DataNotifier<Object> pushMsg(boolean micro, Environment env, Filename filename, String raw, Object thisArg, Object ...args) {
|
|
||||||
return pushMsg(() -> {
|
|
||||||
var ctx = new Context(env);
|
|
||||||
return ctx.compile(filename, raw).call(new Context(env), thisArg, args);
|
|
||||||
}, micro);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.runtime;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public interface Extensions {
|
|
||||||
public static Extensions EMPTY = new Extensions() {
|
|
||||||
@Override public <T> void add(Key<T> key, T obj) { }
|
|
||||||
@Override public boolean remove(Key<?> key) { return false; }
|
|
||||||
|
|
||||||
@Override public <T> T get(Key<T> key) { return null; }
|
|
||||||
@Override public boolean has(Key<?> key) { return false; }
|
|
||||||
@Override public Iterable<Key<?>> keys() { return List.of(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
<T> T get(Key<T> key);
|
|
||||||
<T> void add(Key<T> key, T obj);
|
|
||||||
Iterable<Key<?>> keys();
|
|
||||||
|
|
||||||
boolean has(Key<?> key);
|
|
||||||
boolean remove(Key<?> key);
|
|
||||||
|
|
||||||
default void add(Key<Void> key) {
|
|
||||||
add(key, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
default boolean hasNotNull(Key<?> key) {
|
|
||||||
return has(key) && get(key) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
default <T> T get(Key<T> key, T defaultVal) {
|
|
||||||
if (has(key)) return get(key);
|
|
||||||
else return defaultVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
default <T> T init(Key<T> key, T val) {
|
|
||||||
if (has(key)) return get(key);
|
|
||||||
else {
|
|
||||||
add(key, val);
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
default void addAll(Extensions source) {
|
|
||||||
if (source == null) return;
|
|
||||||
for (var key : source.keys()) {
|
|
||||||
add((Key<Object>)key, (Object)source.get(key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Extensions wrap(Extensions ext) {
|
|
||||||
if (ext == null) return EMPTY;
|
|
||||||
else return ext;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.runtime;
|
|
||||||
|
|
||||||
public class Key<T> {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.runtime.debug;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.WeakHashMap;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Filename;
|
|
||||||
import me.topchetoeu.jscript.common.FunctionBody;
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
|
||||||
import me.topchetoeu.jscript.common.mapping.FunctionMap;
|
|
||||||
import me.topchetoeu.jscript.runtime.Context;
|
|
||||||
import me.topchetoeu.jscript.runtime.Extensions;
|
|
||||||
import me.topchetoeu.jscript.runtime.Frame;
|
|
||||||
import me.topchetoeu.jscript.runtime.Key;
|
|
||||||
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
|
|
||||||
import me.topchetoeu.jscript.runtime.values.CodeFunction;
|
|
||||||
import me.topchetoeu.jscript.runtime.values.FunctionValue;
|
|
||||||
|
|
||||||
public class DebugContext {
|
|
||||||
public static final Key<DebugContext> KEY = new Key<>();
|
|
||||||
public static final Key<Void> IGNORE = new Key<>();
|
|
||||||
|
|
||||||
private HashMap<Filename, String> sources;
|
|
||||||
private WeakHashMap<FunctionBody, FunctionMap> maps;
|
|
||||||
private DebugHandler debugger;
|
|
||||||
|
|
||||||
public boolean attachDebugger(DebugHandler debugger) {
|
|
||||||
if (this.debugger != null) return false;
|
|
||||||
|
|
||||||
if (sources != null) {
|
|
||||||
for (var source : sources.entrySet()) debugger.onSourceLoad(source.getKey(), source.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
this.debugger = debugger;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
public boolean detachDebugger() {
|
|
||||||
this.debugger = null;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DebugHandler debugger() {
|
|
||||||
if (debugger == null) return DebugHandler.empty();
|
|
||||||
else return debugger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FunctionMap getMap(FunctionBody func) {
|
|
||||||
if (maps == null) return null;
|
|
||||||
return maps.get(func);
|
|
||||||
}
|
|
||||||
public FunctionMap getMap(FunctionValue func) {
|
|
||||||
if (maps == null || !(func instanceof CodeFunction)) return null;
|
|
||||||
return getMap(((CodeFunction)func).body);
|
|
||||||
}
|
|
||||||
public FunctionMap getMapOrEmpty(FunctionBody func) {
|
|
||||||
if (maps == null) return FunctionMap.EMPTY;
|
|
||||||
var res = maps.get(func);
|
|
||||||
if (res == null) return FunctionMap.EMPTY;
|
|
||||||
else return res;
|
|
||||||
}
|
|
||||||
public FunctionMap getMapOrEmpty(FunctionValue func) {
|
|
||||||
if (maps == null || !(func instanceof CodeFunction)) return FunctionMap.EMPTY;
|
|
||||||
return getMapOrEmpty(((CodeFunction)func).body);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onFramePop(Context ctx, Frame frame) {
|
|
||||||
if (debugger != null) debugger.onFramePop(ctx, frame);
|
|
||||||
}
|
|
||||||
public void onFramePush(Context ctx, Frame frame) {
|
|
||||||
if (debugger != null) debugger.onFramePush(ctx, frame);
|
|
||||||
}
|
|
||||||
public boolean onInstruction(Context ctx, Frame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught) {
|
|
||||||
if (debugger != null) return debugger.onInstruction(ctx, frame, instruction, returnVal, error, caught);
|
|
||||||
else return false;
|
|
||||||
}
|
|
||||||
public void onSource(Filename filename, String source) {
|
|
||||||
if (debugger != null) debugger.onSourceLoad(filename, source);
|
|
||||||
if (sources != null) sources.put(filename, source);
|
|
||||||
}
|
|
||||||
public void onFunctionLoad(FunctionBody func, FunctionMap map) {
|
|
||||||
if (maps != null) maps.put(func, map);
|
|
||||||
if (debugger != null) debugger.onFunctionLoad(func, map);
|
|
||||||
}
|
|
||||||
|
|
||||||
private DebugContext(boolean enabled) {
|
|
||||||
if (enabled) {
|
|
||||||
sources = new HashMap<>();
|
|
||||||
maps = new WeakHashMap<>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public DebugContext() {
|
|
||||||
this(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean enabled(Extensions exts) {
|
|
||||||
return exts != null && exts.hasNotNull(KEY) && !exts.has(IGNORE);
|
|
||||||
}
|
|
||||||
public static DebugContext get(Extensions exts) {
|
|
||||||
if (enabled(exts)) return exts.get(KEY);
|
|
||||||
else return new DebugContext(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<String> stackTrace(Context ctx) {
|
|
||||||
var res = new ArrayList<String>();
|
|
||||||
var dbgCtx = get(ctx);
|
|
||||||
|
|
||||||
for (var el : ctx.frames()) {
|
|
||||||
var name = el.function.name;
|
|
||||||
|
|
||||||
var map = dbgCtx.getMapOrEmpty(el.function);
|
|
||||||
Location loc = null;
|
|
||||||
|
|
||||||
if (map != null) {
|
|
||||||
loc = map.toLocation(el.codePtr, true);
|
|
||||||
if (loc == null) loc = map.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.runtime.debug;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Filename;
|
|
||||||
import me.topchetoeu.jscript.common.FunctionBody;
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.mapping.FunctionMap;
|
|
||||||
import me.topchetoeu.jscript.runtime.Context;
|
|
||||||
import me.topchetoeu.jscript.runtime.Frame;
|
|
||||||
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
|
|
||||||
|
|
||||||
public interface DebugHandler {
|
|
||||||
/**
|
|
||||||
* Called when a script has been loaded
|
|
||||||
* @param filename The name of the source
|
|
||||||
* @param source The name of the source
|
|
||||||
* @param breakpoints A set of all the breakpointable locations in this source
|
|
||||||
* @param map The source map associated with this file. null if this source map isn't mapped
|
|
||||||
*/
|
|
||||||
void onSourceLoad(Filename filename, String source);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a function body has been loaded
|
|
||||||
* @param body The body loaded
|
|
||||||
* @param map The map of the function
|
|
||||||
*/
|
|
||||||
void onFunctionLoad(FunctionBody body, FunctionMap map);
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * Called when a function body has been loaded
|
|
||||||
// * @param body The body loaded
|
|
||||||
// * @param map The map of the function
|
|
||||||
// */
|
|
||||||
// void onFunctionUnload(FunctionBody body, FunctionMap map);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called immediatly before an instruction is executed, as well as after an instruction, if it has threw or returned.
|
|
||||||
* This function might pause in order to await debugging commands.
|
|
||||||
* @param ctx The context of execution
|
|
||||||
* @param frame The frame in which execution is occuring
|
|
||||||
* @param instruction The instruction which was or will be executed
|
|
||||||
* @param returnVal The return value of the instruction, Values.NO_RETURN if none
|
|
||||||
* @param error The error that the instruction threw, null if none
|
|
||||||
* @param caught Whether or not the error has been caught
|
|
||||||
* @return Whether or not the frame should restart (currently does nothing)
|
|
||||||
*/
|
|
||||||
boolean onInstruction(Context ctx, Frame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called immediatly before a frame has been pushed on the frame stack.
|
|
||||||
* This function might pause in order to await debugging commands.
|
|
||||||
* @param ctx The context of execution
|
|
||||||
* @param frame The code frame which was pushed
|
|
||||||
*/
|
|
||||||
void onFramePush(Context ctx, Frame frame);
|
|
||||||
/**
|
|
||||||
* Called immediatly after a frame has been popped out of the frame stack.
|
|
||||||
* This function might pause in order to await debugging commands.
|
|
||||||
* @param ctx The context of execution
|
|
||||||
* @param frame The code frame which was popped out
|
|
||||||
*/
|
|
||||||
void onFramePop(Context ctx, Frame frame);
|
|
||||||
|
|
||||||
public static DebugHandler empty() {
|
|
||||||
return new DebugHandler () {
|
|
||||||
@Override public void onFramePop(Context ctx, Frame frame) { }
|
|
||||||
@Override public void onFramePush(Context ctx, Frame frame) { }
|
|
||||||
@Override public boolean onInstruction(Context ctx, Frame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
@Override public void onSourceLoad(Filename filename, String source) { }
|
|
||||||
@Override public void onFunctionLoad(FunctionBody body, FunctionMap map) { }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.runtime.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.FunctionBody;
|
|
||||||
import me.topchetoeu.jscript.runtime.Context;
|
|
||||||
import me.topchetoeu.jscript.runtime.Environment;
|
|
||||||
import me.topchetoeu.jscript.runtime.Frame;
|
|
||||||
import me.topchetoeu.jscript.runtime.scope.ValueVariable;
|
|
||||||
|
|
||||||
public class CodeFunction extends FunctionValue {
|
|
||||||
public final FunctionBody body;
|
|
||||||
public final ValueVariable[] captures;
|
|
||||||
public Environment environment;
|
|
||||||
|
|
||||||
// public Location loc() {
|
|
||||||
// for (var instr : body.instructions) {
|
|
||||||
// if (instr.location != null) return instr.location;
|
|
||||||
// }
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
// public String readable() {
|
|
||||||
// var loc = loc();
|
|
||||||
// if (loc == null) return name;
|
|
||||||
// else if (name.equals("")) return loc.toString();
|
|
||||||
// else return name + "@" + loc;
|
|
||||||
// }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object call(Context ctx, Object thisArg, Object ...args) {
|
|
||||||
var frame = new Frame(ctx, thisArg, args, this);
|
|
||||||
|
|
||||||
frame.onPush();
|
|
||||||
|
|
||||||
try {
|
|
||||||
while (true) {
|
|
||||||
var res = frame.next(Values.NO_RETURN, Values.NO_RETURN, null);
|
|
||||||
if (res != Values.NO_RETURN) return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
frame.onPop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public CodeFunction(Environment environment, String name, FunctionBody body, ValueVariable[] captures) {
|
|
||||||
super(name, body.argsN);
|
|
||||||
this.captures = captures;
|
|
||||||
this.environment = environment;
|
|
||||||
this.body = body;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.utils;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Filename;
|
|
||||||
import me.topchetoeu.jscript.common.FunctionBody;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.parsing.Parsing;
|
|
||||||
import me.topchetoeu.jscript.runtime.Compiler;
|
|
||||||
import me.topchetoeu.jscript.runtime.Extensions;
|
|
||||||
import me.topchetoeu.jscript.runtime.debug.DebugContext;
|
|
||||||
|
|
||||||
public class JSCompiler implements Compiler {
|
|
||||||
public final Extensions ext;
|
|
||||||
|
|
||||||
private void registerFunc(FunctionBody body, CompileResult res) {
|
|
||||||
var map = res.map();
|
|
||||||
|
|
||||||
DebugContext.get(ext).onFunctionLoad(body, map);
|
|
||||||
|
|
||||||
for (var i = 0; i < body.children.length; i++) {
|
|
||||||
registerFunc(body.children[i], res.children.get(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public FunctionBody compile(Filename filename, String source) {
|
|
||||||
var res = Parsing.compile(filename, source);
|
|
||||||
var func = res.body();
|
|
||||||
DebugContext.get(ext).onSource(filename, source);
|
|
||||||
registerFunc(func, res);
|
|
||||||
|
|
||||||
return func;
|
|
||||||
}
|
|
||||||
|
|
||||||
public JSCompiler(Extensions ext) {
|
|
||||||
this.ext = ext;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.utils.filesystem;
|
|
||||||
|
|
||||||
public enum ActionType {
|
|
||||||
UNKNOWN(0, "An operation performed upon", "An operation was performed upon"),
|
|
||||||
READ(1, "Reading from", "Read from"),
|
|
||||||
WRITE(2, "Writting to", "Wrote to"),
|
|
||||||
SEEK(3, "Seeking in", "Sought in"),
|
|
||||||
CLOSE(4, "Closing", "Closed"),
|
|
||||||
STAT(5, "Stat of", "Statted"),
|
|
||||||
OPEN(6, "Opening", "Opened"),
|
|
||||||
CREATE(7, "Creating", "Created"),
|
|
||||||
DELETE(8, "Deleting", "Deleted"),
|
|
||||||
CLOSE_FS(9, "Closing filesystem", "Closed filesystem");
|
|
||||||
|
|
||||||
public final int code;
|
|
||||||
public final String continuous, past;
|
|
||||||
|
|
||||||
public String readable(boolean usePast) {
|
|
||||||
if (usePast) return past;
|
|
||||||
else return continuous;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ActionType(int code, String continuous, String past) {
|
|
||||||
this.code = code;
|
|
||||||
this.continuous = continuous;
|
|
||||||
this.past = past;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.utils.filesystem;
|
|
||||||
|
|
||||||
public abstract class BaseFile<T> implements File {
|
|
||||||
private T handle;
|
|
||||||
private Mode mode;
|
|
||||||
|
|
||||||
protected final T handle() {
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract int onRead(byte[] buff);
|
|
||||||
protected abstract void onWrite(byte[] buff);
|
|
||||||
protected abstract long onSeek(long offset, int pos);
|
|
||||||
protected abstract boolean onClose();
|
|
||||||
|
|
||||||
@Override public synchronized int read(byte[] buff) {
|
|
||||||
try {
|
|
||||||
if (handle == null) throw new FilesystemException(ErrorReason.CLOSED);
|
|
||||||
if (!mode.readable) throw new FilesystemException(ErrorReason.NO_PERMISSION, "File not open for reading.");
|
|
||||||
return onRead(buff);
|
|
||||||
}
|
|
||||||
catch (FilesystemException e) { throw e.setAction(ActionType.READ); }
|
|
||||||
}
|
|
||||||
@Override public synchronized void write(byte[] buff) {
|
|
||||||
try {
|
|
||||||
if (handle == null) throw new FilesystemException(ErrorReason.CLOSED);
|
|
||||||
if (!mode.writable) throw new FilesystemException(ErrorReason.NO_PERMISSION, "File not open for writting.");
|
|
||||||
onWrite(buff);
|
|
||||||
}
|
|
||||||
catch (FilesystemException e) { throw e.setAction(ActionType.WRITE); }
|
|
||||||
}
|
|
||||||
@Override public synchronized long seek(long offset, int pos) {
|
|
||||||
try {
|
|
||||||
if (handle == null) throw new FilesystemException(ErrorReason.CLOSED);
|
|
||||||
if (!mode.writable) throw new FilesystemException(ErrorReason.NO_PERMISSION, "File not open for seeking.");
|
|
||||||
return onSeek(offset, pos);
|
|
||||||
}
|
|
||||||
catch (FilesystemException e) { throw e.setAction(ActionType.SEEK); }
|
|
||||||
}
|
|
||||||
@Override public synchronized boolean close() {
|
|
||||||
if (handle != null) {
|
|
||||||
try {
|
|
||||||
var res = onClose();
|
|
||||||
handle = null;
|
|
||||||
mode = Mode.NONE;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
catch (FilesystemException e) { throw e.setAction(ActionType.CLOSE); }
|
|
||||||
}
|
|
||||||
else return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BaseFile(T handle, Mode mode) {
|
|
||||||
this.mode = mode;
|
|
||||||
this.handle = handle;
|
|
||||||
|
|
||||||
if (mode == Mode.NONE) this.handle = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.utils.filesystem;
|
|
||||||
|
|
||||||
public enum ErrorReason {
|
|
||||||
UNKNOWN(0, "failed", false),
|
|
||||||
NO_PERMISSION(1, "is not allowed", false),
|
|
||||||
CLOSED(1, "that was closed", true),
|
|
||||||
UNSUPPORTED(2, "is not supported", false),
|
|
||||||
ILLEGAL_ARGS(3, "with illegal arguments", true),
|
|
||||||
DOESNT_EXIST(4, "that doesn't exist", true),
|
|
||||||
ALREADY_EXISTS(5, "that already exists", true),
|
|
||||||
ILLEGAL_PATH(6, "with illegal path", true),
|
|
||||||
NO_PARENT(7, "with a missing parent folder", true);
|
|
||||||
|
|
||||||
public final int code;
|
|
||||||
public final boolean usePast;
|
|
||||||
public final String readable;
|
|
||||||
|
|
||||||
private ErrorReason(int code, String readable, boolean usePast) {
|
|
||||||
this.code = code;
|
|
||||||
this.readable = readable;
|
|
||||||
this.usePast = usePast;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,169 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.utils.filesystem;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Buffer;
|
|
||||||
public interface File {
|
|
||||||
default int read(byte[] buff) { throw new FilesystemException(ErrorReason.UNSUPPORTED).setAction(ActionType.READ); }
|
|
||||||
default void write(byte[] buff) { throw new FilesystemException(ErrorReason.UNSUPPORTED).setAction(ActionType.WRITE); }
|
|
||||||
default long seek(long offset, int pos) { throw new FilesystemException(ErrorReason.UNSUPPORTED).setAction(ActionType.SEEK); }
|
|
||||||
default boolean close() { return false; }
|
|
||||||
|
|
||||||
default byte[] readAll() {
|
|
||||||
var parts = new LinkedList<byte[]>();
|
|
||||||
var sizes = new LinkedList<Integer>();
|
|
||||||
var buff = new byte[1024];
|
|
||||||
var size = 0;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
var n = read(buff);
|
|
||||||
if (n < 0) break;
|
|
||||||
else if (n == 0) continue;
|
|
||||||
|
|
||||||
parts.add(buff);
|
|
||||||
sizes.add(n);
|
|
||||||
size += n;
|
|
||||||
buff = new byte[1024];
|
|
||||||
}
|
|
||||||
|
|
||||||
buff = new byte[size];
|
|
||||||
|
|
||||||
var i = 0;
|
|
||||||
var j = 0;
|
|
||||||
|
|
||||||
for (var part : parts) {
|
|
||||||
var currSize = sizes.get(j++);
|
|
||||||
|
|
||||||
System.arraycopy(part, 0, buff, i, currSize);
|
|
||||||
i += currSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
return buff;
|
|
||||||
}
|
|
||||||
default String readToString() {
|
|
||||||
return new String(readAll());
|
|
||||||
}
|
|
||||||
default String readLine() {
|
|
||||||
var res = new Buffer();
|
|
||||||
var buff = new byte[1];
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if (read(buff) == 0) {
|
|
||||||
if (res.length() == 0) return null;
|
|
||||||
else break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buff[0] == '\n') break;
|
|
||||||
|
|
||||||
res.write(res.length(), buff);
|
|
||||||
}
|
|
||||||
return new String(res.data());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static File ofStream(InputStream str) {
|
|
||||||
return new File() {
|
|
||||||
@Override public synchronized int read(byte[] buff) {
|
|
||||||
try {
|
|
||||||
try { return str.read(buff); }
|
|
||||||
catch (NullPointerException e) { throw new FilesystemException(ErrorReason.ILLEGAL_ARGS, e.getMessage()); }
|
|
||||||
catch (IOException e) { throw new FilesystemException(ErrorReason.UNKNOWN, e.getMessage()); }
|
|
||||||
}
|
|
||||||
catch (FilesystemException e) { throw e.setAction(ActionType.READ); }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
public static File ofStream(OutputStream str) {
|
|
||||||
return new File() {
|
|
||||||
@Override public synchronized void write(byte[] buff) {
|
|
||||||
try {
|
|
||||||
try { str.write(buff); }
|
|
||||||
catch (NullPointerException e) {throw new FilesystemException(ErrorReason.ILLEGAL_ARGS, e.getMessage()); }
|
|
||||||
catch (IOException e) { throw new FilesystemException(ErrorReason.UNKNOWN, e.getMessage()); }
|
|
||||||
}
|
|
||||||
catch (FilesystemException e) { throw e.setAction(ActionType.WRITE); }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
public static File ofLineWriter(LineWriter writer) {
|
|
||||||
var buff = new Buffer();
|
|
||||||
return new File() {
|
|
||||||
@Override public synchronized void write(byte[] val) {
|
|
||||||
try {
|
|
||||||
if (val == null) throw new FilesystemException(ErrorReason.ILLEGAL_ARGS, "Given buffer is null.");
|
|
||||||
|
|
||||||
for (var b : val) {
|
|
||||||
if (b == '\n') {
|
|
||||||
try {
|
|
||||||
writer.writeLine(new String(buff.data()));
|
|
||||||
buff.clear();
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
throw new FilesystemException(ErrorReason.UNKNOWN, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else buff.append(b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (FilesystemException e) { throw e.setAction(ActionType.WRITE); }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
public static File ofLineReader(LineReader reader) {
|
|
||||||
return new File() {
|
|
||||||
private int offset = 0;
|
|
||||||
private byte[] prev = new byte[0];
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized int read(byte[] buff) {
|
|
||||||
try {
|
|
||||||
if (buff == null) throw new FilesystemException(ErrorReason.ILLEGAL_ARGS, "Given buffer is null.");
|
|
||||||
var ptr = 0;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if (prev == null) break;
|
|
||||||
if (offset >= prev.length) {
|
|
||||||
try {
|
|
||||||
var line = reader.readLine();
|
|
||||||
|
|
||||||
if (line == null) {
|
|
||||||
prev = null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else prev = (line + "\n").getBytes();
|
|
||||||
|
|
||||||
offset = 0;
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
throw new FilesystemException(ErrorReason.UNKNOWN, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ptr + prev.length - offset > buff.length) {
|
|
||||||
var n = buff.length - ptr;
|
|
||||||
System.arraycopy(prev, offset, buff, ptr, buff.length - ptr);
|
|
||||||
offset += n;
|
|
||||||
ptr += n;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var n = prev.length - offset;
|
|
||||||
System.arraycopy(prev, offset, buff, ptr, n);
|
|
||||||
offset += n;
|
|
||||||
ptr += n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
catch (FilesystemException e) { throw e.setAction(ActionType.READ); }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
public static File ofIterator(Iterator<String> it) {
|
|
||||||
return ofLineReader(LineReader.ofIterator(it));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.utils.filesystem;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.runtime.Extensions;
|
|
||||||
import me.topchetoeu.jscript.runtime.Key;
|
|
||||||
|
|
||||||
public interface Filesystem {
|
|
||||||
public static final Key<Filesystem> KEY = new Key<>();
|
|
||||||
|
|
||||||
default String normalize(String... path) { return Paths.normalize(path); }
|
|
||||||
default boolean create(String path, EntryType type) { throw new FilesystemException(ErrorReason.UNSUPPORTED).setAction(ActionType.CREATE); }
|
|
||||||
File open(String path, Mode mode);
|
|
||||||
FileStat stat(String path);
|
|
||||||
void close();
|
|
||||||
|
|
||||||
public static Filesystem get(Extensions exts) {
|
|
||||||
return exts.get(KEY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.utils.filesystem;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
|
|
||||||
import me.topchetoeu.jscript.runtime.values.Values;
|
|
||||||
|
|
||||||
public class FilesystemException extends RuntimeException {
|
|
||||||
public final ErrorReason reason;
|
|
||||||
public final String details;
|
|
||||||
private ActionType action;
|
|
||||||
private EntryType entry = EntryType.FILE;
|
|
||||||
private String path;
|
|
||||||
|
|
||||||
public FilesystemException setPath(String path) {
|
|
||||||
this.path = path;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
public FilesystemException setAction(ActionType action) {
|
|
||||||
if (action == null) action = ActionType.UNKNOWN;
|
|
||||||
|
|
||||||
this.action = action;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
public FilesystemException setEntry(EntryType entry) {
|
|
||||||
if (entry == null) entry = EntryType.NONE;
|
|
||||||
|
|
||||||
this.entry = entry;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ActionType action() {
|
|
||||||
return action;
|
|
||||||
}
|
|
||||||
public String path() {
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
public EntryType entry() {
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EngineException toEngineException() {
|
|
||||||
var res = EngineException.ofError("IOError", getMessage());
|
|
||||||
|
|
||||||
Values.setMember(null, res.value, "action", action.code);
|
|
||||||
Values.setMember(null, res.value, "reason", reason.code);
|
|
||||||
Values.setMember(null, res.value, "path", path);
|
|
||||||
Values.setMember(null, res.value, "entry", entry.name);
|
|
||||||
if (details != null) Values.setMember(null, res.value, "details", details);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public String getMessage() {
|
|
||||||
var parts = new ArrayList<String>(10);
|
|
||||||
|
|
||||||
path = String.join(" ", parts).trim();
|
|
||||||
if (path.isEmpty()) path = null;
|
|
||||||
parts.clear();
|
|
||||||
|
|
||||||
parts.add(action == null ? "An action performed upon " : action.readable(reason.usePast));
|
|
||||||
|
|
||||||
if (entry == EntryType.FILE) parts.add("file");
|
|
||||||
if (entry == EntryType.FOLDER) parts.add("folder");
|
|
||||||
|
|
||||||
if (path != null) parts.add(path);
|
|
||||||
|
|
||||||
parts.add(reason.readable);
|
|
||||||
|
|
||||||
var msg = String.join(" ", parts);
|
|
||||||
if (details != null) msg += ": " + details;
|
|
||||||
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FilesystemException(ErrorReason type, String details) {
|
|
||||||
super();
|
|
||||||
if (type == null) type = ErrorReason.UNKNOWN;
|
|
||||||
|
|
||||||
this.details = details;
|
|
||||||
this.reason = type;
|
|
||||||
}
|
|
||||||
public FilesystemException(ErrorReason type) {
|
|
||||||
this(type, null);
|
|
||||||
}
|
|
||||||
public FilesystemException() {
|
|
||||||
this(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.utils.filesystem;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class HandleManager {
|
|
||||||
private Set<File> files = new HashSet<>();
|
|
||||||
|
|
||||||
public File put(File val) {
|
|
||||||
var handle = new File() {
|
|
||||||
@Override public int read(byte[] buff) {
|
|
||||||
return val.read(buff);
|
|
||||||
}
|
|
||||||
@Override public void write(byte[] buff) {
|
|
||||||
val.write(buff);
|
|
||||||
}
|
|
||||||
@Override public long seek(long offset, int pos) {
|
|
||||||
return val.seek(offset, pos);
|
|
||||||
}
|
|
||||||
@Override public boolean close() {
|
|
||||||
return files.remove(this) && val.close();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
files.add(handle);
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
public void close() {
|
|
||||||
while (!files.isEmpty()) {
|
|
||||||
files.stream().findFirst().get().close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.utils.filesystem;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
|
|
||||||
public interface LineReader {
|
|
||||||
String readLine() throws IOException;
|
|
||||||
|
|
||||||
public static LineReader ofIterator(Iterator<String> it) {
|
|
||||||
return () -> {
|
|
||||||
if (it.hasNext()) return it.next();
|
|
||||||
else return null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.utils.filesystem;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public interface LineWriter {
|
|
||||||
void writeLine(String value) throws IOException;
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.utils.filesystem;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Buffer;
|
|
||||||
|
|
||||||
class MemoryFile extends BaseFile<Buffer> {
|
|
||||||
private int ptr;
|
|
||||||
|
|
||||||
@Override protected int onRead(byte[] buff) {
|
|
||||||
if (ptr >= handle().length()) return -1;
|
|
||||||
var res = handle().read(ptr, buff);
|
|
||||||
ptr += res;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
@Override protected void onWrite(byte[] buff) {
|
|
||||||
handle().write(ptr, buff);
|
|
||||||
ptr += buff.length;
|
|
||||||
}
|
|
||||||
@Override protected long onSeek(long offset, int pos) {
|
|
||||||
if (pos == 0) ptr = (int)offset;
|
|
||||||
else if (pos == 1) ptr += (int)offset;
|
|
||||||
else if (pos == 2) ptr = handle().length() - (int)offset;
|
|
||||||
|
|
||||||
if (ptr < 0) ptr = 0;
|
|
||||||
if (ptr > handle().length()) ptr = handle().length();
|
|
||||||
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
@Override protected boolean onClose() {
|
|
||||||
ptr = 0;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MemoryFile(Buffer buff, Mode mode) {
|
|
||||||
super(buff, mode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.utils.filesystem;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Buffer;
|
|
||||||
import me.topchetoeu.jscript.common.Filename;
|
|
||||||
|
|
||||||
public class MemoryFilesystem implements Filesystem {
|
|
||||||
public final Mode mode;
|
|
||||||
private HashMap<Path, Buffer> files = new HashMap<>();
|
|
||||||
private HashSet<Path> folders = new HashSet<>();
|
|
||||||
private HandleManager handles = new HandleManager();
|
|
||||||
|
|
||||||
private Path realPath(String path) {
|
|
||||||
return Filename.normalize(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public String normalize(String... path) {
|
|
||||||
return Paths.normalize(path);
|
|
||||||
}
|
|
||||||
@Override public synchronized File open(String _path, Mode perms) {
|
|
||||||
try {
|
|
||||||
var path = realPath(_path);
|
|
||||||
var pcount = path.getNameCount();
|
|
||||||
|
|
||||||
if (files.containsKey(path)) return handles.put(new MemoryFile(files.get(path), perms));
|
|
||||||
else if (folders.contains(path)) {
|
|
||||||
var res = new StringBuilder();
|
|
||||||
|
|
||||||
for (var folder : folders) {
|
|
||||||
if (pcount + 1 != folder.getNameCount()) continue;
|
|
||||||
if (!folder.startsWith(path)) continue;
|
|
||||||
res.append(folder.toFile().getName()).append('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var file : files.keySet()) {
|
|
||||||
if (pcount + 1 != file.getNameCount()) continue;
|
|
||||||
if (!file.startsWith(path)) continue;
|
|
||||||
res.append(file.toFile().getName()).append('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
return handles.put(new MemoryFile(new Buffer(res.toString().getBytes()), perms.intersect(Mode.READ)));
|
|
||||||
}
|
|
||||||
else throw new FilesystemException(ErrorReason.DOESNT_EXIST);
|
|
||||||
}
|
|
||||||
catch (FilesystemException e) { throw e.setPath(_path).setAction(ActionType.OPEN); }
|
|
||||||
}
|
|
||||||
@Override public synchronized boolean create(String _path, EntryType type) {
|
|
||||||
try {
|
|
||||||
var path = realPath(_path);
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case FILE:
|
|
||||||
if (!folders.contains(path.getParent())) throw new FilesystemException(ErrorReason.NO_PARENT);
|
|
||||||
if (folders.contains(path) || files.containsKey(path)) return false;
|
|
||||||
files.put(path, new Buffer());
|
|
||||||
return true;
|
|
||||||
case FOLDER:
|
|
||||||
if (!folders.contains(path.getParent())) throw new FilesystemException(ErrorReason.NO_PARENT);
|
|
||||||
if (folders.contains(path) || files.containsKey(path)) return false;
|
|
||||||
folders.add(path);
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
case NONE:
|
|
||||||
return folders.remove(path) || files.remove(path) != null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (FilesystemException e) { throw e.setPath(_path).setAction(ActionType.CREATE); }
|
|
||||||
}
|
|
||||||
@Override public synchronized FileStat stat(String _path) {
|
|
||||||
var path = realPath(_path);
|
|
||||||
|
|
||||||
if (files.containsKey(path)) return new FileStat(mode, EntryType.FILE);
|
|
||||||
else if (folders.contains(path)) return new FileStat(mode, EntryType.FOLDER);
|
|
||||||
else return new FileStat(Mode.NONE, EntryType.NONE);
|
|
||||||
}
|
|
||||||
@Override public synchronized void close() throws FilesystemException {
|
|
||||||
handles.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
public MemoryFilesystem put(String path, byte[] data) {
|
|
||||||
var _path = realPath(path);
|
|
||||||
var _curr = "/";
|
|
||||||
|
|
||||||
for (var seg : _path) {
|
|
||||||
create(_curr, EntryType.FOLDER);
|
|
||||||
_curr += seg + "/";
|
|
||||||
}
|
|
||||||
|
|
||||||
files.put(_path, new Buffer(data));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MemoryFilesystem(Mode mode) {
|
|
||||||
this.mode = mode;
|
|
||||||
folders.add(Path.of("/"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.utils.filesystem;
|
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.RandomAccessFile;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
public class PhysicalFile extends BaseFile<RandomAccessFile> {
|
|
||||||
@Override protected int onRead(byte[] buff) {
|
|
||||||
try { return handle().read(buff); }
|
|
||||||
catch (IOException e) { throw new FilesystemException(ErrorReason.NO_PERMISSION).setAction(ActionType.READ); }
|
|
||||||
}
|
|
||||||
@Override protected void onWrite(byte[] buff) {
|
|
||||||
try { handle().write(buff); }
|
|
||||||
catch (IOException e) { throw new FilesystemException(ErrorReason.NO_PERMISSION).setAction(ActionType.WRITE); }
|
|
||||||
}
|
|
||||||
@Override protected long onSeek(long offset, int pos) {
|
|
||||||
try {
|
|
||||||
if (pos == 1) offset += handle().getFilePointer();
|
|
||||||
else if (pos == 2) offset += handle().length();
|
|
||||||
handle().seek(offset);
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
catch (IOException e) { throw new FilesystemException(ErrorReason.NO_PERMISSION).setAction(ActionType.SEEK); }
|
|
||||||
}
|
|
||||||
@Override protected boolean onClose() {
|
|
||||||
try { handle().close(); }
|
|
||||||
catch (IOException e) {} // SHUT
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PhysicalFile(Path path, Mode mode) throws FileNotFoundException {
|
|
||||||
super(new RandomAccessFile(path.toFile(), mode.name), mode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.utils.filesystem;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.FileAlreadyExistsException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.NoSuchFileException;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
public class PhysicalFilesystem implements Filesystem {
|
|
||||||
public final String root;
|
|
||||||
private HandleManager handles = new HandleManager();
|
|
||||||
|
|
||||||
private void checkMode(Path path, Mode mode) {
|
|
||||||
if (!path.startsWith(root)) throw new FilesystemException(ErrorReason.NO_PERMISSION, "Tried to jailbreak the sandbox.");
|
|
||||||
|
|
||||||
if (mode.readable && !Files.isReadable(path)) throw new FilesystemException(ErrorReason.NO_PERMISSION, "No read permissions");
|
|
||||||
if (mode.writable && !Files.isWritable(path)) throw new FilesystemException(ErrorReason.NO_PERMISSION, "No write permissions");
|
|
||||||
}
|
|
||||||
|
|
||||||
private Path realPath(String path) {
|
|
||||||
return Path.of(Paths.chroot(root, path));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public String normalize(String... paths) {
|
|
||||||
return Paths.normalize(paths);
|
|
||||||
}
|
|
||||||
@Override public synchronized File open(String _path, Mode perms) {
|
|
||||||
try {
|
|
||||||
var path = realPath(normalize(_path));
|
|
||||||
checkMode(path, perms);
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (Files.isDirectory(path)) return handles.put(File.ofIterator(
|
|
||||||
Files.list(path).map(v -> v.getFileName().toString()).iterator()
|
|
||||||
));
|
|
||||||
else return handles.put(new PhysicalFile(path, perms));
|
|
||||||
}
|
|
||||||
catch (IOException e) { throw new FilesystemException(ErrorReason.DOESNT_EXIST); }
|
|
||||||
}
|
|
||||||
catch (FilesystemException e) { throw e.setAction(ActionType.OPEN).setPath(_path); }
|
|
||||||
}
|
|
||||||
@Override public synchronized boolean create(String _path, EntryType type) {
|
|
||||||
try {
|
|
||||||
var path = realPath(_path);
|
|
||||||
|
|
||||||
try {
|
|
||||||
switch (type) {
|
|
||||||
case FILE:
|
|
||||||
Files.createFile(path);
|
|
||||||
break;
|
|
||||||
case FOLDER:
|
|
||||||
Files.createDirectories(path);
|
|
||||||
break;
|
|
||||||
case NONE:
|
|
||||||
default:
|
|
||||||
Files.delete(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (FileAlreadyExistsException | NoSuchFileException e) { return false; }
|
|
||||||
catch (IOException e) { throw new FilesystemException(ErrorReason.NO_PARENT); }
|
|
||||||
}
|
|
||||||
catch (FilesystemException e) { throw e.setAction(ActionType.CREATE).setPath(_path); }
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
@Override public synchronized FileStat stat(String _path) {
|
|
||||||
var path = realPath(_path);
|
|
||||||
|
|
||||||
if (!Files.exists(path)) return new FileStat(Mode.NONE, EntryType.NONE);
|
|
||||||
|
|
||||||
var perms = Mode.NONE;
|
|
||||||
|
|
||||||
if (Files.isReadable(path)) {
|
|
||||||
if (Files.isWritable(path)) perms = Mode.READ_WRITE;
|
|
||||||
else perms = Mode.READ;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (perms == Mode.NONE) return new FileStat(Mode.NONE, EntryType.NONE);
|
|
||||||
|
|
||||||
return new FileStat(
|
|
||||||
perms,
|
|
||||||
Files.isDirectory(path) ? EntryType.FOLDER : EntryType.FILE
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@Override public synchronized void close() throws FilesystemException {
|
|
||||||
try {
|
|
||||||
handles.close();
|
|
||||||
}
|
|
||||||
catch (FilesystemException e) { throw e.setAction(ActionType.CLOSE_FS); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public PhysicalFilesystem(String root) {
|
|
||||||
this.root = Paths.normalize(Path.of(root).toAbsolutePath().toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.utils.filesystem;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Filename;
|
|
||||||
import me.topchetoeu.jscript.utils.permissions.Matcher;
|
|
||||||
import me.topchetoeu.jscript.utils.permissions.Permission;
|
|
||||||
import me.topchetoeu.jscript.utils.permissions.PermissionsProvider;
|
|
||||||
|
|
||||||
public class RootFilesystem implements Filesystem {
|
|
||||||
public final Map<String, Filesystem> protocols = new HashMap<>();
|
|
||||||
public final PermissionsProvider perms;
|
|
||||||
|
|
||||||
public static final Permission PERM_READ = new Permission("jscript.file.read", Matcher.fileWildcard());
|
|
||||||
public static final Permission PERM_WRITE = new Permission("jscript.file.read", Matcher.fileWildcard());
|
|
||||||
|
|
||||||
private boolean canRead(String _path) {
|
|
||||||
return perms.hasPermission(PERM_READ, _path);
|
|
||||||
}
|
|
||||||
private boolean canWrite(String _path) {
|
|
||||||
return perms.hasPermission(PERM_WRITE, _path);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void modeAllowed(String _path, Mode mode) throws FilesystemException {
|
|
||||||
if (mode.readable && perms != null && !canRead(_path)) {
|
|
||||||
throw new FilesystemException(ErrorReason.NO_PERMISSION, "No read permissions").setPath(_path);
|
|
||||||
}
|
|
||||||
if (mode.writable && perms != null && !canWrite(_path)) {
|
|
||||||
throw new FilesystemException(ErrorReason.NO_PERMISSION, "No wtrite permissions").setPath(_path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Filesystem getProtocol(Filename filename) {
|
|
||||||
var protocol = protocols.get(filename.protocol);
|
|
||||||
|
|
||||||
if (protocol == null) {
|
|
||||||
throw new FilesystemException(ErrorReason.DOESNT_EXIST, "The protocol '" + filename.protocol + "' doesn't exist.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return protocol;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public String normalize(String... paths) {
|
|
||||||
if (paths.length == 0) return "file://";
|
|
||||||
else {
|
|
||||||
var filename = Filename.parse(paths[0]);
|
|
||||||
var protocol = protocols.get(filename.protocol);
|
|
||||||
paths[0] = filename.path;
|
|
||||||
|
|
||||||
|
|
||||||
if (protocol == null) return Paths.normalize(paths);
|
|
||||||
else return filename.protocol + "://" + protocol.normalize(paths);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Override public synchronized File open(String path, Mode perms) throws FilesystemException {
|
|
||||||
try {
|
|
||||||
var filename = Filename.parse(path);
|
|
||||||
var protocol = getProtocol(filename);
|
|
||||||
|
|
||||||
modeAllowed(filename.toString(), perms);
|
|
||||||
return protocol.open(filename.path, perms);
|
|
||||||
}
|
|
||||||
catch (FilesystemException e) { throw e.setPath(path).setAction(ActionType.OPEN); }
|
|
||||||
}
|
|
||||||
@Override public synchronized boolean create(String path, EntryType type) throws FilesystemException {
|
|
||||||
try {
|
|
||||||
var filename = Filename.parse(path);
|
|
||||||
var protocol = getProtocol(filename);
|
|
||||||
|
|
||||||
modeAllowed(filename.toString(), Mode.WRITE);
|
|
||||||
return protocol.create(filename.path, type);
|
|
||||||
}
|
|
||||||
catch (FilesystemException e) { throw e.setPath(path).setAction(ActionType.CREATE); }
|
|
||||||
}
|
|
||||||
@Override public synchronized FileStat stat(String path) throws FilesystemException {
|
|
||||||
try {
|
|
||||||
var filename = Filename.parse(path);
|
|
||||||
var protocol = getProtocol(filename);
|
|
||||||
|
|
||||||
return protocol.stat(filename.path);
|
|
||||||
}
|
|
||||||
catch (FilesystemException e) { throw e.setPath(path).setAction(ActionType.STAT); }
|
|
||||||
}
|
|
||||||
@Override public synchronized void close() throws FilesystemException {
|
|
||||||
try {
|
|
||||||
for (var protocol : protocols.values()) {
|
|
||||||
protocol.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
protocols.clear();
|
|
||||||
}
|
|
||||||
catch (FilesystemException e) { throw e.setAction(ActionType.CLOSE_FS); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public RootFilesystem(PermissionsProvider perms) {
|
|
||||||
this.perms = perms;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.utils.filesystem;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
public class STDFilesystem implements Filesystem {
|
|
||||||
private File in;
|
|
||||||
private File out;
|
|
||||||
private File err;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String normalize(String... path) {
|
|
||||||
var res = Paths.normalize(path);
|
|
||||||
while (res.startsWith("/")) res = res.substring(1);
|
|
||||||
while (res.endsWith("/")) res = res.substring(0, res.length() - 1);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public synchronized File open(String path, Mode mode) {
|
|
||||||
path = normalize(path);
|
|
||||||
if (in != null && path.equals("in")) return in;
|
|
||||||
else if (out != null && path.equals("out")) return out;
|
|
||||||
else if (err != null && path.equals("err")) return err;
|
|
||||||
else throw new FilesystemException(ErrorReason.DOESNT_EXIST).setAction(ActionType.OPEN).setPath(path);
|
|
||||||
}
|
|
||||||
@Override public synchronized FileStat stat(String path) {
|
|
||||||
path = normalize(path);
|
|
||||||
if (path.equals("in") || path.equals("out") || path.equals("err")) return new FileStat(Mode.READ_WRITE, EntryType.FILE);
|
|
||||||
else return new FileStat(Mode.NONE, EntryType.NONE);
|
|
||||||
}
|
|
||||||
@Override public synchronized void close() {
|
|
||||||
in = out = err = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public STDFilesystem(File in, File out, File err) {
|
|
||||||
this.in = in;
|
|
||||||
this.out = out;
|
|
||||||
this.err = err;
|
|
||||||
}
|
|
||||||
public STDFilesystem(InputStream in, OutputStream out, OutputStream err) {
|
|
||||||
if (in != null) this.in = File.ofStream(in);
|
|
||||||
if (out != null) this.out = File.ofStream(out);
|
|
||||||
if (err != null) this.err = File.ofStream(err);
|
|
||||||
}
|
|
||||||
public STDFilesystem(LineReader in, LineWriter out) {
|
|
||||||
if (in != null) this.in = File.ofLineReader(in);
|
|
||||||
if (out != null) {
|
|
||||||
this.out = File.ofLineWriter(out);
|
|
||||||
this.err = File.ofLineWriter(out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.utils.permissions;
|
|
||||||
|
|
||||||
import java.util.LinkedList;
|
|
||||||
|
|
||||||
public interface Matcher {
|
|
||||||
static class State {
|
|
||||||
public final int predI, trgI, wildcardI;
|
|
||||||
public final boolean wildcard;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return String.format("State [pr=%s;trg=%s;wildN=%s;wild=%s]", predI, trgI, wildcardI, wildcard);
|
|
||||||
}
|
|
||||||
|
|
||||||
public State(int predicateI, int targetI, int wildcardI, boolean wildcard) {
|
|
||||||
this.predI = predicateI;
|
|
||||||
this.trgI = targetI;
|
|
||||||
this.wildcardI = wildcardI;
|
|
||||||
this.wildcard = wildcard;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean match(String predicate, String value);
|
|
||||||
|
|
||||||
public static Matcher fileWildcard() {
|
|
||||||
return (predicate, value) -> execWildcard(predicate, value, '/');
|
|
||||||
}
|
|
||||||
public static Matcher namespaceWildcard() {
|
|
||||||
return (predicate, value) -> execWildcard(predicate, value, '.');
|
|
||||||
}
|
|
||||||
public static Matcher wildcard() {
|
|
||||||
return (predicate, value) -> execWildcard(predicate, value, '\0');
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean execWildcard(String predicate, String target, char delim) {
|
|
||||||
if (predicate.equals("")) return target.equals("");
|
|
||||||
|
|
||||||
var queue = new LinkedList<State>();
|
|
||||||
queue.push(new State(0, 0, 0, false));
|
|
||||||
|
|
||||||
while (!queue.isEmpty()) {
|
|
||||||
var state = queue.poll();
|
|
||||||
var predEnd = state.predI >= predicate.length();
|
|
||||||
|
|
||||||
if (state.trgI >= target.length()) return predEnd;
|
|
||||||
var predC = predEnd ? 0 : predicate.charAt(state.predI);
|
|
||||||
var trgC = target.charAt(state.trgI);
|
|
||||||
|
|
||||||
if (state.wildcard) {
|
|
||||||
if (state.wildcardI == 2 || trgC != delim) {
|
|
||||||
queue.add(new State(state.predI, state.trgI + 1, state.wildcardI, true));
|
|
||||||
}
|
|
||||||
queue.add(new State(state.predI, state.trgI, 0, false));
|
|
||||||
}
|
|
||||||
else if (predC == '*') {
|
|
||||||
queue.add(new State(state.predI + 1, state.trgI, state.wildcardI + 1, false));
|
|
||||||
}
|
|
||||||
else if (state.wildcardI > 0) {
|
|
||||||
if (state.wildcardI > 2) throw new IllegalArgumentException("Too many sequential stars.");
|
|
||||||
queue.add(new State(state.predI, state.trgI, state.wildcardI, true));
|
|
||||||
}
|
|
||||||
else if (!predEnd && (predC == '?' || predC == trgC)) {
|
|
||||||
queue.add(new State(state.predI + 1, state.trgI + 1, 0, false));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.utils.permissions;
|
|
||||||
|
|
||||||
|
|
||||||
public class Permission {
|
|
||||||
public final String namespace;
|
|
||||||
public final Matcher matcher;
|
|
||||||
|
|
||||||
@Override public String toString() {
|
|
||||||
return namespace;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Permission(String namespace, Matcher matcher) {
|
|
||||||
this.namespace = namespace;
|
|
||||||
this.matcher = matcher;
|
|
||||||
}
|
|
||||||
public Permission(String raw) {
|
|
||||||
this(raw, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.utils.permissions;
|
|
||||||
|
|
||||||
public class PermissionPredicate {
|
|
||||||
public final String namespace;
|
|
||||||
public final String value;
|
|
||||||
public final boolean denies;
|
|
||||||
|
|
||||||
public boolean match(Permission permission, String value) {
|
|
||||||
if (!match(permission)) return false;
|
|
||||||
if (this.value == null || value == null) return true;
|
|
||||||
if (permission.matcher == null) return true;
|
|
||||||
else return permission.matcher.match(this.value, value);
|
|
||||||
}
|
|
||||||
public boolean match(Permission permission) {
|
|
||||||
return Matcher.namespaceWildcard().match(namespace, permission.namespace);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
if (value != null) return namespace + ":" + value;
|
|
||||||
else return namespace;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PermissionPredicate(String raw) {
|
|
||||||
raw = raw.trim();
|
|
||||||
|
|
||||||
if (raw.startsWith("!")) {
|
|
||||||
denies = true;
|
|
||||||
raw = raw.substring(1).trim();
|
|
||||||
}
|
|
||||||
else denies = false;
|
|
||||||
|
|
||||||
var i = raw.indexOf(':');
|
|
||||||
|
|
||||||
if (i > 0) {
|
|
||||||
value = raw.substring(i + 1);
|
|
||||||
namespace = raw.substring(0, i);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
value = null;
|
|
||||||
namespace = raw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.utils.permissions;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
public class PermissionsManager implements PermissionsProvider {
|
|
||||||
public final ArrayList<PermissionPredicate> predicates = new ArrayList<>();
|
|
||||||
|
|
||||||
public PermissionsProvider add(PermissionPredicate perm) {
|
|
||||||
predicates.add(perm);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
public PermissionsProvider add(String perm) {
|
|
||||||
predicates.add(new PermissionPredicate(perm));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public boolean hasPermission(Permission perm, String value) {
|
|
||||||
for (var el : predicates) {
|
|
||||||
if (el.match(perm, value)) {
|
|
||||||
if (el.denies) return false;
|
|
||||||
else return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
@Override public boolean hasPermission(Permission perm) {
|
|
||||||
for (var el : predicates) {
|
|
||||||
if (el.match(perm)) {
|
|
||||||
if (el.denies) return false;
|
|
||||||
else return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PermissionsProvider addFromStream(InputStream stream) throws IOException {
|
|
||||||
var reader = new BufferedReader(new InputStreamReader(stream));
|
|
||||||
String line;
|
|
||||||
|
|
||||||
while ((line = reader.readLine()) != null) {
|
|
||||||
var i = line.indexOf('#');
|
|
||||||
if (i >= 0) line = line.substring(0, i);
|
|
||||||
|
|
||||||
line = line.trim();
|
|
||||||
|
|
||||||
if (line.isEmpty()) continue;
|
|
||||||
|
|
||||||
add(line);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.utils.permissions;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.runtime.Extensions;
|
|
||||||
import me.topchetoeu.jscript.runtime.Key;
|
|
||||||
|
|
||||||
public interface PermissionsProvider {
|
|
||||||
public static final Key<PermissionsProvider> KEY = new Key<>();
|
|
||||||
public static final PermissionsProvider ALL_PERMS = (perm, value) -> true;
|
|
||||||
|
|
||||||
boolean hasPermission(Permission perm, String value);
|
|
||||||
|
|
||||||
default boolean hasPermission(Permission perm) {
|
|
||||||
return hasPermission(perm, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
default boolean hasPermission(String perm, String value, Matcher matcher) {
|
|
||||||
return hasPermission(new Permission(perm, matcher), value);
|
|
||||||
}
|
|
||||||
default boolean hasPermission(String perm, Matcher matcher) {
|
|
||||||
return hasPermission(new Permission(perm, matcher));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static PermissionsProvider get(Extensions exts) {
|
|
||||||
return (perm, value) -> {
|
|
||||||
if (exts.hasNotNull(KEY)) return exts.get(KEY).hasPermission(perm);
|
|
||||||
else return true;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -24,11 +24,6 @@ public class Buffer {
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clear() {
|
|
||||||
data = new byte[128];
|
|
||||||
length = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void append(byte b) {
|
public void append(byte b) {
|
||||||
write(length, new byte[] { b });
|
write(length, new byte[] { b });
|
||||||
}
|
}
|
||||||
@@ -7,17 +7,22 @@ public class Filename {
|
|||||||
public final String protocol;
|
public final String protocol;
|
||||||
public final String path;
|
public final String path;
|
||||||
|
|
||||||
@Override public String toString() {
|
public String toString() {
|
||||||
return protocol + "://" + path;
|
return protocol + "://" + path;
|
||||||
}
|
}
|
||||||
@Override public int hashCode() {
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
final int prime = 31;
|
final int prime = 31;
|
||||||
int result = 1;
|
int result = 1;
|
||||||
result = prime * result + protocol.hashCode();
|
result = prime * result + protocol.hashCode();
|
||||||
result = prime * result + path.hashCode();
|
result = prime * result + path.hashCode();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@Override public boolean equals(Object obj) {
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
if (this == obj) return true;
|
if (this == obj) return true;
|
||||||
if (obj == null) return false;
|
if (obj == null) return false;
|
||||||
if (getClass() != obj.getClass()) return false;
|
if (getClass() != obj.getClass()) return false;
|
||||||
@@ -36,6 +41,9 @@ public class Filename {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public Filename(String protocol, String path) {
|
public Filename(String protocol, String path) {
|
||||||
path = path.trim();
|
path = path.trim();
|
||||||
protocol = protocol.trim();
|
protocol = protocol.trim();
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
package me.topchetoeu.jscript.common;
|
package me.topchetoeu.jscript.common;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
public class Location implements Comparable<Location> {
|
public class Location implements Comparable<Location> {
|
||||||
public static final Location INTERNAL = new Location(-1, -1, new Filename("jscript", "native"));
|
public static final Location INTERNAL = new Location(0, 0, new Filename("jscript", "native"));
|
||||||
private int line;
|
private int line;
|
||||||
private int start;
|
private int start;
|
||||||
private Filename filename;
|
private Filename filename;
|
||||||
@@ -14,13 +12,7 @@ public class Location implements Comparable<Location> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
var res = new ArrayList<String>();
|
return filename.toString() + ":" + line + ":" + start;
|
||||||
|
|
||||||
if (filename != null) res.add(filename.toString());
|
|
||||||
if (line >= 0) res.add(line + "");
|
|
||||||
if (start >= 0) res.add(start + "");
|
|
||||||
|
|
||||||
return String.join(":", res);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Location add(int n, boolean clone) {
|
public Location add(int n, boolean clone) {
|
||||||
@@ -1,18 +1,9 @@
|
|||||||
package me.topchetoeu.jscript.common;
|
package me.topchetoeu.jscript.common;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.json.JSON;
|
|
||||||
|
|
||||||
public class Metadata {
|
public class Metadata {
|
||||||
private static final String VERSION;
|
private static final String VERSION = "${VERSION}";
|
||||||
private static final String AUTHOR;
|
private static final String AUTHOR = "${AUTHOR}";
|
||||||
private static final String NAME;
|
private static final String NAME = "${NAME}";
|
||||||
|
|
||||||
static {
|
|
||||||
var data = JSON.parse(null, Reading.resourceToString("metadata.json")).map();
|
|
||||||
VERSION = data.string("version");
|
|
||||||
AUTHOR = data.string("author");
|
|
||||||
NAME = data.string("name");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String version() {
|
public static String version() {
|
||||||
if (VERSION.equals("$" + "{VERSION}")) return "1337-devel";
|
if (VERSION.equals("$" + "{VERSION}")) return "1337-devel";
|
||||||
@@ -13,6 +13,11 @@ public class Reading {
|
|||||||
return reader.readLine();
|
return reader.readLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the given stream to a string
|
||||||
|
* @param in
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
public static String streamToString(InputStream in) {
|
public static String streamToString(InputStream in) {
|
||||||
try {
|
try {
|
||||||
return new String(in.readAllBytes());
|
return new String(in.readAllBytes());
|
||||||
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,6 +1,6 @@
|
|||||||
package me.topchetoeu.jscript.common.events;
|
package me.topchetoeu.jscript.common.events;
|
||||||
|
|
||||||
public class DataNotifier<T> {
|
public class DataNotifier<T> implements Awaitable<T> {
|
||||||
private Notifier notifier = new Notifier();
|
private Notifier notifier = new Notifier();
|
||||||
private boolean isErr;
|
private boolean isErr;
|
||||||
private T val;
|
private T val;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package me.topchetoeu.jscript.common.events;
|
package me.topchetoeu.jscript.common.events;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.runtime.exceptions.InterruptException;
|
import me.topchetoeu.jscript.core.exceptions.InterruptException;
|
||||||
|
|
||||||
public class Notifier {
|
public class Notifier {
|
||||||
private boolean ok = false;
|
private boolean ok = false;
|
||||||
@@ -5,16 +5,16 @@ import java.util.List;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Filename;
|
import me.topchetoeu.jscript.common.Filename;
|
||||||
import me.topchetoeu.jscript.compilation.parsing.Operator;
|
import me.topchetoeu.jscript.core.engine.Context;
|
||||||
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
|
import me.topchetoeu.jscript.core.engine.values.ArrayValue;
|
||||||
import me.topchetoeu.jscript.compilation.parsing.Parsing;
|
import me.topchetoeu.jscript.core.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.compilation.parsing.Token;
|
import me.topchetoeu.jscript.core.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.runtime.Context;
|
import me.topchetoeu.jscript.core.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
|
import me.topchetoeu.jscript.core.exceptions.SyntaxException;
|
||||||
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
|
import me.topchetoeu.jscript.core.parsing.Operator;
|
||||||
import me.topchetoeu.jscript.runtime.values.ArrayValue;
|
import me.topchetoeu.jscript.core.parsing.ParseRes;
|
||||||
import me.topchetoeu.jscript.runtime.values.ObjectValue;
|
import me.topchetoeu.jscript.core.parsing.Parsing;
|
||||||
import me.topchetoeu.jscript.runtime.values.Values;
|
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) {
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
package me.topchetoeu.jscript.core.compilation;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
import me.topchetoeu.jscript.common.Location;
|
||||||
import me.topchetoeu.jscript.common.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
package me.topchetoeu.jscript.core.compilation;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
import me.topchetoeu.jscript.common.Location;
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.compilation.values.FunctionStatement;
|
import me.topchetoeu.jscript.core.compilation.values.FunctionStatement;
|
||||||
|
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class CompoundStatement extends Statement {
|
public class CompoundStatement extends Statement {
|
||||||
public final Statement[] statements;
|
public final Statement[] statements;
|
||||||
@@ -22,16 +22,16 @@ public class CompoundStatement extends Statement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void declare(CompileResult target) {
|
public void declare(ScopeRecord varsScope) {
|
||||||
for (var stm : statements) stm.declare(target);
|
for (var stm : statements) stm.declare(varsScope);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileResult target, boolean pollute, BreakpointType type) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType type) {
|
||||||
List<Statement> statements = new Vector<Statement>();
|
List<Statement> statements = new Vector<Statement>();
|
||||||
if (separateFuncs) for (var stm : this.statements) {
|
if (separateFuncs) for (var stm : this.statements) {
|
||||||
if (stm instanceof FunctionStatement && ((FunctionStatement)stm).statement) {
|
if (stm instanceof FunctionStatement && ((FunctionStatement)stm).statement) {
|
||||||
stm.compile(target, false);
|
stm.compile(target, scope, false);
|
||||||
}
|
}
|
||||||
else statements.add(stm);
|
else statements.add(stm);
|
||||||
}
|
}
|
||||||
@@ -42,12 +42,12 @@ public class CompoundStatement extends Statement {
|
|||||||
for (var i = 0; i < statements.size(); i++) {
|
for (var i = 0; i < statements.size(); i++) {
|
||||||
var stm = statements.get(i);
|
var stm = statements.get(i);
|
||||||
|
|
||||||
if (i != statements.size() - 1) stm.compile(target, false, BreakpointType.STEP_OVER);
|
if (i != statements.size() - 1) stm.compile(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
else stm.compile(target, polluted = pollute, BreakpointType.STEP_OVER);
|
else stm.compile(target, scope, polluted = pollute, BreakpointType.STEP_OVER);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!polluted && pollute) {
|
if (!polluted && pollute) {
|
||||||
target.add(Instruction.pushUndefined());
|
target.add(Instruction.loadValue(loc(), null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user