Merge pull request #11 from TopchetoEU/TopchetoEU/modules
Module support
This commit is contained in:
commit
09eb6507dc
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
* -text
|
29
.gitignore
vendored
29
.gitignore
vendored
@ -1,11 +1,18 @@
|
|||||||
.vscode
|
*
|
||||||
.gradle
|
|
||||||
.ignore
|
!/src
|
||||||
/out
|
!/src/**/*
|
||||||
/build
|
|
||||||
/bin
|
/src/assets/js/ts.js
|
||||||
/dst
|
|
||||||
/*.js
|
!/tests
|
||||||
!/build.js
|
!/tests/**/*
|
||||||
/dead-code
|
|
||||||
/Metadata.java
|
!/.github
|
||||||
|
!/.github/**/*
|
||||||
|
|
||||||
|
!/.gitignore
|
||||||
|
!/.gitattributes
|
||||||
|
!/build.js
|
||||||
|
!/LICENSE
|
||||||
|
!/README.md
|
||||||
|
44
README.md
44
README.md
@ -2,44 +2,28 @@
|
|||||||
|
|
||||||
**NOTE: This had nothing to do with Microsoft's dialect of EcmaScript**
|
**NOTE: This had nothing to do with Microsoft's dialect of EcmaScript**
|
||||||
|
|
||||||
**WARNING: Currently, this code is mostly undocumented. Proceed with caution and a psychiatrist.**
|
**WARNING: Currently, this code is undocumented. Proceed with caution and a psychiatrist.**
|
||||||
|
|
||||||
JScript is an engine, capable of running EcmaScript 5, written entirely in Java. This engine has been developed with the goal of being easy to integrate with your preexisting codebase, **THE GOAL OF THIS ENGINE IS NOT PERFORMANCE**. My crude experiments show that this engine is 50x-100x slower than V8, which, although bad, is acceptable for most simple scripting purposes. Note that although the codebase has a Main class, this isn't meant to be a standalone program, but instead a library for running JavaScript code.
|
JScript is an engine, capable of running EcmaScript 5, written entirely in Java. This engine has been developed with the goal of being easy to integrate with your preexisting codebase, **THE GOAL OF THIS ENGINE IS NOT PERFORMANCE**. My crude experiments show that this engine is 50x-100x slower than V8, which, although bad, is acceptable for most simple scripting purposes. Note that although the codebase has a Main class, this isn't meant to be a standalone program, but instead a library for running JavaScript code.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
The following will create a REPL using the engine as a backend. Not that this won't properly log errors. I recommend checking out the implementation in `Main.main`:
|
The following is going to execute a simple javascript statement:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
var engine = new Engine(true /* false if you dont want debugging */);
|
var engine = new Engine(false);
|
||||||
var env = new Environment(null, null, null);
|
// Initialize a standard environment, with implementations of most basic standard libraries (Object, Array, Symbol, etc.)
|
||||||
var debugger = new DebugServer();
|
var env = Internals.apply(new Environment());
|
||||||
|
|
||||||
// Create one target for the engine and start debugging server
|
|
||||||
debugger.targets.put("target", (socket, req) -> new SimpleDebugger(socket, engine));
|
|
||||||
debugger.start(new InetSocketAddress("127.0.0.1", 9229), true);
|
|
||||||
|
|
||||||
// Queue code to load internal libraries and start engine
|
// Queue code to load internal libraries and start engine
|
||||||
engine.pushMsg(false, null, new Internals().getApplier(env));
|
var awaitable = engine.pushMsg(false, env, new Filename("tmp", "eval"), "10 + Math.sqrt(5 / 3)", null);
|
||||||
engine.start();
|
// Run the engine on the same thread, until the event loop runs empty
|
||||||
|
engine.run(true);
|
||||||
|
|
||||||
while (true) {
|
// Get our result
|
||||||
try {
|
System.out.println(awaitable.await());
|
||||||
var raw = Reading.read();
|
|
||||||
if (raw == null) break;
|
|
||||||
|
|
||||||
// Push a message to the engine with the raw REPL code
|
|
||||||
var res = engine.pushMsg(
|
|
||||||
false, new Context(engine).pushEnv(env),
|
|
||||||
new Filename("jscript", "repl.js"), raw, null
|
|
||||||
).await();
|
|
||||||
|
|
||||||
Values.printValue(null, res);
|
|
||||||
}
|
|
||||||
catch (EngineException e) { Values.printError(e, ""); }
|
|
||||||
catch (SyntaxException ex) {
|
|
||||||
System.out.println("Syntax error:" + ex.msg);
|
|
||||||
}
|
|
||||||
catch (IOException e) { }
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## NOTE:
|
||||||
|
|
||||||
|
To setup the typescript bundle in your sources, run `node build.js init-ts`. This will download the latest version of typescript, minify it, and add it to your src/assets folder. If you are going to work with the `node build.js debug|release` command, this is not a necessary step.
|
||||||
|
126
build.js
126
build.js
@ -2,16 +2,8 @@ const { spawn } = require('child_process');
|
|||||||
const fs = require('fs/promises');
|
const fs = require('fs/promises');
|
||||||
const pt = require('path');
|
const pt = require('path');
|
||||||
const { argv, exit } = require('process');
|
const { argv, exit } = require('process');
|
||||||
|
const { Readable } = require('stream');
|
||||||
|
|
||||||
const conf = {
|
|
||||||
name: "java-jscript",
|
|
||||||
author: "TopchetoEU",
|
|
||||||
javahome: "",
|
|
||||||
version: argv[3]
|
|
||||||
};
|
|
||||||
|
|
||||||
if (conf.version.startsWith('refs/tags/')) conf.version = conf.version.substring(10);
|
|
||||||
if (conf.version.startsWith('v')) conf.version = conf.version.substring(1);
|
|
||||||
|
|
||||||
async function* find(src, dst, wildcard) {
|
async function* find(src, dst, wildcard) {
|
||||||
const stat = await fs.stat(src);
|
const stat = await fs.stat(src);
|
||||||
@ -36,9 +28,9 @@ async function copy(src, dst, wildcard) {
|
|||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
function run(cmd, ...args) {
|
function run(suppressOutput, cmd, ...args) {
|
||||||
return new Promise((res, rej) => {
|
return new Promise((res, rej) => {
|
||||||
const proc = spawn(cmd, args, { stdio: 'inherit' });
|
const proc = spawn(cmd, args, { stdio: suppressOutput ? 'ignore' : 'inherit' });
|
||||||
proc.once('exit', code => {
|
proc.once('exit', code => {
|
||||||
if (code === 0) res(code);
|
if (code === 0) res(code);
|
||||||
else rej(new Error(`Process ${cmd} exited with code ${code}.`));
|
else rej(new Error(`Process ${cmd} exited with code ${code}.`));
|
||||||
@ -46,7 +38,84 @@ function run(cmd, ...args) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function compileJava() {
|
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 = { 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 {
|
try {
|
||||||
await fs.writeFile('Metadata.java', (await fs.readFile('src/me/topchetoeu/jscript/Metadata.java')).toString()
|
await fs.writeFile('Metadata.java', (await fs.readFile('src/me/topchetoeu/jscript/Metadata.java')).toString()
|
||||||
.replace('${VERSION}', conf.version)
|
.replace('${VERSION}', conf.version)
|
||||||
@ -57,8 +126,10 @@ async function compileJava() {
|
|||||||
if (argv[2] === 'debug') args.push('-g');
|
if (argv[2] === 'debug') args.push('-g');
|
||||||
args.push('-d', 'dst/classes', 'Metadata.java');
|
args.push('-d', 'dst/classes', 'Metadata.java');
|
||||||
|
|
||||||
|
console.log('Compiling java project...');
|
||||||
for await (const path of find('src', undefined, v => v.endsWith('.java') && !v.endsWith('Metadata.java'))) args.push(path);
|
for await (const path of find('src', undefined, v => v.endsWith('.java') && !v.endsWith('Metadata.java'))) args.push(path);
|
||||||
await run(conf.javahome + 'javac', ...args);
|
await run(false, conf.javahome + 'javac', ...args);
|
||||||
|
console.log('Compiled java project!');
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
await fs.rm('Metadata.java');
|
await fs.rm('Metadata.java');
|
||||||
@ -67,10 +138,31 @@ async function compileJava() {
|
|||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
try { await fs.rm('dst', { recursive: true }); } catch {}
|
if (argv[2] === 'init-ts') {
|
||||||
await copy('src', 'dst/classes', v => !v.endsWith('.java'));
|
await downloadTypescript('src/assets/js/ts.js');
|
||||||
await compileJava();
|
}
|
||||||
await run('jar', '-c', '-f', 'dst/jscript.jar', '-e', 'me.topchetoeu.jscript.Main', '-C', 'dst/classes', '.');
|
else {
|
||||||
|
const conf = {
|
||||||
|
name: "java-jscript",
|
||||||
|
author: "TopchetoEU",
|
||||||
|
javahome: "",
|
||||||
|
version: argv[3]
|
||||||
|
};
|
||||||
|
|
||||||
|
if (conf.version.startsWith('refs/tags/')) conf.version = conf.version.substring(10);
|
||||||
|
if (conf.version.startsWith('v')) conf.version = conf.version.substring(1);
|
||||||
|
|
||||||
|
try { await fs.rm('dst', { recursive: true }); } catch {}
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
downloadTypescript('dst/classes/assets/js/ts.js'),
|
||||||
|
copy('src', 'dst/classes', v => !v.endsWith('.java')),
|
||||||
|
compileJava(conf),
|
||||||
|
]);
|
||||||
|
|
||||||
|
await run(true, 'jar', '-c', '-f', 'dst/jscript.jar', '-e', 'me.topchetoeu.jscript.Main', '-C', 'dst/classes', '.');
|
||||||
|
console.log('Done!');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
if (argv[2] === 'debug') throw e;
|
if (argv[2] === 'debug') throw e;
|
||||||
|
226
src/assets/js/bootstrap.js
vendored
226
src/assets/js/bootstrap.js
vendored
@ -1,113 +1,113 @@
|
|||||||
(function (ts, env, libs) {
|
(function (ts, env, libs) {
|
||||||
var src = '', version = 0;
|
var src = '', version = 0;
|
||||||
var lib = libs.concat([
|
var lib = libs.concat([
|
||||||
'declare function exit(): never;',
|
'declare function exit(): never;',
|
||||||
'declare function go(): any;',
|
'declare function go(): any;',
|
||||||
'declare function getTsDeclarations(): string[];'
|
'declare function getTsDeclarations(): string[];'
|
||||||
]).join('');
|
]).join('');
|
||||||
var libSnapshot = ts.ScriptSnapshot.fromString(lib);
|
var libSnapshot = ts.ScriptSnapshot.fromString(lib);
|
||||||
var environments = {};
|
var environments = {};
|
||||||
var declSnapshots = [];
|
var declSnapshots = [];
|
||||||
|
|
||||||
var settings = {
|
var settings = {
|
||||||
outDir: "/out",
|
outDir: "/out",
|
||||||
declarationDir: "/out",
|
declarationDir: "/out",
|
||||||
target: ts.ScriptTarget.ES5,
|
target: ts.ScriptTarget.ES5,
|
||||||
lib: [ ],
|
lib: [ ],
|
||||||
module: ts.ModuleKind.None,
|
module: ts.ModuleKind.None,
|
||||||
declaration: true,
|
declaration: true,
|
||||||
stripInternal: true,
|
stripInternal: true,
|
||||||
downlevelIteration: true,
|
downlevelIteration: true,
|
||||||
forceConsistentCasingInFileNames: true,
|
forceConsistentCasingInFileNames: true,
|
||||||
experimentalDecorators: true,
|
experimentalDecorators: true,
|
||||||
strict: true,
|
strict: true,
|
||||||
sourceMap: true,
|
sourceMap: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
var reg = ts.createDocumentRegistry();
|
var reg = ts.createDocumentRegistry();
|
||||||
var service = ts.createLanguageService({
|
var service = ts.createLanguageService({
|
||||||
getCurrentDirectory: function() { return "/"; },
|
getCurrentDirectory: function() { return "/"; },
|
||||||
getDefaultLibFileName: function() { return "/lib.d.ts"; },
|
getDefaultLibFileName: function() { return "/lib.d.ts"; },
|
||||||
getScriptFileNames: function() {
|
getScriptFileNames: function() {
|
||||||
var res = [ "/src.ts", "/lib.d.ts" ];
|
var res = [ "/src.ts", "/lib.d.ts" ];
|
||||||
for (var i = 0; i < declSnapshots.length; i++) res.push("/glob." + (i + 1) + ".d.ts");
|
for (var i = 0; i < declSnapshots.length; i++) res.push("/glob." + (i + 1) + ".d.ts");
|
||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
getCompilationSettings: function () { return settings; },
|
getCompilationSettings: function () { return settings; },
|
||||||
fileExists: function(filename) { return filename === "/lib.d.ts" || filename === "/src.ts" || filename === "/glob.d.ts"; },
|
fileExists: function(filename) { return filename === "/lib.d.ts" || filename === "/src.ts" || filename === "/glob.d.ts"; },
|
||||||
|
|
||||||
getScriptSnapshot: function(filename) {
|
getScriptSnapshot: function(filename) {
|
||||||
if (filename === "/lib.d.ts") return libSnapshot;
|
if (filename === "/lib.d.ts") return libSnapshot;
|
||||||
if (filename === "/src.ts") return ts.ScriptSnapshot.fromString(src);
|
if (filename === "/src.ts") return ts.ScriptSnapshot.fromString(src);
|
||||||
|
|
||||||
var index = /\/glob\.(\d+)\.d\.ts/g.exec(filename);
|
var index = /\/glob\.(\d+)\.d\.ts/g.exec(filename);
|
||||||
if (index && index[1] && (index = Number(index[1])) && index > 0 && index <= declSnapshots.length) {
|
if (index && index[1] && (index = Number(index[1])) && index > 0 && index <= declSnapshots.length) {
|
||||||
return declSnapshots[index - 1];
|
return declSnapshots[index - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error("File '" + filename + "' doesn't exist.");
|
throw new Error("File '" + filename + "' doesn't exist.");
|
||||||
},
|
},
|
||||||
getScriptVersion: function (filename) {
|
getScriptVersion: function (filename) {
|
||||||
if (filename === "/lib.d.ts" || filename.startsWith("/glob.")) return 0;
|
if (filename === "/lib.d.ts" || filename.startsWith("/glob.")) return 0;
|
||||||
else return version;
|
else return version;
|
||||||
},
|
},
|
||||||
}, reg);
|
}, reg);
|
||||||
|
|
||||||
service.getEmitOutput("/lib.d.ts");
|
service.getEmitOutput("/lib.d.ts");
|
||||||
log("Loaded libraries!");
|
log("Loaded libraries!");
|
||||||
|
|
||||||
var oldCompile = env.compile;
|
var oldCompile = env.compile;
|
||||||
|
|
||||||
function compile(code, filename, env) {
|
function compile(code, filename, env) {
|
||||||
src = code;
|
src = code;
|
||||||
version++;
|
version++;
|
||||||
|
|
||||||
if (!environments[env.id]) environments[env.id] = []
|
if (!environments[env.id]) environments[env.id] = []
|
||||||
declSnapshots = environments[env.id];
|
var decls = declSnapshots = environments[env.id];
|
||||||
var emit = service.getEmitOutput("/src.ts");
|
var emit = service.getEmitOutput("/src.ts");
|
||||||
|
|
||||||
var diagnostics = []
|
var diagnostics = []
|
||||||
.concat(service.getCompilerOptionsDiagnostics())
|
.concat(service.getCompilerOptionsDiagnostics())
|
||||||
.concat(service.getSyntacticDiagnostics("/src.ts"))
|
.concat(service.getSyntacticDiagnostics("/src.ts"))
|
||||||
.concat(service.getSemanticDiagnostics("/src.ts"))
|
.concat(service.getSemanticDiagnostics("/src.ts"))
|
||||||
.map(function (diagnostic) {
|
.map(function (diagnostic) {
|
||||||
var message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
|
var message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
|
||||||
if (diagnostic.file) {
|
if (diagnostic.file) {
|
||||||
var pos = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
|
var pos = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
|
||||||
var file = diagnostic.file.fileName.substring(1);
|
var file = diagnostic.file.fileName.substring(1);
|
||||||
if (file === "src.ts") file = filename;
|
if (file === "src.ts") file = filename;
|
||||||
return file + ":" + (pos.line + 1) + ":" + (pos.character + 1) + ": " + message;
|
return file + ":" + (pos.line + 1) + ":" + (pos.character + 1) + ": " + message;
|
||||||
}
|
}
|
||||||
else return message;
|
else return message;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (diagnostics.length > 0) {
|
if (diagnostics.length > 0) {
|
||||||
throw new SyntaxError(diagnostics.join("\n"));
|
throw new SyntaxError(diagnostics.join("\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
var map = JSON.parse(emit.outputFiles[0].text);
|
var map = JSON.parse(emit.outputFiles[0].text);
|
||||||
var result = emit.outputFiles[1].text;
|
var result = emit.outputFiles[1].text;
|
||||||
var declaration = emit.outputFiles[2].text;
|
var declaration = emit.outputFiles[2].text;
|
||||||
|
|
||||||
var compiled = oldCompile(result, filename, env);
|
var compiled = oldCompile(result, filename, env);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
function: function () {
|
function: function () {
|
||||||
var val = compiled.function.apply(this, arguments);
|
var val = compiled.function.apply(this, arguments);
|
||||||
if (declaration !== '') declSnapshots.push(ts.ScriptSnapshot.fromString(declaration));
|
if (declaration !== '') decls.push(ts.ScriptSnapshot.fromString(declaration));
|
||||||
return val;
|
return val;
|
||||||
},
|
},
|
||||||
breakpoints: compiled.breakpoints,
|
breakpoints: compiled.breakpoints,
|
||||||
mapChain: compiled.mapChain.concat(map.mappings),
|
mapChain: compiled.mapChain.concat(map.mappings),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function apply(env) {
|
function apply(env) {
|
||||||
env.compile = compile;
|
env.compile = compile;
|
||||||
env.global.getTsDeclarations = function() {
|
env.global.getTsDeclarations = function() {
|
||||||
return environments[env.id];
|
return environments[env.id];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
apply(env);
|
apply(env);
|
||||||
})(arguments[0], arguments[1], arguments[2]);
|
})(arguments[0], arguments[1], arguments[2]);
|
||||||
|
1241
src/assets/js/lib.d.ts
vendored
1241
src/assets/js/lib.d.ts
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -1,6 +1,7 @@
|
|||||||
package me.topchetoeu.jscript;
|
package me.topchetoeu.jscript;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
public class Filename {
|
public class Filename {
|
||||||
public final String protocol;
|
public final String protocol;
|
||||||
@ -40,9 +41,7 @@ public class Filename {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Filename fromFile(File file) {
|
|
||||||
return new Filename("file", file.getAbsolutePath());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Filename(String protocol, String path) {
|
public Filename(String protocol, String path) {
|
||||||
@ -57,4 +56,10 @@ public class Filename {
|
|||||||
if (i >= 0) return new Filename(val.substring(0, i).trim(), val.substring(i + 3).trim());
|
if (i >= 0) return new Filename(val.substring(0, i).trim(), val.substring(i + 3).trim());
|
||||||
else return new Filename("file", val.trim());
|
else return new Filename("file", val.trim());
|
||||||
}
|
}
|
||||||
|
public static Path normalize(String path) {
|
||||||
|
return Path.of(Path.of("/" + path.trim().replace("\\", "/")).normalize().toString().substring(1));
|
||||||
|
}
|
||||||
|
public static Filename fromFile(File file) {
|
||||||
|
return new Filename("file", file.getAbsolutePath());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,170 +1,170 @@
|
|||||||
package me.topchetoeu.jscript;
|
package me.topchetoeu.jscript;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.Engine;
|
import me.topchetoeu.jscript.engine.Engine;
|
||||||
import me.topchetoeu.jscript.engine.Environment;
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
import me.topchetoeu.jscript.engine.debug.DebugServer;
|
import me.topchetoeu.jscript.engine.debug.DebugServer;
|
||||||
import me.topchetoeu.jscript.engine.debug.SimpleDebugger;
|
import me.topchetoeu.jscript.engine.debug.SimpleDebugger;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.events.Observer;
|
import me.topchetoeu.jscript.events.Observer;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.exceptions.InterruptException;
|
import me.topchetoeu.jscript.exceptions.InterruptException;
|
||||||
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
||||||
import me.topchetoeu.jscript.filesystem.MemoryFilesystem;
|
import me.topchetoeu.jscript.filesystem.MemoryFilesystem;
|
||||||
import me.topchetoeu.jscript.filesystem.Mode;
|
import me.topchetoeu.jscript.filesystem.Mode;
|
||||||
import me.topchetoeu.jscript.filesystem.PhysicalFilesystem;
|
import me.topchetoeu.jscript.filesystem.PhysicalFilesystem;
|
||||||
import me.topchetoeu.jscript.lib.Internals;
|
import me.topchetoeu.jscript.lib.Internals;
|
||||||
|
import me.topchetoeu.jscript.modules.ModuleRepo;
|
||||||
public class Main {
|
|
||||||
public static class Printer implements Observer<Object> {
|
public class Main {
|
||||||
public void next(Object data) {
|
public static class Printer implements Observer<Object> {
|
||||||
Values.printValue(null, data);
|
public void next(Object data) {
|
||||||
System.out.println();
|
Values.printValue(null, data);
|
||||||
}
|
System.out.println();
|
||||||
|
}
|
||||||
public void error(RuntimeException err) {
|
|
||||||
Values.printError(err, null);
|
public void error(RuntimeException err) {
|
||||||
}
|
Values.printError(err, null);
|
||||||
|
}
|
||||||
public void finish() {
|
|
||||||
engineTask.interrupt();
|
public void finish() {
|
||||||
}
|
engineTask.interrupt();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
static Thread engineTask, debugTask;
|
|
||||||
static Engine engine = new Engine(true);
|
static Thread engineTask, debugTask;
|
||||||
static DebugServer debugServer = new DebugServer();
|
static Engine engine = new Engine(true);
|
||||||
static Environment environment = new Environment(null, null, null);
|
static DebugServer debugServer = new DebugServer();
|
||||||
|
static Environment environment = new Environment(null, null, null);
|
||||||
static int j = 0;
|
|
||||||
static boolean exited = false;
|
static int j = 0;
|
||||||
static String[] args;
|
static boolean exited = false;
|
||||||
|
static String[] args;
|
||||||
private static void reader() {
|
|
||||||
try {
|
private static void reader() {
|
||||||
for (var arg : args) {
|
try {
|
||||||
try {
|
for (var arg : args) {
|
||||||
if (arg.equals("--ts")) initTypescript();
|
try {
|
||||||
else {
|
if (arg.equals("--ts")) initTypescript();
|
||||||
var file = Path.of(arg);
|
else {
|
||||||
var raw = Files.readString(file);
|
var file = Path.of(arg);
|
||||||
var res = engine.pushMsg(
|
var raw = Files.readString(file);
|
||||||
false, new Context(engine, environment),
|
var res = engine.pushMsg(
|
||||||
Filename.fromFile(file.toFile()),
|
false, environment,
|
||||||
raw, null
|
Filename.fromFile(file.toFile()),
|
||||||
).await();
|
raw, null
|
||||||
Values.printValue(null, res);
|
).await();
|
||||||
System.out.println();
|
Values.printValue(null, res);
|
||||||
}
|
System.out.println();
|
||||||
}
|
}
|
||||||
catch (EngineException e) { Values.printError(e, null); }
|
}
|
||||||
}
|
catch (EngineException e) { Values.printError(e, null); }
|
||||||
for (var i = 0; ; i++) {
|
}
|
||||||
try {
|
for (var i = 0; ; i++) {
|
||||||
var raw = Reading.read();
|
try {
|
||||||
|
var raw = Reading.read();
|
||||||
if (raw == null) break;
|
|
||||||
var res = engine.pushMsg(
|
if (raw == null) break;
|
||||||
false, new Context(engine, environment),
|
var res = engine.pushMsg(
|
||||||
new Filename("jscript", "repl/" + i + ".js"),
|
false, environment,
|
||||||
raw, null
|
new Filename("jscript", "repl/" + i + ".js"),
|
||||||
).await();
|
raw, null
|
||||||
Values.printValue(null, res);
|
).await();
|
||||||
System.out.println();
|
Values.printValue(null, res);
|
||||||
}
|
System.out.println();
|
||||||
catch (EngineException e) { Values.printError(e, null); }
|
}
|
||||||
catch (SyntaxException e) { Values.printError(e, null); }
|
catch (EngineException e) { Values.printError(e, null); }
|
||||||
}
|
catch (SyntaxException e) { Values.printError(e, null); }
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
}
|
||||||
System.out.println(e.toString());
|
catch (IOException e) {
|
||||||
exited = true;
|
System.out.println(e.toString());
|
||||||
}
|
exited = true;
|
||||||
catch (RuntimeException ex) {
|
}
|
||||||
if (!exited) {
|
catch (RuntimeException ex) {
|
||||||
System.out.println("Internal error ocurred:");
|
if (!exited) {
|
||||||
ex.printStackTrace();
|
System.out.println("Internal error ocurred:");
|
||||||
}
|
ex.printStackTrace();
|
||||||
}
|
}
|
||||||
if (exited) {
|
}
|
||||||
debugTask.interrupt();
|
if (exited) {
|
||||||
engineTask.interrupt();
|
debugTask.interrupt();
|
||||||
}
|
engineTask.interrupt();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private static void initEnv() {
|
|
||||||
environment = Internals.apply(environment);
|
private static void initEnv() {
|
||||||
|
environment = Internals.apply(environment);
|
||||||
environment.global.define(false, new NativeFunction("exit", (_ctx, th, args) -> {
|
|
||||||
exited = true;
|
environment.global.define(false, new NativeFunction("exit", (_ctx, th, args) -> {
|
||||||
throw new InterruptException();
|
exited = true;
|
||||||
}));
|
throw new InterruptException();
|
||||||
environment.global.define(false, new NativeFunction("go", (_ctx, th, args) -> {
|
}));
|
||||||
try {
|
environment.global.define(false, new NativeFunction("go", (_ctx, th, args) -> {
|
||||||
var f = Path.of("do.js");
|
try {
|
||||||
var func = _ctx.compile(new Filename("do", "do/" + j++ + ".js"), new String(Files.readAllBytes(f)));
|
var f = Path.of("do.js");
|
||||||
return func.call(_ctx);
|
var func = _ctx.compile(new Filename("do", "do/" + j++ + ".js"), new String(Files.readAllBytes(f)));
|
||||||
}
|
return func.call(_ctx);
|
||||||
catch (IOException e) {
|
}
|
||||||
throw new EngineException("Couldn't open do.js");
|
catch (IOException e) {
|
||||||
}
|
throw new EngineException("Couldn't open do.js");
|
||||||
}));
|
}
|
||||||
|
}));
|
||||||
environment.filesystem.protocols.put("temp", new MemoryFilesystem(Mode.READ_WRITE));
|
|
||||||
environment.filesystem.protocols.put("file", new PhysicalFilesystem(Path.of(".").toAbsolutePath()));
|
environment.filesystem.protocols.put("temp", new MemoryFilesystem(Mode.READ_WRITE));
|
||||||
}
|
environment.filesystem.protocols.put("file", new PhysicalFilesystem("."));
|
||||||
private static void initEngine() {
|
environment.modules.repos.put("file", ModuleRepo.ofFilesystem(environment.filesystem));
|
||||||
debugServer.targets.put("target", (ws, req) -> new SimpleDebugger(ws, engine));
|
}
|
||||||
engineTask = engine.start();
|
private static void initEngine() {
|
||||||
debugTask = debugServer.start(new InetSocketAddress("127.0.0.1", 9229), true);
|
debugServer.targets.put("target", (ws, req) -> new SimpleDebugger(ws, engine));
|
||||||
}
|
engineTask = engine.start();
|
||||||
private static void initTypescript() {
|
debugTask = debugServer.start(new InetSocketAddress("127.0.0.1", 9229), true);
|
||||||
try {
|
}
|
||||||
var tsEnv = Internals.apply(new Environment(null, null, null));
|
private static void initTypescript() {
|
||||||
tsEnv.stackVisible = false;
|
try {
|
||||||
tsEnv.global.define(null, "module", false, new ObjectValue());
|
var tsEnv = Internals.apply(new Environment(null, null, null));
|
||||||
var bsEnv = Internals.apply(new Environment(null, null, null));
|
tsEnv.stackVisible = false;
|
||||||
bsEnv.stackVisible = false;
|
tsEnv.global.define(null, "module", false, new ObjectValue());
|
||||||
|
var bsEnv = Internals.apply(new Environment(null, null, null));
|
||||||
engine.pushMsg(
|
bsEnv.stackVisible = false;
|
||||||
false, new Context(engine, tsEnv),
|
|
||||||
new Filename("jscript", "ts.js"),
|
engine.pushMsg(
|
||||||
Reading.resourceToString("js/ts.js"), null
|
false, tsEnv,
|
||||||
).await();
|
new Filename("jscript", "ts.js"),
|
||||||
System.out.println("Loaded typescript!");
|
Reading.resourceToString("js/ts.js"), null
|
||||||
|
).await();
|
||||||
var ctx = new Context(engine, bsEnv);
|
System.out.println("Loaded typescript!");
|
||||||
|
|
||||||
engine.pushMsg(
|
engine.pushMsg(
|
||||||
false, ctx,
|
false, bsEnv,
|
||||||
new Filename("jscript", "bootstrap.js"), Reading.resourceToString("js/bootstrap.js"), null,
|
new Filename("jscript", "bootstrap.js"), Reading.resourceToString("js/bootstrap.js"), null,
|
||||||
tsEnv.global.get(ctx, "ts"), environment, new ArrayValue(null, Reading.resourceToString("js/lib.d.ts"))
|
tsEnv.global.get(new Context(engine, bsEnv), "ts"), environment, new ArrayValue(null, Reading.resourceToString("js/lib.d.ts"))
|
||||||
).await();
|
).await();
|
||||||
}
|
}
|
||||||
catch (EngineException e) {
|
catch (EngineException e) {
|
||||||
Values.printError(e, "(while initializing TS)");
|
Values.printError(e, "(while initializing TS)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String args[]) {
|
public static void main(String args[]) {
|
||||||
System.out.println(String.format("Running %s v%s by %s", Metadata.name(), Metadata.version(), Metadata.author()));
|
System.out.println(String.format("Running %s v%s by %s", Metadata.name(), Metadata.version(), Metadata.author()));
|
||||||
|
|
||||||
Main.args = args;
|
Main.args = args;
|
||||||
var reader = new Thread(Main::reader);
|
var reader = new Thread(Main::reader);
|
||||||
|
|
||||||
initEnv();
|
initEnv();
|
||||||
initEngine();
|
initEngine();
|
||||||
|
|
||||||
reader.setDaemon(true);
|
reader.setDaemon(true);
|
||||||
reader.setName("STD Reader");
|
reader.setName("STD Reader");
|
||||||
reader.start();
|
reader.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,52 +1,52 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
package me.topchetoeu.jscript.compilation;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.compilation.values.FunctionStatement;
|
import me.topchetoeu.jscript.compilation.values.FunctionStatement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class VariableDeclareStatement extends Statement {
|
public class VariableDeclareStatement extends Statement {
|
||||||
public static class Pair {
|
public static class Pair {
|
||||||
public final String name;
|
public final String name;
|
||||||
public final Statement value;
|
public final Statement value;
|
||||||
public final Location location;
|
public final Location location;
|
||||||
|
|
||||||
public Pair(String name, Statement value, Location location) {
|
public Pair(String name, Statement value, Location location) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.location = location;
|
this.location = location;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final List<Pair> values;
|
public final List<Pair> values;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void declare(ScopeRecord varsScope) {
|
public void declare(ScopeRecord varsScope) {
|
||||||
for (var key : values) {
|
for (var key : values) {
|
||||||
varsScope.define(key.name);
|
varsScope.define(key.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
for (var entry : values) {
|
for (var entry : values) {
|
||||||
if (entry.name == null) continue;
|
if (entry.name == null) continue;
|
||||||
var key = scope.getKey(entry.name);
|
var key = scope.getKey(entry.name);
|
||||||
|
|
||||||
if (key instanceof String) target.add(Instruction.makeVar(entry.location, (String)key));
|
if (key instanceof String) target.add(Instruction.makeVar(entry.location, (String)key));
|
||||||
|
|
||||||
if (entry.value != null) {
|
if (entry.value != null) {
|
||||||
FunctionStatement.compileWithName(entry.value, target, scope, true, entry.name, BreakpointType.STEP_OVER);
|
FunctionStatement.compileWithName(entry.value, target, scope, true, entry.name, BreakpointType.STEP_OVER);
|
||||||
target.add(Instruction.storeVar(entry.location, key));
|
target.add(Instruction.storeVar(entry.location, key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public VariableDeclareStatement(Location loc, List<Pair> values) {
|
public VariableDeclareStatement(Location loc, List<Pair> values) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.values = values;
|
this.values = values;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class BreakStatement extends Statement {
|
public class BreakStatement extends Statement {
|
||||||
public final String label;
|
public final String label;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
target.add(Instruction.nop(loc(), "break", label));
|
target.add(Instruction.nop(loc(), "break", label));
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public BreakStatement(Location loc, String label) {
|
public BreakStatement(Location loc, String label) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.label = label;
|
this.label = label;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class ContinueStatement extends Statement {
|
public class ContinueStatement extends Statement {
|
||||||
public final String label;
|
public final String label;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
target.add(Instruction.nop(loc(), "cont", label));
|
target.add(Instruction.nop(loc(), "cont", label));
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContinueStatement(Location loc, String label) {
|
public ContinueStatement(Location loc, String label) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.label = label;
|
this.label = label;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class DebugStatement extends Statement {
|
public class DebugStatement extends Statement {
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
target.add(Instruction.debug(loc()));
|
target.add(Instruction.debug(loc()));
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public DebugStatement(Location loc) {
|
public DebugStatement(Location loc) {
|
||||||
super(loc);
|
super(loc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,27 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class DeleteStatement extends Statement {
|
public class DeleteStatement extends Statement {
|
||||||
public final Statement key;
|
public final Statement key;
|
||||||
public final Statement value;
|
public final Statement value;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
value.compile(target, scope, true);
|
value.compile(target, scope, true);
|
||||||
key.compile(target, scope, true);
|
key.compile(target, scope, true);
|
||||||
|
|
||||||
target.add(Instruction.delete(loc()));
|
target.add(Instruction.delete(loc()));
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), true));
|
if (pollute) target.add(Instruction.loadValue(loc(), true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public DeleteStatement(Location loc, Statement key, Statement value) {
|
public DeleteStatement(Location loc, Statement key, Statement value) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,37 +1,37 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class DoWhileStatement extends Statement {
|
public class DoWhileStatement extends Statement {
|
||||||
public final Statement condition, body;
|
public final Statement condition, body;
|
||||||
public final String label;
|
public final String label;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void declare(ScopeRecord globScope) {
|
public void declare(ScopeRecord globScope) {
|
||||||
body.declare(globScope);
|
body.declare(globScope);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
body.compile(target, scope, false, BreakpointType.STEP_OVER);
|
body.compile(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
int mid = target.size();
|
int mid = target.size();
|
||||||
condition.compile(target, scope, true, BreakpointType.STEP_OVER);
|
condition.compile(target, scope, true, BreakpointType.STEP_OVER);
|
||||||
int end = target.size();
|
int end = target.size();
|
||||||
|
|
||||||
WhileStatement.replaceBreaks(target, label, start, mid - 1, mid, end + 1);
|
WhileStatement.replaceBreaks(target, label, start, mid - 1, mid, end + 1);
|
||||||
target.add(Instruction.jmpIf(loc(), start - end));
|
target.add(Instruction.jmpIf(loc(), start - end));
|
||||||
}
|
}
|
||||||
|
|
||||||
public DoWhileStatement(Location loc, String label, Statement condition, Statement body) {
|
public DoWhileStatement(Location loc, String label, Statement condition, Statement body) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.label = label;
|
this.label = label;
|
||||||
this.condition = condition;
|
this.condition = condition;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,73 +1,73 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.engine.Operation;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class ForInStatement extends Statement {
|
public class ForInStatement extends Statement {
|
||||||
public final String varName;
|
public final String varName;
|
||||||
public final boolean isDeclaration;
|
public final boolean isDeclaration;
|
||||||
public final Statement varValue, object, body;
|
public final Statement varValue, object, body;
|
||||||
public final String label;
|
public final String label;
|
||||||
public final Location varLocation;
|
public final Location varLocation;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void declare(ScopeRecord globScope) {
|
public void declare(ScopeRecord globScope) {
|
||||||
body.declare(globScope);
|
body.declare(globScope);
|
||||||
if (isDeclaration) globScope.define(varName);
|
if (isDeclaration) globScope.define(varName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
var key = scope.getKey(varName);
|
var key = scope.getKey(varName);
|
||||||
|
|
||||||
int first = target.size();
|
int first = target.size();
|
||||||
if (key instanceof String) target.add(Instruction.makeVar(loc(), (String)key));
|
if (key instanceof String) target.add(Instruction.makeVar(loc(), (String)key));
|
||||||
|
|
||||||
if (varValue != null) {
|
if (varValue != null) {
|
||||||
varValue.compile(target, scope, true);
|
varValue.compile(target, scope, true);
|
||||||
target.add(Instruction.storeVar(loc(), scope.getKey(varName)));
|
target.add(Instruction.storeVar(loc(), scope.getKey(varName)));
|
||||||
}
|
}
|
||||||
|
|
||||||
object.compile(target, scope, true, BreakpointType.STEP_OVER);
|
object.compile(target, scope, true, BreakpointType.STEP_OVER);
|
||||||
target.add(Instruction.keys(loc(), true));
|
target.add(Instruction.keys(loc(), true));
|
||||||
|
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
target.add(Instruction.dup(loc()));
|
target.add(Instruction.dup(loc()));
|
||||||
target.add(Instruction.loadValue(loc(), null));
|
target.add(Instruction.loadValue(loc(), null));
|
||||||
target.add(Instruction.operation(loc(), Operation.EQUALS));
|
target.add(Instruction.operation(loc(), Operation.EQUALS));
|
||||||
int mid = target.size();
|
int mid = target.size();
|
||||||
target.add(Instruction.nop(loc()));
|
target.add(Instruction.nop(loc()));
|
||||||
|
|
||||||
target.add(Instruction.loadMember(varLocation, "value"));
|
target.add(Instruction.loadMember(varLocation, "value"));
|
||||||
target.add(Instruction.storeVar(object.loc(), key));
|
target.add(Instruction.storeVar(object.loc(), key));
|
||||||
target.setDebug(BreakpointType.STEP_OVER);
|
target.setDebug(BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
body.compile(target, scope, false, BreakpointType.STEP_OVER);
|
body.compile(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
int end = target.size();
|
int end = target.size();
|
||||||
|
|
||||||
WhileStatement.replaceBreaks(target, label, mid + 1, end, start, end + 1);
|
WhileStatement.replaceBreaks(target, label, mid + 1, end, start, end + 1);
|
||||||
|
|
||||||
target.add(Instruction.jmp(loc(), start - end));
|
target.add(Instruction.jmp(loc(), start - end));
|
||||||
target.add(Instruction.discard(loc()));
|
target.add(Instruction.discard(loc()));
|
||||||
target.set(mid, Instruction.jmpIf(loc(), end - mid + 1));
|
target.set(mid, Instruction.jmpIf(loc(), end - mid + 1));
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
target.get(first).locate(loc());
|
target.get(first).locate(loc());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ForInStatement(Location loc, Location varLocation, String label, boolean isDecl, String varName, Statement varValue, Statement object, Statement body) {
|
public ForInStatement(Location loc, Location varLocation, String label, boolean isDecl, String varName, Statement varValue, Statement object, Statement body) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.varLocation = varLocation;
|
this.varLocation = varLocation;
|
||||||
this.label = label;
|
this.label = label;
|
||||||
this.isDeclaration = isDecl;
|
this.isDeclaration = isDecl;
|
||||||
this.varName = varName;
|
this.varName = varName;
|
||||||
this.varValue = varValue;
|
this.varValue = varValue;
|
||||||
this.object = object;
|
this.object = object;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,47 +1,47 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class ForStatement extends Statement {
|
public class ForStatement extends Statement {
|
||||||
public final Statement declaration, assignment, condition, body;
|
public final Statement declaration, assignment, condition, body;
|
||||||
public final String label;
|
public final String label;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void declare(ScopeRecord globScope) {
|
public void declare(ScopeRecord globScope) {
|
||||||
declaration.declare(globScope);
|
declaration.declare(globScope);
|
||||||
body.declare(globScope);
|
body.declare(globScope);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
declaration.compile(target, scope, false, BreakpointType.STEP_OVER);
|
declaration.compile(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
condition.compile(target, scope, true, BreakpointType.STEP_OVER);
|
condition.compile(target, scope, true, BreakpointType.STEP_OVER);
|
||||||
int mid = target.size();
|
int mid = target.size();
|
||||||
target.add(Instruction.nop(null));
|
target.add(Instruction.nop(null));
|
||||||
body.compile(target, scope, false, BreakpointType.STEP_OVER);
|
body.compile(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
int beforeAssign = target.size();
|
int beforeAssign = target.size();
|
||||||
assignment.compile(target, scope, false, BreakpointType.STEP_OVER);
|
assignment.compile(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
int end = target.size();
|
int end = target.size();
|
||||||
|
|
||||||
WhileStatement.replaceBreaks(target, label, mid + 1, end, beforeAssign, end + 1);
|
WhileStatement.replaceBreaks(target, label, mid + 1, end, beforeAssign, end + 1);
|
||||||
|
|
||||||
target.add(Instruction.jmp(loc(), start - end));
|
target.add(Instruction.jmp(loc(), start - end));
|
||||||
target.set(mid, Instruction.jmpIfNot(loc(), end - mid + 1));
|
target.set(mid, Instruction.jmpIfNot(loc(), end - mid + 1));
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ForStatement(Location loc, String label, Statement declaration, Statement condition, Statement assignment, Statement body) {
|
public ForStatement(Location loc, String label, Statement declaration, Statement condition, Statement assignment, Statement body) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.label = label;
|
this.label = label;
|
||||||
this.declaration = declaration;
|
this.declaration = declaration;
|
||||||
this.condition = condition;
|
this.condition = condition;
|
||||||
this.assignment = assignment;
|
this.assignment = assignment;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,52 +1,52 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class IfStatement extends Statement {
|
public class IfStatement extends Statement {
|
||||||
public final Statement condition, body, elseBody;
|
public final Statement condition, body, elseBody;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void declare(ScopeRecord globScope) {
|
public void declare(ScopeRecord globScope) {
|
||||||
body.declare(globScope);
|
body.declare(globScope);
|
||||||
if (elseBody != null) elseBody.declare(globScope);
|
if (elseBody != null) elseBody.declare(globScope);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType breakpoint) {
|
@Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType breakpoint) {
|
||||||
condition.compile(target, scope, true, breakpoint);
|
condition.compile(target, scope, true, breakpoint);
|
||||||
|
|
||||||
if (elseBody == null) {
|
if (elseBody == null) {
|
||||||
int i = target.size();
|
int i = target.size();
|
||||||
target.add(Instruction.nop(null));
|
target.add(Instruction.nop(null));
|
||||||
body.compile(target, scope, pollute, breakpoint);
|
body.compile(target, scope, pollute, breakpoint);
|
||||||
int endI = target.size();
|
int endI = target.size();
|
||||||
target.set(i, Instruction.jmpIfNot(loc(), endI - i));
|
target.set(i, Instruction.jmpIfNot(loc(), endI - i));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
target.add(Instruction.nop(null));
|
target.add(Instruction.nop(null));
|
||||||
body.compile(target, scope, pollute, breakpoint);
|
body.compile(target, scope, pollute, breakpoint);
|
||||||
target.add(Instruction.nop(null));
|
target.add(Instruction.nop(null));
|
||||||
int mid = target.size();
|
int mid = target.size();
|
||||||
elseBody.compile(target, scope, pollute, breakpoint);
|
elseBody.compile(target, scope, pollute, breakpoint);
|
||||||
int end = target.size();
|
int end = target.size();
|
||||||
|
|
||||||
target.set(start, Instruction.jmpIfNot(loc(), mid - start));
|
target.set(start, Instruction.jmpIfNot(loc(), mid - start));
|
||||||
target.set(mid - 1, Instruction.jmp(loc(), end - mid + 1));
|
target.set(mid - 1, Instruction.jmp(loc(), end - mid + 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
@Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
compile(target, scope, pollute, BreakpointType.STEP_IN);
|
compile(target, scope, pollute, BreakpointType.STEP_IN);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IfStatement(Location loc, Statement condition, Statement body, Statement elseBody) {
|
public IfStatement(Location loc, Statement condition, Statement body, Statement elseBody) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.condition = condition;
|
this.condition = condition;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
this.elseBody = elseBody;
|
this.elseBody = elseBody;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class ReturnStatement extends Statement {
|
public class ReturnStatement extends Statement {
|
||||||
public final Statement value;
|
public final Statement value;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
if (value == null) target.add(Instruction.loadValue(loc(), null));
|
if (value == null) target.add(Instruction.loadValue(loc(), null));
|
||||||
else value.compile(target, scope, true);
|
else value.compile(target, scope, true);
|
||||||
target.add(Instruction.ret(loc()));
|
target.add(Instruction.ret(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReturnStatement(Location loc, Statement value) {
|
public ReturnStatement(Location loc, Statement value) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,87 +1,87 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.Type;
|
import me.topchetoeu.jscript.compilation.Instruction.Type;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.engine.Operation;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class SwitchStatement extends Statement {
|
public class SwitchStatement extends Statement {
|
||||||
public static class SwitchCase {
|
public static class SwitchCase {
|
||||||
public final Statement value;
|
public final Statement value;
|
||||||
public final int statementI;
|
public final int statementI;
|
||||||
|
|
||||||
public SwitchCase(Statement value, int statementI) {
|
public SwitchCase(Statement value, int statementI) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.statementI = statementI;
|
this.statementI = statementI;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final Statement value;
|
public final Statement value;
|
||||||
public final SwitchCase[] cases;
|
public final SwitchCase[] cases;
|
||||||
public final Statement[] body;
|
public final Statement[] body;
|
||||||
public final int defaultI;
|
public final int defaultI;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void declare(ScopeRecord varsScope) {
|
public void declare(ScopeRecord varsScope) {
|
||||||
for (var stm : body) stm.declare(varsScope);
|
for (var stm : body) stm.declare(varsScope);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
var caseToStatement = new HashMap<Integer, Integer>();
|
var caseToStatement = new HashMap<Integer, Integer>();
|
||||||
var statementToIndex = new HashMap<Integer, Integer>();
|
var statementToIndex = new HashMap<Integer, Integer>();
|
||||||
|
|
||||||
value.compile(target, scope, true, BreakpointType.STEP_OVER);
|
value.compile(target, scope, true, BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
for (var ccase : cases) {
|
for (var ccase : cases) {
|
||||||
target.add(Instruction.dup(loc()));
|
target.add(Instruction.dup(loc()));
|
||||||
ccase.value.compile(target, scope, true);
|
ccase.value.compile(target, scope, true);
|
||||||
target.add(Instruction.operation(loc(), Operation.EQUALS));
|
target.add(Instruction.operation(loc(), Operation.EQUALS));
|
||||||
caseToStatement.put(target.size(), ccase.statementI);
|
caseToStatement.put(target.size(), ccase.statementI);
|
||||||
target.add(Instruction.nop(null));
|
target.add(Instruction.nop(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
|
|
||||||
target.add(Instruction.nop(null));
|
target.add(Instruction.nop(null));
|
||||||
|
|
||||||
for (var stm : body) {
|
for (var stm : body) {
|
||||||
statementToIndex.put(statementToIndex.size(), target.size());
|
statementToIndex.put(statementToIndex.size(), target.size());
|
||||||
stm.compile(target, scope, false, BreakpointType.STEP_OVER);
|
stm.compile(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
}
|
}
|
||||||
|
|
||||||
int end = target.size();
|
int end = target.size();
|
||||||
target.add(Instruction.discard(loc()));
|
target.add(Instruction.discard(loc()));
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
|
|
||||||
if (defaultI < 0 || defaultI >= body.length) target.set(start, Instruction.jmp(loc(), end - start));
|
if (defaultI < 0 || defaultI >= body.length) target.set(start, Instruction.jmp(loc(), end - start));
|
||||||
else target.set(start, Instruction.jmp(loc(), statementToIndex.get(defaultI) - start));
|
else target.set(start, Instruction.jmp(loc(), statementToIndex.get(defaultI) - start));
|
||||||
|
|
||||||
for (int i = start; i < end; i++) {
|
for (int i = start; i < end; i++) {
|
||||||
var instr = target.get(i);
|
var instr = target.get(i);
|
||||||
if (instr.type == Type.NOP && instr.is(0, "break") && instr.get(1) == null) {
|
if (instr.type == Type.NOP && instr.is(0, "break") && instr.get(1) == null) {
|
||||||
target.set(i, Instruction.jmp(loc(), end - i).locate(instr.location));
|
target.set(i, Instruction.jmp(loc(), end - i).locate(instr.location));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (var el : caseToStatement.entrySet()) {
|
for (var el : caseToStatement.entrySet()) {
|
||||||
var i = statementToIndex.get(el.getValue());
|
var i = statementToIndex.get(el.getValue());
|
||||||
if (i == null) i = end;
|
if (i == null) i = end;
|
||||||
target.set(el.getKey(), Instruction.jmpIf(loc(), i - el.getKey()));
|
target.set(el.getKey(), Instruction.jmpIf(loc(), i - el.getKey()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SwitchStatement(Location loc, Statement value, int defaultI, SwitchCase[] cases, Statement[] body) {
|
public SwitchStatement(Location loc, Statement value, int defaultI, SwitchCase[] cases, Statement[] body) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.defaultI = defaultI;
|
this.defaultI = defaultI;
|
||||||
this.cases = cases;
|
this.cases = cases;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class ThrowStatement extends Statement {
|
public class ThrowStatement extends Statement {
|
||||||
public final Statement value;
|
public final Statement value;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
value.compile(target, scope, true);
|
value.compile(target, scope, true);
|
||||||
target.add(Instruction.throwInstr(loc()));
|
target.add(Instruction.throwInstr(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ThrowStatement(Location loc, Statement value) {
|
public ThrowStatement(Location loc, Statement value) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,61 +1,61 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.engine.scope.GlobalScope;
|
import me.topchetoeu.jscript.engine.scope.GlobalScope;
|
||||||
import me.topchetoeu.jscript.engine.scope.LocalScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.LocalScopeRecord;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class TryStatement extends Statement {
|
public class TryStatement extends Statement {
|
||||||
public final Statement tryBody;
|
public final Statement tryBody;
|
||||||
public final Statement catchBody;
|
public final Statement catchBody;
|
||||||
public final Statement finallyBody;
|
public final Statement finallyBody;
|
||||||
public final String name;
|
public final String name;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void declare(ScopeRecord globScope) {
|
public void declare(ScopeRecord globScope) {
|
||||||
tryBody.declare(globScope);
|
tryBody.declare(globScope);
|
||||||
if (catchBody != null) catchBody.declare(globScope);
|
if (catchBody != null) catchBody.declare(globScope);
|
||||||
if (finallyBody != null) finallyBody.declare(globScope);
|
if (finallyBody != null) finallyBody.declare(globScope);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType bpt) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType bpt) {
|
||||||
target.add(Instruction.nop(null));
|
target.add(Instruction.nop(null));
|
||||||
|
|
||||||
int start = target.size(), catchStart = -1, finallyStart = -1;
|
int start = target.size(), catchStart = -1, finallyStart = -1;
|
||||||
|
|
||||||
tryBody.compile(target, scope, false);
|
tryBody.compile(target, scope, false);
|
||||||
target.add(Instruction.tryEnd(loc()));
|
target.add(Instruction.tryEnd(loc()));
|
||||||
|
|
||||||
if (catchBody != null) {
|
if (catchBody != null) {
|
||||||
catchStart = target.size() - start;
|
catchStart = target.size() - start;
|
||||||
var local = scope instanceof GlobalScope ? scope.child() : (LocalScopeRecord)scope;
|
var local = scope instanceof GlobalScope ? scope.child() : (LocalScopeRecord)scope;
|
||||||
local.define(name, true);
|
local.define(name, true);
|
||||||
catchBody.compile(target, scope, false);
|
catchBody.compile(target, scope, false);
|
||||||
local.undefine();
|
local.undefine();
|
||||||
target.add(Instruction.tryEnd(loc()));
|
target.add(Instruction.tryEnd(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (finallyBody != null) {
|
if (finallyBody != null) {
|
||||||
finallyStart = target.size() - start;
|
finallyStart = target.size() - start;
|
||||||
finallyBody.compile(target, scope, false);
|
finallyBody.compile(target, scope, false);
|
||||||
target.add(Instruction.tryEnd(loc()));
|
target.add(Instruction.tryEnd(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
target.set(start - 1, Instruction.tryStart(loc(), catchStart, finallyStart, target.size() - start));
|
target.set(start - 1, Instruction.tryStart(loc(), catchStart, finallyStart, target.size() - start));
|
||||||
target.setDebug(start - 1, BreakpointType.STEP_OVER);
|
target.setDebug(start - 1, BreakpointType.STEP_OVER);
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public TryStatement(Location loc, Statement tryBody, Statement catchBody, Statement finallyBody, String name) {
|
public TryStatement(Location loc, Statement tryBody, Statement catchBody, Statement finallyBody, String name) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.tryBody = tryBody;
|
this.tryBody = tryBody;
|
||||||
this.catchBody = catchBody;
|
this.catchBody = catchBody;
|
||||||
this.finallyBody = finallyBody;
|
this.finallyBody = finallyBody;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,54 +1,54 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.Type;
|
import me.topchetoeu.jscript.compilation.Instruction.Type;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class WhileStatement extends Statement {
|
public class WhileStatement extends Statement {
|
||||||
public final Statement condition, body;
|
public final Statement condition, body;
|
||||||
public final String label;
|
public final String label;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void declare(ScopeRecord globScope) {
|
public void declare(ScopeRecord globScope) {
|
||||||
body.declare(globScope);
|
body.declare(globScope);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
condition.compile(target, scope, true);
|
condition.compile(target, scope, true);
|
||||||
int mid = target.size();
|
int mid = target.size();
|
||||||
target.add(Instruction.nop(null));
|
target.add(Instruction.nop(null));
|
||||||
body.compile(target, scope, false, BreakpointType.STEP_OVER);
|
body.compile(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
int end = target.size();
|
int end = target.size();
|
||||||
|
|
||||||
replaceBreaks(target, label, mid + 1, end, start, end + 1);
|
replaceBreaks(target, label, mid + 1, end, start, end + 1);
|
||||||
|
|
||||||
target.add(Instruction.jmp(loc(), start - end));
|
target.add(Instruction.jmp(loc(), start - end));
|
||||||
target.set(mid, Instruction.jmpIfNot(loc(), end - mid + 1));
|
target.set(mid, Instruction.jmpIfNot(loc(), end - mid + 1));
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public WhileStatement(Location loc, String label, Statement condition, Statement body) {
|
public WhileStatement(Location loc, String label, Statement condition, Statement body) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.label = label;
|
this.label = label;
|
||||||
this.condition = condition;
|
this.condition = condition;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void replaceBreaks(CompileTarget target, String label, int start, int end, int continuePoint, int breakPoint) {
|
public static void replaceBreaks(CompileTarget target, String label, int start, int end, int continuePoint, int breakPoint) {
|
||||||
for (int i = start; i < end; i++) {
|
for (int i = start; i < end; i++) {
|
||||||
var instr = target.get(i);
|
var instr = target.get(i);
|
||||||
if (instr.type == Type.NOP && instr.is(0, "cont") && (instr.get(1) == null || instr.is(1, label))) {
|
if (instr.type == Type.NOP && instr.is(0, "cont") && (instr.get(1) == null || instr.is(1, label))) {
|
||||||
target.set(i, Instruction.jmp(instr.location, continuePoint - i).setDbgData(target.get(i)));
|
target.set(i, Instruction.jmp(instr.location, continuePoint - i).setDbgData(target.get(i)));
|
||||||
}
|
}
|
||||||
if (instr.type == Type.NOP && instr.is(0, "break") && (instr.get(1) == null || instr.is(1, label))) {
|
if (instr.type == Type.NOP && instr.is(0, "break") && (instr.get(1) == null || instr.is(1, label))) {
|
||||||
target.set(i, Instruction.jmp(instr.location, breakPoint - i).setDbgData(target.get(i)));
|
target.set(i, Instruction.jmp(instr.location, breakPoint - i).setDbgData(target.get(i)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,41 +1,41 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class ArrayStatement extends Statement {
|
public class ArrayStatement extends Statement {
|
||||||
public final Statement[] statements;
|
public final Statement[] statements;
|
||||||
|
|
||||||
@Override public boolean pure() {
|
@Override public boolean pure() {
|
||||||
for (var stm : statements) {
|
for (var stm : statements) {
|
||||||
if (!stm.pure()) return false;
|
if (!stm.pure()) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
target.add(Instruction.loadArr(loc(), statements.length));
|
target.add(Instruction.loadArr(loc(), statements.length));
|
||||||
|
|
||||||
for (var i = 0; i < statements.length; i++) {
|
for (var i = 0; i < statements.length; i++) {
|
||||||
var el = statements[i];
|
var el = statements[i];
|
||||||
if (el != null) {
|
if (el != null) {
|
||||||
target.add(Instruction.dup(loc()));
|
target.add(Instruction.dup(loc()));
|
||||||
target.add(Instruction.loadValue(loc(), i));
|
target.add(Instruction.loadValue(loc(), i));
|
||||||
el.compile(target, scope, true);
|
el.compile(target, scope, true);
|
||||||
target.add(Instruction.storeMember(loc()));
|
target.add(Instruction.storeMember(loc()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pollute) target.add(Instruction.discard(loc()));
|
if (!pollute) target.add(Instruction.discard(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayStatement(Location loc, Statement[] statements) {
|
public ArrayStatement(Location loc, Statement[] statements) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.statements = statements;
|
this.statements = statements;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,51 +1,51 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class CallStatement extends Statement {
|
public class CallStatement extends Statement {
|
||||||
public final Statement func;
|
public final Statement func;
|
||||||
public final Statement[] args;
|
public final Statement[] args;
|
||||||
public final boolean isNew;
|
public final boolean isNew;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType type) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType type) {
|
||||||
if (isNew) func.compile(target, scope, true);
|
if (isNew) func.compile(target, scope, true);
|
||||||
else if (func instanceof IndexStatement) {
|
else if (func instanceof IndexStatement) {
|
||||||
((IndexStatement)func).compile(target, scope, true, true);
|
((IndexStatement)func).compile(target, scope, true, true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
target.add(Instruction.loadValue(loc(), null));
|
target.add(Instruction.loadValue(loc(), null));
|
||||||
func.compile(target, scope, true);
|
func.compile(target, scope, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var arg : args) arg.compile(target, scope, true);
|
for (var arg : args) arg.compile(target, scope, true);
|
||||||
|
|
||||||
if (isNew) target.add(Instruction.callNew(loc(), args.length));
|
if (isNew) target.add(Instruction.callNew(loc(), args.length));
|
||||||
else target.add(Instruction.call(loc(), args.length));
|
else target.add(Instruction.call(loc(), args.length));
|
||||||
target.setDebug(type);
|
target.setDebug(type);
|
||||||
|
|
||||||
if (!pollute) target.add(Instruction.discard(loc()));
|
if (!pollute) target.add(Instruction.discard(loc()));
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
compile(target, scope, pollute, BreakpointType.STEP_IN);
|
compile(target, scope, pollute, BreakpointType.STEP_IN);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CallStatement(Location loc, boolean isNew, Statement func, Statement ...args) {
|
public CallStatement(Location loc, boolean isNew, Statement func, Statement ...args) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.isNew = isNew;
|
this.isNew = isNew;
|
||||||
this.func = func;
|
this.func = func;
|
||||||
this.args = args;
|
this.args = args;
|
||||||
}
|
}
|
||||||
public CallStatement(Location loc, boolean isNew, Statement obj, Object key, Statement ...args) {
|
public CallStatement(Location loc, boolean isNew, Statement obj, Object key, Statement ...args) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.isNew = isNew;
|
this.isNew = isNew;
|
||||||
this.func = new IndexStatement(loc, obj, new ConstantStatement(loc, key));
|
this.func = new IndexStatement(loc, obj, new ConstantStatement(loc, key));
|
||||||
this.args = args;
|
this.args = args;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,32 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.AssignableStatement;
|
import me.topchetoeu.jscript.compilation.AssignableStatement;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.engine.Operation;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class ChangeStatement extends Statement {
|
public class ChangeStatement extends Statement {
|
||||||
public final AssignableStatement value;
|
public final AssignableStatement value;
|
||||||
public final double addAmount;
|
public final double addAmount;
|
||||||
public final boolean postfix;
|
public final boolean postfix;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
value.toAssign(new ConstantStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, scope, true);
|
value.toAssign(new ConstantStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, scope, true);
|
||||||
if (!pollute) target.add(Instruction.discard(loc()));
|
if (!pollute) target.add(Instruction.discard(loc()));
|
||||||
else if (postfix) {
|
else if (postfix) {
|
||||||
target.add(Instruction.loadValue(loc(), addAmount));
|
target.add(Instruction.loadValue(loc(), addAmount));
|
||||||
target.add(Instruction.operation(loc(), Operation.SUBTRACT));
|
target.add(Instruction.operation(loc(), Operation.SUBTRACT));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChangeStatement(Location loc, AssignableStatement value, double addAmount, boolean postfix) {
|
public ChangeStatement(Location loc, AssignableStatement value, double addAmount, boolean postfix) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.addAmount = addAmount;
|
this.addAmount = addAmount;
|
||||||
this.postfix = postfix;
|
this.postfix = postfix;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class ConstantStatement extends Statement {
|
public class ConstantStatement extends Statement {
|
||||||
public final Object value;
|
public final Object value;
|
||||||
|
|
||||||
@Override public boolean pure() { return true; }
|
@Override public boolean pure() { return true; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), value));
|
if (pollute) target.add(Instruction.loadValue(loc(), value));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConstantStatement(Location loc, Object val) {
|
public ConstantStatement(Location loc, Object val) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.value = val;
|
this.value = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,24 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class DiscardStatement extends Statement {
|
public class DiscardStatement extends Statement {
|
||||||
public final Statement value;
|
public final Statement value;
|
||||||
|
|
||||||
@Override public boolean pure() { return value.pure(); }
|
@Override public boolean pure() { return value.pure(); }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
value.compile(target, scope, false);
|
value.compile(target, scope, false);
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public DiscardStatement(Location loc, Statement val) {
|
public DiscardStatement(Location loc, Statement val) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.value = val;
|
this.value = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,139 +1,139 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.CompoundStatement;
|
import me.topchetoeu.jscript.compilation.CompoundStatement;
|
||||||
import me.topchetoeu.jscript.compilation.FunctionBody;
|
import me.topchetoeu.jscript.compilation.FunctionBody;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.Type;
|
import me.topchetoeu.jscript.compilation.Instruction.Type;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
||||||
|
|
||||||
public class FunctionStatement extends Statement {
|
public class FunctionStatement extends Statement {
|
||||||
public final CompoundStatement body;
|
public final CompoundStatement body;
|
||||||
public final String varName;
|
public final String varName;
|
||||||
public final String[] args;
|
public final String[] args;
|
||||||
public final boolean statement;
|
public final boolean statement;
|
||||||
public final Location end;
|
public final Location end;
|
||||||
|
|
||||||
private static Random rand = new Random();
|
private static Random rand = new Random();
|
||||||
|
|
||||||
@Override public boolean pure() { return varName == null && statement; }
|
@Override public boolean pure() { return varName == null && statement; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void declare(ScopeRecord scope) {
|
public void declare(ScopeRecord scope) {
|
||||||
if (varName != null && statement) scope.define(varName);
|
if (varName != null && statement) scope.define(varName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void checkBreakAndCont(CompileTarget target, int start) {
|
public static void checkBreakAndCont(CompileTarget target, int start) {
|
||||||
for (int i = start; i < target.size(); i++) {
|
for (int i = start; i < target.size(); i++) {
|
||||||
if (target.get(i).type == Type.NOP) {
|
if (target.get(i).type == Type.NOP) {
|
||||||
if (target.get(i).is(0, "break") ) {
|
if (target.get(i).is(0, "break") ) {
|
||||||
throw new SyntaxException(target.get(i).location, "Break was placed outside a loop.");
|
throw new SyntaxException(target.get(i).location, "Break was placed outside a loop.");
|
||||||
}
|
}
|
||||||
if (target.get(i).is(0, "cont")) {
|
if (target.get(i).is(0, "cont")) {
|
||||||
throw new SyntaxException(target.get(i).location, "Continue was placed outside a loop.");
|
throw new SyntaxException(target.get(i).location, "Continue was placed outside a loop.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private long compileBody(CompileTarget target, ScopeRecord scope, boolean polute, BreakpointType bp) {
|
private long compileBody(CompileTarget target, ScopeRecord scope, boolean polute, BreakpointType bp) {
|
||||||
for (var i = 0; i < args.length; i++) {
|
for (var i = 0; i < args.length; i++) {
|
||||||
for (var j = 0; j < i; j++) {
|
for (var j = 0; j < i; j++) {
|
||||||
if (args[i].equals(args[j])) {
|
if (args[i].equals(args[j])) {
|
||||||
throw new SyntaxException(loc(), "Duplicate parameter '" + args[i] + "'.");
|
throw new SyntaxException(loc(), "Duplicate parameter '" + args[i] + "'.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var id = rand.nextLong();
|
var id = rand.nextLong();
|
||||||
var subscope = scope.child();
|
var subscope = scope.child();
|
||||||
var subtarget = new CompileTarget(target.functions, target.breakpoints);
|
var subtarget = new CompileTarget(target.functions, target.breakpoints);
|
||||||
|
|
||||||
subscope.define("this");
|
subscope.define("this");
|
||||||
var argsVar = subscope.define("arguments");
|
var argsVar = subscope.define("arguments");
|
||||||
|
|
||||||
if (args.length > 0) {
|
if (args.length > 0) {
|
||||||
for (var i = 0; i < args.length; i++) {
|
for (var i = 0; i < args.length; i++) {
|
||||||
subtarget.add(Instruction.loadVar(loc(), argsVar));
|
subtarget.add(Instruction.loadVar(loc(), argsVar));
|
||||||
subtarget.add(Instruction.loadMember(loc(), i));
|
subtarget.add(Instruction.loadMember(loc(), i));
|
||||||
subtarget.add(Instruction.storeVar(loc(), subscope.define(args[i])));
|
subtarget.add(Instruction.storeVar(loc(), subscope.define(args[i])));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!statement && this.varName != null) {
|
if (!statement && this.varName != null) {
|
||||||
subtarget.add(Instruction.storeSelfFunc(loc(), (int)subscope.define(this.varName)));
|
subtarget.add(Instruction.storeSelfFunc(loc(), (int)subscope.define(this.varName)));
|
||||||
subtarget.setDebug(bp);
|
subtarget.setDebug(bp);
|
||||||
}
|
}
|
||||||
|
|
||||||
body.declare(subscope);
|
body.declare(subscope);
|
||||||
body.compile(subtarget, subscope, false);
|
body.compile(subtarget, subscope, false);
|
||||||
subtarget.add(Instruction.ret(end));
|
subtarget.add(Instruction.ret(end));
|
||||||
checkBreakAndCont(subtarget, 0);
|
checkBreakAndCont(subtarget, 0);
|
||||||
|
|
||||||
if (polute) target.add(Instruction.loadFunc(loc(), id, subscope.getCaptures()));
|
if (polute) target.add(Instruction.loadFunc(loc(), id, subscope.getCaptures()));
|
||||||
target.functions.put(id, new FunctionBody(
|
target.functions.put(id, new FunctionBody(
|
||||||
subscope.localsCount(), args.length,
|
subscope.localsCount(), args.length,
|
||||||
subtarget.array(), subscope.captures(), subscope.locals()
|
subtarget.array(), subscope.captures(), subscope.locals()
|
||||||
));
|
));
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, String name, BreakpointType bp) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, String name, BreakpointType bp) {
|
||||||
if (this.varName != null) name = this.varName;
|
if (this.varName != null) name = this.varName;
|
||||||
|
|
||||||
var hasVar = this.varName != null && statement;
|
var hasVar = this.varName != null && statement;
|
||||||
var hasName = name != null;
|
var hasName = name != null;
|
||||||
|
|
||||||
compileBody(target, scope, pollute || hasVar || hasName, bp);
|
compileBody(target, scope, pollute || hasVar || hasName, bp);
|
||||||
|
|
||||||
if (hasName) {
|
if (hasName) {
|
||||||
if (pollute || hasVar) target.add(Instruction.dup(loc()));
|
if (pollute || hasVar) target.add(Instruction.dup(loc()));
|
||||||
target.add(Instruction.loadValue(loc(), "name"));
|
target.add(Instruction.loadValue(loc(), "name"));
|
||||||
target.add(Instruction.loadValue(loc(), name));
|
target.add(Instruction.loadValue(loc(), name));
|
||||||
target.add(Instruction.storeMember(loc()));
|
target.add(Instruction.storeMember(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasVar) {
|
if (hasVar) {
|
||||||
var key = scope.getKey(this.varName);
|
var key = scope.getKey(this.varName);
|
||||||
|
|
||||||
if (key instanceof String) target.add(Instruction.makeVar(loc(), (String)key));
|
if (key instanceof String) target.add(Instruction.makeVar(loc(), (String)key));
|
||||||
target.add(Instruction.storeVar(loc(), scope.getKey(this.varName), false));
|
target.add(Instruction.storeVar(loc(), scope.getKey(this.varName), false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, String name) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, String name) {
|
||||||
compile(target, scope, pollute, name, BreakpointType.NONE);
|
compile(target, scope, pollute, name, BreakpointType.NONE);
|
||||||
}
|
}
|
||||||
@Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType bp) {
|
@Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType bp) {
|
||||||
compile(target, scope, pollute, (String)null, bp);
|
compile(target, scope, pollute, (String)null, bp);
|
||||||
}
|
}
|
||||||
@Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
@Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
compile(target, scope, pollute, (String)null, BreakpointType.NONE);
|
compile(target, scope, pollute, (String)null, BreakpointType.NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FunctionStatement(Location loc, Location end, String varName, String[] args, boolean statement, CompoundStatement body) {
|
public FunctionStatement(Location loc, Location end, String varName, String[] args, boolean statement, CompoundStatement body) {
|
||||||
super(loc);
|
super(loc);
|
||||||
|
|
||||||
this.end = end;
|
this.end = end;
|
||||||
this.varName = varName;
|
this.varName = varName;
|
||||||
this.statement = statement;
|
this.statement = statement;
|
||||||
|
|
||||||
this.args = args;
|
this.args = args;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void compileWithName(Statement stm, CompileTarget target, ScopeRecord scope, boolean pollute, String name) {
|
public static void compileWithName(Statement stm, CompileTarget target, ScopeRecord scope, boolean pollute, String name) {
|
||||||
if (stm instanceof FunctionStatement) ((FunctionStatement)stm).compile(target, scope, pollute, name);
|
if (stm instanceof FunctionStatement) ((FunctionStatement)stm).compile(target, scope, pollute, name);
|
||||||
else stm.compile(target, scope, pollute);
|
else stm.compile(target, scope, pollute);
|
||||||
}
|
}
|
||||||
public static void compileWithName(Statement stm, CompileTarget target, ScopeRecord scope, boolean pollute, String name, BreakpointType bp) {
|
public static void compileWithName(Statement stm, CompileTarget target, ScopeRecord scope, boolean pollute, String name, BreakpointType bp) {
|
||||||
if (stm instanceof FunctionStatement) ((FunctionStatement)stm).compile(target, scope, pollute, name, bp);
|
if (stm instanceof FunctionStatement) ((FunctionStatement)stm).compile(target, scope, pollute, name, bp);
|
||||||
else stm.compile(target, scope, pollute, bp);
|
else stm.compile(target, scope, pollute, bp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class GlobalThisStatement extends Statement {
|
public class GlobalThisStatement extends Statement {
|
||||||
@Override public boolean pure() { return true; }
|
@Override public boolean pure() { return true; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
if (pollute) target.add(Instruction.loadGlob(loc()));
|
if (pollute) target.add(Instruction.loadGlob(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public GlobalThisStatement(Location loc) {
|
public GlobalThisStatement(Location loc) {
|
||||||
super(loc);
|
super(loc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,48 +1,48 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.engine.Operation;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class IndexAssignStatement extends Statement {
|
public class IndexAssignStatement extends Statement {
|
||||||
public final Statement object;
|
public final Statement object;
|
||||||
public final Statement index;
|
public final Statement index;
|
||||||
public final Statement value;
|
public final Statement value;
|
||||||
public final Operation operation;
|
public final Operation operation;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
if (operation != null) {
|
if (operation != null) {
|
||||||
object.compile(target, scope, true);
|
object.compile(target, scope, true);
|
||||||
index.compile(target, scope, true);
|
index.compile(target, scope, true);
|
||||||
target.add(Instruction.dup(loc(), 2));
|
target.add(Instruction.dup(loc(), 2));
|
||||||
|
|
||||||
target.add(Instruction.loadMember(loc()));
|
target.add(Instruction.loadMember(loc()));
|
||||||
value.compile(target, scope, true);
|
value.compile(target, scope, true);
|
||||||
target.add(Instruction.operation(loc(), operation));
|
target.add(Instruction.operation(loc(), operation));
|
||||||
|
|
||||||
target.add(Instruction.storeMember(loc(), pollute));
|
target.add(Instruction.storeMember(loc(), pollute));
|
||||||
target.setDebug(BreakpointType.STEP_IN);
|
target.setDebug(BreakpointType.STEP_IN);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
object.compile(target, scope, true);
|
object.compile(target, scope, true);
|
||||||
index.compile(target, scope, true);
|
index.compile(target, scope, true);
|
||||||
value.compile(target, scope, true);
|
value.compile(target, scope, true);
|
||||||
|
|
||||||
target.add(Instruction.storeMember(loc(), pollute));
|
target.add(Instruction.storeMember(loc(), pollute));
|
||||||
target.setDebug(BreakpointType.STEP_IN);
|
target.setDebug(BreakpointType.STEP_IN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IndexAssignStatement(Location loc, Statement object, Statement index, Statement value, Operation operation) {
|
public IndexAssignStatement(Location loc, Statement object, Statement index, Statement value, Operation operation) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.object = object;
|
this.object = object;
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.operation = operation;
|
this.operation = operation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,49 +1,49 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.AssignableStatement;
|
import me.topchetoeu.jscript.compilation.AssignableStatement;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.engine.Operation;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class IndexStatement extends AssignableStatement {
|
public class IndexStatement extends AssignableStatement {
|
||||||
public final Statement object;
|
public final Statement object;
|
||||||
public final Statement index;
|
public final Statement index;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Statement toAssign(Statement val, Operation operation) {
|
public Statement toAssign(Statement val, Operation operation) {
|
||||||
return new IndexAssignStatement(loc(), object, index, val, operation);
|
return new IndexAssignStatement(loc(), object, index, val, operation);
|
||||||
}
|
}
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean dupObj, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean dupObj, boolean pollute) {
|
||||||
object.compile(target, scope, true);
|
object.compile(target, scope, true);
|
||||||
if (dupObj) target.add(Instruction.dup(loc()));
|
if (dupObj) target.add(Instruction.dup(loc()));
|
||||||
if (index instanceof ConstantStatement) {
|
if (index instanceof ConstantStatement) {
|
||||||
target.add(Instruction.loadMember(loc(), ((ConstantStatement)index).value));
|
target.add(Instruction.loadMember(loc(), ((ConstantStatement)index).value));
|
||||||
target.setDebug(BreakpointType.STEP_IN);
|
target.setDebug(BreakpointType.STEP_IN);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
index.compile(target, scope, true);
|
index.compile(target, scope, true);
|
||||||
target.add(Instruction.loadMember(loc()));
|
target.add(Instruction.loadMember(loc()));
|
||||||
target.setDebug(BreakpointType.STEP_IN);
|
target.setDebug(BreakpointType.STEP_IN);
|
||||||
if (!pollute) target.add(Instruction.discard(loc()));
|
if (!pollute) target.add(Instruction.discard(loc()));
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
compile(target, scope, false, pollute);
|
compile(target, scope, false, pollute);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IndexStatement(Location loc, Statement object, Statement index) {
|
public IndexStatement(Location loc, Statement object, Statement index) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.object = object;
|
this.object = object;
|
||||||
this.index = index;
|
this.index = index;
|
||||||
}
|
}
|
||||||
public IndexStatement(Location loc, Statement object, Object index) {
|
public IndexStatement(Location loc, Statement object, Object index) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.object = object;
|
this.object = object;
|
||||||
this.index = new ConstantStatement(loc, index);
|
this.index = new ConstantStatement(loc, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,39 +1,39 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
|
|
||||||
public class LazyAndStatement extends Statement {
|
public class LazyAndStatement extends Statement {
|
||||||
public final Statement first, second;
|
public final Statement first, second;
|
||||||
|
|
||||||
@Override public boolean pure() { return first.pure() && second.pure(); }
|
@Override public boolean pure() { return first.pure() && second.pure(); }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
if (first instanceof ConstantStatement) {
|
if (first instanceof ConstantStatement) {
|
||||||
if (Values.not(((ConstantStatement)first).value)) {
|
if (Values.not(((ConstantStatement)first).value)) {
|
||||||
first.compile(target, scope, pollute);
|
first.compile(target, scope, pollute);
|
||||||
}
|
}
|
||||||
else second.compile(target, scope, pollute);
|
else second.compile(target, scope, pollute);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
first.compile(target, scope, true);
|
first.compile(target, scope, true);
|
||||||
if (pollute) target.add(Instruction.dup(loc()));
|
if (pollute) target.add(Instruction.dup(loc()));
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
target.add(Instruction.nop(null));
|
target.add(Instruction.nop(null));
|
||||||
if (pollute) target.add(Instruction.discard(loc()));
|
if (pollute) target.add(Instruction.discard(loc()));
|
||||||
second.compile(target, scope, pollute);
|
second.compile(target, scope, pollute);
|
||||||
target.set(start, Instruction.jmpIfNot(loc(), target.size() - start));
|
target.set(start, Instruction.jmpIfNot(loc(), target.size() - start));
|
||||||
}
|
}
|
||||||
|
|
||||||
public LazyAndStatement(Location loc, Statement first, Statement second) {
|
public LazyAndStatement(Location loc, Statement first, Statement second) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.first = first;
|
this.first = first;
|
||||||
this.second = second;
|
this.second = second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,39 +1,39 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
|
|
||||||
public class LazyOrStatement extends Statement {
|
public class LazyOrStatement extends Statement {
|
||||||
public final Statement first, second;
|
public final Statement first, second;
|
||||||
|
|
||||||
@Override public boolean pure() { return first.pure() && second.pure(); }
|
@Override public boolean pure() { return first.pure() && second.pure(); }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
if (first instanceof ConstantStatement) {
|
if (first instanceof ConstantStatement) {
|
||||||
if (Values.not(((ConstantStatement)first).value)) {
|
if (Values.not(((ConstantStatement)first).value)) {
|
||||||
second.compile(target, scope, pollute);
|
second.compile(target, scope, pollute);
|
||||||
}
|
}
|
||||||
else first.compile(target, scope, pollute);
|
else first.compile(target, scope, pollute);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
first.compile(target, scope, true);
|
first.compile(target, scope, true);
|
||||||
if (pollute) target.add(Instruction.dup(loc()));
|
if (pollute) target.add(Instruction.dup(loc()));
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
target.add(Instruction.nop(null));
|
target.add(Instruction.nop(null));
|
||||||
if (pollute) target.add(Instruction.discard(loc()));
|
if (pollute) target.add(Instruction.discard(loc()));
|
||||||
second.compile(target, scope, pollute);
|
second.compile(target, scope, pollute);
|
||||||
target.set(start, Instruction.jmpIf(loc(), target.size() - start));
|
target.set(start, Instruction.jmpIf(loc(), target.size() - start));
|
||||||
}
|
}
|
||||||
|
|
||||||
public LazyOrStatement(Location loc, Statement first, Statement second) {
|
public LazyOrStatement(Location loc, Statement first, Statement second) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.first = first;
|
this.first = first;
|
||||||
this.second = second;
|
this.second = second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,63 +1,63 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class ObjectStatement extends Statement {
|
public class ObjectStatement extends Statement {
|
||||||
public final Map<Object, Statement> map;
|
public final Map<Object, Statement> map;
|
||||||
public final Map<Object, FunctionStatement> getters;
|
public final Map<Object, FunctionStatement> getters;
|
||||||
public final Map<Object, FunctionStatement> setters;
|
public final Map<Object, FunctionStatement> setters;
|
||||||
|
|
||||||
@Override public boolean pure() {
|
@Override public boolean pure() {
|
||||||
for (var el : map.values()) {
|
for (var el : map.values()) {
|
||||||
if (!el.pure()) return false;
|
if (!el.pure()) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
target.add(Instruction.loadObj(loc()));
|
target.add(Instruction.loadObj(loc()));
|
||||||
|
|
||||||
for (var el : map.entrySet()) {
|
for (var el : map.entrySet()) {
|
||||||
target.add(Instruction.dup(loc()));
|
target.add(Instruction.dup(loc()));
|
||||||
target.add(Instruction.loadValue(loc(), el.getKey()));
|
target.add(Instruction.loadValue(loc(), el.getKey()));
|
||||||
var val = el.getValue();
|
var val = el.getValue();
|
||||||
FunctionStatement.compileWithName(val, target, scope, true, el.getKey().toString());
|
FunctionStatement.compileWithName(val, target, scope, true, el.getKey().toString());
|
||||||
target.add(Instruction.storeMember(loc()));
|
target.add(Instruction.storeMember(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
var keys = new ArrayList<Object>();
|
var keys = new ArrayList<Object>();
|
||||||
keys.addAll(getters.keySet());
|
keys.addAll(getters.keySet());
|
||||||
keys.addAll(setters.keySet());
|
keys.addAll(setters.keySet());
|
||||||
|
|
||||||
for (var key : keys) {
|
for (var key : keys) {
|
||||||
if (key instanceof String) target.add(Instruction.loadValue(loc(), (String)key));
|
if (key instanceof String) target.add(Instruction.loadValue(loc(), (String)key));
|
||||||
else target.add(Instruction.loadValue(loc(), (Double)key));
|
else target.add(Instruction.loadValue(loc(), (Double)key));
|
||||||
|
|
||||||
if (getters.containsKey(key)) getters.get(key).compile(target, scope, true);
|
if (getters.containsKey(key)) getters.get(key).compile(target, scope, true);
|
||||||
else target.add(Instruction.loadValue(loc(), null));
|
else target.add(Instruction.loadValue(loc(), null));
|
||||||
|
|
||||||
if (setters.containsKey(key)) setters.get(key).compile(target, scope, true);
|
if (setters.containsKey(key)) setters.get(key).compile(target, scope, true);
|
||||||
else target.add(Instruction.loadValue(loc(), null));
|
else target.add(Instruction.loadValue(loc(), null));
|
||||||
|
|
||||||
target.add(Instruction.defProp(loc()));
|
target.add(Instruction.defProp(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pollute) target.add(Instruction.discard(loc()));
|
if (!pollute) target.add(Instruction.discard(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObjectStatement(Location loc, Map<Object, Statement> map, Map<Object, FunctionStatement> getters, Map<Object, FunctionStatement> setters) {
|
public ObjectStatement(Location loc, Map<Object, Statement> map, Map<Object, FunctionStatement> getters, Map<Object, FunctionStatement> setters) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.map = map;
|
this.map = map;
|
||||||
this.getters = getters;
|
this.getters = getters;
|
||||||
this.setters = setters;
|
this.setters = setters;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,37 +1,37 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.engine.Operation;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class OperationStatement extends Statement {
|
public class OperationStatement extends Statement {
|
||||||
public final Statement[] args;
|
public final Statement[] args;
|
||||||
public final Operation operation;
|
public final Operation operation;
|
||||||
|
|
||||||
@Override public boolean pure() {
|
@Override public boolean pure() {
|
||||||
for (var el : args) {
|
for (var el : args) {
|
||||||
if (!el.pure()) return false;
|
if (!el.pure()) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
for (var arg : args) {
|
for (var arg : args) {
|
||||||
arg.compile(target, scope, true);
|
arg.compile(target, scope, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pollute) target.add(Instruction.operation(loc(), operation));
|
if (pollute) target.add(Instruction.operation(loc(), operation));
|
||||||
else target.add(Instruction.discard(loc()));
|
else target.add(Instruction.discard(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public OperationStatement(Location loc, Operation operation, Statement ...args) {
|
public OperationStatement(Location loc, Operation operation, Statement ...args) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.operation = operation;
|
this.operation = operation;
|
||||||
this.args = args;
|
this.args = args;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class RegexStatement extends Statement {
|
public class RegexStatement extends Statement {
|
||||||
public final String pattern, flags;
|
public final String pattern, flags;
|
||||||
|
|
||||||
// Not really pure, since a function is called, but can be ignored.
|
// Not really pure, since a function is called, but can be ignored.
|
||||||
@Override public boolean pure() { return true; }
|
@Override public boolean pure() { return true; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
target.add(Instruction.loadRegex(loc(), pattern, flags));
|
target.add(Instruction.loadRegex(loc(), pattern, flags));
|
||||||
if (!pollute) target.add(Instruction.discard(loc()));
|
if (!pollute) target.add(Instruction.discard(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public RegexStatement(Location loc, String pattern, String flags) {
|
public RegexStatement(Location loc, String pattern, String flags) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.pattern = pattern;
|
this.pattern = pattern;
|
||||||
this.flags = flags;
|
this.flags = flags;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,33 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class TypeofStatement extends Statement {
|
public class TypeofStatement extends Statement {
|
||||||
public final Statement value;
|
public final Statement value;
|
||||||
|
|
||||||
// Not really pure, since a variable from the global scope could be accessed,
|
// Not really pure, since a variable from the global scope could be accessed,
|
||||||
// which could lead to code execution, that would get omitted
|
// which could lead to code execution, that would get omitted
|
||||||
@Override public boolean pure() { return true; }
|
@Override public boolean pure() { return true; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
if (value instanceof VariableStatement) {
|
if (value instanceof VariableStatement) {
|
||||||
var i = scope.getKey(((VariableStatement)value).name);
|
var i = scope.getKey(((VariableStatement)value).name);
|
||||||
if (i instanceof String) {
|
if (i instanceof String) {
|
||||||
target.add(Instruction.typeof(loc(), (String)i));
|
target.add(Instruction.typeof(loc(), (String)i));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
value.compile(target, scope, pollute);
|
value.compile(target, scope, pollute);
|
||||||
target.add(Instruction.typeof(loc()));
|
target.add(Instruction.typeof(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public TypeofStatement(Location loc, Statement value) {
|
public TypeofStatement(Location loc, Statement value) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,38 +1,38 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.engine.Operation;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class VariableAssignStatement extends Statement {
|
public class VariableAssignStatement extends Statement {
|
||||||
public final String name;
|
public final String name;
|
||||||
public final Statement value;
|
public final Statement value;
|
||||||
public final Operation operation;
|
public final Operation operation;
|
||||||
|
|
||||||
@Override public boolean pure() { return false; }
|
@Override public boolean pure() { return false; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
var i = scope.getKey(name);
|
var i = scope.getKey(name);
|
||||||
if (operation != null) {
|
if (operation != null) {
|
||||||
target.add(Instruction.loadVar(loc(), i));
|
target.add(Instruction.loadVar(loc(), i));
|
||||||
FunctionStatement.compileWithName(value, target, scope, true, name);
|
FunctionStatement.compileWithName(value, target, scope, true, name);
|
||||||
target.add(Instruction.operation(loc(), operation));
|
target.add(Instruction.operation(loc(), operation));
|
||||||
target.add(Instruction.storeVar(loc(), i, pollute));
|
target.add(Instruction.storeVar(loc(), i, pollute));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
FunctionStatement.compileWithName(value, target, scope, true, name);
|
FunctionStatement.compileWithName(value, target, scope, true, name);
|
||||||
target.add(Instruction.storeVar(loc(), i, pollute));
|
target.add(Instruction.storeVar(loc(), i, pollute));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public VariableAssignStatement(Location loc, String name, Statement val, Operation operation) {
|
public VariableAssignStatement(Location loc, String name, Statement val, Operation operation) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.value = val;
|
this.value = val;
|
||||||
this.operation = operation;
|
this.operation = operation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class VariableIndexStatement extends Statement {
|
public class VariableIndexStatement extends Statement {
|
||||||
public final int index;
|
public final int index;
|
||||||
|
|
||||||
@Override public boolean pure() { return true; }
|
@Override public boolean pure() { return true; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
if (pollute) target.add(Instruction.loadVar(loc(), index));
|
if (pollute) target.add(Instruction.loadVar(loc(), index));
|
||||||
}
|
}
|
||||||
|
|
||||||
public VariableIndexStatement(Location loc, int i) {
|
public VariableIndexStatement(Location loc, int i) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.index = i;
|
this.index = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,32 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.AssignableStatement;
|
import me.topchetoeu.jscript.compilation.AssignableStatement;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.engine.Operation;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class VariableStatement extends AssignableStatement {
|
public class VariableStatement extends AssignableStatement {
|
||||||
public final String name;
|
public final String name;
|
||||||
|
|
||||||
@Override public boolean pure() { return false; }
|
@Override public boolean pure() { return false; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Statement toAssign(Statement val, Operation operation) {
|
public Statement toAssign(Statement val, Operation operation) {
|
||||||
return new VariableAssignStatement(loc(), name, val, operation);
|
return new VariableAssignStatement(loc(), name, val, operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
var i = scope.getKey(name);
|
var i = scope.getKey(name);
|
||||||
target.add(Instruction.loadVar(loc(), i));
|
target.add(Instruction.loadVar(loc(), i));
|
||||||
if (!pollute) target.add(Instruction.discard(loc()));
|
if (!pollute) target.add(Instruction.discard(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public VariableStatement(Location loc, String name) {
|
public VariableStatement(Location loc, String name) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,125 +1,124 @@
|
|||||||
package me.topchetoeu.jscript.engine;
|
package me.topchetoeu.jscript.engine;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Filename;
|
import me.topchetoeu.jscript.Filename;
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.mapping.SourceMap;
|
import me.topchetoeu.jscript.mapping.SourceMap;
|
||||||
|
|
||||||
public class Context {
|
public class Context {
|
||||||
private final Stack<Environment> env = new Stack<>();
|
private final Stack<Environment> env = new Stack<>();
|
||||||
private final ArrayList<CodeFrame> frames = new ArrayList<>();
|
private final ArrayList<CodeFrame> frames = new ArrayList<>();
|
||||||
public final Engine engine;
|
public final Engine engine;
|
||||||
|
|
||||||
public Environment environment() {
|
public Environment environment() {
|
||||||
return env.empty() ? null : env.peek();
|
return env.empty() ? null : env.peek();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Context pushEnv(Environment env) {
|
private Context pushEnv(Environment env) {
|
||||||
this.env.push(env);
|
this.env.push(env);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
public void popEnv() {
|
private void popEnv() {
|
||||||
if (!env.empty()) this.env.pop();
|
if (!env.empty()) this.env.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
public FunctionValue compile(Filename filename, String raw) {
|
public FunctionValue compile(Filename filename, String raw) {
|
||||||
var env = environment();
|
var env = environment();
|
||||||
var result = env.compile.call(this, null, raw, filename.toString(), env);
|
var result = env.compile.call(this, null, raw, filename.toString(), env);
|
||||||
|
|
||||||
var function = (FunctionValue)Values.getMember(this, result, "function");
|
var function = (FunctionValue)Values.getMember(this, result, "function");
|
||||||
if (!engine.debugging) return function;
|
if (!engine.debugging) return function;
|
||||||
|
|
||||||
var rawMapChain = ((ArrayValue)Values.getMember(this, result, "mapChain")).toArray();
|
var rawMapChain = ((ArrayValue)Values.getMember(this, result, "mapChain")).toArray();
|
||||||
var breakpoints = new TreeSet<>(
|
var breakpoints = new TreeSet<>(
|
||||||
Arrays.stream(((ArrayValue)Values.getMember(this, result, "breakpoints")).toArray())
|
Arrays.stream(((ArrayValue)Values.getMember(this, result, "breakpoints")).toArray())
|
||||||
.map(v -> Location.parse(Values.toString(this, v)))
|
.map(v -> Location.parse(Values.toString(this, v)))
|
||||||
.collect(Collectors.toList())
|
.collect(Collectors.toList())
|
||||||
);
|
);
|
||||||
var maps = new SourceMap[rawMapChain.length];
|
var maps = new SourceMap[rawMapChain.length];
|
||||||
|
|
||||||
for (var i = 0; i < maps.length; i++) maps[i] = SourceMap.parse(Values.toString(this, (String)rawMapChain[i]));
|
for (var i = 0; i < maps.length; i++) maps[i] = SourceMap.parse(Values.toString(this, (String)rawMapChain[i]));
|
||||||
|
|
||||||
var map = SourceMap.chain(maps);
|
var map = SourceMap.chain(maps);
|
||||||
|
|
||||||
if (map != null) {
|
if (map != null) {
|
||||||
var newBreakpoints = new TreeSet<Location>();
|
var newBreakpoints = new TreeSet<Location>();
|
||||||
for (var bp : breakpoints) {
|
for (var bp : breakpoints) {
|
||||||
bp = map.toCompiled(bp);
|
bp = map.toCompiled(bp);
|
||||||
if (bp != null) newBreakpoints.add(bp);
|
if (bp != null) newBreakpoints.add(bp);
|
||||||
}
|
}
|
||||||
breakpoints = newBreakpoints;
|
breakpoints = newBreakpoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
engine.onSource(filename, raw, breakpoints, map);
|
engine.onSource(filename, raw, breakpoints, map);
|
||||||
|
|
||||||
return function;
|
return function;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void pushFrame(CodeFrame frame) {
|
public void pushFrame(CodeFrame frame) {
|
||||||
frames.add(frame);
|
frames.add(frame);
|
||||||
if (frames.size() > engine.maxStackFrames) throw EngineException.ofRange("Stack overflow!");
|
if (frames.size() > engine.maxStackFrames) throw EngineException.ofRange("Stack overflow!");
|
||||||
pushEnv(frame.function.environment);
|
pushEnv(frame.function.environment);
|
||||||
engine.onFramePush(this, frame);
|
engine.onFramePush(this, frame);
|
||||||
}
|
}
|
||||||
public boolean popFrame(CodeFrame frame) {
|
public boolean popFrame(CodeFrame frame) {
|
||||||
if (frames.size() == 0) return false;
|
if (frames.size() == 0) return false;
|
||||||
if (frames.get(frames.size() - 1) != frame) return false;
|
if (frames.get(frames.size() - 1) != frame) return false;
|
||||||
frames.remove(frames.size() - 1);
|
frames.remove(frames.size() - 1);
|
||||||
popEnv();
|
popEnv();
|
||||||
engine.onFramePop(this, frame);
|
engine.onFramePop(this, frame);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
public CodeFrame peekFrame() {
|
public CodeFrame peekFrame() {
|
||||||
if (frames.size() == 0) return null;
|
if (frames.size() == 0) return null;
|
||||||
return frames.get(frames.size() - 1);
|
return frames.get(frames.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<CodeFrame> frames() {
|
public List<CodeFrame> frames() {
|
||||||
return Collections.unmodifiableList(frames);
|
return Collections.unmodifiableList(frames);
|
||||||
}
|
}
|
||||||
public List<String> stackTrace() {
|
public List<String> stackTrace() {
|
||||||
var res = new ArrayList<String>();
|
var res = new ArrayList<String>();
|
||||||
|
|
||||||
for (var i = frames.size() - 1; i >= 0; i--) {
|
for (var i = frames.size() - 1; i >= 0; i--) {
|
||||||
var el = frames.get(i);
|
var el = frames.get(i);
|
||||||
var name = el.function.name;
|
var name = el.function.name;
|
||||||
Location loc = null;
|
Location loc = null;
|
||||||
|
|
||||||
for (var j = el.codePtr; j >= 0 && loc == null; j--) loc = el.function.body[j].location;
|
for (var j = el.codePtr; j >= 0 && loc == null; j--) loc = el.function.body[j].location;
|
||||||
if (loc == null) loc = el.function.loc();
|
if (loc == null) loc = el.function.loc();
|
||||||
|
|
||||||
var trace = "";
|
var trace = "";
|
||||||
|
|
||||||
if (loc != null) trace += "at " + loc.toString() + " ";
|
if (loc != null) trace += "at " + loc.toString() + " ";
|
||||||
if (name != null && !name.equals("")) trace += "in " + name + " ";
|
if (name != null && !name.equals("")) trace += "in " + name + " ";
|
||||||
|
|
||||||
trace = trace.trim();
|
trace = trace.trim();
|
||||||
|
|
||||||
if (!trace.equals("")) res.add(trace);
|
if (!trace.equals("")) res.add(trace);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Context(Engine engine) {
|
public Context(Engine engine) {
|
||||||
this.engine = engine;
|
this.engine = engine;
|
||||||
}
|
}
|
||||||
public Context(Engine engine, Environment env) {
|
public Context(Engine engine, Environment env) {
|
||||||
this(engine);
|
this(engine);
|
||||||
this.pushEnv(env);
|
if (env != null) this.pushEnv(env);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -1,175 +1,175 @@
|
|||||||
package me.topchetoeu.jscript.engine;
|
package me.topchetoeu.jscript.engine;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
import java.util.concurrent.PriorityBlockingQueue;
|
import java.util.concurrent.PriorityBlockingQueue;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Filename;
|
import me.topchetoeu.jscript.Filename;
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.FunctionBody;
|
import me.topchetoeu.jscript.compilation.FunctionBody;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.engine.debug.DebugController;
|
import me.topchetoeu.jscript.engine.debug.DebugController;
|
||||||
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.events.Awaitable;
|
import me.topchetoeu.jscript.events.Awaitable;
|
||||||
import me.topchetoeu.jscript.events.DataNotifier;
|
import me.topchetoeu.jscript.events.DataNotifier;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.exceptions.InterruptException;
|
import me.topchetoeu.jscript.exceptions.InterruptException;
|
||||||
import me.topchetoeu.jscript.mapping.SourceMap;
|
import me.topchetoeu.jscript.mapping.SourceMap;
|
||||||
|
|
||||||
public class Engine implements DebugController {
|
public class Engine implements DebugController {
|
||||||
private class UncompiledFunction extends FunctionValue {
|
private class UncompiledFunction extends FunctionValue {
|
||||||
public final Filename filename;
|
public final Filename filename;
|
||||||
public final String raw;
|
public final String raw;
|
||||||
private FunctionValue compiled = null;
|
private FunctionValue compiled = null;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object call(Context ctx, Object thisArg, Object ...args) {
|
public Object call(Context ctx, Object thisArg, Object ...args) {
|
||||||
if (compiled == null) compiled = ctx.compile(filename, raw);
|
if (compiled == null) compiled = ctx.compile(filename, raw);
|
||||||
return compiled.call(ctx, thisArg, args);
|
return compiled.call(ctx, thisArg, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UncompiledFunction(Filename filename, String raw) {
|
public UncompiledFunction(Filename filename, String raw) {
|
||||||
super(filename + "", 0);
|
super(filename + "", 0);
|
||||||
this.filename = filename;
|
this.filename = filename;
|
||||||
this.raw = raw;
|
this.raw = raw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Task implements Comparable<Task> {
|
private static class Task implements Comparable<Task> {
|
||||||
public final FunctionValue func;
|
public final FunctionValue func;
|
||||||
public final Object thisArg;
|
public final Object thisArg;
|
||||||
public final Object[] args;
|
public final Object[] args;
|
||||||
public final DataNotifier<Object> notifier = new DataNotifier<>();
|
public final DataNotifier<Object> notifier = new DataNotifier<>();
|
||||||
public final Context ctx;
|
public final Context ctx;
|
||||||
public final boolean micro;
|
public final boolean micro;
|
||||||
|
|
||||||
public Task(Context ctx, FunctionValue func, Object thisArg, Object[] args, boolean micro) {
|
public Task(Context ctx, FunctionValue func, Object thisArg, Object[] args, boolean micro) {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.func = func;
|
this.func = func;
|
||||||
this.thisArg = thisArg;
|
this.thisArg = thisArg;
|
||||||
this.args = args;
|
this.args = args;
|
||||||
this.micro = micro;
|
this.micro = micro;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(Task other) {
|
public int compareTo(Task other) {
|
||||||
return Integer.compare(this.micro ? 0 : 1, other.micro ? 0 : 1);
|
return Integer.compare(this.micro ? 0 : 1, other.micro ? 0 : 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int nextId = 0;
|
private static int nextId = 0;
|
||||||
public static final HashMap<Long, FunctionBody> functions = new HashMap<>();
|
public static final HashMap<Long, FunctionBody> functions = new HashMap<>();
|
||||||
|
|
||||||
public final int id = ++nextId;
|
public final int id = ++nextId;
|
||||||
public final boolean debugging;
|
public final boolean debugging;
|
||||||
public int maxStackFrames = 10000;
|
public int maxStackFrames = 10000;
|
||||||
|
|
||||||
private final HashMap<Filename, String> sources = new HashMap<>();
|
private final HashMap<Filename, String> sources = new HashMap<>();
|
||||||
private final HashMap<Filename, TreeSet<Location>> bpts = new HashMap<>();
|
private final HashMap<Filename, TreeSet<Location>> bpts = new HashMap<>();
|
||||||
private final HashMap<Filename, SourceMap> maps = new HashMap<>();
|
private final HashMap<Filename, SourceMap> maps = new HashMap<>();
|
||||||
|
|
||||||
public Location mapToCompiled(Location location) {
|
public Location mapToCompiled(Location location) {
|
||||||
var map = maps.get(location.filename());
|
var map = maps.get(location.filename());
|
||||||
if (map == null) return location;
|
if (map == null) return location;
|
||||||
return map.toCompiled(location);
|
return map.toCompiled(location);
|
||||||
}
|
}
|
||||||
public Location mapToOriginal(Location location) {
|
public Location mapToOriginal(Location location) {
|
||||||
var map = maps.get(location.filename());
|
var map = maps.get(location.filename());
|
||||||
if (map == null) return location;
|
if (map == null) return location;
|
||||||
return map.toOriginal(location);
|
return map.toOriginal(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DebugController debugger;
|
private DebugController debugger;
|
||||||
private Thread thread;
|
private Thread thread;
|
||||||
private PriorityBlockingQueue<Task> tasks = new PriorityBlockingQueue<>();
|
private PriorityBlockingQueue<Task> tasks = new PriorityBlockingQueue<>();
|
||||||
|
|
||||||
public synchronized boolean attachDebugger(DebugController debugger) {
|
public synchronized boolean attachDebugger(DebugController debugger) {
|
||||||
if (!debugging || this.debugger != null) return false;
|
if (!debugging || this.debugger != null) return false;
|
||||||
|
|
||||||
for (var source : sources.entrySet()) debugger.onSource(
|
for (var source : sources.entrySet()) debugger.onSource(
|
||||||
source.getKey(), source.getValue(),
|
source.getKey(), source.getValue(),
|
||||||
bpts.get(source.getKey()),
|
bpts.get(source.getKey()),
|
||||||
maps.get(source.getKey())
|
maps.get(source.getKey())
|
||||||
);
|
);
|
||||||
|
|
||||||
this.debugger = debugger;
|
this.debugger = debugger;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
public synchronized boolean detachDebugger() {
|
public synchronized boolean detachDebugger() {
|
||||||
if (!debugging || this.debugger == null) return false;
|
if (!debugging || this.debugger == null) return false;
|
||||||
this.debugger = null;
|
this.debugger = null;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void runTask(Task task) {
|
private void runTask(Task task) {
|
||||||
try {
|
try {
|
||||||
task.notifier.next(task.func.call(task.ctx, task.thisArg, task.args));
|
task.notifier.next(task.func.call(task.ctx, task.thisArg, task.args));
|
||||||
}
|
}
|
||||||
catch (RuntimeException e) {
|
catch (RuntimeException e) {
|
||||||
if (e instanceof InterruptException) throw e;
|
if (e instanceof InterruptException) throw e;
|
||||||
task.notifier.error(e);
|
task.notifier.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void run(boolean untilEmpty) {
|
public void run(boolean untilEmpty) {
|
||||||
while (!untilEmpty || !tasks.isEmpty()) {
|
while (!untilEmpty || !tasks.isEmpty()) {
|
||||||
try {
|
try {
|
||||||
runTask(tasks.take());
|
runTask(tasks.take());
|
||||||
}
|
}
|
||||||
catch (InterruptedException | InterruptException e) {
|
catch (InterruptedException | InterruptException e) {
|
||||||
for (var msg : tasks) msg.notifier.error(new InterruptException(e));
|
for (var msg : tasks) msg.notifier.error(new InterruptException(e));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Thread start() {
|
public Thread start() {
|
||||||
if (this.thread == null) {
|
if (this.thread == null) {
|
||||||
this.thread = new Thread(() -> run(false), "JavaScript Runner #" + id);
|
this.thread = new Thread(() -> run(false), "JavaScript Runner #" + id);
|
||||||
this.thread.start();
|
this.thread.start();
|
||||||
}
|
}
|
||||||
return this.thread;
|
return this.thread;
|
||||||
}
|
}
|
||||||
public void stop() {
|
public void stop() {
|
||||||
thread.interrupt();
|
thread.interrupt();
|
||||||
thread = null;
|
thread = null;
|
||||||
}
|
}
|
||||||
public boolean inExecThread() {
|
public boolean inExecThread() {
|
||||||
return Thread.currentThread() == thread;
|
return Thread.currentThread() == thread;
|
||||||
}
|
}
|
||||||
public synchronized boolean isRunning() {
|
public synchronized boolean isRunning() {
|
||||||
return this.thread != null;
|
return this.thread != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Awaitable<Object> pushMsg(boolean micro, Context ctx, FunctionValue func, Object thisArg, Object ...args) {
|
public Awaitable<Object> pushMsg(boolean micro, Environment env, FunctionValue func, Object thisArg, Object ...args) {
|
||||||
var msg = new Task(ctx == null ? new Context(this) : ctx, func, thisArg, args, micro);
|
var msg = new Task(new Context(this, env), func, thisArg, args, micro);
|
||||||
tasks.add(msg);
|
tasks.add(msg);
|
||||||
return msg.notifier;
|
return msg.notifier;
|
||||||
}
|
}
|
||||||
public Awaitable<Object> pushMsg(boolean micro, Context ctx, Filename filename, String raw, Object thisArg, Object ...args) {
|
public Awaitable<Object> pushMsg(boolean micro, Environment env, Filename filename, String raw, Object thisArg, Object ...args) {
|
||||||
return pushMsg(micro, ctx, new UncompiledFunction(filename, raw), thisArg, args);
|
return pushMsg(micro, env, new UncompiledFunction(filename, raw), thisArg, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFramePush(Context ctx, CodeFrame frame) {
|
public void onFramePush(Context ctx, CodeFrame frame) {
|
||||||
if (debugging && debugger != null) debugger.onFramePush(ctx, frame);
|
if (debugging && debugger != null) debugger.onFramePush(ctx, frame);
|
||||||
}
|
}
|
||||||
@Override public void onFramePop(Context ctx, CodeFrame frame) {
|
@Override public void onFramePop(Context ctx, CodeFrame frame) {
|
||||||
if (debugging && debugger != null) debugger.onFramePop(ctx, frame);
|
if (debugging && debugger != null) debugger.onFramePop(ctx, frame);
|
||||||
}
|
}
|
||||||
@Override public boolean onInstruction(Context ctx, CodeFrame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught) {
|
@Override public boolean onInstruction(Context ctx, CodeFrame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught) {
|
||||||
if (debugging && debugger != null) return debugger.onInstruction(ctx, frame, instruction, returnVal, error, caught);
|
if (debugging && debugger != null) return debugger.onInstruction(ctx, frame, instruction, returnVal, error, caught);
|
||||||
else return false;
|
else return false;
|
||||||
}
|
}
|
||||||
@Override public void onSource(Filename filename, String source, TreeSet<Location> breakpoints, SourceMap map) {
|
@Override public void onSource(Filename filename, String source, TreeSet<Location> breakpoints, SourceMap map) {
|
||||||
if (!debugging) return;
|
if (!debugging) return;
|
||||||
if (debugger != null) debugger.onSource(filename, source, breakpoints, map);
|
if (debugger != null) debugger.onSource(filename, source, breakpoints, map);
|
||||||
sources.put(filename, source);
|
sources.put(filename, source);
|
||||||
bpts.put(filename, breakpoints);
|
bpts.put(filename, breakpoints);
|
||||||
maps.put(filename, map);
|
maps.put(filename, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Engine(boolean debugging) {
|
public Engine(boolean debugging) {
|
||||||
this.debugging = debugging;
|
this.debugging = debugging;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,131 +1,136 @@
|
|||||||
package me.topchetoeu.jscript.engine;
|
package me.topchetoeu.jscript.engine;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Filename;
|
import me.topchetoeu.jscript.Filename;
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.engine.scope.GlobalScope;
|
import me.topchetoeu.jscript.engine.scope.GlobalScope;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Symbol;
|
import me.topchetoeu.jscript.engine.values.Symbol;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.filesystem.RootFilesystem;
|
import me.topchetoeu.jscript.filesystem.RootFilesystem;
|
||||||
import me.topchetoeu.jscript.interop.Native;
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
import me.topchetoeu.jscript.interop.NativeGetter;
|
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||||
import me.topchetoeu.jscript.interop.NativeSetter;
|
import me.topchetoeu.jscript.interop.NativeSetter;
|
||||||
import me.topchetoeu.jscript.interop.NativeWrapperProvider;
|
import me.topchetoeu.jscript.interop.NativeWrapperProvider;
|
||||||
import me.topchetoeu.jscript.parsing.Parsing;
|
import me.topchetoeu.jscript.modules.RootModuleRepo;
|
||||||
import me.topchetoeu.jscript.permissions.Permission;
|
import me.topchetoeu.jscript.parsing.Parsing;
|
||||||
import me.topchetoeu.jscript.permissions.PermissionsProvider;
|
import me.topchetoeu.jscript.permissions.Permission;
|
||||||
|
import me.topchetoeu.jscript.permissions.PermissionsProvider;
|
||||||
public class Environment implements PermissionsProvider {
|
|
||||||
private HashMap<String, ObjectValue> prototypes = new HashMap<>();
|
// TODO: Remove hardcoded extensions form environment
|
||||||
|
public class Environment implements PermissionsProvider {
|
||||||
public final Data data = new Data();
|
private HashMap<String, ObjectValue> prototypes = new HashMap<>();
|
||||||
public static final HashMap<String, Symbol> symbols = new HashMap<>();
|
|
||||||
|
public final Data data = new Data();
|
||||||
public GlobalScope global;
|
public static final HashMap<String, Symbol> symbols = new HashMap<>();
|
||||||
public WrappersProvider wrappers;
|
|
||||||
public PermissionsProvider permissions = null;
|
public GlobalScope global;
|
||||||
public final RootFilesystem filesystem = new RootFilesystem(this);
|
public WrappersProvider wrappers;
|
||||||
|
|
||||||
private static int nextId = 0;
|
public PermissionsProvider permissions = null;
|
||||||
|
public final RootFilesystem filesystem = new RootFilesystem(this);
|
||||||
@Native public boolean stackVisible = true;
|
public final RootModuleRepo modules = new RootModuleRepo();
|
||||||
@Native public int id = ++nextId;
|
public String moduleCwd = "/";
|
||||||
|
|
||||||
@Native public FunctionValue compile = new NativeFunction("compile", (ctx, thisArg, args) -> {
|
private static int nextId = 0;
|
||||||
var source = Values.toString(ctx, args[0]);
|
|
||||||
var filename = Values.toString(ctx, args[1]);
|
@Native public boolean stackVisible = true;
|
||||||
var isDebug = Values.toBoolean(args[2]);
|
@Native public int id = ++nextId;
|
||||||
|
|
||||||
var env = Values.wrapper(args[2], Environment.class);
|
@Native public FunctionValue compile = new NativeFunction("compile", (ctx, thisArg, args) -> {
|
||||||
var res = new ObjectValue();
|
var source = Values.toString(ctx, args[0]);
|
||||||
|
var filename = Values.toString(ctx, args[1]);
|
||||||
var target = Parsing.compile(env, Filename.parse(filename), source);
|
var isDebug = Values.toBoolean(args[2]);
|
||||||
Engine.functions.putAll(target.functions);
|
|
||||||
Engine.functions.remove(0l);
|
var env = Values.wrapper(args[2], Environment.class);
|
||||||
|
var res = new ObjectValue();
|
||||||
res.defineProperty(ctx, "function", target.func(env));
|
|
||||||
res.defineProperty(ctx, "mapChain", new ArrayValue());
|
var target = Parsing.compile(env, Filename.parse(filename), source);
|
||||||
|
Engine.functions.putAll(target.functions);
|
||||||
|
Engine.functions.remove(0l);
|
||||||
if (isDebug) {
|
|
||||||
res.defineProperty(ctx, "breakpoints", ArrayValue.of(ctx, target.breakpoints.stream().map(Location::toString).collect(Collectors.toList())));
|
res.defineProperty(ctx, "function", target.func(env));
|
||||||
}
|
res.defineProperty(ctx, "mapChain", new ArrayValue());
|
||||||
|
|
||||||
return res;
|
if (isDebug) {
|
||||||
});
|
res.defineProperty(ctx, "breakpoints", ArrayValue.of(ctx, target.breakpoints.stream().map(Location::toString).collect(Collectors.toList())));
|
||||||
@Native public FunctionValue regexConstructor = new NativeFunction("RegExp", (ctx, thisArg, args) -> {
|
}
|
||||||
throw EngineException.ofError("Regular expressions not supported.").setCtx(ctx.environment(), ctx.engine);
|
|
||||||
});
|
return res;
|
||||||
|
});
|
||||||
public Environment addData(Data data) {
|
@Native public FunctionValue regexConstructor = new NativeFunction("RegExp", (ctx, thisArg, args) -> {
|
||||||
this.data.addAll(data);
|
throw EngineException.ofError("Regular expressions not supported.").setCtx(ctx.environment(), ctx.engine);
|
||||||
return this;
|
});
|
||||||
}
|
|
||||||
|
@Native public ObjectValue proto(String name) {
|
||||||
@Native public ObjectValue proto(String name) {
|
return prototypes.get(name);
|
||||||
return prototypes.get(name);
|
}
|
||||||
}
|
@Native public void setProto(String name, ObjectValue val) {
|
||||||
@Native public void setProto(String name, ObjectValue val) {
|
prototypes.put(name, val);
|
||||||
prototypes.put(name, val);
|
}
|
||||||
}
|
|
||||||
|
@Native public Symbol symbol(String name) {
|
||||||
@Native public Symbol symbol(String name) {
|
return getSymbol(name);
|
||||||
return getSymbol(name);
|
}
|
||||||
}
|
|
||||||
|
@NativeGetter("global") public ObjectValue getGlobal() {
|
||||||
@NativeGetter("global") public ObjectValue getGlobal() {
|
return global.obj;
|
||||||
return global.obj;
|
}
|
||||||
}
|
@NativeSetter("global") public void setGlobal(ObjectValue val) {
|
||||||
@NativeSetter("global") public void setGlobal(ObjectValue val) {
|
global = new GlobalScope(val);
|
||||||
global = new GlobalScope(val);
|
}
|
||||||
}
|
|
||||||
|
@Native public Environment fork() {
|
||||||
@Native public Environment fork() {
|
var res = new Environment(compile, null, global);
|
||||||
var res = new Environment(compile, null, global);
|
res.wrappers = wrappers.fork(res);
|
||||||
res.wrappers = wrappers.fork(res);
|
res.regexConstructor = regexConstructor;
|
||||||
res.regexConstructor = regexConstructor;
|
res.prototypes = new HashMap<>(prototypes);
|
||||||
res.prototypes = new HashMap<>(prototypes);
|
return res;
|
||||||
return res;
|
}
|
||||||
}
|
@Native public Environment child() {
|
||||||
@Native public Environment child() {
|
var res = fork();
|
||||||
var res = fork();
|
res.global = res.global.globalChild();
|
||||||
res.global = res.global.globalChild();
|
res.permissions = this.permissions;
|
||||||
return res;
|
res.filesystem.protocols.putAll(this.filesystem.protocols);
|
||||||
}
|
res.modules.repos.putAll(this.modules.repos);
|
||||||
|
return res;
|
||||||
@Override public boolean hasPermission(Permission perm, char delim) {
|
}
|
||||||
return permissions == null || permissions.hasPermission(perm, delim);
|
|
||||||
}
|
@Override public boolean hasPermission(Permission perm, char delim) {
|
||||||
@Override public boolean hasPermission(Permission perm) {
|
return permissions == null || permissions.hasPermission(perm, delim);
|
||||||
return permissions == null || permissions.hasPermission(perm);
|
}
|
||||||
}
|
@Override public boolean hasPermission(Permission perm) {
|
||||||
|
return permissions == null || permissions.hasPermission(perm);
|
||||||
public Context context(Engine engine) {
|
}
|
||||||
return new Context(engine).pushEnv(this);
|
|
||||||
}
|
public Context context(Engine engine) {
|
||||||
|
return new Context(engine, this);
|
||||||
public static Symbol getSymbol(String name) {
|
}
|
||||||
if (symbols.containsKey(name)) return symbols.get(name);
|
|
||||||
else {
|
public static Symbol getSymbol(String name) {
|
||||||
var res = new Symbol(name);
|
if (symbols.containsKey(name)) return symbols.get(name);
|
||||||
symbols.put(name, res);
|
else {
|
||||||
return res;
|
var res = new Symbol(name);
|
||||||
}
|
symbols.put(name, res);
|
||||||
}
|
return res;
|
||||||
|
}
|
||||||
public Environment(FunctionValue compile, WrappersProvider nativeConverter, GlobalScope global) {
|
}
|
||||||
if (compile != null) this.compile = compile;
|
|
||||||
if (nativeConverter == null) nativeConverter = new NativeWrapperProvider(this);
|
public Environment(FunctionValue compile, WrappersProvider nativeConverter, GlobalScope global) {
|
||||||
if (global == null) global = new GlobalScope();
|
if (compile != null) this.compile = compile;
|
||||||
|
if (nativeConverter == null) nativeConverter = new NativeWrapperProvider(this);
|
||||||
this.wrappers = nativeConverter;
|
if (global == null) global = new GlobalScope();
|
||||||
this.global = global;
|
|
||||||
}
|
this.wrappers = nativeConverter;
|
||||||
}
|
this.global = global;
|
||||||
|
}
|
||||||
|
public Environment() {
|
||||||
|
this(null, null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,51 +1,51 @@
|
|||||||
package me.topchetoeu.jscript.engine.debug;
|
package me.topchetoeu.jscript.engine.debug;
|
||||||
|
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Filename;
|
import me.topchetoeu.jscript.Filename;
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.mapping.SourceMap;
|
import me.topchetoeu.jscript.mapping.SourceMap;
|
||||||
|
|
||||||
public interface DebugController {
|
public interface DebugController {
|
||||||
/**
|
/**
|
||||||
* Called when a script has been loaded
|
* Called when a script has been loaded
|
||||||
* @param filename The name of the source
|
* @param filename The name of the source
|
||||||
* @param source 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 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
|
* @param map The source map associated with this file. null if this source map isn't mapped
|
||||||
*/
|
*/
|
||||||
void onSource(Filename filename, String source, TreeSet<Location> breakpoints, SourceMap map);
|
void onSource(Filename filename, String source, TreeSet<Location> breakpoints, SourceMap map);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called immediatly before an instruction is executed, as well as after an instruction, if it has threw or returned.
|
* 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.
|
* This function might pause in order to await debugging commands.
|
||||||
* @param ctx The context of execution
|
* @param ctx The context of execution
|
||||||
* @param frame The frame in which execution is occuring
|
* @param frame The frame in which execution is occuring
|
||||||
* @param instruction The instruction which was or will be executed
|
* @param instruction The instruction which was or will be executed
|
||||||
* @param loc The most recent location the code frame has been at
|
* @param loc The most recent location the code frame has been at
|
||||||
* @param returnVal The return value of the instruction, Runners.NO_RETURN if none
|
* @param returnVal The return value of the instruction, Runners.NO_RETURN if none
|
||||||
* @param error The error that the instruction threw, null if none
|
* @param error The error that the instruction threw, null if none
|
||||||
* @param caught Whether or not the error has been caught
|
* @param caught Whether or not the error has been caught
|
||||||
* @return Whether or not the frame should restart
|
* @return Whether or not the frame should restart
|
||||||
*/
|
*/
|
||||||
boolean onInstruction(Context ctx, CodeFrame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught);
|
boolean onInstruction(Context ctx, CodeFrame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called immediatly before a frame has been pushed on the frame stack.
|
* Called immediatly before a frame has been pushed on the frame stack.
|
||||||
* This function might pause in order to await debugging commands.
|
* This function might pause in order to await debugging commands.
|
||||||
* @param ctx The context of execution
|
* @param ctx The context of execution
|
||||||
* @param frame The code frame which was pushed
|
* @param frame The code frame which was pushed
|
||||||
*/
|
*/
|
||||||
void onFramePush(Context ctx, CodeFrame frame);
|
void onFramePush(Context ctx, CodeFrame frame);
|
||||||
/**
|
/**
|
||||||
* Called immediatly after a frame has been popped out of the frame stack.
|
* Called immediatly after a frame has been popped out of the frame stack.
|
||||||
* This function might pause in order to await debugging commands.
|
* This function might pause in order to await debugging commands.
|
||||||
* @param ctx The context of execution
|
* @param ctx The context of execution
|
||||||
* @param frame The code frame which was popped out
|
* @param frame The code frame which was popped out
|
||||||
*/
|
*/
|
||||||
void onFramePop(Context ctx, CodeFrame frame);
|
void onFramePop(Context ctx, CodeFrame frame);
|
||||||
}
|
}
|
||||||
|
@ -1,34 +1,34 @@
|
|||||||
package me.topchetoeu.jscript.engine.debug;
|
package me.topchetoeu.jscript.engine.debug;
|
||||||
|
|
||||||
public interface DebugHandler {
|
public interface DebugHandler {
|
||||||
void enable(V8Message msg);
|
void enable(V8Message msg);
|
||||||
void disable(V8Message msg);
|
void disable(V8Message msg);
|
||||||
|
|
||||||
void setBreakpointByUrl(V8Message msg);
|
void setBreakpointByUrl(V8Message msg);
|
||||||
void removeBreakpoint(V8Message msg);
|
void removeBreakpoint(V8Message msg);
|
||||||
void continueToLocation(V8Message msg);
|
void continueToLocation(V8Message msg);
|
||||||
|
|
||||||
void getScriptSource(V8Message msg);
|
void getScriptSource(V8Message msg);
|
||||||
void getPossibleBreakpoints(V8Message msg);
|
void getPossibleBreakpoints(V8Message msg);
|
||||||
|
|
||||||
void resume(V8Message msg);
|
void resume(V8Message msg);
|
||||||
void pause(V8Message msg);
|
void pause(V8Message msg);
|
||||||
|
|
||||||
void stepInto(V8Message msg);
|
void stepInto(V8Message msg);
|
||||||
void stepOut(V8Message msg);
|
void stepOut(V8Message msg);
|
||||||
void stepOver(V8Message msg);
|
void stepOver(V8Message msg);
|
||||||
|
|
||||||
void setPauseOnExceptions(V8Message msg);
|
void setPauseOnExceptions(V8Message msg);
|
||||||
|
|
||||||
void evaluateOnCallFrame(V8Message msg);
|
void evaluateOnCallFrame(V8Message msg);
|
||||||
|
|
||||||
void getProperties(V8Message msg);
|
void getProperties(V8Message msg);
|
||||||
void releaseObjectGroup(V8Message msg);
|
void releaseObjectGroup(V8Message msg);
|
||||||
void releaseObject(V8Message msg);
|
void releaseObject(V8Message msg);
|
||||||
/**
|
/**
|
||||||
* This method might not execute the actual code for well-known requests
|
* This method might not execute the actual code for well-known requests
|
||||||
*/
|
*/
|
||||||
void callFunctionOn(V8Message msg);
|
void callFunctionOn(V8Message msg);
|
||||||
|
|
||||||
void runtimeEnable(V8Message msg);
|
void runtimeEnable(V8Message msg);
|
||||||
}
|
}
|
||||||
|
@ -1,245 +1,245 @@
|
|||||||
package me.topchetoeu.jscript.engine.debug;
|
package me.topchetoeu.jscript.engine.debug;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Metadata;
|
import me.topchetoeu.jscript.Metadata;
|
||||||
import me.topchetoeu.jscript.Reading;
|
import me.topchetoeu.jscript.Reading;
|
||||||
import me.topchetoeu.jscript.engine.debug.WebSocketMessage.Type;
|
import me.topchetoeu.jscript.engine.debug.WebSocketMessage.Type;
|
||||||
import me.topchetoeu.jscript.events.Notifier;
|
import me.topchetoeu.jscript.events.Notifier;
|
||||||
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
||||||
import me.topchetoeu.jscript.exceptions.UncheckedException;
|
import me.topchetoeu.jscript.exceptions.UncheckedException;
|
||||||
import me.topchetoeu.jscript.exceptions.UncheckedIOException;
|
import me.topchetoeu.jscript.exceptions.UncheckedIOException;
|
||||||
import me.topchetoeu.jscript.json.JSON;
|
import me.topchetoeu.jscript.json.JSON;
|
||||||
import me.topchetoeu.jscript.json.JSONList;
|
import me.topchetoeu.jscript.json.JSONList;
|
||||||
import me.topchetoeu.jscript.json.JSONMap;
|
import me.topchetoeu.jscript.json.JSONMap;
|
||||||
|
|
||||||
public class DebugServer {
|
public class DebugServer {
|
||||||
public static String browserDisplayName = Metadata.name() + "/" + Metadata.version();
|
public static String browserDisplayName = Metadata.name() + "/" + Metadata.version();
|
||||||
|
|
||||||
public final HashMap<String, DebuggerProvider> targets = new HashMap<>();
|
public final HashMap<String, DebuggerProvider> targets = new HashMap<>();
|
||||||
|
|
||||||
private final byte[] favicon, index, protocol;
|
private final byte[] favicon, index, protocol;
|
||||||
private final Notifier connNotifier = new Notifier();
|
private final Notifier connNotifier = new Notifier();
|
||||||
|
|
||||||
private static void send(HttpRequest req, String val) throws IOException {
|
private static void send(HttpRequest req, String val) throws IOException {
|
||||||
req.writeResponse(200, "OK", "application/json", val.getBytes());
|
req.writeResponse(200, "OK", "application/json", val.getBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
// SILENCE JAVA
|
// SILENCE JAVA
|
||||||
private MessageDigest getDigestInstance() {
|
private MessageDigest getDigestInstance() {
|
||||||
try {
|
try {
|
||||||
return MessageDigest.getInstance("sha1");
|
return MessageDigest.getInstance("sha1");
|
||||||
}
|
}
|
||||||
catch (Throwable e) { throw new UncheckedException(e); }
|
catch (Throwable e) { throw new UncheckedException(e); }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Thread runAsync(Runnable func, String name) {
|
private static Thread runAsync(Runnable func, String name) {
|
||||||
var res = new Thread(func);
|
var res = new Thread(func);
|
||||||
res.setName(name);
|
res.setName(name);
|
||||||
res.start();
|
res.start();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handle(WebSocket ws, Debugger debugger) {
|
private void handle(WebSocket ws, Debugger debugger) {
|
||||||
WebSocketMessage raw;
|
WebSocketMessage raw;
|
||||||
|
|
||||||
debugger.connect();
|
debugger.connect();
|
||||||
|
|
||||||
while ((raw = ws.receive()) != null) {
|
while ((raw = ws.receive()) != null) {
|
||||||
if (raw.type != Type.Text) {
|
if (raw.type != Type.Text) {
|
||||||
ws.send(new V8Error("Expected a text message."));
|
ws.send(new V8Error("Expected a text message."));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
V8Message msg;
|
V8Message msg;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
msg = new V8Message(raw.textData());
|
msg = new V8Message(raw.textData());
|
||||||
}
|
}
|
||||||
catch (SyntaxException e) {
|
catch (SyntaxException e) {
|
||||||
ws.send(new V8Error(e.getMessage()));
|
ws.send(new V8Error(e.getMessage()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
switch (msg.name) {
|
switch (msg.name) {
|
||||||
case "Debugger.enable":
|
case "Debugger.enable":
|
||||||
connNotifier.next();
|
connNotifier.next();
|
||||||
debugger.enable(msg); continue;
|
debugger.enable(msg); continue;
|
||||||
case "Debugger.disable": debugger.disable(msg); continue;
|
case "Debugger.disable": debugger.disable(msg); continue;
|
||||||
|
|
||||||
case "Debugger.setBreakpointByUrl": debugger.setBreakpointByUrl(msg); continue;
|
case "Debugger.setBreakpointByUrl": debugger.setBreakpointByUrl(msg); continue;
|
||||||
case "Debugger.removeBreakpoint": debugger.removeBreakpoint(msg); continue;
|
case "Debugger.removeBreakpoint": debugger.removeBreakpoint(msg); continue;
|
||||||
case "Debugger.continueToLocation": debugger.continueToLocation(msg); continue;
|
case "Debugger.continueToLocation": debugger.continueToLocation(msg); continue;
|
||||||
|
|
||||||
case "Debugger.getScriptSource": debugger.getScriptSource(msg); continue;
|
case "Debugger.getScriptSource": debugger.getScriptSource(msg); continue;
|
||||||
case "Debugger.getPossibleBreakpoints": debugger.getPossibleBreakpoints(msg); continue;
|
case "Debugger.getPossibleBreakpoints": debugger.getPossibleBreakpoints(msg); continue;
|
||||||
|
|
||||||
case "Debugger.resume": debugger.resume(msg); continue;
|
case "Debugger.resume": debugger.resume(msg); continue;
|
||||||
case "Debugger.pause": debugger.pause(msg); continue;
|
case "Debugger.pause": debugger.pause(msg); continue;
|
||||||
|
|
||||||
case "Debugger.stepInto": debugger.stepInto(msg); continue;
|
case "Debugger.stepInto": debugger.stepInto(msg); continue;
|
||||||
case "Debugger.stepOut": debugger.stepOut(msg); continue;
|
case "Debugger.stepOut": debugger.stepOut(msg); continue;
|
||||||
case "Debugger.stepOver": debugger.stepOver(msg); continue;
|
case "Debugger.stepOver": debugger.stepOver(msg); continue;
|
||||||
|
|
||||||
case "Debugger.setPauseOnExceptions": debugger.setPauseOnExceptions(msg); continue;
|
case "Debugger.setPauseOnExceptions": debugger.setPauseOnExceptions(msg); continue;
|
||||||
case "Debugger.evaluateOnCallFrame": debugger.evaluateOnCallFrame(msg); continue;
|
case "Debugger.evaluateOnCallFrame": debugger.evaluateOnCallFrame(msg); continue;
|
||||||
|
|
||||||
case "Runtime.releaseObjectGroup": debugger.releaseObjectGroup(msg); continue;
|
case "Runtime.releaseObjectGroup": debugger.releaseObjectGroup(msg); continue;
|
||||||
case "Runtime.releaseObject": debugger.releaseObject(msg); continue;
|
case "Runtime.releaseObject": debugger.releaseObject(msg); continue;
|
||||||
case "Runtime.getProperties": debugger.getProperties(msg); continue;
|
case "Runtime.getProperties": debugger.getProperties(msg); continue;
|
||||||
case "Runtime.callFunctionOn": debugger.callFunctionOn(msg); continue;
|
case "Runtime.callFunctionOn": debugger.callFunctionOn(msg); continue;
|
||||||
// case "NodeWorker.enable": debugger.nodeWorkerEnable(msg); continue;
|
// case "NodeWorker.enable": debugger.nodeWorkerEnable(msg); continue;
|
||||||
case "Runtime.enable": debugger.runtimeEnable(msg); continue;
|
case "Runtime.enable": debugger.runtimeEnable(msg); continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
msg.name.startsWith("DOM.") ||
|
msg.name.startsWith("DOM.") ||
|
||||||
msg.name.startsWith("DOMDebugger.") ||
|
msg.name.startsWith("DOMDebugger.") ||
|
||||||
msg.name.startsWith("Emulation.") ||
|
msg.name.startsWith("Emulation.") ||
|
||||||
msg.name.startsWith("Input.") ||
|
msg.name.startsWith("Input.") ||
|
||||||
msg.name.startsWith("Network.") ||
|
msg.name.startsWith("Network.") ||
|
||||||
msg.name.startsWith("Page.")
|
msg.name.startsWith("Page.")
|
||||||
) ws.send(new V8Error("This isn't a browser..."));
|
) ws.send(new V8Error("This isn't a browser..."));
|
||||||
else ws.send(new V8Error("This API is not supported yet."));
|
else ws.send(new V8Error("This API is not supported yet."));
|
||||||
}
|
}
|
||||||
catch (Throwable e) {
|
catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
throw new UncheckedException(e);
|
throw new UncheckedException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debugger.disconnect();
|
debugger.disconnect();
|
||||||
}
|
}
|
||||||
private void onWsConnect(HttpRequest req, Socket socket, DebuggerProvider debuggerProvider) {
|
private void onWsConnect(HttpRequest req, Socket socket, DebuggerProvider debuggerProvider) {
|
||||||
var key = req.headers.get("sec-websocket-key");
|
var key = req.headers.get("sec-websocket-key");
|
||||||
|
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
req.writeResponse(
|
req.writeResponse(
|
||||||
426, "Upgrade Required", "text/txt",
|
426, "Upgrade Required", "text/txt",
|
||||||
"Expected a WS upgrade".getBytes()
|
"Expected a WS upgrade".getBytes()
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var resKey = Base64.getEncoder().encodeToString(getDigestInstance().digest(
|
var resKey = Base64.getEncoder().encodeToString(getDigestInstance().digest(
|
||||||
(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes()
|
(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes()
|
||||||
));
|
));
|
||||||
|
|
||||||
req.writeCode(101, "Switching Protocols");
|
req.writeCode(101, "Switching Protocols");
|
||||||
req.writeHeader("Connection", "Upgrade");
|
req.writeHeader("Connection", "Upgrade");
|
||||||
req.writeHeader("Sec-WebSocket-Accept", resKey);
|
req.writeHeader("Sec-WebSocket-Accept", resKey);
|
||||||
req.writeLastHeader("Upgrade", "WebSocket");
|
req.writeLastHeader("Upgrade", "WebSocket");
|
||||||
|
|
||||||
var ws = new WebSocket(socket);
|
var ws = new WebSocket(socket);
|
||||||
var debugger = debuggerProvider.getDebugger(ws, req);
|
var debugger = debuggerProvider.getDebugger(ws, req);
|
||||||
|
|
||||||
if (debugger == null) {
|
if (debugger == null) {
|
||||||
ws.close();
|
ws.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
runAsync(() -> {
|
runAsync(() -> {
|
||||||
try { handle(ws, debugger); }
|
try { handle(ws, debugger); }
|
||||||
catch (RuntimeException e) {
|
catch (RuntimeException e) {
|
||||||
ws.send(new V8Error(e.getMessage()));
|
ws.send(new V8Error(e.getMessage()));
|
||||||
}
|
}
|
||||||
finally { ws.close(); debugger.disconnect(); }
|
finally { ws.close(); debugger.disconnect(); }
|
||||||
}, "Debug Handler");
|
}, "Debug Handler");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void awaitConnection() {
|
public void awaitConnection() {
|
||||||
connNotifier.await();
|
connNotifier.await();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run(InetSocketAddress address) {
|
public void run(InetSocketAddress address) {
|
||||||
try {
|
try {
|
||||||
ServerSocket server = new ServerSocket();
|
ServerSocket server = new ServerSocket();
|
||||||
server.bind(address);
|
server.bind(address);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while (true) {
|
while (true) {
|
||||||
var socket = server.accept();
|
var socket = server.accept();
|
||||||
var req = HttpRequest.read(socket);
|
var req = HttpRequest.read(socket);
|
||||||
|
|
||||||
if (req == null) continue;
|
if (req == null) continue;
|
||||||
|
|
||||||
switch (req.path) {
|
switch (req.path) {
|
||||||
case "/json/version":
|
case "/json/version":
|
||||||
send(req, "{\"Browser\":\"" + browserDisplayName + "\",\"Protocol-Version\":\"1.1\"}");
|
send(req, "{\"Browser\":\"" + browserDisplayName + "\",\"Protocol-Version\":\"1.1\"}");
|
||||||
break;
|
break;
|
||||||
case "/json/list":
|
case "/json/list":
|
||||||
case "/json": {
|
case "/json": {
|
||||||
var res = new JSONList();
|
var res = new JSONList();
|
||||||
|
|
||||||
for (var el : targets.entrySet()) {
|
for (var el : targets.entrySet()) {
|
||||||
res.add(new JSONMap()
|
res.add(new JSONMap()
|
||||||
.set("description", "JScript debugger")
|
.set("description", "JScript debugger")
|
||||||
.set("favicon", "/favicon.ico")
|
.set("favicon", "/favicon.ico")
|
||||||
.set("id", el.getKey())
|
.set("id", el.getKey())
|
||||||
.set("type", "node")
|
.set("type", "node")
|
||||||
.set("webSocketDebuggerUrl", "ws://" + address.getHostString() + ":" + address.getPort() + "/" + el.getKey())
|
.set("webSocketDebuggerUrl", "ws://" + address.getHostString() + ":" + address.getPort() + "/" + el.getKey())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
send(req, JSON.stringify(res));
|
send(req, JSON.stringify(res));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "/json/protocol":
|
case "/json/protocol":
|
||||||
req.writeResponse(200, "OK", "application/json", protocol);
|
req.writeResponse(200, "OK", "application/json", protocol);
|
||||||
break;
|
break;
|
||||||
case "/json/new":
|
case "/json/new":
|
||||||
case "/json/activate":
|
case "/json/activate":
|
||||||
case "/json/close":
|
case "/json/close":
|
||||||
case "/devtools/inspector.html":
|
case "/devtools/inspector.html":
|
||||||
req.writeResponse(
|
req.writeResponse(
|
||||||
501, "Not Implemented", "text/txt",
|
501, "Not Implemented", "text/txt",
|
||||||
"This feature isn't (and probably won't be) implemented.".getBytes()
|
"This feature isn't (and probably won't be) implemented.".getBytes()
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "/":
|
case "/":
|
||||||
case "/index.html":
|
case "/index.html":
|
||||||
req.writeResponse(200, "OK", "text/html", index);
|
req.writeResponse(200, "OK", "text/html", index);
|
||||||
break;
|
break;
|
||||||
case "/favicon.ico":
|
case "/favicon.ico":
|
||||||
req.writeResponse(200, "OK", "image/png", favicon);
|
req.writeResponse(200, "OK", "image/png", favicon);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (req.path.length() > 1 && targets.containsKey(req.path.substring(1))) {
|
if (req.path.length() > 1 && targets.containsKey(req.path.substring(1))) {
|
||||||
onWsConnect(req, socket, targets.get(req.path.substring(1)));
|
onWsConnect(req, socket, targets.get(req.path.substring(1)));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally { server.close(); }
|
finally { server.close(); }
|
||||||
}
|
}
|
||||||
catch (IOException e) { throw new UncheckedIOException(e); }
|
catch (IOException e) { throw new UncheckedIOException(e); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public Thread start(InetSocketAddress address, boolean daemon) {
|
public Thread start(InetSocketAddress address, boolean daemon) {
|
||||||
var res = new Thread(() -> run(address), "Debug Server");
|
var res = new Thread(() -> run(address), "Debug Server");
|
||||||
res.setDaemon(daemon);
|
res.setDaemon(daemon);
|
||||||
res.start();
|
res.start();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DebugServer() {
|
public DebugServer() {
|
||||||
try {
|
try {
|
||||||
this.favicon = Reading.resourceToStream("debugger/favicon.png").readAllBytes();
|
this.favicon = Reading.resourceToStream("debugger/favicon.png").readAllBytes();
|
||||||
this.protocol = Reading.resourceToStream("debugger/protocol.json").readAllBytes();
|
this.protocol = Reading.resourceToStream("debugger/protocol.json").readAllBytes();
|
||||||
this.index = Reading.resourceToString("debugger/index.html")
|
this.index = Reading.resourceToString("debugger/index.html")
|
||||||
.replace("${NAME}", Metadata.name())
|
.replace("${NAME}", Metadata.name())
|
||||||
.replace("${VERSION}", Metadata.version())
|
.replace("${VERSION}", Metadata.version())
|
||||||
.replace("${AUTHOR}", Metadata.author())
|
.replace("${AUTHOR}", Metadata.author())
|
||||||
.getBytes();
|
.getBytes();
|
||||||
}
|
}
|
||||||
catch (IOException e) { throw new UncheckedIOException(e); }
|
catch (IOException e) { throw new UncheckedIOException(e); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,317 +1,318 @@
|
|||||||
package me.topchetoeu.jscript.engine.frame;
|
package me.topchetoeu.jscript.engine.frame;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.scope.LocalScope;
|
import me.topchetoeu.jscript.engine.scope.LocalScope;
|
||||||
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.engine.values.ScopeValue;
|
import me.topchetoeu.jscript.engine.values.ScopeValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.exceptions.InterruptException;
|
import me.topchetoeu.jscript.exceptions.InterruptException;
|
||||||
|
|
||||||
public class CodeFrame {
|
public class CodeFrame {
|
||||||
public static enum TryState {
|
public static enum TryState {
|
||||||
TRY,
|
TRY,
|
||||||
CATCH,
|
CATCH,
|
||||||
FINALLY,
|
FINALLY,
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class TryCtx {
|
public static class TryCtx {
|
||||||
public final int start, end, catchStart, finallyStart;
|
public final int start, end, catchStart, finallyStart;
|
||||||
public final int restoreStackPtr;
|
public final int restoreStackPtr;
|
||||||
public final TryState state;
|
public final TryState state;
|
||||||
public final EngineException error;
|
public final EngineException error;
|
||||||
public final PendingResult result;
|
public final PendingResult result;
|
||||||
|
|
||||||
public boolean hasCatch() { return catchStart >= 0; }
|
public boolean hasCatch() { return catchStart >= 0; }
|
||||||
public boolean hasFinally() { return finallyStart >= 0; }
|
public boolean hasFinally() { return finallyStart >= 0; }
|
||||||
|
|
||||||
public boolean inBounds(int ptr) {
|
public boolean inBounds(int ptr) {
|
||||||
return ptr >= start && ptr < end;
|
return ptr >= start && ptr < end;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TryCtx _catch(EngineException e) {
|
public TryCtx _catch(EngineException e) {
|
||||||
if (error != null) e.setCause(error);
|
if (error != null) e.setCause(error);
|
||||||
return new TryCtx(TryState.CATCH, e, result, restoreStackPtr, start, end, -1, finallyStart);
|
return new TryCtx(TryState.CATCH, e, result, restoreStackPtr, start, end, -1, finallyStart);
|
||||||
}
|
}
|
||||||
public TryCtx _finally(PendingResult res) {
|
public TryCtx _finally(PendingResult res) {
|
||||||
return new TryCtx(TryState.FINALLY, error, res, restoreStackPtr, start, end, -1, -1);
|
return new TryCtx(TryState.FINALLY, error, res, restoreStackPtr, start, end, -1, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TryCtx(TryState state, EngineException err, PendingResult res, int stackPtr, int start, int end, int catchStart, int finallyStart) {
|
public TryCtx(TryState state, EngineException err, PendingResult res, int stackPtr, int start, int end, int catchStart, int finallyStart) {
|
||||||
this.catchStart = catchStart;
|
this.catchStart = catchStart;
|
||||||
this.finallyStart = finallyStart;
|
this.finallyStart = finallyStart;
|
||||||
this.restoreStackPtr = stackPtr;
|
this.restoreStackPtr = stackPtr;
|
||||||
this.result = res == null ? PendingResult.ofNone() : res;
|
this.result = res == null ? PendingResult.ofNone() : res;
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.start = start;
|
this.start = start;
|
||||||
this.end = end;
|
this.end = end;
|
||||||
this.error = err;
|
this.error = err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class PendingResult {
|
private static class PendingResult {
|
||||||
public final boolean isReturn, isJump, isThrow;
|
public final boolean isReturn, isJump, isThrow;
|
||||||
public final Object value;
|
public final Object value;
|
||||||
public final EngineException error;
|
public final EngineException error;
|
||||||
public final int ptr;
|
public final int ptr;
|
||||||
public final Instruction instruction;
|
public final Instruction instruction;
|
||||||
|
|
||||||
private PendingResult(Instruction instr, boolean isReturn, boolean isJump, boolean isThrow, Object value, EngineException error, int ptr) {
|
private PendingResult(Instruction instr, boolean isReturn, boolean isJump, boolean isThrow, Object value, EngineException error, int ptr) {
|
||||||
this.instruction = instr;
|
this.instruction = instr;
|
||||||
this.isReturn = isReturn;
|
this.isReturn = isReturn;
|
||||||
this.isJump = isJump;
|
this.isJump = isJump;
|
||||||
this.isThrow = isThrow;
|
this.isThrow = isThrow;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.error = error;
|
this.error = error;
|
||||||
this.ptr = ptr;
|
this.ptr = ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PendingResult ofNone() {
|
public static PendingResult ofNone() {
|
||||||
return new PendingResult(null, false, false, false, null, null, 0);
|
return new PendingResult(null, false, false, false, null, null, 0);
|
||||||
}
|
}
|
||||||
public static PendingResult ofReturn(Object value, Instruction instr) {
|
public static PendingResult ofReturn(Object value, Instruction instr) {
|
||||||
return new PendingResult(instr, true, false, false, value, null, 0);
|
return new PendingResult(instr, true, false, false, value, null, 0);
|
||||||
}
|
}
|
||||||
public static PendingResult ofThrow(EngineException error, Instruction instr) {
|
public static PendingResult ofThrow(EngineException error, Instruction instr) {
|
||||||
return new PendingResult(instr, false, false, true, null, error, 0);
|
return new PendingResult(instr, false, false, true, null, error, 0);
|
||||||
}
|
}
|
||||||
public static PendingResult ofJump(int codePtr, Instruction instr) {
|
public static PendingResult ofJump(int codePtr, Instruction instr) {
|
||||||
return new PendingResult(instr, false, true, false, null, null, codePtr);
|
return new PendingResult(instr, false, true, false, null, null, codePtr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final LocalScope scope;
|
public final LocalScope scope;
|
||||||
public final Object thisArg;
|
public final Object thisArg;
|
||||||
public final Object[] args;
|
public final Object[] args;
|
||||||
public final Stack<TryCtx> tryStack = new Stack<>();
|
public final Stack<TryCtx> tryStack = new Stack<>();
|
||||||
public final CodeFunction function;
|
public final CodeFunction function;
|
||||||
public Object[] stack = new Object[32];
|
public Object[] stack = new Object[32];
|
||||||
public int stackPtr = 0;
|
public int stackPtr = 0;
|
||||||
public int codePtr = 0;
|
public int codePtr = 0;
|
||||||
public boolean jumpFlag = false, popTryFlag = false;
|
public boolean jumpFlag = false, popTryFlag = false;
|
||||||
private Location prevLoc = null;
|
private Location prevLoc = null;
|
||||||
|
|
||||||
public ObjectValue getLocalScope(Context ctx, boolean props) {
|
public ObjectValue getLocalScope(Context ctx, boolean props) {
|
||||||
var names = new String[scope.locals.length];
|
var names = new String[scope.locals.length];
|
||||||
|
|
||||||
for (int i = 0; i < scope.locals.length; i++) {
|
for (int i = 0; i < scope.locals.length; i++) {
|
||||||
var name = "local_" + (i - 2);
|
var name = "local_" + (i - 2);
|
||||||
|
|
||||||
if (i == 0) name = "this";
|
if (i == 0) name = "this";
|
||||||
else if (i == 1) name = "arguments";
|
else if (i == 1) name = "arguments";
|
||||||
else if (i < function.localNames.length) name = function.localNames[i];
|
else if (i < function.localNames.length) name = function.localNames[i];
|
||||||
|
|
||||||
names[i] = name;
|
names[i] = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ScopeValue(scope.locals, names);
|
return new ScopeValue(scope.locals, names);
|
||||||
}
|
}
|
||||||
public ObjectValue getCaptureScope(Context ctx, boolean props) {
|
public ObjectValue getCaptureScope(Context ctx, boolean props) {
|
||||||
var names = new String[scope.captures.length];
|
var names = new String[scope.captures.length];
|
||||||
|
|
||||||
for (int i = 0; i < scope.captures.length; i++) {
|
for (int i = 0; i < scope.captures.length; i++) {
|
||||||
var name = "capture_" + (i - 2);
|
var name = "capture_" + (i - 2);
|
||||||
if (i < function.captureNames.length) name = function.captureNames[i];
|
if (i < function.captureNames.length) name = function.captureNames[i];
|
||||||
names[i] = name;
|
names[i] = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ScopeValue(scope.captures, names);
|
return new ScopeValue(scope.captures, names);
|
||||||
}
|
}
|
||||||
public ObjectValue getValStackScope(Context ctx) {
|
public ObjectValue getValStackScope(Context ctx) {
|
||||||
return new ObjectValue() {
|
return new ObjectValue() {
|
||||||
@Override
|
@Override
|
||||||
protected Object getField(Context ctx, Object key) {
|
protected Object getField(Context ctx, Object key) {
|
||||||
var i = (int)Values.toNumber(ctx, key);
|
var i = (int)Values.toNumber(ctx, key);
|
||||||
if (i < 0 || i >= stackPtr) return null;
|
if (i < 0 || i >= stackPtr) return null;
|
||||||
else return stack[i];
|
else return stack[i];
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
protected boolean hasField(Context ctx, Object key) {
|
protected boolean hasField(Context ctx, Object key) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public List<Object> keys(boolean includeNonEnumerable) {
|
public List<Object> keys(boolean includeNonEnumerable) {
|
||||||
var res = super.keys(includeNonEnumerable);
|
var res = super.keys(includeNonEnumerable);
|
||||||
for (var i = 0; i < stackPtr; i++) res.add(i);
|
for (var i = 0; i < stackPtr; i++) res.add(i);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addTry(int start, int end, int catchStart, int finallyStart) {
|
public void addTry(int start, int end, int catchStart, int finallyStart) {
|
||||||
var err = tryStack.empty() ? null : tryStack.peek().error;
|
var err = tryStack.empty() ? null : tryStack.peek().error;
|
||||||
var res = new TryCtx(TryState.TRY, err, null, stackPtr, start, end, catchStart, finallyStart);
|
var res = new TryCtx(TryState.TRY, err, null, stackPtr, start, end, catchStart, finallyStart);
|
||||||
|
|
||||||
tryStack.add(res);
|
tryStack.add(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object peek() {
|
public Object peek() {
|
||||||
return peek(0);
|
return peek(0);
|
||||||
}
|
}
|
||||||
public Object peek(int offset) {
|
public Object peek(int offset) {
|
||||||
if (stackPtr <= offset) return null;
|
if (stackPtr <= offset) return null;
|
||||||
else return stack[stackPtr - 1 - offset];
|
else return stack[stackPtr - 1 - offset];
|
||||||
}
|
}
|
||||||
public Object pop() {
|
public Object pop() {
|
||||||
if (stackPtr == 0) return null;
|
if (stackPtr == 0) return null;
|
||||||
return stack[--stackPtr];
|
return stack[--stackPtr];
|
||||||
}
|
}
|
||||||
public Object[] take(int n) {
|
public Object[] take(int n) {
|
||||||
int srcI = stackPtr - n;
|
int srcI = stackPtr - n;
|
||||||
if (srcI < 0) srcI = 0;
|
if (srcI < 0) srcI = 0;
|
||||||
|
|
||||||
int dstI = n + srcI - stackPtr;
|
int dstI = n + srcI - stackPtr;
|
||||||
int copyN = stackPtr - srcI;
|
int copyN = stackPtr - srcI;
|
||||||
|
|
||||||
Object[] res = new Object[n];
|
Object[] res = new Object[n];
|
||||||
System.arraycopy(stack, srcI, res, dstI, copyN);
|
System.arraycopy(stack, srcI, res, dstI, copyN);
|
||||||
stackPtr -= copyN;
|
stackPtr -= copyN;
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
public void push(Context ctx, Object val) {
|
public void push(Context ctx, Object val) {
|
||||||
if (stack.length <= stackPtr) {
|
if (stack.length <= stackPtr) {
|
||||||
var newStack = new Object[stack.length * 2];
|
var newStack = new Object[stack.length * 2];
|
||||||
System.arraycopy(stack, 0, newStack, 0, stack.length);
|
System.arraycopy(stack, 0, newStack, 0, stack.length);
|
||||||
stack = newStack;
|
stack = newStack;
|
||||||
}
|
}
|
||||||
stack[stackPtr++] = Values.normalize(ctx, val);
|
stack[stackPtr++] = Values.normalize(ctx, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object next(Context ctx, Object value, Object returnValue, EngineException error) {
|
public Object next(Context ctx, Object value, Object returnValue, EngineException error) {
|
||||||
if (value != Runners.NO_RETURN) push(ctx, value);
|
if (value != Runners.NO_RETURN) push(ctx, value);
|
||||||
|
|
||||||
Instruction instr = null;
|
Instruction instr = null;
|
||||||
if (codePtr >= 0 && codePtr < function.body.length) instr = function.body[codePtr];
|
if (codePtr >= 0 && codePtr < function.body.length) instr = function.body[codePtr];
|
||||||
|
|
||||||
if (returnValue == Runners.NO_RETURN && error == null) {
|
if (returnValue == Runners.NO_RETURN && error == null) {
|
||||||
try {
|
try {
|
||||||
if (Thread.currentThread().isInterrupted()) throw new InterruptException();
|
if (Thread.currentThread().isInterrupted()) throw new InterruptException();
|
||||||
|
|
||||||
if (instr == null) returnValue = null;
|
if (instr == null) returnValue = null;
|
||||||
else {
|
else {
|
||||||
// System.out.println(instr + "@" + instr.location);
|
// System.out.println(instr + "@" + instr.location);
|
||||||
ctx.engine.onInstruction(ctx, this, instr, Runners.NO_RETURN, null, false);
|
ctx.engine.onInstruction(ctx, this, instr, Runners.NO_RETURN, null, false);
|
||||||
|
|
||||||
if (instr.location != null) prevLoc = instr.location;
|
if (instr.location != null) prevLoc = instr.location;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.jumpFlag = this.popTryFlag = false;
|
this.jumpFlag = this.popTryFlag = false;
|
||||||
returnValue = Runners.exec(ctx, instr, this);
|
returnValue = Runners.exec(ctx, instr, this);
|
||||||
}
|
}
|
||||||
catch (EngineException e) {
|
catch (EngineException e) {
|
||||||
error = e.add(ctx, function.name, prevLoc);
|
error = e.add(ctx, function.name, prevLoc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (EngineException e) { error = e; }
|
catch (EngineException e) { error = e; }
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!tryStack.empty()) {
|
while (!tryStack.empty()) {
|
||||||
var tryCtx = tryStack.peek();
|
var tryCtx = tryStack.peek();
|
||||||
TryCtx newCtx = null;
|
TryCtx newCtx = null;
|
||||||
|
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
if (tryCtx.hasCatch()) newCtx = tryCtx._catch(error);
|
if (tryCtx.hasCatch()) newCtx = tryCtx._catch(error);
|
||||||
else if (tryCtx.hasFinally()) newCtx = tryCtx._finally(PendingResult.ofThrow(error, instr));
|
else if (tryCtx.hasFinally()) newCtx = tryCtx._finally(PendingResult.ofThrow(error, instr));
|
||||||
}
|
}
|
||||||
else if (returnValue != Runners.NO_RETURN) {
|
else if (returnValue != Runners.NO_RETURN) {
|
||||||
if (tryCtx.hasFinally()) newCtx = tryCtx._finally(PendingResult.ofReturn(returnValue, instr));
|
if (tryCtx.hasFinally()) newCtx = tryCtx._finally(PendingResult.ofReturn(returnValue, instr));
|
||||||
}
|
}
|
||||||
else if (jumpFlag && !tryCtx.inBounds(codePtr)) {
|
else if (jumpFlag && !tryCtx.inBounds(codePtr)) {
|
||||||
if (tryCtx.hasFinally()) newCtx = tryCtx._finally(PendingResult.ofJump(codePtr, instr));
|
if (tryCtx.hasFinally()) newCtx = tryCtx._finally(PendingResult.ofJump(codePtr, instr));
|
||||||
}
|
}
|
||||||
else if (!this.popTryFlag) newCtx = tryCtx;
|
else if (!this.popTryFlag) newCtx = tryCtx;
|
||||||
|
|
||||||
if (newCtx != null) {
|
if (newCtx != null) {
|
||||||
if (newCtx != tryCtx) {
|
if (newCtx != tryCtx) {
|
||||||
switch (newCtx.state) {
|
switch (newCtx.state) {
|
||||||
case CATCH:
|
case CATCH:
|
||||||
if (tryCtx.state != TryState.CATCH) scope.catchVars.add(new ValueVariable(false, error.value));
|
if (tryCtx.state != TryState.CATCH) scope.catchVars.add(new ValueVariable(false, error.value));
|
||||||
codePtr = tryCtx.catchStart;
|
codePtr = tryCtx.catchStart;
|
||||||
stackPtr = tryCtx.restoreStackPtr;
|
stackPtr = tryCtx.restoreStackPtr;
|
||||||
break;
|
break;
|
||||||
case FINALLY:
|
case FINALLY:
|
||||||
if (tryCtx.state == TryState.CATCH) scope.catchVars.remove(scope.catchVars.size() - 1);
|
if (tryCtx.state == TryState.CATCH) scope.catchVars.remove(scope.catchVars.size() - 1);
|
||||||
codePtr = tryCtx.finallyStart;
|
codePtr = tryCtx.finallyStart;
|
||||||
stackPtr = tryCtx.restoreStackPtr;
|
stackPtr = tryCtx.restoreStackPtr;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
tryStack.pop();
|
tryStack.pop();
|
||||||
tryStack.push(newCtx);
|
tryStack.push(newCtx);
|
||||||
}
|
}
|
||||||
error = null;
|
error = null;
|
||||||
returnValue = Runners.NO_RETURN;
|
returnValue = Runners.NO_RETURN;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (tryCtx.state == TryState.CATCH) scope.catchVars.remove(scope.catchVars.size() - 1);
|
popTryFlag = false;
|
||||||
|
if (tryCtx.state == TryState.CATCH) scope.catchVars.remove(scope.catchVars.size() - 1);
|
||||||
if (tryCtx.state != TryState.FINALLY && tryCtx.hasFinally()) {
|
|
||||||
codePtr = tryCtx.finallyStart;
|
if (tryCtx.state != TryState.FINALLY && tryCtx.hasFinally()) {
|
||||||
stackPtr = tryCtx.restoreStackPtr;
|
codePtr = tryCtx.finallyStart;
|
||||||
tryStack.pop();
|
stackPtr = tryCtx.restoreStackPtr;
|
||||||
tryStack.push(tryCtx._finally(null));
|
tryStack.pop();
|
||||||
break;
|
tryStack.push(tryCtx._finally(null));
|
||||||
}
|
break;
|
||||||
else {
|
}
|
||||||
tryStack.pop();
|
else {
|
||||||
codePtr = tryCtx.end;
|
tryStack.pop();
|
||||||
if (tryCtx.result.instruction != null) instr = tryCtx.result.instruction;
|
codePtr = tryCtx.end;
|
||||||
if (tryCtx.result.isJump) {
|
if (tryCtx.result.instruction != null) instr = tryCtx.result.instruction;
|
||||||
codePtr = tryCtx.result.ptr;
|
if (tryCtx.result.isJump) {
|
||||||
jumpFlag = true;
|
codePtr = tryCtx.result.ptr;
|
||||||
}
|
jumpFlag = true;
|
||||||
if (tryCtx.result.isReturn) returnValue = tryCtx.result.value;
|
}
|
||||||
if (tryCtx.result.isThrow) {
|
if (tryCtx.result.isReturn) returnValue = tryCtx.result.value;
|
||||||
error = tryCtx.result.error;
|
if (tryCtx.result.isThrow) {
|
||||||
}
|
error = tryCtx.result.error;
|
||||||
if (error != null) error.setCause(tryCtx.error);
|
}
|
||||||
continue;
|
if (error != null) error.setCause(tryCtx.error);
|
||||||
}
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (error != null) {
|
|
||||||
var caught = false;
|
if (error != null) {
|
||||||
|
var caught = false;
|
||||||
for (var frame : ctx.frames()) {
|
|
||||||
for (var tryCtx : frame.tryStack) {
|
for (var frame : ctx.frames()) {
|
||||||
if (tryCtx.state == TryState.TRY) caught = true;
|
for (var tryCtx : frame.tryStack) {
|
||||||
}
|
if (tryCtx.state == TryState.TRY) caught = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
ctx.engine.onInstruction(ctx, this, instr, null, error, caught);
|
|
||||||
throw error;
|
ctx.engine.onInstruction(ctx, this, instr, null, error, caught);
|
||||||
}
|
throw error;
|
||||||
if (returnValue != Runners.NO_RETURN) {
|
}
|
||||||
ctx.engine.onInstruction(ctx, this, instr, returnValue, null, false);
|
if (returnValue != Runners.NO_RETURN) {
|
||||||
return returnValue;
|
ctx.engine.onInstruction(ctx, this, instr, returnValue, null, false);
|
||||||
}
|
return returnValue;
|
||||||
|
}
|
||||||
return Runners.NO_RETURN;
|
|
||||||
}
|
return Runners.NO_RETURN;
|
||||||
|
}
|
||||||
public CodeFrame(Context ctx, Object thisArg, Object[] args, CodeFunction func) {
|
|
||||||
this.args = args.clone();
|
public CodeFrame(Context ctx, Object thisArg, Object[] args, CodeFunction func) {
|
||||||
this.scope = new LocalScope(func.localsN, func.captures);
|
this.args = args.clone();
|
||||||
this.scope.get(0).set(null, thisArg);
|
this.scope = new LocalScope(func.localsN, func.captures);
|
||||||
var argsObj = new ArrayValue();
|
this.scope.get(0).set(null, thisArg);
|
||||||
for (var i = 0; i < args.length; i++) {
|
var argsObj = new ArrayValue();
|
||||||
argsObj.set(ctx, i, args[i]);
|
for (var i = 0; i < args.length; i++) {
|
||||||
}
|
argsObj.set(ctx, i, args[i]);
|
||||||
this.scope.get(1).value = argsObj;
|
}
|
||||||
|
this.scope.get(1).value = argsObj;
|
||||||
this.thisArg = thisArg;
|
|
||||||
this.function = func;
|
this.thisArg = thisArg;
|
||||||
}
|
this.function = func;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package me.topchetoeu.jscript.engine.frame;
|
package me.topchetoeu.jscript.engine.frame;
|
||||||
|
|
||||||
public enum ConvertHint {
|
public enum ConvertHint {
|
||||||
TOSTRING,
|
TOSTRING,
|
||||||
VALUEOF,
|
VALUEOF,
|
||||||
}
|
}
|
@ -1,9 +1,9 @@
|
|||||||
package me.topchetoeu.jscript.engine.frame;
|
package me.topchetoeu.jscript.engine.frame;
|
||||||
|
|
||||||
public class InstructionResult {
|
public class InstructionResult {
|
||||||
public final Object value;
|
public final Object value;
|
||||||
|
|
||||||
public InstructionResult(Object value) {
|
public InstructionResult(Object value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,350 +1,350 @@
|
|||||||
package me.topchetoeu.jscript.engine.frame;
|
package me.topchetoeu.jscript.engine.frame;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.Engine;
|
import me.topchetoeu.jscript.engine.Engine;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.engine.Operation;
|
||||||
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Symbol;
|
import me.topchetoeu.jscript.engine.values.Symbol;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
|
|
||||||
public class Runners {
|
public class Runners {
|
||||||
public static final Object NO_RETURN = new Object();
|
public static final Object NO_RETURN = new Object();
|
||||||
|
|
||||||
public static Object execReturn(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execReturn(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
return frame.pop();
|
return frame.pop();
|
||||||
}
|
}
|
||||||
public static Object execThrow(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execThrow(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
throw new EngineException(frame.pop());
|
throw new EngineException(frame.pop());
|
||||||
}
|
}
|
||||||
public static Object execThrowSyntax(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execThrowSyntax(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
throw EngineException.ofSyntax((String)instr.get(0));
|
throw EngineException.ofSyntax((String)instr.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object execCall(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execCall(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
var callArgs = frame.take(instr.get(0));
|
var callArgs = frame.take(instr.get(0));
|
||||||
var func = frame.pop();
|
var func = frame.pop();
|
||||||
var thisArg = frame.pop();
|
var thisArg = frame.pop();
|
||||||
|
|
||||||
frame.push(ctx, Values.call(ctx, func, thisArg, callArgs));
|
frame.push(ctx, Values.call(ctx, func, thisArg, callArgs));
|
||||||
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execCallNew(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execCallNew(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
var callArgs = frame.take(instr.get(0));
|
var callArgs = frame.take(instr.get(0));
|
||||||
var funcObj = frame.pop();
|
var funcObj = frame.pop();
|
||||||
|
|
||||||
frame.push(ctx, Values.callNew(ctx, funcObj, callArgs));
|
frame.push(ctx, Values.callNew(ctx, funcObj, callArgs));
|
||||||
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object execMakeVar(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execMakeVar(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
var name = (String)instr.get(0);
|
var name = (String)instr.get(0);
|
||||||
ctx.environment().global.define(name);
|
ctx.environment().global.define(name);
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execDefProp(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execDefProp(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
var setter = frame.pop();
|
var setter = frame.pop();
|
||||||
var getter = frame.pop();
|
var getter = frame.pop();
|
||||||
var name = frame.pop();
|
var name = frame.pop();
|
||||||
var obj = frame.pop();
|
var obj = frame.pop();
|
||||||
|
|
||||||
if (getter != null && !Values.isFunction(getter)) throw EngineException.ofType("Getter must be a function or undefined.");
|
if (getter != null && !Values.isFunction(getter)) throw EngineException.ofType("Getter must be a function or undefined.");
|
||||||
if (setter != null && !Values.isFunction(setter)) throw EngineException.ofType("Setter must be a function or undefined.");
|
if (setter != null && !Values.isFunction(setter)) throw EngineException.ofType("Setter must be a function or undefined.");
|
||||||
if (!Values.isObject(obj)) throw EngineException.ofType("Property apply target must be an object.");
|
if (!Values.isObject(obj)) throw EngineException.ofType("Property apply target must be an object.");
|
||||||
Values.object(obj).defineProperty(ctx, name, Values.function(getter), Values.function(setter), false, false);
|
Values.object(obj).defineProperty(ctx, name, Values.function(getter), Values.function(setter), false, false);
|
||||||
|
|
||||||
frame.push(ctx, obj);
|
frame.push(ctx, obj);
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execInstanceof(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execInstanceof(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
var type = frame.pop();
|
var type = frame.pop();
|
||||||
var obj = frame.pop();
|
var obj = frame.pop();
|
||||||
|
|
||||||
if (!Values.isPrimitive(type)) {
|
if (!Values.isPrimitive(type)) {
|
||||||
var proto = Values.getMember(ctx, type, "prototype");
|
var proto = Values.getMember(ctx, type, "prototype");
|
||||||
frame.push(ctx, Values.isInstanceOf(ctx, obj, proto));
|
frame.push(ctx, Values.isInstanceOf(ctx, obj, proto));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
frame.push(ctx, false);
|
frame.push(ctx, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execKeys(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execKeys(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
var val = frame.pop();
|
var val = frame.pop();
|
||||||
|
|
||||||
var members = Values.getMembers(ctx, val, false, false);
|
var members = Values.getMembers(ctx, val, false, false);
|
||||||
Collections.reverse(members);
|
Collections.reverse(members);
|
||||||
|
|
||||||
frame.push(ctx, null);
|
frame.push(ctx, null);
|
||||||
|
|
||||||
for (var el : members) {
|
for (var el : members) {
|
||||||
if (el instanceof Symbol) continue;
|
if (el instanceof Symbol) continue;
|
||||||
var obj = new ObjectValue();
|
var obj = new ObjectValue();
|
||||||
obj.defineProperty(ctx, "value", el);
|
obj.defineProperty(ctx, "value", el);
|
||||||
frame.push(ctx, obj);
|
frame.push(ctx, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object execTryStart(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execTryStart(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
int start = frame.codePtr + 1;
|
int start = frame.codePtr + 1;
|
||||||
int catchStart = (int)instr.get(0);
|
int catchStart = (int)instr.get(0);
|
||||||
int finallyStart = (int)instr.get(1);
|
int finallyStart = (int)instr.get(1);
|
||||||
if (finallyStart >= 0) finallyStart += start;
|
if (finallyStart >= 0) finallyStart += start;
|
||||||
if (catchStart >= 0) catchStart += start;
|
if (catchStart >= 0) catchStart += start;
|
||||||
int end = (int)instr.get(2) + start;
|
int end = (int)instr.get(2) + start;
|
||||||
frame.addTry(start, end, catchStart, finallyStart);
|
frame.addTry(start, end, catchStart, finallyStart);
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execTryEnd(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execTryEnd(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
frame.popTryFlag = true;
|
frame.popTryFlag = true;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object execDup(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execDup(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
int count = instr.get(0);
|
int count = instr.get(0);
|
||||||
|
|
||||||
for (var i = 0; i < count; i++) {
|
for (var i = 0; i < count; i++) {
|
||||||
frame.push(ctx, frame.peek(count - 1));
|
frame.push(ctx, frame.peek(count - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execLoadUndefined(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execLoadUndefined(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
frame.push(ctx, null);
|
frame.push(ctx, null);
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execLoadValue(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execLoadValue(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
frame.push(ctx, instr.get(0));
|
frame.push(ctx, instr.get(0));
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execLoadVar(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execLoadVar(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
var i = instr.get(0);
|
var i = instr.get(0);
|
||||||
|
|
||||||
if (i instanceof String) frame.push(ctx, ctx.environment().global.get(ctx, (String)i));
|
if (i instanceof String) frame.push(ctx, ctx.environment().global.get(ctx, (String)i));
|
||||||
else frame.push(ctx, frame.scope.get((int)i).get(ctx));
|
else frame.push(ctx, frame.scope.get((int)i).get(ctx));
|
||||||
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execLoadObj(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execLoadObj(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
frame.push(ctx, new ObjectValue());
|
frame.push(ctx, new ObjectValue());
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execLoadGlob(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execLoadGlob(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
frame.push(ctx, ctx.environment().global.obj);
|
frame.push(ctx, ctx.environment().global.obj);
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execLoadArr(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execLoadArr(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
var res = new ArrayValue();
|
var res = new ArrayValue();
|
||||||
res.setSize(instr.get(0));
|
res.setSize(instr.get(0));
|
||||||
frame.push(ctx, res);
|
frame.push(ctx, res);
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execLoadFunc(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execLoadFunc(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
long id = (Long)instr.get(0);
|
long id = (Long)instr.get(0);
|
||||||
var captures = new ValueVariable[instr.params.length - 1];
|
var captures = new ValueVariable[instr.params.length - 1];
|
||||||
|
|
||||||
for (var i = 1; i < instr.params.length; i++) {
|
for (var i = 1; i < instr.params.length; i++) {
|
||||||
captures[i - 1] = frame.scope.get(instr.get(i));
|
captures[i - 1] = frame.scope.get(instr.get(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
var func = new CodeFunction(ctx.environment(), "", Engine.functions.get(id), captures);
|
var func = new CodeFunction(ctx.environment(), "", Engine.functions.get(id), captures);
|
||||||
|
|
||||||
frame.push(ctx, func);
|
frame.push(ctx, func);
|
||||||
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execLoadMember(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execLoadMember(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
var key = frame.pop();
|
var key = frame.pop();
|
||||||
var obj = frame.pop();
|
var obj = frame.pop();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
frame.push(ctx, Values.getMember(ctx, obj, key));
|
frame.push(ctx, Values.getMember(ctx, obj, key));
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException e) {
|
catch (IllegalArgumentException e) {
|
||||||
throw EngineException.ofType(e.getMessage());
|
throw EngineException.ofType(e.getMessage());
|
||||||
}
|
}
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execLoadKeyMember(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execLoadKeyMember(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
frame.push(ctx, instr.get(0));
|
frame.push(ctx, instr.get(0));
|
||||||
return execLoadMember(ctx, instr, frame);
|
return execLoadMember(ctx, instr, frame);
|
||||||
}
|
}
|
||||||
public static Object execLoadRegEx(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execLoadRegEx(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
frame.push(ctx, ctx.environment().regexConstructor.call(ctx, null, instr.get(0), instr.get(1)));
|
frame.push(ctx, ctx.environment().regexConstructor.call(ctx, null, instr.get(0), instr.get(1)));
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object execDiscard(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execDiscard(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
frame.pop();
|
frame.pop();
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execStoreMember(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execStoreMember(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
var val = frame.pop();
|
var val = frame.pop();
|
||||||
var key = frame.pop();
|
var key = frame.pop();
|
||||||
var obj = frame.pop();
|
var obj = frame.pop();
|
||||||
|
|
||||||
if (!Values.setMember(ctx, obj, key, val)) throw EngineException.ofSyntax("Can't set member '" + key + "'.");
|
if (!Values.setMember(ctx, obj, key, val)) throw EngineException.ofSyntax("Can't set member '" + key + "'.");
|
||||||
if ((boolean)instr.get(0)) frame.push(ctx, val);
|
if ((boolean)instr.get(0)) frame.push(ctx, val);
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execStoreVar(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execStoreVar(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
var val = (boolean)instr.get(1) ? frame.peek() : frame.pop();
|
var val = (boolean)instr.get(1) ? frame.peek() : frame.pop();
|
||||||
var i = instr.get(0);
|
var i = instr.get(0);
|
||||||
|
|
||||||
if (i instanceof String) ctx.environment().global.set(ctx, (String)i, val);
|
if (i instanceof String) ctx.environment().global.set(ctx, (String)i, val);
|
||||||
else frame.scope.get((int)i).set(ctx, val);
|
else frame.scope.get((int)i).set(ctx, val);
|
||||||
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execStoreSelfFunc(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execStoreSelfFunc(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
frame.scope.locals[(int)instr.get(0)].set(ctx, frame.function);
|
frame.scope.locals[(int)instr.get(0)].set(ctx, frame.function);
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object execJmp(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execJmp(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
frame.codePtr += (int)instr.get(0);
|
frame.codePtr += (int)instr.get(0);
|
||||||
frame.jumpFlag = true;
|
frame.jumpFlag = true;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execJmpIf(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execJmpIf(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
if (Values.toBoolean(frame.pop())) {
|
if (Values.toBoolean(frame.pop())) {
|
||||||
frame.codePtr += (int)instr.get(0);
|
frame.codePtr += (int)instr.get(0);
|
||||||
frame.jumpFlag = true;
|
frame.jumpFlag = true;
|
||||||
}
|
}
|
||||||
else frame.codePtr ++;
|
else frame.codePtr ++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execJmpIfNot(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execJmpIfNot(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
if (Values.not(frame.pop())) {
|
if (Values.not(frame.pop())) {
|
||||||
frame.codePtr += (int)instr.get(0);
|
frame.codePtr += (int)instr.get(0);
|
||||||
frame.jumpFlag = true;
|
frame.jumpFlag = true;
|
||||||
}
|
}
|
||||||
else frame.codePtr ++;
|
else frame.codePtr ++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object execIn(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execIn(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
var obj = frame.pop();
|
var obj = frame.pop();
|
||||||
var index = frame.pop();
|
var index = frame.pop();
|
||||||
|
|
||||||
frame.push(ctx, Values.hasMember(ctx, obj, index, false));
|
frame.push(ctx, Values.hasMember(ctx, obj, index, false));
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execTypeof(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execTypeof(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
String name = instr.get(0);
|
String name = instr.get(0);
|
||||||
Object obj;
|
Object obj;
|
||||||
|
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
if (ctx.environment().global.has(ctx, name)) {
|
if (ctx.environment().global.has(ctx, name)) {
|
||||||
obj = ctx.environment().global.get(ctx, name);
|
obj = ctx.environment().global.get(ctx, name);
|
||||||
}
|
}
|
||||||
else obj = null;
|
else obj = null;
|
||||||
}
|
}
|
||||||
else obj = frame.pop();
|
else obj = frame.pop();
|
||||||
|
|
||||||
frame.push(ctx, Values.type(obj));
|
frame.push(ctx, Values.type(obj));
|
||||||
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execNop(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execNop(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object execDelete(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execDelete(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
var key = frame.pop();
|
var key = frame.pop();
|
||||||
var val = frame.pop();
|
var val = frame.pop();
|
||||||
|
|
||||||
if (!Values.deleteMember(ctx, val, key)) throw EngineException.ofSyntax("Can't delete member '" + key + "'.");
|
if (!Values.deleteMember(ctx, val, key)) throw EngineException.ofSyntax("Can't delete member '" + key + "'.");
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object execOperation(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execOperation(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
Operation op = instr.get(0);
|
Operation op = instr.get(0);
|
||||||
var args = new Object[op.operands];
|
var args = new Object[op.operands];
|
||||||
|
|
||||||
for (var i = op.operands - 1; i >= 0; i--) args[i] = frame.pop();
|
for (var i = op.operands - 1; i >= 0; i--) args[i] = frame.pop();
|
||||||
|
|
||||||
frame.push(ctx, Values.operation(ctx, op, args));
|
frame.push(ctx, Values.operation(ctx, op, args));
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object exec(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object exec(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
switch (instr.type) {
|
switch (instr.type) {
|
||||||
case NOP: return execNop(ctx, instr, frame);
|
case NOP: return execNop(ctx, instr, frame);
|
||||||
case RETURN: return execReturn(ctx, instr, frame);
|
case RETURN: return execReturn(ctx, instr, frame);
|
||||||
case THROW: return execThrow(ctx, instr, frame);
|
case THROW: return execThrow(ctx, instr, frame);
|
||||||
case THROW_SYNTAX: return execThrowSyntax(ctx, instr, frame);
|
case THROW_SYNTAX: return execThrowSyntax(ctx, instr, frame);
|
||||||
case CALL: return execCall(ctx, instr, frame);
|
case CALL: return execCall(ctx, instr, frame);
|
||||||
case CALL_NEW: return execCallNew(ctx, instr, frame);
|
case CALL_NEW: return execCallNew(ctx, instr, frame);
|
||||||
case TRY_START: return execTryStart(ctx, instr, frame);
|
case TRY_START: return execTryStart(ctx, instr, frame);
|
||||||
case TRY_END: return execTryEnd(ctx, instr, frame);
|
case TRY_END: return execTryEnd(ctx, instr, frame);
|
||||||
|
|
||||||
case DUP: return execDup(ctx, instr, frame);
|
case DUP: return execDup(ctx, instr, frame);
|
||||||
case LOAD_VALUE: return execLoadValue(ctx, instr, frame);
|
case LOAD_VALUE: return execLoadValue(ctx, instr, frame);
|
||||||
case LOAD_VAR: return execLoadVar(ctx, instr, frame);
|
case LOAD_VAR: return execLoadVar(ctx, instr, frame);
|
||||||
case LOAD_OBJ: return execLoadObj(ctx, instr, frame);
|
case LOAD_OBJ: return execLoadObj(ctx, instr, frame);
|
||||||
case LOAD_ARR: return execLoadArr(ctx, instr, frame);
|
case LOAD_ARR: return execLoadArr(ctx, instr, frame);
|
||||||
case LOAD_FUNC: return execLoadFunc(ctx, instr, frame);
|
case LOAD_FUNC: return execLoadFunc(ctx, instr, frame);
|
||||||
case LOAD_MEMBER: return execLoadMember(ctx, instr, frame);
|
case LOAD_MEMBER: return execLoadMember(ctx, instr, frame);
|
||||||
case LOAD_VAL_MEMBER: return execLoadKeyMember(ctx, instr, frame);
|
case LOAD_VAL_MEMBER: return execLoadKeyMember(ctx, instr, frame);
|
||||||
case LOAD_REGEX: return execLoadRegEx(ctx, instr, frame);
|
case LOAD_REGEX: return execLoadRegEx(ctx, instr, frame);
|
||||||
case LOAD_GLOB: return execLoadGlob(ctx, instr, frame);
|
case LOAD_GLOB: return execLoadGlob(ctx, instr, frame);
|
||||||
|
|
||||||
case DISCARD: return execDiscard(ctx, instr, frame);
|
case DISCARD: return execDiscard(ctx, instr, frame);
|
||||||
case STORE_MEMBER: return execStoreMember(ctx, instr, frame);
|
case STORE_MEMBER: return execStoreMember(ctx, instr, frame);
|
||||||
case STORE_VAR: return execStoreVar(ctx, instr, frame);
|
case STORE_VAR: return execStoreVar(ctx, instr, frame);
|
||||||
case STORE_SELF_FUNC: return execStoreSelfFunc(ctx, instr, frame);
|
case STORE_SELF_FUNC: return execStoreSelfFunc(ctx, instr, frame);
|
||||||
case MAKE_VAR: return execMakeVar(ctx, instr, frame);
|
case MAKE_VAR: return execMakeVar(ctx, instr, frame);
|
||||||
|
|
||||||
case KEYS: return execKeys(ctx, instr, frame);
|
case KEYS: return execKeys(ctx, instr, frame);
|
||||||
case DEF_PROP: return execDefProp(ctx, instr, frame);
|
case DEF_PROP: return execDefProp(ctx, instr, frame);
|
||||||
case TYPEOF: return execTypeof(ctx, instr, frame);
|
case TYPEOF: return execTypeof(ctx, instr, frame);
|
||||||
case DELETE: return execDelete(ctx, instr, frame);
|
case DELETE: return execDelete(ctx, instr, frame);
|
||||||
|
|
||||||
case JMP: return execJmp(ctx, instr, frame);
|
case JMP: return execJmp(ctx, instr, frame);
|
||||||
case JMP_IF: return execJmpIf(ctx, instr, frame);
|
case JMP_IF: return execJmpIf(ctx, instr, frame);
|
||||||
case JMP_IFN: return execJmpIfNot(ctx, instr, frame);
|
case JMP_IFN: return execJmpIfNot(ctx, instr, frame);
|
||||||
|
|
||||||
case OPERATION: return execOperation(ctx, instr, frame);
|
case OPERATION: return execOperation(ctx, instr, frame);
|
||||||
|
|
||||||
default: throw EngineException.ofSyntax("Invalid instruction " + instr.type.name() + ".");
|
default: throw EngineException.ofSyntax("Invalid instruction " + instr.type.name() + ".");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,78 +1,78 @@
|
|||||||
package me.topchetoeu.jscript.engine.scope;
|
package me.topchetoeu.jscript.engine.scope;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
|
|
||||||
public class GlobalScope implements ScopeRecord {
|
public class GlobalScope implements ScopeRecord {
|
||||||
public final ObjectValue obj;
|
public final ObjectValue obj;
|
||||||
|
|
||||||
public boolean has(Context ctx, String name) {
|
public boolean has(Context ctx, String name) {
|
||||||
return obj.hasMember(ctx, name, false);
|
return obj.hasMember(ctx, name, false);
|
||||||
}
|
}
|
||||||
public Object getKey(String name) {
|
public Object getKey(String name) {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GlobalScope globalChild() {
|
public GlobalScope globalChild() {
|
||||||
var obj = new ObjectValue();
|
var obj = new ObjectValue();
|
||||||
obj.setPrototype(null, this.obj);
|
obj.setPrototype(null, this.obj);
|
||||||
return new GlobalScope(obj);
|
return new GlobalScope(obj);
|
||||||
}
|
}
|
||||||
public LocalScopeRecord child() {
|
public LocalScopeRecord child() {
|
||||||
return new LocalScopeRecord();
|
return new LocalScopeRecord();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object define(String name) {
|
public Object define(String name) {
|
||||||
if (obj.hasMember(null, name, true)) return name;
|
if (obj.hasMember(null, name, true)) return name;
|
||||||
obj.defineProperty(null, name, null);
|
obj.defineProperty(null, name, null);
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
public void define(String name, Variable val) {
|
public void define(String name, Variable val) {
|
||||||
obj.defineProperty(null, name,
|
obj.defineProperty(null, name,
|
||||||
new NativeFunction("get " + name, (ctx, th, a) -> val.get(ctx)),
|
new NativeFunction("get " + name, (ctx, th, a) -> val.get(ctx)),
|
||||||
new NativeFunction("set " + name, (ctx, th, args) -> { val.set(ctx, args.length > 0 ? args[0] : null); return null; }),
|
new NativeFunction("set " + name, (ctx, th, args) -> { val.set(ctx, args.length > 0 ? args[0] : null); return null; }),
|
||||||
true, true
|
true, true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
public void define(Context ctx, String name, boolean readonly, Object val) {
|
public void define(Context ctx, String name, boolean readonly, Object val) {
|
||||||
obj.defineProperty(ctx, name, val, readonly, true, true);
|
obj.defineProperty(ctx, name, val, readonly, true, true);
|
||||||
}
|
}
|
||||||
public void define(String ...names) {
|
public void define(String ...names) {
|
||||||
for (var n : names) define(n);
|
for (var n : names) define(n);
|
||||||
}
|
}
|
||||||
public void define(boolean readonly, FunctionValue val) {
|
public void define(boolean readonly, FunctionValue val) {
|
||||||
define(null, val.name, readonly, val);
|
define(null, val.name, readonly, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object get(Context ctx, String name) {
|
public Object get(Context ctx, String name) {
|
||||||
if (!obj.hasMember(ctx, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist.");
|
if (!obj.hasMember(ctx, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist.");
|
||||||
else return obj.getMember(ctx, name);
|
else return obj.getMember(ctx, name);
|
||||||
}
|
}
|
||||||
public void set(Context ctx, String name, Object val) {
|
public void set(Context ctx, String name, Object val) {
|
||||||
if (!obj.hasMember(ctx, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist.");
|
if (!obj.hasMember(ctx, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist.");
|
||||||
if (!obj.setMember(ctx, name, val, false)) throw EngineException.ofSyntax("The global '" + name + "' is readonly.");
|
if (!obj.setMember(ctx, name, val, false)) throw EngineException.ofSyntax("The global '" + name + "' is readonly.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> keys() {
|
public Set<String> keys() {
|
||||||
var res = new HashSet<String>();
|
var res = new HashSet<String>();
|
||||||
|
|
||||||
for (var key : keys()) {
|
for (var key : keys()) {
|
||||||
if (key instanceof String) res.add((String)key);
|
if (key instanceof String) res.add((String)key);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GlobalScope() {
|
public GlobalScope() {
|
||||||
this.obj = new ObjectValue();
|
this.obj = new ObjectValue();
|
||||||
}
|
}
|
||||||
public GlobalScope(ObjectValue val) {
|
public GlobalScope(ObjectValue val) {
|
||||||
this.obj = val;
|
this.obj = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,29 @@
|
|||||||
package me.topchetoeu.jscript.engine.scope;
|
package me.topchetoeu.jscript.engine.scope;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public class LocalScope {
|
public class LocalScope {
|
||||||
public final ValueVariable[] captures;
|
public final ValueVariable[] captures;
|
||||||
public final ValueVariable[] locals;
|
public final ValueVariable[] locals;
|
||||||
public final ArrayList<ValueVariable> catchVars = new ArrayList<>();
|
public final ArrayList<ValueVariable> catchVars = new ArrayList<>();
|
||||||
|
|
||||||
public ValueVariable get(int i) {
|
public ValueVariable get(int i) {
|
||||||
if (i >= locals.length) return catchVars.get(i - locals.length);
|
if (i >= locals.length) return catchVars.get(i - locals.length);
|
||||||
if (i >= 0) return locals[i];
|
if (i >= 0) return locals[i];
|
||||||
else return captures[~i];
|
else return captures[~i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public int size() {
|
public int size() {
|
||||||
return captures.length + locals.length;
|
return captures.length + locals.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalScope(int n, ValueVariable[] captures) {
|
public LocalScope(int n, ValueVariable[] captures) {
|
||||||
locals = new ValueVariable[n];
|
locals = new ValueVariable[n];
|
||||||
this.captures = captures;
|
this.captures = captures;
|
||||||
|
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
locals[i] = new ValueVariable(false, null);
|
locals[i] = new ValueVariable(false, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,77 +1,77 @@
|
|||||||
package me.topchetoeu.jscript.engine.scope;
|
package me.topchetoeu.jscript.engine.scope;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public class LocalScopeRecord implements ScopeRecord {
|
public class LocalScopeRecord implements ScopeRecord {
|
||||||
public final LocalScopeRecord parent;
|
public final LocalScopeRecord parent;
|
||||||
|
|
||||||
private final ArrayList<String> captures = new ArrayList<>();
|
private final ArrayList<String> captures = new ArrayList<>();
|
||||||
private final ArrayList<String> locals = new ArrayList<>();
|
private final ArrayList<String> locals = new ArrayList<>();
|
||||||
|
|
||||||
public String[] captures() {
|
public String[] captures() {
|
||||||
return captures.toArray(String[]::new);
|
return captures.toArray(String[]::new);
|
||||||
}
|
}
|
||||||
public String[] locals() {
|
public String[] locals() {
|
||||||
return locals.toArray(String[]::new);
|
return locals.toArray(String[]::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalScopeRecord child() {
|
public LocalScopeRecord child() {
|
||||||
return new LocalScopeRecord(this);
|
return new LocalScopeRecord(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int localsCount() {
|
public int localsCount() {
|
||||||
return locals.size();
|
return locals.size();
|
||||||
}
|
}
|
||||||
public int capturesCount() {
|
public int capturesCount() {
|
||||||
return captures.size();
|
return captures.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int[] getCaptures() {
|
public int[] getCaptures() {
|
||||||
var buff = new int[captures.size()];
|
var buff = new int[captures.size()];
|
||||||
var i = 0;
|
var i = 0;
|
||||||
|
|
||||||
for (var name : captures) {
|
for (var name : captures) {
|
||||||
var index = parent.getKey(name);
|
var index = parent.getKey(name);
|
||||||
if (index instanceof Integer) buff[i++] = (int)index;
|
if (index instanceof Integer) buff[i++] = (int)index;
|
||||||
}
|
}
|
||||||
|
|
||||||
var res = new int[i];
|
var res = new int[i];
|
||||||
System.arraycopy(buff, 0, res, 0, i);
|
System.arraycopy(buff, 0, res, 0, i);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object getKey(String name) {
|
public Object getKey(String name) {
|
||||||
var capI = captures.indexOf(name);
|
var capI = captures.indexOf(name);
|
||||||
var locI = locals.lastIndexOf(name);
|
var locI = locals.lastIndexOf(name);
|
||||||
if (locI >= 0) return locI;
|
if (locI >= 0) return locI;
|
||||||
if (capI >= 0) return ~capI;
|
if (capI >= 0) return ~capI;
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
var res = parent.getKey(name);
|
var res = parent.getKey(name);
|
||||||
if (res != null && res instanceof Integer) {
|
if (res != null && res instanceof Integer) {
|
||||||
captures.add(name);
|
captures.add(name);
|
||||||
return -captures.size();
|
return -captures.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
public Object define(String name, boolean force) {
|
public Object define(String name, boolean force) {
|
||||||
if (!force && locals.contains(name)) return locals.indexOf(name);
|
if (!force && locals.contains(name)) return locals.indexOf(name);
|
||||||
locals.add(name);
|
locals.add(name);
|
||||||
return locals.size() - 1;
|
return locals.size() - 1;
|
||||||
}
|
}
|
||||||
public Object define(String name) {
|
public Object define(String name) {
|
||||||
return define(name, false);
|
return define(name, false);
|
||||||
}
|
}
|
||||||
public void undefine() {
|
public void undefine() {
|
||||||
locals.remove(locals.size() - 1);
|
locals.remove(locals.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalScopeRecord() {
|
public LocalScopeRecord() {
|
||||||
this.parent = null;
|
this.parent = null;
|
||||||
}
|
}
|
||||||
public LocalScopeRecord(LocalScopeRecord parent) {
|
public LocalScopeRecord(LocalScopeRecord parent) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package me.topchetoeu.jscript.engine.scope;
|
package me.topchetoeu.jscript.engine.scope;
|
||||||
|
|
||||||
public interface ScopeRecord {
|
public interface ScopeRecord {
|
||||||
public Object getKey(String name);
|
public Object getKey(String name);
|
||||||
public Object define(String name);
|
public Object define(String name);
|
||||||
public LocalScopeRecord child();
|
public LocalScopeRecord child();
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,28 @@
|
|||||||
package me.topchetoeu.jscript.engine.scope;
|
package me.topchetoeu.jscript.engine.scope;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
|
|
||||||
public class ValueVariable implements Variable {
|
public class ValueVariable implements Variable {
|
||||||
public boolean readonly;
|
public boolean readonly;
|
||||||
public Object value;
|
public Object value;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean readonly() { return readonly; }
|
public boolean readonly() { return readonly; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object get(Context ctx) {
|
public Object get(Context ctx) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void set(Context ctx, Object val) {
|
public void set(Context ctx, Object val) {
|
||||||
if (readonly) return;
|
if (readonly) return;
|
||||||
this.value = Values.normalize(ctx, val);
|
this.value = Values.normalize(ctx, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValueVariable(boolean readonly, Object val) {
|
public ValueVariable(boolean readonly, Object val) {
|
||||||
this.readonly = readonly;
|
this.readonly = readonly;
|
||||||
this.value = val;
|
this.value = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package me.topchetoeu.jscript.engine.scope;
|
package me.topchetoeu.jscript.engine.scope;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
|
||||||
public interface Variable {
|
public interface Variable {
|
||||||
Object get(Context ctx);
|
Object get(Context ctx);
|
||||||
default boolean readonly() { return true; }
|
default boolean readonly() { return true; }
|
||||||
default void set(Context ctx, Object val) { }
|
default void set(Context ctx, Object val) { }
|
||||||
}
|
}
|
||||||
|
@ -1,220 +1,220 @@
|
|||||||
package me.topchetoeu.jscript.engine.values;
|
package me.topchetoeu.jscript.engine.values;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
|
||||||
// TODO: Make methods generic
|
// TODO: Make methods generic
|
||||||
public class ArrayValue extends ObjectValue implements Iterable<Object> {
|
public class ArrayValue extends ObjectValue implements Iterable<Object> {
|
||||||
private static final Object UNDEFINED = new Object();
|
private static final Object UNDEFINED = new Object();
|
||||||
private Object[] values;
|
private Object[] values;
|
||||||
private int size;
|
private int size;
|
||||||
|
|
||||||
private Object[] alloc(int index) {
|
private Object[] alloc(int index) {
|
||||||
index++;
|
index++;
|
||||||
if (index < values.length) return values;
|
if (index < values.length) return values;
|
||||||
if (index < values.length * 2) index = values.length * 2;
|
if (index < values.length * 2) index = values.length * 2;
|
||||||
|
|
||||||
var arr = new Object[index];
|
var arr = new Object[index];
|
||||||
System.arraycopy(values, 0, arr, 0, values.length);
|
System.arraycopy(values, 0, arr, 0, values.length);
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int size() { return size; }
|
public int size() { return size; }
|
||||||
public boolean setSize(int val) {
|
public boolean setSize(int val) {
|
||||||
if (val < 0) return false;
|
if (val < 0) return false;
|
||||||
if (size > val) shrink(size - val);
|
if (size > val) shrink(size - val);
|
||||||
else {
|
else {
|
||||||
values = alloc(val);
|
values = alloc(val);
|
||||||
size = val;
|
size = val;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object get(int i) {
|
public Object get(int i) {
|
||||||
if (i < 0 || i >= size) return null;
|
if (i < 0 || i >= size) return null;
|
||||||
var res = values[i];
|
var res = values[i];
|
||||||
if (res == UNDEFINED) return null;
|
if (res == UNDEFINED) return null;
|
||||||
else return res;
|
else return res;
|
||||||
}
|
}
|
||||||
public void set(Context ctx, int i, Object val) {
|
public void set(Context ctx, int i, Object val) {
|
||||||
if (i < 0) return;
|
if (i < 0) return;
|
||||||
|
|
||||||
values = alloc(i);
|
values = alloc(i);
|
||||||
|
|
||||||
val = Values.normalize(ctx, val);
|
val = Values.normalize(ctx, val);
|
||||||
if (val == null) val = UNDEFINED;
|
if (val == null) val = UNDEFINED;
|
||||||
values[i] = val;
|
values[i] = val;
|
||||||
if (i >= size) size = i + 1;
|
if (i >= size) size = i + 1;
|
||||||
}
|
}
|
||||||
public boolean has(int i) {
|
public boolean has(int i) {
|
||||||
return i >= 0 && i < size && values[i] != null;
|
return i >= 0 && i < size && values[i] != null;
|
||||||
}
|
}
|
||||||
public void remove(int i) {
|
public void remove(int i) {
|
||||||
if (i < 0 || i >= values.length) return;
|
if (i < 0 || i >= values.length) return;
|
||||||
values[i] = null;
|
values[i] = null;
|
||||||
}
|
}
|
||||||
public void shrink(int n) {
|
public void shrink(int n) {
|
||||||
if (n >= values.length) {
|
if (n >= values.length) {
|
||||||
values = new Object[16];
|
values = new Object[16];
|
||||||
size = 0;
|
size = 0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
values[--size] = null;
|
values[--size] = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object[] toArray() {
|
public Object[] toArray() {
|
||||||
Object[] res = new Object[size];
|
Object[] res = new Object[size];
|
||||||
copyTo(res, 0, 0, size);
|
copyTo(res, 0, 0, size);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
public void copyTo(Object[] arr, int sourceStart, int destStart, int count) {
|
public void copyTo(Object[] arr, int sourceStart, int destStart, int count) {
|
||||||
for (var i = 0; i < count; i++) {
|
for (var i = 0; i < count; i++) {
|
||||||
if (i + sourceStart < 0 || i + sourceStart >= size) arr[i + destStart] = null;
|
if (i + sourceStart < 0 || i + sourceStart >= size) arr[i + destStart] = null;
|
||||||
if (values[i + sourceStart] == UNDEFINED) arr[i + destStart] = null;
|
if (values[i + sourceStart] == UNDEFINED) arr[i + destStart] = null;
|
||||||
else arr[i + sourceStart] = values[i + destStart];
|
else arr[i + sourceStart] = values[i + destStart];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void copyTo(Context ctx, ArrayValue arr, int sourceStart, int destStart, int count) {
|
public void copyTo(Context ctx, ArrayValue arr, int sourceStart, int destStart, int count) {
|
||||||
// Iterate in reverse to reallocate at most once
|
// Iterate in reverse to reallocate at most once
|
||||||
for (var i = count - 1; i >= 0; i--) {
|
for (var i = count - 1; i >= 0; i--) {
|
||||||
if (i + sourceStart < 0 || i + sourceStart >= size) arr.remove(i + destStart);
|
if (i + sourceStart < 0 || i + sourceStart >= size) arr.remove(i + destStart);
|
||||||
if (values[i + sourceStart] == UNDEFINED) arr.set(ctx, i + destStart, null);
|
if (values[i + sourceStart] == UNDEFINED) arr.set(ctx, i + destStart, null);
|
||||||
else if (values[i + sourceStart] == null) arr.remove(i + destStart);
|
else if (values[i + sourceStart] == null) arr.remove(i + destStart);
|
||||||
else arr.set(ctx, i + destStart, values[i + sourceStart]);
|
else arr.set(ctx, i + destStart, values[i + sourceStart]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void copyFrom(Context ctx, Object[] arr, int sourceStart, int destStart, int count) {
|
public void copyFrom(Context ctx, Object[] arr, int sourceStart, int destStart, int count) {
|
||||||
for (var i = 0; i < count; i++) {
|
for (var i = 0; i < count; i++) {
|
||||||
set(ctx, i + destStart, arr[i + sourceStart]);
|
set(ctx, i + destStart, arr[i + sourceStart]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void move(int srcI, int dstI, int n) {
|
public void move(int srcI, int dstI, int n) {
|
||||||
values = alloc(dstI + n);
|
values = alloc(dstI + n);
|
||||||
|
|
||||||
System.arraycopy(values, srcI, values, dstI, n);
|
System.arraycopy(values, srcI, values, dstI, n);
|
||||||
|
|
||||||
if (dstI + n >= size) size = dstI + n;
|
if (dstI + n >= size) size = dstI + n;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sort(Comparator<Object> comparator) {
|
public void sort(Comparator<Object> comparator) {
|
||||||
Arrays.sort(values, 0, size, (a, b) -> {
|
Arrays.sort(values, 0, size, (a, b) -> {
|
||||||
var _a = 0;
|
var _a = 0;
|
||||||
var _b = 0;
|
var _b = 0;
|
||||||
|
|
||||||
if (a == UNDEFINED) _a = 1;
|
if (a == UNDEFINED) _a = 1;
|
||||||
if (a == null) _a = 2;
|
if (a == null) _a = 2;
|
||||||
|
|
||||||
if (b == UNDEFINED) _b = 1;
|
if (b == UNDEFINED) _b = 1;
|
||||||
if (b == null) _b = 2;
|
if (b == null) _b = 2;
|
||||||
|
|
||||||
if (_a != 0 || _b != 0) return Integer.compare(_a, _b);
|
if (_a != 0 || _b != 0) return Integer.compare(_a, _b);
|
||||||
|
|
||||||
return comparator.compare(a, b);
|
return comparator.compare(a, b);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object getField(Context ctx, Object key) {
|
protected Object getField(Context ctx, Object key) {
|
||||||
if (key instanceof Number) {
|
if (key instanceof Number) {
|
||||||
var i = ((Number)key).doubleValue();
|
var i = ((Number)key).doubleValue();
|
||||||
if (i >= 0 && i - Math.floor(i) == 0) {
|
if (i >= 0 && i - Math.floor(i) == 0) {
|
||||||
return get((int)i);
|
return get((int)i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.getField(ctx, key);
|
return super.getField(ctx, key);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
protected boolean setField(Context ctx, Object key, Object val) {
|
protected boolean setField(Context ctx, Object key, Object val) {
|
||||||
if (key instanceof Number) {
|
if (key instanceof Number) {
|
||||||
var i = Values.number(key);
|
var i = Values.number(key);
|
||||||
if (i >= 0 && i - Math.floor(i) == 0) {
|
if (i >= 0 && i - Math.floor(i) == 0) {
|
||||||
set(ctx, (int)i, val);
|
set(ctx, (int)i, val);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.setField(ctx, key, val);
|
return super.setField(ctx, key, val);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
protected boolean hasField(Context ctx, Object key) {
|
protected boolean hasField(Context ctx, Object key) {
|
||||||
if (key instanceof Number) {
|
if (key instanceof Number) {
|
||||||
var i = Values.number(key);
|
var i = Values.number(key);
|
||||||
if (i >= 0 && i - Math.floor(i) == 0) {
|
if (i >= 0 && i - Math.floor(i) == 0) {
|
||||||
return has((int)i);
|
return has((int)i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.hasField(ctx, key);
|
return super.hasField(ctx, key);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
protected void deleteField(Context ctx, Object key) {
|
protected void deleteField(Context ctx, Object key) {
|
||||||
if (key instanceof Number) {
|
if (key instanceof Number) {
|
||||||
var i = Values.number(key);
|
var i = Values.number(key);
|
||||||
if (i >= 0 && i - Math.floor(i) == 0) {
|
if (i >= 0 && i - Math.floor(i) == 0) {
|
||||||
remove((int)i);
|
remove((int)i);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super.deleteField(ctx, key);
|
super.deleteField(ctx, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Object> keys(boolean includeNonEnumerable) {
|
public List<Object> keys(boolean includeNonEnumerable) {
|
||||||
var res = super.keys(includeNonEnumerable);
|
var res = super.keys(includeNonEnumerable);
|
||||||
for (var i = 0; i < size(); i++) {
|
for (var i = 0; i < size(); i++) {
|
||||||
if (has(i)) res.add(i);
|
if (has(i)) res.add(i);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<Object> iterator() {
|
public Iterator<Object> iterator() {
|
||||||
return new Iterator<Object>() {
|
return new Iterator<Object>() {
|
||||||
private int i = 0;
|
private int i = 0;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasNext() {
|
public boolean hasNext() {
|
||||||
return i < size();
|
return i < size();
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public Object next() {
|
public Object next() {
|
||||||
if (!hasNext()) return null;
|
if (!hasNext()) return null;
|
||||||
return get(i++);
|
return get(i++);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayValue() {
|
public ArrayValue() {
|
||||||
super(PlaceholderProto.ARRAY);
|
super(PlaceholderProto.ARRAY);
|
||||||
values = new Object[16];
|
values = new Object[16];
|
||||||
size = 0;
|
size = 0;
|
||||||
}
|
}
|
||||||
public ArrayValue(int cap) {
|
public ArrayValue(int cap) {
|
||||||
super(PlaceholderProto.ARRAY);
|
super(PlaceholderProto.ARRAY);
|
||||||
values = new Object[cap];
|
values = new Object[cap];
|
||||||
size = 0;
|
size = 0;
|
||||||
}
|
}
|
||||||
public ArrayValue(Context ctx, Object ...values) {
|
public ArrayValue(Context ctx, Object ...values) {
|
||||||
this();
|
this();
|
||||||
this.values = new Object[values.length];
|
this.values = new Object[values.length];
|
||||||
size = values.length;
|
size = values.length;
|
||||||
|
|
||||||
for (var i = 0; i < size; i++) this.values[i] = Values.normalize(ctx, values[i]);
|
for (var i = 0; i < size; i++) this.values[i] = Values.normalize(ctx, values[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ArrayValue of(Context ctx, Collection<?> values) {
|
public static ArrayValue of(Context ctx, Collection<?> values) {
|
||||||
return new ArrayValue(ctx, values.toArray(Object[]::new));
|
return new ArrayValue(ctx, values.toArray(Object[]::new));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,57 +1,57 @@
|
|||||||
package me.topchetoeu.jscript.engine.values;
|
package me.topchetoeu.jscript.engine.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.FunctionBody;
|
import me.topchetoeu.jscript.compilation.FunctionBody;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.Environment;
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
||||||
import me.topchetoeu.jscript.engine.frame.Runners;
|
import me.topchetoeu.jscript.engine.frame.Runners;
|
||||||
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
||||||
|
|
||||||
public class CodeFunction extends FunctionValue {
|
public class CodeFunction extends FunctionValue {
|
||||||
public final int localsN;
|
public final int localsN;
|
||||||
public final Instruction[] body;
|
public final Instruction[] body;
|
||||||
public final String[] captureNames, localNames;
|
public final String[] captureNames, localNames;
|
||||||
public final ValueVariable[] captures;
|
public final ValueVariable[] captures;
|
||||||
public Environment environment;
|
public Environment environment;
|
||||||
|
|
||||||
public Location loc() {
|
public Location loc() {
|
||||||
for (var instr : body) {
|
for (var instr : body) {
|
||||||
if (instr.location != null) return instr.location;
|
if (instr.location != null) return instr.location;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
public String readable() {
|
public String readable() {
|
||||||
var loc = loc();
|
var loc = loc();
|
||||||
if (loc == null) return name;
|
if (loc == null) return name;
|
||||||
else if (name.equals("")) return loc.toString();
|
else if (name.equals("")) return loc.toString();
|
||||||
else return name + "@" + loc;
|
else return name + "@" + loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object call(Context ctx, Object thisArg, Object ...args) {
|
public Object call(Context ctx, Object thisArg, Object ...args) {
|
||||||
var frame = new CodeFrame(ctx, thisArg, args, this);
|
var frame = new CodeFrame(ctx, thisArg, args, this);
|
||||||
try {
|
try {
|
||||||
ctx.pushFrame(frame);
|
ctx.pushFrame(frame);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
var res = frame.next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, null);
|
var res = frame.next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, null);
|
||||||
if (res != Runners.NO_RETURN) return res;
|
if (res != Runners.NO_RETURN) return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
ctx.popFrame(frame);
|
ctx.popFrame(frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public CodeFunction(Environment environment, String name, FunctionBody body, ValueVariable... captures) {
|
public CodeFunction(Environment environment, String name, FunctionBody body, ValueVariable... captures) {
|
||||||
super(name, body.argsN);
|
super(name, body.argsN);
|
||||||
this.captures = captures;
|
this.captures = captures;
|
||||||
this.captureNames = body.captureNames;
|
this.captureNames = body.captureNames;
|
||||||
this.localNames = body.localNames;
|
this.localNames = body.localNames;
|
||||||
this.environment = environment;
|
this.environment = environment;
|
||||||
this.localsN = body.localsN;
|
this.localsN = body.localsN;
|
||||||
this.body = body.instructions;
|
this.body = body.instructions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,70 +1,70 @@
|
|||||||
package me.topchetoeu.jscript.engine.values;
|
package me.topchetoeu.jscript.engine.values;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
|
||||||
public abstract class FunctionValue extends ObjectValue {
|
public abstract class FunctionValue extends ObjectValue {
|
||||||
public String name = "";
|
public String name = "";
|
||||||
public boolean special = false;
|
public boolean special = false;
|
||||||
public int length;
|
public int length;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format("function %s(...)", name);
|
return String.format("function %s(...)", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract Object call(Context ctx, Object thisArg, Object ...args);
|
public abstract Object call(Context ctx, Object thisArg, Object ...args);
|
||||||
public Object call(Context ctx) {
|
public Object call(Context ctx) {
|
||||||
return call(ctx, null);
|
return call(ctx, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object getField(Context ctx, Object key) {
|
protected Object getField(Context ctx, Object key) {
|
||||||
if ("name".equals(key)) return name;
|
if ("name".equals(key)) return name;
|
||||||
if ("length".equals(key)) return length;
|
if ("length".equals(key)) return length;
|
||||||
return super.getField(ctx, key);
|
return super.getField(ctx, key);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
protected boolean setField(Context ctx, Object key, Object val) {
|
protected boolean setField(Context ctx, Object key, Object val) {
|
||||||
if ("name".equals(key)) name = Values.toString(ctx, val);
|
if ("name".equals(key)) name = Values.toString(ctx, val);
|
||||||
else if ("length".equals(key)) length = (int)Values.toNumber(ctx, val);
|
else if ("length".equals(key)) length = (int)Values.toNumber(ctx, val);
|
||||||
else return super.setField(ctx, key, val);
|
else return super.setField(ctx, key, val);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
protected boolean hasField(Context ctx, Object key) {
|
protected boolean hasField(Context ctx, Object key) {
|
||||||
if ("name".equals(key)) return true;
|
if ("name".equals(key)) return true;
|
||||||
if ("length".equals(key)) return true;
|
if ("length".equals(key)) return true;
|
||||||
return super.hasField(ctx, key);
|
return super.hasField(ctx, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Object> keys(boolean includeNonEnumerable) {
|
public List<Object> keys(boolean includeNonEnumerable) {
|
||||||
var res = super.keys(includeNonEnumerable);
|
var res = super.keys(includeNonEnumerable);
|
||||||
if (includeNonEnumerable) {
|
if (includeNonEnumerable) {
|
||||||
res.add("name");
|
res.add("name");
|
||||||
res.add("length");
|
res.add("length");
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FunctionValue(String name, int length) {
|
public FunctionValue(String name, int length) {
|
||||||
super(PlaceholderProto.FUNCTION);
|
super(PlaceholderProto.FUNCTION);
|
||||||
|
|
||||||
if (name == null) name = "";
|
if (name == null) name = "";
|
||||||
this.length = length;
|
this.length = length;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
|
||||||
nonConfigurableSet.add("name");
|
nonConfigurableSet.add("name");
|
||||||
nonEnumerableSet.add("name");
|
nonEnumerableSet.add("name");
|
||||||
nonWritableSet.add("length");
|
nonWritableSet.add("length");
|
||||||
nonConfigurableSet.add("length");
|
nonConfigurableSet.add("length");
|
||||||
nonEnumerableSet.add("length");
|
nonEnumerableSet.add("length");
|
||||||
|
|
||||||
var proto = new ObjectValue();
|
var proto = new ObjectValue();
|
||||||
proto.defineProperty(null, "constructor", this, true, false, false);
|
proto.defineProperty(null, "constructor", this, true, false, false);
|
||||||
this.defineProperty(null, "prototype", proto, true, false, false);
|
this.defineProperty(null, "prototype", proto, true, false, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
package me.topchetoeu.jscript.engine.values;
|
package me.topchetoeu.jscript.engine.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
|
||||||
public class NativeFunction extends FunctionValue {
|
public class NativeFunction extends FunctionValue {
|
||||||
public static interface NativeFunctionRunner {
|
public static interface NativeFunctionRunner {
|
||||||
Object run(Context ctx, Object thisArg, Object[] args);
|
Object run(Context ctx, Object thisArg, Object[] args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final NativeFunctionRunner action;
|
public final NativeFunctionRunner action;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object call(Context ctx, Object thisArg, Object ...args) {
|
public Object call(Context ctx, Object thisArg, Object ...args) {
|
||||||
return action.run(ctx, thisArg, args);
|
return action.run(ctx, thisArg, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public NativeFunction(String name, NativeFunctionRunner action) {
|
public NativeFunction(String name, NativeFunctionRunner action) {
|
||||||
super(name, 0);
|
super(name, 0);
|
||||||
this.action = action;
|
this.action = action;
|
||||||
}
|
}
|
||||||
public NativeFunction(NativeFunctionRunner action) {
|
public NativeFunction(NativeFunctionRunner action) {
|
||||||
super("", 0);
|
super("", 0);
|
||||||
this.action = action;
|
this.action = action;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,32 @@
|
|||||||
package me.topchetoeu.jscript.engine.values;
|
package me.topchetoeu.jscript.engine.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
|
||||||
public class NativeWrapper extends ObjectValue {
|
public class NativeWrapper extends ObjectValue {
|
||||||
private static final Object NATIVE_PROTO = new Object();
|
private static final Object NATIVE_PROTO = new Object();
|
||||||
public final Object wrapped;
|
public final Object wrapped;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ObjectValue getPrototype(Context ctx) {
|
public ObjectValue getPrototype(Context ctx) {
|
||||||
if (prototype == NATIVE_PROTO) return ctx.environment().wrappers.getProto(wrapped.getClass());
|
if (prototype == NATIVE_PROTO) return ctx.environment().wrappers.getProto(wrapped.getClass());
|
||||||
else return super.getPrototype(ctx);
|
else return super.getPrototype(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return wrapped.toString();
|
return wrapped.toString();
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
return wrapped.equals(obj);
|
return wrapped.equals(obj);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return wrapped.hashCode();
|
return wrapped.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public NativeWrapper(Object wrapped) {
|
public NativeWrapper(Object wrapped) {
|
||||||
this.wrapped = wrapped;
|
this.wrapped = wrapped;
|
||||||
prototype = NATIVE_PROTO;
|
prototype = NATIVE_PROTO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,344 +1,344 @@
|
|||||||
package me.topchetoeu.jscript.engine.values;
|
package me.topchetoeu.jscript.engine.values;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
|
||||||
public class ObjectValue {
|
public class ObjectValue {
|
||||||
public static enum PlaceholderProto {
|
public static enum PlaceholderProto {
|
||||||
NONE,
|
NONE,
|
||||||
OBJECT,
|
OBJECT,
|
||||||
ARRAY,
|
ARRAY,
|
||||||
FUNCTION,
|
FUNCTION,
|
||||||
ERROR,
|
ERROR,
|
||||||
SYNTAX_ERROR,
|
SYNTAX_ERROR,
|
||||||
TYPE_ERROR,
|
TYPE_ERROR,
|
||||||
RANGE_ERROR,
|
RANGE_ERROR,
|
||||||
}
|
}
|
||||||
public static enum State {
|
public static enum State {
|
||||||
NORMAL,
|
NORMAL,
|
||||||
NO_EXTENSIONS,
|
NO_EXTENSIONS,
|
||||||
SEALED,
|
SEALED,
|
||||||
FROZEN,
|
FROZEN,
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Property {
|
public static class Property {
|
||||||
public final FunctionValue getter;
|
public final FunctionValue getter;
|
||||||
public final FunctionValue setter;
|
public final FunctionValue setter;
|
||||||
|
|
||||||
public Property(FunctionValue getter, FunctionValue setter) {
|
public Property(FunctionValue getter, FunctionValue setter) {
|
||||||
this.getter = getter;
|
this.getter = getter;
|
||||||
this.setter = setter;
|
this.setter = setter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Object OBJ_PROTO = new Object();
|
private static final Object OBJ_PROTO = new Object();
|
||||||
private static final Object ARR_PROTO = new Object();
|
private static final Object ARR_PROTO = new Object();
|
||||||
private static final Object FUNC_PROTO = new Object();
|
private static final Object FUNC_PROTO = new Object();
|
||||||
private static final Object ERR_PROTO = new Object();
|
private static final Object ERR_PROTO = new Object();
|
||||||
private static final Object SYNTAX_ERR_PROTO = new Object();
|
private static final Object SYNTAX_ERR_PROTO = new Object();
|
||||||
private static final Object TYPE_ERR_PROTO = new Object();
|
private static final Object TYPE_ERR_PROTO = new Object();
|
||||||
private static final Object RANGE_ERR_PROTO = new Object();
|
private static final Object RANGE_ERR_PROTO = new Object();
|
||||||
|
|
||||||
protected Object prototype;
|
protected Object prototype;
|
||||||
|
|
||||||
public State state = State.NORMAL;
|
public State state = State.NORMAL;
|
||||||
public LinkedHashMap<Object, Object> values = new LinkedHashMap<>();
|
public LinkedHashMap<Object, Object> values = new LinkedHashMap<>();
|
||||||
public LinkedHashMap<Object, Property> properties = new LinkedHashMap<>();
|
public LinkedHashMap<Object, Property> properties = new LinkedHashMap<>();
|
||||||
public LinkedHashSet<Object> nonWritableSet = new LinkedHashSet<>();
|
public LinkedHashSet<Object> nonWritableSet = new LinkedHashSet<>();
|
||||||
public LinkedHashSet<Object> nonConfigurableSet = new LinkedHashSet<>();
|
public LinkedHashSet<Object> nonConfigurableSet = new LinkedHashSet<>();
|
||||||
public LinkedHashSet<Object> nonEnumerableSet = new LinkedHashSet<>();
|
public LinkedHashSet<Object> nonEnumerableSet = new LinkedHashSet<>();
|
||||||
|
|
||||||
public final boolean memberWritable(Object key) {
|
public final boolean memberWritable(Object key) {
|
||||||
if (state == State.FROZEN) return false;
|
if (state == State.FROZEN) return false;
|
||||||
return !values.containsKey(key) || !nonWritableSet.contains(key);
|
return !values.containsKey(key) || !nonWritableSet.contains(key);
|
||||||
}
|
}
|
||||||
public final boolean memberConfigurable(Object key) {
|
public final boolean memberConfigurable(Object key) {
|
||||||
if (state == State.SEALED || state == State.FROZEN) return false;
|
if (state == State.SEALED || state == State.FROZEN) return false;
|
||||||
return !nonConfigurableSet.contains(key);
|
return !nonConfigurableSet.contains(key);
|
||||||
}
|
}
|
||||||
public final boolean memberEnumerable(Object key) {
|
public final boolean memberEnumerable(Object key) {
|
||||||
return !nonEnumerableSet.contains(key);
|
return !nonEnumerableSet.contains(key);
|
||||||
}
|
}
|
||||||
public final boolean extensible() {
|
public final boolean extensible() {
|
||||||
return state == State.NORMAL;
|
return state == State.NORMAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void preventExtensions() {
|
public final void preventExtensions() {
|
||||||
if (state == State.NORMAL) state = State.NO_EXTENSIONS;
|
if (state == State.NORMAL) state = State.NO_EXTENSIONS;
|
||||||
}
|
}
|
||||||
public final void seal() {
|
public final void seal() {
|
||||||
if (state == State.NORMAL || state == State.NO_EXTENSIONS) state = State.SEALED;
|
if (state == State.NORMAL || state == State.NO_EXTENSIONS) state = State.SEALED;
|
||||||
}
|
}
|
||||||
public final void freeze() {
|
public final void freeze() {
|
||||||
state = State.FROZEN;
|
state = State.FROZEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean defineProperty(Context ctx, Object key, Object val, boolean writable, boolean configurable, boolean enumerable) {
|
public final boolean defineProperty(Context ctx, Object key, Object val, boolean writable, boolean configurable, boolean enumerable) {
|
||||||
key = Values.normalize(ctx, key); val = Values.normalize(ctx, val);
|
key = Values.normalize(ctx, key); val = Values.normalize(ctx, val);
|
||||||
boolean reconfigured =
|
boolean reconfigured =
|
||||||
writable != memberWritable(key) ||
|
writable != memberWritable(key) ||
|
||||||
configurable != memberConfigurable(key) ||
|
configurable != memberConfigurable(key) ||
|
||||||
enumerable != memberEnumerable(key);
|
enumerable != memberEnumerable(key);
|
||||||
|
|
||||||
if (!reconfigured) {
|
if (!reconfigured) {
|
||||||
if (!memberWritable(key)) {
|
if (!memberWritable(key)) {
|
||||||
var a = values.get(key);
|
var a = values.get(key);
|
||||||
var b = val;
|
var b = val;
|
||||||
if (a == null || b == null) return a == null && b == null;
|
if (a == null || b == null) return a == null && b == null;
|
||||||
return a == b || a.equals(b);
|
return a == b || a.equals(b);
|
||||||
}
|
}
|
||||||
values.put(key, val);
|
values.put(key, val);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
properties.containsKey(key) &&
|
properties.containsKey(key) &&
|
||||||
values.get(key) == val &&
|
values.get(key) == val &&
|
||||||
!reconfigured
|
!reconfigured
|
||||||
) return true;
|
) return true;
|
||||||
|
|
||||||
if (!extensible() && !values.containsKey(key) && !properties.containsKey(key)) return false;
|
if (!extensible() && !values.containsKey(key) && !properties.containsKey(key)) return false;
|
||||||
if (!memberConfigurable(key)) return false;
|
if (!memberConfigurable(key)) return false;
|
||||||
|
|
||||||
nonWritableSet.remove(key);
|
nonWritableSet.remove(key);
|
||||||
nonEnumerableSet.remove(key);
|
nonEnumerableSet.remove(key);
|
||||||
properties.remove(key);
|
properties.remove(key);
|
||||||
values.remove(key);
|
values.remove(key);
|
||||||
|
|
||||||
if (!writable) nonWritableSet.add(key);
|
if (!writable) nonWritableSet.add(key);
|
||||||
if (!configurable) nonConfigurableSet.add(key);
|
if (!configurable) nonConfigurableSet.add(key);
|
||||||
if (!enumerable) nonEnumerableSet.add(key);
|
if (!enumerable) nonEnumerableSet.add(key);
|
||||||
|
|
||||||
values.put(key, val);
|
values.put(key, val);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
public final boolean defineProperty(Context ctx, Object key, Object val) {
|
public final boolean defineProperty(Context ctx, Object key, Object val) {
|
||||||
return defineProperty(ctx, key, val, true, true, true);
|
return defineProperty(ctx, key, val, true, true, true);
|
||||||
}
|
}
|
||||||
public final boolean defineProperty(Context ctx, Object key, FunctionValue getter, FunctionValue setter, boolean configurable, boolean enumerable) {
|
public final boolean defineProperty(Context ctx, Object key, FunctionValue getter, FunctionValue setter, boolean configurable, boolean enumerable) {
|
||||||
key = Values.normalize(ctx, key);
|
key = Values.normalize(ctx, key);
|
||||||
if (
|
if (
|
||||||
properties.containsKey(key) &&
|
properties.containsKey(key) &&
|
||||||
properties.get(key).getter == getter &&
|
properties.get(key).getter == getter &&
|
||||||
properties.get(key).setter == setter &&
|
properties.get(key).setter == setter &&
|
||||||
!configurable == nonConfigurableSet.contains(key) &&
|
!configurable == nonConfigurableSet.contains(key) &&
|
||||||
!enumerable == nonEnumerableSet.contains(key)
|
!enumerable == nonEnumerableSet.contains(key)
|
||||||
) return true;
|
) return true;
|
||||||
if (!extensible() && !values.containsKey(key) && !properties.containsKey(key)) return false;
|
if (!extensible() && !values.containsKey(key) && !properties.containsKey(key)) return false;
|
||||||
if (!memberConfigurable(key)) return false;
|
if (!memberConfigurable(key)) return false;
|
||||||
|
|
||||||
nonWritableSet.remove(key);
|
nonWritableSet.remove(key);
|
||||||
nonEnumerableSet.remove(key);
|
nonEnumerableSet.remove(key);
|
||||||
properties.remove(key);
|
properties.remove(key);
|
||||||
values.remove(key);
|
values.remove(key);
|
||||||
|
|
||||||
if (!configurable) nonConfigurableSet.add(key);
|
if (!configurable) nonConfigurableSet.add(key);
|
||||||
if (!enumerable) nonEnumerableSet.add(key);
|
if (!enumerable) nonEnumerableSet.add(key);
|
||||||
|
|
||||||
properties.put(key, new Property(getter, setter));
|
properties.put(key, new Property(getter, setter));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObjectValue getPrototype(Context ctx) {
|
public ObjectValue getPrototype(Context ctx) {
|
||||||
try {
|
try {
|
||||||
if (prototype == OBJ_PROTO) return ctx.environment().proto("object");
|
if (prototype == OBJ_PROTO) return ctx.environment().proto("object");
|
||||||
if (prototype == ARR_PROTO) return ctx.environment().proto("array");
|
if (prototype == ARR_PROTO) return ctx.environment().proto("array");
|
||||||
if (prototype == FUNC_PROTO) return ctx.environment().proto("function");
|
if (prototype == FUNC_PROTO) return ctx.environment().proto("function");
|
||||||
if (prototype == ERR_PROTO) return ctx.environment().proto("error");
|
if (prototype == ERR_PROTO) return ctx.environment().proto("error");
|
||||||
if (prototype == RANGE_ERR_PROTO) return ctx.environment().proto("rangeErr");
|
if (prototype == RANGE_ERR_PROTO) return ctx.environment().proto("rangeErr");
|
||||||
if (prototype == SYNTAX_ERR_PROTO) return ctx.environment().proto("syntaxErr");
|
if (prototype == SYNTAX_ERR_PROTO) return ctx.environment().proto("syntaxErr");
|
||||||
if (prototype == TYPE_ERR_PROTO) return ctx.environment().proto("typeErr");
|
if (prototype == TYPE_ERR_PROTO) return ctx.environment().proto("typeErr");
|
||||||
}
|
}
|
||||||
catch (NullPointerException e) { return null; }
|
catch (NullPointerException e) { return null; }
|
||||||
|
|
||||||
return (ObjectValue)prototype;
|
return (ObjectValue)prototype;
|
||||||
}
|
}
|
||||||
public final boolean setPrototype(Context ctx, Object val) {
|
public final boolean setPrototype(Context ctx, Object val) {
|
||||||
val = Values.normalize(ctx, val);
|
val = Values.normalize(ctx, val);
|
||||||
|
|
||||||
if (!extensible()) return false;
|
if (!extensible()) return false;
|
||||||
if (val == null || val == Values.NULL) {
|
if (val == null || val == Values.NULL) {
|
||||||
prototype = null;
|
prototype = null;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (Values.isObject(val)) {
|
else if (Values.isObject(val)) {
|
||||||
var obj = Values.object(val);
|
var obj = Values.object(val);
|
||||||
|
|
||||||
if (ctx != null && ctx.environment() != null) {
|
if (ctx != null && ctx.environment() != null) {
|
||||||
if (obj == ctx.environment().proto("object")) prototype = OBJ_PROTO;
|
if (obj == ctx.environment().proto("object")) prototype = OBJ_PROTO;
|
||||||
else if (obj == ctx.environment().proto("array")) prototype = ARR_PROTO;
|
else if (obj == ctx.environment().proto("array")) prototype = ARR_PROTO;
|
||||||
else if (obj == ctx.environment().proto("function")) prototype = FUNC_PROTO;
|
else if (obj == ctx.environment().proto("function")) prototype = FUNC_PROTO;
|
||||||
else if (obj == ctx.environment().proto("error")) prototype = ERR_PROTO;
|
else if (obj == ctx.environment().proto("error")) prototype = ERR_PROTO;
|
||||||
else if (obj == ctx.environment().proto("syntaxErr")) prototype = SYNTAX_ERR_PROTO;
|
else if (obj == ctx.environment().proto("syntaxErr")) prototype = SYNTAX_ERR_PROTO;
|
||||||
else if (obj == ctx.environment().proto("typeErr")) prototype = TYPE_ERR_PROTO;
|
else if (obj == ctx.environment().proto("typeErr")) prototype = TYPE_ERR_PROTO;
|
||||||
else if (obj == ctx.environment().proto("rangeErr")) prototype = RANGE_ERR_PROTO;
|
else if (obj == ctx.environment().proto("rangeErr")) prototype = RANGE_ERR_PROTO;
|
||||||
else prototype = obj;
|
else prototype = obj;
|
||||||
}
|
}
|
||||||
else prototype = obj;
|
else prototype = obj;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
public final boolean setPrototype(PlaceholderProto val) {
|
public final boolean setPrototype(PlaceholderProto val) {
|
||||||
if (!extensible()) return false;
|
if (!extensible()) return false;
|
||||||
switch (val) {
|
switch (val) {
|
||||||
case OBJECT: prototype = OBJ_PROTO; break;
|
case OBJECT: prototype = OBJ_PROTO; break;
|
||||||
case FUNCTION: prototype = FUNC_PROTO; break;
|
case FUNCTION: prototype = FUNC_PROTO; break;
|
||||||
case ARRAY: prototype = ARR_PROTO; break;
|
case ARRAY: prototype = ARR_PROTO; break;
|
||||||
case ERROR: prototype = ERR_PROTO; break;
|
case ERROR: prototype = ERR_PROTO; break;
|
||||||
case SYNTAX_ERROR: prototype = SYNTAX_ERR_PROTO; break;
|
case SYNTAX_ERROR: prototype = SYNTAX_ERR_PROTO; break;
|
||||||
case TYPE_ERROR: prototype = TYPE_ERR_PROTO; break;
|
case TYPE_ERROR: prototype = TYPE_ERR_PROTO; break;
|
||||||
case RANGE_ERROR: prototype = RANGE_ERR_PROTO; break;
|
case RANGE_ERROR: prototype = RANGE_ERR_PROTO; break;
|
||||||
case NONE: prototype = null; break;
|
case NONE: prototype = null; break;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Property getProperty(Context ctx, Object key) {
|
protected Property getProperty(Context ctx, Object key) {
|
||||||
if (properties.containsKey(key)) return properties.get(key);
|
if (properties.containsKey(key)) return properties.get(key);
|
||||||
var proto = getPrototype(ctx);
|
var proto = getPrototype(ctx);
|
||||||
if (proto != null) return proto.getProperty(ctx, key);
|
if (proto != null) return proto.getProperty(ctx, key);
|
||||||
else return null;
|
else return null;
|
||||||
}
|
}
|
||||||
protected Object getField(Context ctx, Object key) {
|
protected Object getField(Context ctx, Object key) {
|
||||||
if (values.containsKey(key)) return values.get(key);
|
if (values.containsKey(key)) return values.get(key);
|
||||||
var proto = getPrototype(ctx);
|
var proto = getPrototype(ctx);
|
||||||
if (proto != null) return proto.getField(ctx, key);
|
if (proto != null) return proto.getField(ctx, key);
|
||||||
else return null;
|
else return null;
|
||||||
}
|
}
|
||||||
protected boolean setField(Context ctx, Object key, Object val) {
|
protected boolean setField(Context ctx, Object key, Object val) {
|
||||||
if (val instanceof FunctionValue && ((FunctionValue)val).name.equals("")) {
|
if (val instanceof FunctionValue && ((FunctionValue)val).name.equals("")) {
|
||||||
((FunctionValue)val).name = Values.toString(ctx, key);
|
((FunctionValue)val).name = Values.toString(ctx, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
values.put(key, val);
|
values.put(key, val);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
protected void deleteField(Context ctx, Object key) {
|
protected void deleteField(Context ctx, Object key) {
|
||||||
values.remove(key);
|
values.remove(key);
|
||||||
}
|
}
|
||||||
protected boolean hasField(Context ctx, Object key) {
|
protected boolean hasField(Context ctx, Object key) {
|
||||||
return values.containsKey(key);
|
return values.containsKey(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final Object getMember(Context ctx, Object key, Object thisArg) {
|
public final Object getMember(Context ctx, Object key, Object thisArg) {
|
||||||
key = Values.normalize(ctx, key);
|
key = Values.normalize(ctx, key);
|
||||||
|
|
||||||
if ("__proto__".equals(key)) {
|
if ("__proto__".equals(key)) {
|
||||||
var res = getPrototype(ctx);
|
var res = getPrototype(ctx);
|
||||||
return res == null ? Values.NULL : res;
|
return res == null ? Values.NULL : res;
|
||||||
}
|
}
|
||||||
|
|
||||||
var prop = getProperty(ctx, key);
|
var prop = getProperty(ctx, key);
|
||||||
|
|
||||||
if (prop != null) {
|
if (prop != null) {
|
||||||
if (prop.getter == null) return null;
|
if (prop.getter == null) return null;
|
||||||
else return prop.getter.call(ctx, Values.normalize(ctx, thisArg));
|
else return prop.getter.call(ctx, Values.normalize(ctx, thisArg));
|
||||||
}
|
}
|
||||||
else return getField(ctx, key);
|
else return getField(ctx, key);
|
||||||
}
|
}
|
||||||
public final Object getMember(Context ctx, Object key) {
|
public final Object getMember(Context ctx, Object key) {
|
||||||
return getMember(ctx, key, this);
|
return getMember(ctx, key, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean setMember(Context ctx, Object key, Object val, Object thisArg, boolean onlyProps) {
|
public final boolean setMember(Context ctx, Object key, Object val, Object thisArg, boolean onlyProps) {
|
||||||
key = Values.normalize(ctx, key); val = Values.normalize(ctx, val);
|
key = Values.normalize(ctx, key); val = Values.normalize(ctx, val);
|
||||||
|
|
||||||
var prop = getProperty(ctx, key);
|
var prop = getProperty(ctx, key);
|
||||||
if (prop != null) {
|
if (prop != null) {
|
||||||
if (prop.setter == null) return false;
|
if (prop.setter == null) return false;
|
||||||
prop.setter.call(ctx, Values.normalize(ctx, thisArg), val);
|
prop.setter.call(ctx, Values.normalize(ctx, thisArg), val);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (onlyProps) return false;
|
else if (onlyProps) return false;
|
||||||
else if (!extensible() && !values.containsKey(key)) return false;
|
else if (!extensible() && !values.containsKey(key)) return false;
|
||||||
else if (key == null) {
|
else if (key == null) {
|
||||||
values.put(key, val);
|
values.put(key, val);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if ("__proto__".equals(key)) return setPrototype(ctx, val);
|
else if ("__proto__".equals(key)) return setPrototype(ctx, val);
|
||||||
else if (nonWritableSet.contains(key)) return false;
|
else if (nonWritableSet.contains(key)) return false;
|
||||||
else return setField(ctx, key, val);
|
else return setField(ctx, key, val);
|
||||||
}
|
}
|
||||||
public final boolean setMember(Context ctx, Object key, Object val, boolean onlyProps) {
|
public final boolean setMember(Context ctx, Object key, Object val, boolean onlyProps) {
|
||||||
return setMember(ctx, Values.normalize(ctx, key), Values.normalize(ctx, val), this, onlyProps);
|
return setMember(ctx, Values.normalize(ctx, key), Values.normalize(ctx, val), this, onlyProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean hasMember(Context ctx, Object key, boolean own) {
|
public final boolean hasMember(Context ctx, Object key, boolean own) {
|
||||||
key = Values.normalize(ctx, key);
|
key = Values.normalize(ctx, key);
|
||||||
|
|
||||||
if (key != null && "__proto__".equals(key)) return true;
|
if (key != null && "__proto__".equals(key)) return true;
|
||||||
if (hasField(ctx, key)) return true;
|
if (hasField(ctx, key)) return true;
|
||||||
if (properties.containsKey(key)) return true;
|
if (properties.containsKey(key)) return true;
|
||||||
if (own) return false;
|
if (own) return false;
|
||||||
var proto = getPrototype(ctx);
|
var proto = getPrototype(ctx);
|
||||||
return proto != null && proto.hasMember(ctx, key, own);
|
return proto != null && proto.hasMember(ctx, key, own);
|
||||||
}
|
}
|
||||||
public final boolean deleteMember(Context ctx, Object key) {
|
public final boolean deleteMember(Context ctx, Object key) {
|
||||||
key = Values.normalize(ctx, key);
|
key = Values.normalize(ctx, key);
|
||||||
|
|
||||||
if (!memberConfigurable(key)) return false;
|
if (!memberConfigurable(key)) return false;
|
||||||
properties.remove(key);
|
properties.remove(key);
|
||||||
nonWritableSet.remove(key);
|
nonWritableSet.remove(key);
|
||||||
nonEnumerableSet.remove(key);
|
nonEnumerableSet.remove(key);
|
||||||
deleteField(ctx, key);
|
deleteField(ctx, key);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final ObjectValue getMemberDescriptor(Context ctx, Object key) {
|
public final ObjectValue getMemberDescriptor(Context ctx, Object key) {
|
||||||
key = Values.normalize(ctx, key);
|
key = Values.normalize(ctx, key);
|
||||||
|
|
||||||
var prop = properties.get(key);
|
var prop = properties.get(key);
|
||||||
var res = new ObjectValue();
|
var res = new ObjectValue();
|
||||||
|
|
||||||
res.defineProperty(ctx, "configurable", memberConfigurable(key));
|
res.defineProperty(ctx, "configurable", memberConfigurable(key));
|
||||||
res.defineProperty(ctx, "enumerable", memberEnumerable(key));
|
res.defineProperty(ctx, "enumerable", memberEnumerable(key));
|
||||||
|
|
||||||
if (prop != null) {
|
if (prop != null) {
|
||||||
res.defineProperty(ctx, "get", prop.getter);
|
res.defineProperty(ctx, "get", prop.getter);
|
||||||
res.defineProperty(ctx, "set", prop.setter);
|
res.defineProperty(ctx, "set", prop.setter);
|
||||||
}
|
}
|
||||||
else if (hasField(ctx, key)) {
|
else if (hasField(ctx, key)) {
|
||||||
res.defineProperty(ctx, "value", values.get(key));
|
res.defineProperty(ctx, "value", values.get(key));
|
||||||
res.defineProperty(ctx, "writable", memberWritable(key));
|
res.defineProperty(ctx, "writable", memberWritable(key));
|
||||||
}
|
}
|
||||||
else return null;
|
else return null;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Object> keys(boolean includeNonEnumerable) {
|
public List<Object> keys(boolean includeNonEnumerable) {
|
||||||
var res = new ArrayList<Object>();
|
var res = new ArrayList<Object>();
|
||||||
|
|
||||||
for (var key : values.keySet()) {
|
for (var key : values.keySet()) {
|
||||||
if (nonEnumerableSet.contains(key) && !includeNonEnumerable) continue;
|
if (nonEnumerableSet.contains(key) && !includeNonEnumerable) continue;
|
||||||
res.add(key);
|
res.add(key);
|
||||||
}
|
}
|
||||||
for (var key : properties.keySet()) {
|
for (var key : properties.keySet()) {
|
||||||
if (nonEnumerableSet.contains(key) && !includeNonEnumerable) continue;
|
if (nonEnumerableSet.contains(key) && !includeNonEnumerable) continue;
|
||||||
res.add(key);
|
res.add(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObjectValue(Context ctx, Map<?, ?> values) {
|
public ObjectValue(Context ctx, Map<?, ?> values) {
|
||||||
this(PlaceholderProto.OBJECT);
|
this(PlaceholderProto.OBJECT);
|
||||||
for (var el : values.entrySet()) {
|
for (var el : values.entrySet()) {
|
||||||
defineProperty(ctx, el.getKey(), el.getValue());
|
defineProperty(ctx, el.getKey(), el.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public ObjectValue(PlaceholderProto proto) {
|
public ObjectValue(PlaceholderProto proto) {
|
||||||
nonConfigurableSet.add("__proto__");
|
nonConfigurableSet.add("__proto__");
|
||||||
nonEnumerableSet.add("__proto__");
|
nonEnumerableSet.add("__proto__");
|
||||||
setPrototype(proto);
|
setPrototype(proto);
|
||||||
}
|
}
|
||||||
public ObjectValue() {
|
public ObjectValue() {
|
||||||
this(PlaceholderProto.OBJECT);
|
this(PlaceholderProto.OBJECT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,54 +1,54 @@
|
|||||||
package me.topchetoeu.jscript.engine.values;
|
package me.topchetoeu.jscript.engine.values;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
||||||
|
|
||||||
public class ScopeValue extends ObjectValue {
|
public class ScopeValue extends ObjectValue {
|
||||||
public final ValueVariable[] variables;
|
public final ValueVariable[] variables;
|
||||||
public final HashMap<String, Integer> names = new HashMap<>();
|
public final HashMap<String, Integer> names = new HashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object getField(Context ctx, Object key) {
|
protected Object getField(Context ctx, Object key) {
|
||||||
if (names.containsKey(key)) return variables[names.get(key)].get(ctx);
|
if (names.containsKey(key)) return variables[names.get(key)].get(ctx);
|
||||||
return super.getField(ctx, key);
|
return super.getField(ctx, key);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
protected boolean setField(Context ctx, Object key, Object val) {
|
protected boolean setField(Context ctx, Object key, Object val) {
|
||||||
if (names.containsKey(key)) {
|
if (names.containsKey(key)) {
|
||||||
variables[names.get(key)].set(ctx, val);
|
variables[names.get(key)].set(ctx, val);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var proto = getPrototype(ctx);
|
var proto = getPrototype(ctx);
|
||||||
if (proto != null && proto.hasField(ctx, key) && proto.setField(ctx, key, val)) return true;
|
if (proto != null && proto.hasField(ctx, key) && proto.setField(ctx, key, val)) return true;
|
||||||
|
|
||||||
return super.setField(ctx, key, val);
|
return super.setField(ctx, key, val);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
protected void deleteField(Context ctx, Object key) {
|
protected void deleteField(Context ctx, Object key) {
|
||||||
if (names.containsKey(key)) return;
|
if (names.containsKey(key)) return;
|
||||||
super.deleteField(ctx, key);
|
super.deleteField(ctx, key);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
protected boolean hasField(Context ctx, Object key) {
|
protected boolean hasField(Context ctx, Object key) {
|
||||||
if (names.containsKey(key)) return true;
|
if (names.containsKey(key)) return true;
|
||||||
return super.hasField(ctx, key);
|
return super.hasField(ctx, key);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public List<Object> keys(boolean includeNonEnumerable) {
|
public List<Object> keys(boolean includeNonEnumerable) {
|
||||||
var res = super.keys(includeNonEnumerable);
|
var res = super.keys(includeNonEnumerable);
|
||||||
res.addAll(names.keySet());
|
res.addAll(names.keySet());
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ScopeValue(ValueVariable[] variables, String[] names) {
|
public ScopeValue(ValueVariable[] variables, String[] names) {
|
||||||
this.variables = variables;
|
this.variables = variables;
|
||||||
for (var i = 0; i < names.length && i < variables.length; i++) {
|
for (var i = 0; i < names.length && i < variables.length; i++) {
|
||||||
this.names.put(names[i], i);
|
this.names.put(names[i], i);
|
||||||
this.nonConfigurableSet.add(names[i]);
|
this.nonConfigurableSet.add(names[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
package me.topchetoeu.jscript.engine.values;
|
package me.topchetoeu.jscript.engine.values;
|
||||||
|
|
||||||
public final class Symbol {
|
public final class Symbol {
|
||||||
public final String value;
|
public final String value;
|
||||||
|
|
||||||
public Symbol(String value) {
|
public Symbol(String value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if (value == null) return "Symbol";
|
if (value == null) return "Symbol";
|
||||||
else return "@@" + value;
|
else return "@@" + value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -741,7 +741,7 @@ public class Values {
|
|||||||
try {
|
try {
|
||||||
if (err instanceof EngineException) {
|
if (err instanceof EngineException) {
|
||||||
var ee = ((EngineException)err);
|
var ee = ((EngineException)err);
|
||||||
System.out.println(prefix + " " + ee.toString(new Context(ee.engine).pushEnv(ee.env)));
|
System.out.println(prefix + " " + ee.toString(new Context(ee.engine, ee.env)));
|
||||||
}
|
}
|
||||||
else if (err instanceof SyntaxException) {
|
else if (err instanceof SyntaxException) {
|
||||||
System.out.println("Syntax error:" + ((SyntaxException)err).msg);
|
System.out.println("Syntax error:" + ((SyntaxException)err).msg);
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
package me.topchetoeu.jscript.events;
|
package me.topchetoeu.jscript.events;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.exceptions.InterruptException;
|
import me.topchetoeu.jscript.exceptions.InterruptException;
|
||||||
|
|
||||||
public interface Awaitable<T> {
|
public interface Awaitable<T> {
|
||||||
T await() throws FinishedException;
|
T await() throws FinishedException;
|
||||||
|
|
||||||
default Observable<T> toObservable() {
|
default Observable<T> toObservable() {
|
||||||
return sub -> {
|
return sub -> {
|
||||||
var thread = new Thread(() -> {
|
var thread = new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
sub.next(await());
|
sub.next(await());
|
||||||
sub.finish();
|
sub.finish();
|
||||||
}
|
}
|
||||||
catch (InterruptException | FinishedException e) { sub.finish(); }
|
catch (InterruptException | FinishedException e) { sub.finish(); }
|
||||||
catch (RuntimeException e) {
|
catch (RuntimeException e) {
|
||||||
sub.error(e);
|
sub.error(e);
|
||||||
}
|
}
|
||||||
}, "Awaiter");
|
}, "Awaiter");
|
||||||
thread.start();
|
thread.start();
|
||||||
|
|
||||||
return () -> thread.interrupt();
|
return () -> thread.interrupt();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,34 +1,34 @@
|
|||||||
package me.topchetoeu.jscript.events;
|
package me.topchetoeu.jscript.events;
|
||||||
|
|
||||||
public class DataNotifier<T> implements Awaitable<T> {
|
public class DataNotifier<T> implements Awaitable<T> {
|
||||||
private Notifier notifier = new Notifier();
|
private Notifier notifier = new Notifier();
|
||||||
private boolean isErr;
|
private boolean isErr;
|
||||||
private T val;
|
private T val;
|
||||||
private RuntimeException err;
|
private RuntimeException err;
|
||||||
|
|
||||||
public void error(RuntimeException t) {
|
public void error(RuntimeException t) {
|
||||||
err = t;
|
err = t;
|
||||||
isErr = true;
|
isErr = true;
|
||||||
notifier.next();
|
notifier.next();
|
||||||
}
|
}
|
||||||
public void error(Throwable t) {
|
public void error(Throwable t) {
|
||||||
error(new RuntimeException(t));
|
error(new RuntimeException(t));
|
||||||
}
|
}
|
||||||
public void next(T val) {
|
public void next(T val) {
|
||||||
this.val = val;
|
this.val = val;
|
||||||
isErr = false;
|
isErr = false;
|
||||||
notifier.next();
|
notifier.next();
|
||||||
}
|
}
|
||||||
public T await() {
|
public T await() {
|
||||||
notifier.await();
|
notifier.await();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (isErr) throw err;
|
if (isErr) throw err;
|
||||||
else return val;
|
else return val;
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
this.err = null;
|
this.err = null;
|
||||||
this.val = null;
|
this.val = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,49 +1,49 @@
|
|||||||
package me.topchetoeu.jscript.events;
|
package me.topchetoeu.jscript.events;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
public class Event<T> implements Observer<T>, Observable<T> {
|
public class Event<T> implements Observer<T>, Observable<T> {
|
||||||
private HashSet<Observer<T>> handlers = new HashSet<>();
|
private HashSet<Observer<T>> handlers = new HashSet<>();
|
||||||
|
|
||||||
public Handle on(Observer<T> handler) {
|
public Handle on(Observer<T> handler) {
|
||||||
if (handlers == null) {
|
if (handlers == null) {
|
||||||
handler.finish();
|
handler.finish();
|
||||||
return () -> {};
|
return () -> {};
|
||||||
}
|
}
|
||||||
|
|
||||||
handlers.add(handler);
|
handlers.add(handler);
|
||||||
return () -> {
|
return () -> {
|
||||||
if (handlers == null) return;
|
if (handlers == null) return;
|
||||||
handlers.remove(handler);
|
handlers.remove(handler);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFinished() {
|
public boolean isFinished() {
|
||||||
return handlers == null;
|
return handlers == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void next(T value) {
|
public void next(T value) {
|
||||||
if (handlers == null) throw new IllegalStateException("Cannot use a finished event.");
|
if (handlers == null) throw new IllegalStateException("Cannot use a finished event.");
|
||||||
for (var handler : handlers) {
|
for (var handler : handlers) {
|
||||||
handler.next(value);
|
handler.next(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void error(RuntimeException value) {
|
public void error(RuntimeException value) {
|
||||||
if (handlers == null) throw new IllegalStateException("Cannot use a finished event.");
|
if (handlers == null) throw new IllegalStateException("Cannot use a finished event.");
|
||||||
for (var handler : handlers) {
|
for (var handler : handlers) {
|
||||||
handler.error(value);
|
handler.error(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
handlers.clear();
|
handlers.clear();
|
||||||
handlers = null;
|
handlers = null;
|
||||||
}
|
}
|
||||||
public void finish() {
|
public void finish() {
|
||||||
if (handlers == null) throw new IllegalStateException("Cannot use a finished event.");
|
if (handlers == null) throw new IllegalStateException("Cannot use a finished event.");
|
||||||
for (var handler : handlers) {
|
for (var handler : handlers) {
|
||||||
handler.finish();
|
handler.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
handlers.clear();
|
handlers.clear();
|
||||||
handlers = null;
|
handlers = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package me.topchetoeu.jscript.events;
|
package me.topchetoeu.jscript.events;
|
||||||
|
|
||||||
public class FinishedException extends RuntimeException {
|
public class FinishedException extends RuntimeException {
|
||||||
public FinishedException() {
|
public FinishedException() {
|
||||||
super("The observable has ended.");
|
super("The observable has ended.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
package me.topchetoeu.jscript.events;
|
package me.topchetoeu.jscript.events;
|
||||||
|
|
||||||
public interface Handle {
|
public interface Handle {
|
||||||
void free();
|
void free();
|
||||||
}
|
}
|
@ -1,19 +1,19 @@
|
|||||||
package me.topchetoeu.jscript.events;
|
package me.topchetoeu.jscript.events;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.exceptions.InterruptException;
|
import me.topchetoeu.jscript.exceptions.InterruptException;
|
||||||
|
|
||||||
public class Notifier {
|
public class Notifier {
|
||||||
private boolean ok = false;
|
private boolean ok = false;
|
||||||
|
|
||||||
public synchronized void next() {
|
public synchronized void next() {
|
||||||
ok = true;
|
ok = true;
|
||||||
notifyAll();
|
notifyAll();
|
||||||
}
|
}
|
||||||
public synchronized void await() {
|
public synchronized void await() {
|
||||||
try {
|
try {
|
||||||
while (!ok) wait();
|
while (!ok) wait();
|
||||||
ok = false;
|
ok = false;
|
||||||
}
|
}
|
||||||
catch (InterruptedException e) { throw new InterruptException(e); }
|
catch (InterruptedException e) { throw new InterruptException(e); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,75 +1,75 @@
|
|||||||
package me.topchetoeu.jscript.events;
|
package me.topchetoeu.jscript.events;
|
||||||
|
|
||||||
public interface Observable<T> {
|
public interface Observable<T> {
|
||||||
Handle on(Observer<T> val);
|
Handle on(Observer<T> val);
|
||||||
|
|
||||||
default Handle once(Observer<T> observer) {
|
default Handle once(Observer<T> observer) {
|
||||||
// Java is fucking retarded
|
// Java is fucking retarded
|
||||||
var unhandler = new Handle[1];
|
var unhandler = new Handle[1];
|
||||||
var shouldUnsub = new boolean[1];
|
var shouldUnsub = new boolean[1];
|
||||||
|
|
||||||
unhandler[0] = on(new Observer<>() {
|
unhandler[0] = on(new Observer<>() {
|
||||||
public void next(T data) {
|
public void next(T data) {
|
||||||
observer.next(data);
|
observer.next(data);
|
||||||
if (unhandler[0] == null) shouldUnsub[0] = true;
|
if (unhandler[0] == null) shouldUnsub[0] = true;
|
||||||
else unhandler[0].free();
|
else unhandler[0].free();
|
||||||
}
|
}
|
||||||
public void error(RuntimeException err) {
|
public void error(RuntimeException err) {
|
||||||
observer.error(err);
|
observer.error(err);
|
||||||
if (unhandler[0] == null) shouldUnsub[0] = true;
|
if (unhandler[0] == null) shouldUnsub[0] = true;
|
||||||
else unhandler[0].free();
|
else unhandler[0].free();
|
||||||
}
|
}
|
||||||
public void finish() {
|
public void finish() {
|
||||||
observer.finish();
|
observer.finish();
|
||||||
if (unhandler[0] == null) shouldUnsub[0] = true;
|
if (unhandler[0] == null) shouldUnsub[0] = true;
|
||||||
else unhandler[0].free();
|
else unhandler[0].free();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (shouldUnsub[0]) {
|
if (shouldUnsub[0]) {
|
||||||
unhandler[0].free();
|
unhandler[0].free();
|
||||||
return () -> {};
|
return () -> {};
|
||||||
}
|
}
|
||||||
else return unhandler[0];
|
else return unhandler[0];
|
||||||
}
|
}
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
default Awaitable<T> toAwaitable() {
|
default Awaitable<T> toAwaitable() {
|
||||||
return () -> {
|
return () -> {
|
||||||
var notifier = new Notifier();
|
var notifier = new Notifier();
|
||||||
var valRef = new Object[1];
|
var valRef = new Object[1];
|
||||||
var isErrRef = new boolean[1];
|
var isErrRef = new boolean[1];
|
||||||
|
|
||||||
once(new Observer<>() {
|
once(new Observer<>() {
|
||||||
public void next(T data) {
|
public void next(T data) {
|
||||||
valRef[0] = data;
|
valRef[0] = data;
|
||||||
notifier.next();
|
notifier.next();
|
||||||
}
|
}
|
||||||
public void error(RuntimeException err) {
|
public void error(RuntimeException err) {
|
||||||
isErrRef[0] = true;
|
isErrRef[0] = true;
|
||||||
valRef[0] = err;
|
valRef[0] = err;
|
||||||
notifier.next();
|
notifier.next();
|
||||||
}
|
}
|
||||||
public void finish() {
|
public void finish() {
|
||||||
isErrRef[0] = true;
|
isErrRef[0] = true;
|
||||||
valRef[0] = new FinishedException();
|
valRef[0] = new FinishedException();
|
||||||
notifier.next();
|
notifier.next();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
notifier.await();
|
notifier.await();
|
||||||
|
|
||||||
if (isErrRef[0]) throw (RuntimeException)valRef[0];
|
if (isErrRef[0]) throw (RuntimeException)valRef[0];
|
||||||
else return (T)valRef[0];
|
else return (T)valRef[0];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
default Observable<T> encapsulate() {
|
default Observable<T> encapsulate() {
|
||||||
return val -> on(val);
|
return val -> on(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
default <T2> Observable<T2> pipe(Pipe<T, T2> pipe) {
|
default <T2> Observable<T2> pipe(Pipe<T, T2> pipe) {
|
||||||
return sub -> on(pipe.apply(sub));
|
return sub -> on(pipe.apply(sub));
|
||||||
}
|
}
|
||||||
default WarmObservable<T> warmUp() {
|
default WarmObservable<T> warmUp() {
|
||||||
return new WarmObservable<>(this);
|
return new WarmObservable<>(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package me.topchetoeu.jscript.events;
|
package me.topchetoeu.jscript.events;
|
||||||
|
|
||||||
public interface Observer<T> {
|
public interface Observer<T> {
|
||||||
public void next(T data);
|
public void next(T data);
|
||||||
public default void error(RuntimeException err) {}
|
public default void error(RuntimeException err) {}
|
||||||
public default void finish() { }
|
public default void finish() { }
|
||||||
}
|
}
|
@ -1,59 +1,59 @@
|
|||||||
package me.topchetoeu.jscript.events;
|
package me.topchetoeu.jscript.events;
|
||||||
|
|
||||||
public interface Pipe<T, T2> {
|
public interface Pipe<T, T2> {
|
||||||
Observer<T> apply(Observer<T2> obs);
|
Observer<T> apply(Observer<T2> obs);
|
||||||
// void next(T val, Observer<T2> target);
|
// void next(T val, Observer<T2> target);
|
||||||
// default void error(RuntimeException err, Observer<T2> target) {
|
// default void error(RuntimeException err, Observer<T2> target) {
|
||||||
// target.error(err);
|
// target.error(err);
|
||||||
// }
|
// }
|
||||||
// default void finish(Observer<T2> target) {
|
// default void finish(Observer<T2> target) {
|
||||||
// target.finish();
|
// target.finish();
|
||||||
// }
|
// }
|
||||||
|
|
||||||
public static interface MapFunc<T1, T2> {
|
public static interface MapFunc<T1, T2> {
|
||||||
T2 map(T1 val);
|
T2 map(T1 val);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T1, T2> Pipe<T1, T2> map(MapFunc<T1, T2> func) {
|
public static <T1, T2> Pipe<T1, T2> map(MapFunc<T1, T2> func) {
|
||||||
return o -> val -> o.next(func.map(val));
|
return o -> val -> o.next(func.map(val));
|
||||||
}
|
}
|
||||||
public static <T> Pipe<T, T> filter(MapFunc<T, Boolean> func) {
|
public static <T> Pipe<T, T> filter(MapFunc<T, Boolean> func) {
|
||||||
return o -> val -> {
|
return o -> val -> {
|
||||||
if (func.map(val)) o.next(val);
|
if (func.map(val)) o.next(val);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
public static <T> Pipe<T, T> skip(int n) {
|
public static <T> Pipe<T, T> skip(int n) {
|
||||||
var i = new int[1];
|
var i = new int[1];
|
||||||
|
|
||||||
return target -> val -> {
|
return target -> val -> {
|
||||||
if (i[0] >= n) target.next(val);
|
if (i[0] >= n) target.next(val);
|
||||||
else i[0]++;
|
else i[0]++;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
public static <T> Pipe<T, T> limit(int n) {
|
public static <T> Pipe<T, T> limit(int n) {
|
||||||
return target -> new Observer<T>() {
|
return target -> new Observer<T>() {
|
||||||
private int i;
|
private int i;
|
||||||
|
|
||||||
public void next(T val) {
|
public void next(T val) {
|
||||||
if (i >= n) target.finish();
|
if (i >= n) target.finish();
|
||||||
else {
|
else {
|
||||||
target.next(val);
|
target.next(val);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void error(RuntimeException err) {
|
public void error(RuntimeException err) {
|
||||||
if (i < n) target.error(err);
|
if (i < n) target.error(err);
|
||||||
}
|
}
|
||||||
public void finish() {
|
public void finish() {
|
||||||
if (i < n) target.finish();
|
if (i < n) target.finish();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
public static <T> Pipe<T, T> first() {
|
public static <T> Pipe<T, T> first() {
|
||||||
return limit(1);
|
return limit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> Pipe<Observable<T>, T> merge() {
|
public static <T> Pipe<Observable<T>, T> merge() {
|
||||||
return target -> val -> val.on(target);
|
return target -> val -> val.on(target);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,46 +1,46 @@
|
|||||||
package me.topchetoeu.jscript.events;
|
package me.topchetoeu.jscript.events;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
public class WarmObservable<T> implements Observable<T>, Handle {
|
public class WarmObservable<T> implements Observable<T>, Handle {
|
||||||
private HashSet<Observer<T>> observers = new HashSet<>();
|
private HashSet<Observer<T>> observers = new HashSet<>();
|
||||||
private Handle handle;
|
private Handle handle;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Handle on(Observer<T> val) {
|
public Handle on(Observer<T> val) {
|
||||||
if (observers == null) return () -> {};
|
if (observers == null) return () -> {};
|
||||||
observers.add(val);
|
observers.add(val);
|
||||||
return () -> observers.remove(val);
|
return () -> observers.remove(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void free() {
|
public void free() {
|
||||||
if (observers == null) return;
|
if (observers == null) return;
|
||||||
handle.free();
|
handle.free();
|
||||||
handle = null;
|
handle = null;
|
||||||
observers = null;
|
observers = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WarmObservable(Observable<T> observable) {
|
public WarmObservable(Observable<T> observable) {
|
||||||
observable.on(new Observer<>() {
|
observable.on(new Observer<>() {
|
||||||
public void next(T data) {
|
public void next(T data) {
|
||||||
for (var obs : observers) obs.next(data);
|
for (var obs : observers) obs.next(data);
|
||||||
}
|
}
|
||||||
public void error(RuntimeException err) {
|
public void error(RuntimeException err) {
|
||||||
for (var obs : observers) obs.error(err);
|
for (var obs : observers) obs.error(err);
|
||||||
handle = null;
|
handle = null;
|
||||||
observers = null;
|
observers = null;
|
||||||
}
|
}
|
||||||
public void finish() {
|
public void finish() {
|
||||||
for (var obs : observers) obs.finish();
|
for (var obs : observers) obs.finish();
|
||||||
handle = null;
|
handle = null;
|
||||||
observers = null;
|
observers = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WarmObservable<T> warmUp() {
|
public WarmObservable<T> warmUp() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,117 +1,117 @@
|
|||||||
package me.topchetoeu.jscript.exceptions;
|
package me.topchetoeu.jscript.exceptions;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.Engine;
|
import me.topchetoeu.jscript.engine.Engine;
|
||||||
import me.topchetoeu.jscript.engine.Environment;
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue.PlaceholderProto;
|
import me.topchetoeu.jscript.engine.values.ObjectValue.PlaceholderProto;
|
||||||
|
|
||||||
public class EngineException extends RuntimeException {
|
public class EngineException extends RuntimeException {
|
||||||
public static class StackElement {
|
public static class StackElement {
|
||||||
public final Location location;
|
public final Location location;
|
||||||
public final String function;
|
public final String function;
|
||||||
public final Context ctx;
|
public final Context ctx;
|
||||||
|
|
||||||
public boolean visible() {
|
public boolean visible() {
|
||||||
return ctx == null || ctx.environment() == null || ctx.environment().stackVisible;
|
return ctx == null || ctx.environment() == null || ctx.environment().stackVisible;
|
||||||
}
|
}
|
||||||
public String toString() {
|
public String toString() {
|
||||||
var res = "";
|
var res = "";
|
||||||
var loc = location;
|
var loc = location;
|
||||||
|
|
||||||
if (loc != null && ctx != null && ctx.engine != null) loc = ctx.engine.mapToCompiled(loc);
|
if (loc != null && ctx != null && ctx.engine != null) loc = ctx.engine.mapToCompiled(loc);
|
||||||
|
|
||||||
if (loc != null) res += "at " + loc.toString() + " ";
|
if (loc != null) res += "at " + loc.toString() + " ";
|
||||||
if (function != null && !function.equals("")) res += "in " + function + " ";
|
if (function != null && !function.equals("")) res += "in " + function + " ";
|
||||||
|
|
||||||
return res.trim();
|
return res.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
public StackElement(Context ctx, Location location, String function) {
|
public StackElement(Context ctx, Location location, String function) {
|
||||||
if (function != null) function = function.trim();
|
if (function != null) function = function.trim();
|
||||||
if (function.equals("")) function = null;
|
if (function.equals("")) function = null;
|
||||||
|
|
||||||
if (ctx == null) this.ctx = null;
|
if (ctx == null) this.ctx = null;
|
||||||
else this.ctx = new Context(ctx.engine).pushEnv(ctx.environment());
|
else this.ctx = new Context(ctx.engine, ctx.environment());
|
||||||
this.location = location;
|
this.location = location;
|
||||||
this.function = function;
|
this.function = function;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final Object value;
|
public final Object value;
|
||||||
public EngineException cause;
|
public EngineException cause;
|
||||||
public Environment env = null;
|
public Environment env = null;
|
||||||
public Engine engine = null;
|
public Engine engine = null;
|
||||||
public final List<StackElement> stackTrace = new ArrayList<>();
|
public final List<StackElement> stackTrace = new ArrayList<>();
|
||||||
|
|
||||||
public EngineException add(Context ctx, String name, Location location) {
|
public EngineException add(Context ctx, String name, Location location) {
|
||||||
var el = new StackElement(ctx, location, name);
|
var el = new StackElement(ctx, location, name);
|
||||||
if (el.function == null && el.location == null) return this;
|
if (el.function == null && el.location == null) return this;
|
||||||
setCtx(ctx.environment(), ctx.engine);
|
setCtx(ctx.environment(), ctx.engine);
|
||||||
stackTrace.add(el);
|
stackTrace.add(el);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
public EngineException setCause(EngineException cause) {
|
public EngineException setCause(EngineException cause) {
|
||||||
this.cause = cause;
|
this.cause = cause;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
public EngineException setCtx(Environment env, Engine engine) {
|
public EngineException setCtx(Environment env, Engine engine) {
|
||||||
if (this.env == null) this.env = env;
|
if (this.env == null) this.env = env;
|
||||||
if (this.engine == null) this.engine = engine;
|
if (this.engine == null) this.engine = engine;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString(Context ctx) {
|
public String toString(Context ctx) {
|
||||||
var ss = new StringBuilder();
|
var ss = new StringBuilder();
|
||||||
try {
|
try {
|
||||||
ss.append(Values.toString(ctx, value)).append('\n');
|
ss.append(Values.toString(ctx, value)).append('\n');
|
||||||
}
|
}
|
||||||
catch (EngineException e) {
|
catch (EngineException e) {
|
||||||
ss.append("[Error while stringifying]\n");
|
ss.append("[Error while stringifying]\n");
|
||||||
}
|
}
|
||||||
for (var line : stackTrace) {
|
for (var line : stackTrace) {
|
||||||
if (line.visible()) ss.append(" ").append(line.toString()).append("\n");
|
if (line.visible()) ss.append(" ").append(line.toString()).append("\n");
|
||||||
}
|
}
|
||||||
if (cause != null) ss.append("Caused by ").append(cause.toString(ctx)).append('\n');
|
if (cause != null) ss.append("Caused by ").append(cause.toString(ctx)).append('\n');
|
||||||
ss.deleteCharAt(ss.length() - 1);
|
ss.deleteCharAt(ss.length() - 1);
|
||||||
return ss.toString();
|
return ss.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Object err(String name, String msg, PlaceholderProto proto) {
|
private static Object err(String name, String msg, PlaceholderProto proto) {
|
||||||
var res = new ObjectValue(proto);
|
var res = new ObjectValue(proto);
|
||||||
if (name != null) res.defineProperty(null, "name", name);
|
if (name != null) res.defineProperty(null, "name", name);
|
||||||
res.defineProperty(null, "message", msg);
|
res.defineProperty(null, "message", msg);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EngineException(Object error) {
|
public EngineException(Object error) {
|
||||||
super(error == null ? "null" : error.toString());
|
super(error == null ? "null" : error.toString());
|
||||||
|
|
||||||
this.value = error;
|
this.value = error;
|
||||||
this.cause = null;
|
this.cause = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EngineException ofError(String name, String msg) {
|
public static EngineException ofError(String name, String msg) {
|
||||||
return new EngineException(err(name, msg, PlaceholderProto.ERROR));
|
return new EngineException(err(name, msg, PlaceholderProto.ERROR));
|
||||||
}
|
}
|
||||||
public static EngineException ofError(String msg) {
|
public static EngineException ofError(String msg) {
|
||||||
return new EngineException(err(null, msg, PlaceholderProto.ERROR));
|
return new EngineException(err(null, msg, PlaceholderProto.ERROR));
|
||||||
}
|
}
|
||||||
public static EngineException ofSyntax(SyntaxException e) {
|
public static EngineException ofSyntax(SyntaxException e) {
|
||||||
return new EngineException(err(null, e.msg, PlaceholderProto.SYNTAX_ERROR)).add(null, null, e.loc);
|
return new EngineException(err(null, e.msg, PlaceholderProto.SYNTAX_ERROR)).add(null, null, e.loc);
|
||||||
}
|
}
|
||||||
public static EngineException ofSyntax(String msg) {
|
public static EngineException ofSyntax(String msg) {
|
||||||
return new EngineException(err(null, msg, PlaceholderProto.SYNTAX_ERROR));
|
return new EngineException(err(null, msg, PlaceholderProto.SYNTAX_ERROR));
|
||||||
}
|
}
|
||||||
public static EngineException ofType(String msg) {
|
public static EngineException ofType(String msg) {
|
||||||
return new EngineException(err(null, msg, PlaceholderProto.TYPE_ERROR));
|
return new EngineException(err(null, msg, PlaceholderProto.TYPE_ERROR));
|
||||||
}
|
}
|
||||||
public static EngineException ofRange(String msg) {
|
public static EngineException ofRange(String msg) {
|
||||||
return new EngineException(err(null, msg, PlaceholderProto.RANGE_ERROR));
|
return new EngineException(err(null, msg, PlaceholderProto.RANGE_ERROR));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
package me.topchetoeu.jscript.exceptions;
|
package me.topchetoeu.jscript.exceptions;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
|
|
||||||
public class SyntaxException extends RuntimeException {
|
public class SyntaxException extends RuntimeException {
|
||||||
public final Location loc;
|
public final Location loc;
|
||||||
public final String msg;
|
public final String msg;
|
||||||
|
|
||||||
public SyntaxException(Location loc, String msg) {
|
public SyntaxException(Location loc, String msg) {
|
||||||
super(String.format("Syntax error (at %s): %s", loc, msg));
|
super(String.format("Syntax error (at %s): %s", loc, msg));
|
||||||
this.loc = loc;
|
this.loc = loc;
|
||||||
this.msg = msg;
|
this.msg = msg;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,41 +1,37 @@
|
|||||||
package me.topchetoeu.jscript.filesystem;
|
package me.topchetoeu.jscript.filesystem;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Buffer;
|
import me.topchetoeu.jscript.Buffer;
|
||||||
|
|
||||||
public interface File {
|
public interface File {
|
||||||
int read(byte[] buff);
|
int read(byte[] buff);
|
||||||
void write(byte[] buff);
|
void write(byte[] buff);
|
||||||
long getPtr();
|
long seek(long offset, int pos);
|
||||||
void setPtr(long offset, int pos);
|
void close();
|
||||||
void close();
|
|
||||||
Mode mode();
|
default String readToString() {
|
||||||
|
long len = seek(0, 2);
|
||||||
default String readToString() {
|
if (len < 0) return null;
|
||||||
setPtr(0, 2);
|
seek(0, 0);
|
||||||
long len = getPtr();
|
|
||||||
if (len < 0) return null;
|
byte[] res = new byte[(int)len];
|
||||||
|
len = read(res);
|
||||||
setPtr(0, 0);
|
|
||||||
|
return new String(res);
|
||||||
byte[] res = new byte[(int)len];
|
}
|
||||||
read(res);
|
default String readLine() {
|
||||||
|
var res = new Buffer();
|
||||||
return new String(res);
|
var buff = new byte[1];
|
||||||
}
|
|
||||||
default String readLine() {
|
while (true) {
|
||||||
var res = new Buffer();
|
if (read(buff) == 0) {
|
||||||
var buff = new byte[1];
|
if (res.length() == 0) return null;
|
||||||
|
else break;
|
||||||
while (true) {
|
}
|
||||||
if (read(buff) == 0) {
|
|
||||||
if (res.length() == 0) return null;
|
if (buff[0] == '\n') break;
|
||||||
else break;
|
|
||||||
}
|
res.write(res.length(), buff);
|
||||||
|
}
|
||||||
if (buff[0] == '\n') break;
|
return new String(res.data());
|
||||||
|
}
|
||||||
res.write(res.length(), buff);
|
|
||||||
}
|
|
||||||
return new String(res.data());
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package me.topchetoeu.jscript.filesystem;
|
package me.topchetoeu.jscript.filesystem;
|
||||||
|
|
||||||
public interface Filesystem {
|
public interface Filesystem {
|
||||||
|
String normalize(String... path);
|
||||||
File open(String path, Mode mode) throws FilesystemException;
|
File open(String path, Mode mode) throws FilesystemException;
|
||||||
void create(String path, EntryType type) throws FilesystemException;
|
void create(String path, EntryType type) throws FilesystemException;
|
||||||
FileStat stat(String path) throws FilesystemException;
|
FileStat stat(String path) throws FilesystemException;
|
||||||
|
@ -12,7 +12,8 @@ public class FilesystemException extends RuntimeException {
|
|||||||
NO_PERMISSIONS_RW(0x5),
|
NO_PERMISSIONS_RW(0x5),
|
||||||
FOLDER_NOT_EMPTY(0x6),
|
FOLDER_NOT_EMPTY(0x6),
|
||||||
ALREADY_EXISTS(0x7),
|
ALREADY_EXISTS(0x7),
|
||||||
FOLDER_EXISTS(0x8);
|
FOLDER_EXISTS(0x8),
|
||||||
|
UNSUPPORTED_OPERATION(0x9);
|
||||||
|
|
||||||
public final int code;
|
public final int code;
|
||||||
|
|
||||||
@ -27,7 +28,8 @@ public class FilesystemException extends RuntimeException {
|
|||||||
"No permissions to read '%s'",
|
"No permissions to read '%s'",
|
||||||
"No permissions to write '%s'",
|
"No permissions to write '%s'",
|
||||||
"Can't delete '%s', since it is a full folder.",
|
"Can't delete '%s', since it is a full folder.",
|
||||||
"'%s' already exists."
|
"'%s' already exists.",
|
||||||
|
"An unsupported operation was performed on the file '%s'."
|
||||||
};
|
};
|
||||||
|
|
||||||
public final String message, filename;
|
public final String message, filename;
|
||||||
|
73
src/me/topchetoeu/jscript/filesystem/ListFile.java
Normal file
73
src/me/topchetoeu/jscript/filesystem/ListFile.java
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package me.topchetoeu.jscript.filesystem;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode;
|
||||||
|
|
||||||
|
public class ListFile implements File {
|
||||||
|
private Iterator<String> it;
|
||||||
|
private String filename;
|
||||||
|
private byte[] currFile;
|
||||||
|
private long ptr = 0, start = 0, end = 0;
|
||||||
|
|
||||||
|
private void next() {
|
||||||
|
if (it != null && it.hasNext()) {
|
||||||
|
start = end;
|
||||||
|
currFile = (it.next() + "\n").getBytes();
|
||||||
|
end = start + currFile.length;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
it = null;
|
||||||
|
currFile = null;
|
||||||
|
end = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
it = null;
|
||||||
|
currFile = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] buff) {
|
||||||
|
if (ptr < start) return 0;
|
||||||
|
if (it == null) return 0;
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
|
while (i < buff.length) {
|
||||||
|
while (i + ptr >= end) {
|
||||||
|
next();
|
||||||
|
if (it == null) return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cpyN = Math.min(currFile.length, buff.length - i);
|
||||||
|
System.arraycopy(currFile, (int)(ptr + i - start), buff, i, cpyN);
|
||||||
|
|
||||||
|
i += cpyN;
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr += i;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long seek(long offset, int pos) {
|
||||||
|
if (pos == 2) throw new FilesystemException(filename, FSCode.UNSUPPORTED_OPERATION);
|
||||||
|
if (pos == 1) offset += ptr;
|
||||||
|
return ptr = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(byte[] buff) {
|
||||||
|
throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_RW);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListFile(String filename, Stream<String> stream) throws IOException {
|
||||||
|
this.it = stream.iterator();
|
||||||
|
this.filename = filename;
|
||||||
|
}
|
||||||
|
}
|
@ -1,67 +1,62 @@
|
|||||||
package me.topchetoeu.jscript.filesystem;
|
package me.topchetoeu.jscript.filesystem;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Buffer;
|
import me.topchetoeu.jscript.Buffer;
|
||||||
import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode;
|
import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode;
|
||||||
|
|
||||||
public class MemoryFile implements File {
|
public class MemoryFile implements File {
|
||||||
private int ptr;
|
private int ptr;
|
||||||
private Mode mode;
|
private Mode mode;
|
||||||
private Buffer data;
|
private Buffer data;
|
||||||
private String filename;
|
private String filename;
|
||||||
|
|
||||||
public Buffer data() { return data; }
|
public Buffer data() { return data; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read(byte[] buff) {
|
public int read(byte[] buff) {
|
||||||
if (data == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
if (data == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
||||||
var res = data.read(ptr, buff);
|
var res = data.read(ptr, buff);
|
||||||
ptr += res;
|
ptr += res;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void write(byte[] buff) {
|
public void write(byte[] buff) {
|
||||||
if (data == null || !mode.writable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_RW);
|
if (data == null || !mode.writable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_RW);
|
||||||
|
|
||||||
data.write(ptr, buff);
|
data.write(ptr, buff);
|
||||||
ptr += buff.length;
|
ptr += buff.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getPtr() {
|
public long seek(long offset, int pos) {
|
||||||
if (data == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
if (data == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
||||||
return ptr;
|
|
||||||
}
|
if (pos == 0) ptr = (int)offset;
|
||||||
@Override
|
else if (pos == 1) ptr += (int)offset;
|
||||||
public void setPtr(long offset, int pos) {
|
else if (pos == 2) ptr = data.length() - (int)offset;
|
||||||
if (data == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
|
||||||
|
if (ptr < 0) ptr = 0;
|
||||||
if (pos == 0) ptr = (int)offset;
|
if (ptr > data.length()) ptr = data.length();
|
||||||
else if (pos == 1) ptr += (int)offset;
|
|
||||||
else if (pos == 2) ptr = data.length() - (int)offset;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
mode = Mode.NONE;
|
mode = Mode.NONE;
|
||||||
ptr = 0;
|
ptr = 0;
|
||||||
}
|
}
|
||||||
@Override
|
|
||||||
public Mode mode() {
|
public MemoryFile(String filename, Buffer buff, Mode mode) {
|
||||||
if (data == null) return Mode.NONE;
|
this.filename = filename;
|
||||||
return mode;
|
this.data = buff;
|
||||||
}
|
this.mode = mode;
|
||||||
|
}
|
||||||
public MemoryFile(String filename, Buffer buff, Mode mode) {
|
|
||||||
this.filename = filename;
|
public static MemoryFile fromFileList(String filename, java.io.File[] list) {
|
||||||
this.data = buff;
|
var res = new StringBuilder();
|
||||||
this.mode = mode;
|
|
||||||
}
|
for (var el : list) res.append(el.getName()).append('\n');
|
||||||
|
|
||||||
public static MemoryFile fromFileList(String filename, java.io.File[] list) {
|
return new MemoryFile(filename, new Buffer(res.toString().getBytes()), Mode.READ);
|
||||||
var res = new StringBuilder();
|
}
|
||||||
|
}
|
||||||
for (var el : list) res.append(el.getName()).append('\n');
|
|
||||||
|
|
||||||
return new MemoryFile(filename, new Buffer(res.toString().getBytes()), Mode.READ);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,90 +1,96 @@
|
|||||||
package me.topchetoeu.jscript.filesystem;
|
package me.topchetoeu.jscript.filesystem;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Buffer;
|
import me.topchetoeu.jscript.Buffer;
|
||||||
import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode;
|
import me.topchetoeu.jscript.Filename;
|
||||||
|
import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode;
|
||||||
public class MemoryFilesystem implements Filesystem {
|
|
||||||
public final Mode mode;
|
public class MemoryFilesystem implements Filesystem {
|
||||||
private HashMap<Path, Buffer> files = new HashMap<>();
|
public final Mode mode;
|
||||||
private HashSet<Path> folders = new HashSet<>();
|
private HashMap<Path, Buffer> files = new HashMap<>();
|
||||||
|
private HashSet<Path> folders = new HashSet<>();
|
||||||
private Path getPath(String name) {
|
|
||||||
return Path.of("/" + name.replace("\\", "/")).normalize();
|
private Path realPath(String path) {
|
||||||
}
|
return Filename.normalize(path);
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public void create(String path, EntryType type) {
|
@Override
|
||||||
var _path = getPath(path);
|
public String normalize(String... path) {
|
||||||
|
return Paths.normalize(path);
|
||||||
switch (type) {
|
}
|
||||||
case FILE:
|
|
||||||
if (!folders.contains(_path.getParent())) throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
@Override
|
||||||
if (folders.contains(_path) || files.containsKey(_path)) throw new FilesystemException(path, FSCode.ALREADY_EXISTS);
|
public void create(String _path, EntryType type) {
|
||||||
if (folders.contains(_path)) throw new FilesystemException(path, FSCode.ALREADY_EXISTS);
|
var path = realPath(_path);
|
||||||
files.put(_path, new Buffer());
|
|
||||||
break;
|
switch (type) {
|
||||||
case FOLDER:
|
case FILE:
|
||||||
if (!folders.contains(_path.getParent())) throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
if (!folders.contains(path.getParent())) throw new FilesystemException(path.toString(), FSCode.DOESNT_EXIST);
|
||||||
if (folders.contains(_path) || files.containsKey(_path)) throw new FilesystemException(path, FSCode.ALREADY_EXISTS);
|
if (folders.contains(path) || files.containsKey(path)) throw new FilesystemException(path.toString(), FSCode.ALREADY_EXISTS);
|
||||||
folders.add(_path);
|
if (folders.contains(path)) throw new FilesystemException(path.toString(), FSCode.ALREADY_EXISTS);
|
||||||
break;
|
files.put(path, new Buffer());
|
||||||
default:
|
break;
|
||||||
case NONE:
|
case FOLDER:
|
||||||
if (!folders.remove(_path) && files.remove(_path) == null) throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
if (!folders.contains(path.getParent())) throw new FilesystemException(_path, FSCode.DOESNT_EXIST);
|
||||||
}
|
if (folders.contains(path) || files.containsKey(path)) throw new FilesystemException(path.toString(), FSCode.ALREADY_EXISTS);
|
||||||
}
|
folders.add(path);
|
||||||
|
break;
|
||||||
@Override
|
default:
|
||||||
public File open(String path, Mode perms) {
|
case NONE:
|
||||||
var _path = getPath(path);
|
if (!folders.remove(path) && files.remove(path) == null) throw new FilesystemException(path.toString(), FSCode.DOESNT_EXIST);
|
||||||
var pcount = _path.getNameCount();
|
}
|
||||||
|
}
|
||||||
if (files.containsKey(_path)) return new MemoryFile(path, files.get(_path), perms);
|
|
||||||
else if (folders.contains(_path)) {
|
@Override
|
||||||
var res = new StringBuilder();
|
public File open(String _path, Mode perms) {
|
||||||
for (var folder : folders) {
|
var path = realPath(_path);
|
||||||
if (pcount + 1 != folder.getNameCount()) continue;
|
var pcount = path.getNameCount();
|
||||||
if (!folder.startsWith(_path)) continue;
|
|
||||||
res.append(folder.toFile().getName()).append('\n');
|
if (files.containsKey(path)) return new MemoryFile(path.toString(), files.get(path), perms);
|
||||||
}
|
else if (folders.contains(path)) {
|
||||||
for (var file : files.keySet()) {
|
var res = new StringBuilder();
|
||||||
if (pcount + 1 != file.getNameCount()) continue;
|
for (var folder : folders) {
|
||||||
if (!file.startsWith(_path)) continue;
|
if (pcount + 1 != folder.getNameCount()) continue;
|
||||||
res.append(file.toFile().getName()).append('\n');
|
if (!folder.startsWith(path)) continue;
|
||||||
}
|
res.append(folder.toFile().getName()).append('\n');
|
||||||
return new MemoryFile(path, new Buffer(res.toString().getBytes()), perms.intersect(Mode.READ));
|
}
|
||||||
}
|
for (var file : files.keySet()) {
|
||||||
else throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
if (pcount + 1 != file.getNameCount()) continue;
|
||||||
}
|
if (!file.startsWith(path)) continue;
|
||||||
|
res.append(file.toFile().getName()).append('\n');
|
||||||
@Override
|
}
|
||||||
public FileStat stat(String path) {
|
return new MemoryFile(path.toString(), new Buffer(res.toString().getBytes()), perms.intersect(Mode.READ));
|
||||||
var _path = getPath(path);
|
}
|
||||||
|
else throw new FilesystemException(path.toString(), FSCode.DOESNT_EXIST);
|
||||||
if (files.containsKey(_path)) return new FileStat(mode, EntryType.FILE);
|
}
|
||||||
else if (folders.contains(_path)) return new FileStat(mode, EntryType.FOLDER);
|
|
||||||
else throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
@Override
|
||||||
}
|
public FileStat stat(String _path) {
|
||||||
|
var path = realPath(_path);
|
||||||
public MemoryFilesystem put(String path, byte[] data) {
|
|
||||||
var _path = getPath(path);
|
if (files.containsKey(path)) return new FileStat(mode, EntryType.FILE);
|
||||||
var _curr = "/";
|
else if (folders.contains(path)) return new FileStat(mode, EntryType.FOLDER);
|
||||||
|
else return new FileStat(Mode.NONE, EntryType.NONE);
|
||||||
for (var seg : _path) {
|
}
|
||||||
create(_curr, EntryType.FOLDER);
|
|
||||||
_curr += seg + "/";
|
public MemoryFilesystem put(String path, byte[] data) {
|
||||||
}
|
var _path = realPath(path);
|
||||||
|
var _curr = "/";
|
||||||
files.put(_path, new Buffer(data));
|
|
||||||
return this;
|
for (var seg : _path) {
|
||||||
}
|
create(_curr, EntryType.FOLDER);
|
||||||
|
_curr += seg + "/";
|
||||||
public MemoryFilesystem(Mode mode) {
|
}
|
||||||
this.mode = mode;
|
|
||||||
folders.add(Path.of("/"));
|
files.put(_path, new Buffer(data));
|
||||||
}
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MemoryFilesystem(Mode mode) {
|
||||||
|
this.mode = mode;
|
||||||
|
folders.add(Path.of("/"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
52
src/me/topchetoeu/jscript/filesystem/Paths.java
Normal file
52
src/me/topchetoeu/jscript/filesystem/Paths.java
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package me.topchetoeu.jscript.filesystem;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class Paths {
|
||||||
|
public static String normalize(String... path) {
|
||||||
|
var parts = String.join("/", path).split("[\\\\/]");
|
||||||
|
var res = new ArrayList<String>();
|
||||||
|
|
||||||
|
for (var part : parts) {
|
||||||
|
if (part.equals("...")) res.clear();
|
||||||
|
else if (part.equals("..")) {
|
||||||
|
if (res.size() > 0) res.remove(res.size() - 1);
|
||||||
|
}
|
||||||
|
else if (!part.equals(".") && !part.isEmpty()) res.add(part);
|
||||||
|
}
|
||||||
|
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
|
for (var el : res) sb.append("/").append(el);
|
||||||
|
|
||||||
|
if (sb.isEmpty()) return "/";
|
||||||
|
else return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String chroot(String root, String path) {
|
||||||
|
return normalize(root) + normalize(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String cwd(String cwd, String path) {
|
||||||
|
return normalize(cwd + "/" + path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String filename(String path) {
|
||||||
|
var i = path.lastIndexOf('/');
|
||||||
|
if (i < 0) i = path.lastIndexOf('\\');
|
||||||
|
|
||||||
|
if (i < 0) return path;
|
||||||
|
else return path.substring(i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String extension(String path) {
|
||||||
|
var i = path.lastIndexOf('.');
|
||||||
|
|
||||||
|
if (i < 0) return "";
|
||||||
|
else return path.substring(i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String dir(String path) {
|
||||||
|
return normalize(path + "/..");
|
||||||
|
}
|
||||||
|
}
|
@ -9,35 +9,30 @@ import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode;
|
|||||||
public class PhysicalFile implements File {
|
public class PhysicalFile implements File {
|
||||||
private String filename;
|
private String filename;
|
||||||
private RandomAccessFile file;
|
private RandomAccessFile file;
|
||||||
private Mode perms;
|
private Mode mode;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read(byte[] buff) {
|
public int read(byte[] buff) {
|
||||||
if (file == null || !perms.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
if (file == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
||||||
else try { return file.read(buff); }
|
else try { return file.read(buff); }
|
||||||
catch (IOException e) { throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R); }
|
catch (IOException e) { throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R); }
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void write(byte[] buff) {
|
public void write(byte[] buff) {
|
||||||
if (file == null || !perms.writable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_RW);
|
if (file == null || !mode.writable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_RW);
|
||||||
else try { file.write(buff); }
|
else try { file.write(buff); }
|
||||||
catch (IOException e) { throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_RW); }
|
catch (IOException e) { throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_RW); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getPtr() {
|
public long seek(long offset, int pos) {
|
||||||
if (file == null || !perms.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
if (file == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
||||||
else try { return file.getFilePointer(); }
|
|
||||||
catch (IOException e) { throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R); }
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void setPtr(long offset, int pos) {
|
|
||||||
if (file == null || !perms.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (pos == 1) pos += file.getFilePointer();
|
if (pos == 1) offset += file.getFilePointer();
|
||||||
else if (pos == 2) pos += file.length();
|
else if (pos == 2) offset += file.length();
|
||||||
file.seek(pos);
|
file.seek(offset);
|
||||||
|
return offset;
|
||||||
}
|
}
|
||||||
catch (IOException e) { throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R); }
|
catch (IOException e) { throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R); }
|
||||||
}
|
}
|
||||||
@ -48,16 +43,15 @@ public class PhysicalFile implements File {
|
|||||||
try { file.close(); }
|
try { file.close(); }
|
||||||
catch (IOException e) {} // SHUT
|
catch (IOException e) {} // SHUT
|
||||||
file = null;
|
file = null;
|
||||||
perms = Mode.NONE;
|
mode = Mode.NONE;
|
||||||
}
|
}
|
||||||
@Override
|
|
||||||
public Mode mode() { return perms; }
|
|
||||||
|
|
||||||
public PhysicalFile(String path, Mode mode) throws FileNotFoundException {
|
public PhysicalFile(String name, String path, Mode mode) throws FileNotFoundException {
|
||||||
|
this.filename = name;
|
||||||
|
this.mode = mode;
|
||||||
|
|
||||||
if (mode == Mode.NONE) file = null;
|
if (mode == Mode.NONE) file = null;
|
||||||
else try { file = new RandomAccessFile(path, mode.name); }
|
else try { file = new RandomAccessFile(path, mode.name); }
|
||||||
catch (FileNotFoundException e) { throw new FilesystemException(filename, FSCode.DOESNT_EXIST); }
|
catch (FileNotFoundException e) { throw new FilesystemException(filename, FSCode.DOESNT_EXIST); }
|
||||||
|
|
||||||
perms = mode;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,74 +1,88 @@
|
|||||||
package me.topchetoeu.jscript.filesystem;
|
package me.topchetoeu.jscript.filesystem;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode;
|
import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode;
|
||||||
|
|
||||||
public class PhysicalFilesystem implements Filesystem {
|
public class PhysicalFilesystem implements Filesystem {
|
||||||
public final Path root;
|
public final String root;
|
||||||
|
|
||||||
private Path getPath(String name) {
|
|
||||||
return root.resolve(name.replace("\\", "/")).normalize();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkMode(Path path, Mode mode) {
|
private void checkMode(Path path, Mode mode) {
|
||||||
if (!path.startsWith(root)) throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_R);
|
if (!path.startsWith(root)) throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_R);
|
||||||
if (mode.readable && !path.toFile().canRead()) throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_R);
|
|
||||||
if (mode.writable && !path.toFile().canWrite()) throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_RW);
|
if (mode.readable && !Files.isReadable(path)) throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_R);
|
||||||
|
if (mode.writable && !Files.isWritable(path)) throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_RW);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path realPath(String path) {
|
||||||
|
return Path.of(Paths.chroot(root, path));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public File open(String path, Mode perms) {
|
public String normalize(String... paths) {
|
||||||
var _path = getPath(path);
|
return Paths.normalize(paths);
|
||||||
var f = _path.toFile();
|
|
||||||
|
|
||||||
checkMode(_path, perms);
|
|
||||||
|
|
||||||
|
|
||||||
if (f.isDirectory()) return MemoryFile.fromFileList(path, f.listFiles());
|
|
||||||
else try { return new PhysicalFile(path, perms); }
|
|
||||||
catch (FileNotFoundException e) { throw new FilesystemException(_path.toString(), FSCode.DOESNT_EXIST); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void create(String path, EntryType type) {
|
public File open(String _path, Mode perms) {
|
||||||
var _path = getPath(path);
|
_path = normalize(_path);
|
||||||
var f = _path.toFile();
|
var path = realPath(_path);
|
||||||
|
|
||||||
switch (type) {
|
checkMode(path, perms);
|
||||||
case FILE:
|
|
||||||
try {
|
try {
|
||||||
if (!f.createNewFile()) throw new FilesystemException(_path.toString(), FSCode.ALREADY_EXISTS);
|
if (Files.isDirectory(path)) return new ListFile(_path, Files.list(path).map((v -> v.getFileName().toString())));
|
||||||
else break;
|
else return new PhysicalFile(_path, path.toString(), perms);
|
||||||
}
|
|
||||||
catch (IOException e) { throw new FilesystemException(_path.toString(), FSCode.NO_PERMISSIONS_RW); }
|
|
||||||
case FOLDER:
|
|
||||||
if (!f.mkdir()) throw new FilesystemException(_path.toString(), FSCode.ALREADY_EXISTS);
|
|
||||||
else break;
|
|
||||||
case NONE:
|
|
||||||
default:
|
|
||||||
if (!f.delete()) throw new FilesystemException(_path.toString(), FSCode.DOESNT_EXIST);
|
|
||||||
else break;
|
|
||||||
}
|
}
|
||||||
|
catch (IOException e) { throw new FilesystemException(path.toString(), FSCode.DOESNT_EXIST); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FileStat stat(String path) {
|
public void create(String _path, EntryType type) {
|
||||||
var _path = getPath(path);
|
var path = realPath(_path);
|
||||||
var f = _path.toFile();
|
|
||||||
|
|
||||||
if (!f.exists()) throw new FilesystemException(_path.toString(), FSCode.DOESNT_EXIST);
|
if (type == EntryType.NONE != Files.exists(path)) throw new FilesystemException(path.toString(), FSCode.ALREADY_EXISTS);
|
||||||
checkMode(_path, Mode.READ);
|
|
||||||
|
try {
|
||||||
|
switch (type) {
|
||||||
|
case FILE:
|
||||||
|
Files.createFile(path);
|
||||||
|
break;
|
||||||
|
case FOLDER:
|
||||||
|
Files.createDirectories(path);
|
||||||
|
break;
|
||||||
|
case NONE:
|
||||||
|
default:
|
||||||
|
Files.delete(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException e) { throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_RW); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public 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(
|
return new FileStat(
|
||||||
f.canWrite() ? Mode.READ_WRITE : Mode.READ,
|
perms,
|
||||||
f.isFile() ? EntryType.FILE : EntryType.FOLDER
|
Files.isDirectory(path) ? EntryType.FOLDER : EntryType.FILE
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PhysicalFilesystem(Path root) {
|
public PhysicalFilesystem(String root) {
|
||||||
this.root = root.toAbsolutePath().normalize();
|
this.root = Paths.normalize(Path.of(root).toAbsolutePath().toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,18 @@ public class RootFilesystem implements Filesystem {
|
|||||||
if (mode.writable && perms != null && !canWrite(_path)) throw new FilesystemException(_path, FSCode.NO_PERMISSIONS_RW);
|
if (mode.writable && perms != null && !canWrite(_path)) throw new FilesystemException(_path, FSCode.NO_PERMISSIONS_RW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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 File open(String path, Mode perms) throws FilesystemException {
|
@Override public File open(String path, Mode perms) throws FilesystemException {
|
||||||
var filename = Filename.parse(path);
|
var filename = Filename.parse(path);
|
||||||
var protocol = protocols.get(filename.protocol);
|
var protocol = protocols.get(filename.protocol);
|
||||||
@ -45,9 +57,8 @@ public class RootFilesystem implements Filesystem {
|
|||||||
var filename = Filename.parse(path);
|
var filename = Filename.parse(path);
|
||||||
var protocol = protocols.get(filename.protocol);
|
var protocol = protocols.get(filename.protocol);
|
||||||
if (protocol == null) throw new FilesystemException(filename.toString(), FSCode.DOESNT_EXIST);
|
if (protocol == null) throw new FilesystemException(filename.toString(), FSCode.DOESNT_EXIST);
|
||||||
modeAllowed(filename.toString(), Mode.READ);
|
|
||||||
|
|
||||||
try { return protocol.stat(path); }
|
try { return protocol.stat(filename.path); }
|
||||||
catch (FilesystemException e) { throw new FilesystemException(filename.toString(), e.code); }
|
catch (FilesystemException e) { throw new FilesystemException(filename.toString(), e.code); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package me.topchetoeu.jscript.interop;
|
package me.topchetoeu.jscript.interop;
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.TYPE })
|
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.TYPE })
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public @interface Native {
|
public @interface Native {
|
||||||
public String value() default "";
|
public String value() default "";
|
||||||
public boolean thisArg() default false;
|
public boolean thisArg() default false;
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package me.topchetoeu.jscript.interop;
|
package me.topchetoeu.jscript.interop;
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
@Target({ ElementType.METHOD })
|
@Target({ ElementType.METHOD })
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public @interface NativeGetter {
|
public @interface NativeGetter {
|
||||||
public String value() default "";
|
public String value() default "";
|
||||||
public boolean thisArg() default false;
|
public boolean thisArg() default false;
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package me.topchetoeu.jscript.interop;
|
package me.topchetoeu.jscript.interop;
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
@Target({ ElementType.METHOD })
|
@Target({ ElementType.METHOD })
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public @interface NativeSetter {
|
public @interface NativeSetter {
|
||||||
public String value() default "";
|
public String value() default "";
|
||||||
public boolean thisArg() default false;
|
public boolean thisArg() default false;
|
||||||
}
|
}
|
||||||
|
@ -267,14 +267,12 @@ public class NativeWrapperProvider implements WrappersProvider {
|
|||||||
else return null;
|
else return null;
|
||||||
}));
|
}));
|
||||||
proto.defineProperty(null, "name", new NativeFunction("name", (ctx, thisArg, args) -> getName(thisArg.getClass())));
|
proto.defineProperty(null, "name", new NativeFunction("name", (ctx, thisArg, args) -> getName(thisArg.getClass())));
|
||||||
|
proto.defineProperty(null, "toString", new NativeFunction("toString", (ctx, thisArg, args) -> thisArg.toString()));
|
||||||
|
|
||||||
var constr = makeConstructor(null, Throwable.class);
|
var constr = makeConstructor(null, Throwable.class);
|
||||||
proto.defineProperty(null, "constructor", constr, true, false, false);
|
proto.defineProperty(null, "constructor", constr, true, false, false);
|
||||||
constr.defineProperty(null, "prototype", proto, true, false, false);
|
constr.defineProperty(null, "prototype", proto, true, false, false);
|
||||||
|
|
||||||
proto.setPrototype(null, getProto(Object.class));
|
|
||||||
constr.setPrototype(null, getConstr(Object.class));
|
|
||||||
|
|
||||||
setProto(Throwable.class, proto);
|
setProto(Throwable.class, proto);
|
||||||
setConstr(Throwable.class, constr);
|
setConstr(Throwable.class, constr);
|
||||||
}
|
}
|
||||||
|
@ -1,127 +1,127 @@
|
|||||||
package me.topchetoeu.jscript.interop;
|
package me.topchetoeu.jscript.interop;
|
||||||
|
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.engine.values.NativeWrapper;
|
import me.topchetoeu.jscript.engine.values.NativeWrapper;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.exceptions.ConvertException;
|
import me.topchetoeu.jscript.exceptions.ConvertException;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.exceptions.InterruptException;
|
import me.topchetoeu.jscript.exceptions.InterruptException;
|
||||||
|
|
||||||
public class OverloadFunction extends FunctionValue {
|
public class OverloadFunction extends FunctionValue {
|
||||||
public final List<Overload> overloads = new ArrayList<>();
|
public final List<Overload> overloads = new ArrayList<>();
|
||||||
|
|
||||||
public Object call(Context ctx, Object thisArg, Object ...args) {
|
public Object call(Context ctx, Object thisArg, Object ...args) {
|
||||||
loop: for (var overload : overloads) {
|
loop: for (var overload : overloads) {
|
||||||
Object[] newArgs = new Object[overload.params.length];
|
Object[] newArgs = new Object[overload.params.length];
|
||||||
|
|
||||||
boolean consumesEngine = overload.params.length > 0 && overload.params[0] == Context.class;
|
boolean consumesEngine = overload.params.length > 0 && overload.params[0] == Context.class;
|
||||||
int start = (consumesEngine ? 1 : 0) + (overload.passThis ? 1 : 0);
|
int start = (consumesEngine ? 1 : 0) + (overload.passThis ? 1 : 0);
|
||||||
int end = overload.params.length - (overload.variadic ? 1 : 0);
|
int end = overload.params.length - (overload.variadic ? 1 : 0);
|
||||||
|
|
||||||
for (var i = start; i < end; i++) {
|
for (var i = start; i < end; i++) {
|
||||||
Object val;
|
Object val;
|
||||||
|
|
||||||
if (i - start >= args.length) val = null;
|
if (i - start >= args.length) val = null;
|
||||||
else val = args[i - start];
|
else val = args[i - start];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
newArgs[i] = Values.convert(ctx, val, overload.params[i]);
|
newArgs[i] = Values.convert(ctx, val, overload.params[i]);
|
||||||
}
|
}
|
||||||
catch (ConvertException e) {
|
catch (ConvertException e) {
|
||||||
if (overloads.size() > 1) continue loop;
|
if (overloads.size() > 1) continue loop;
|
||||||
else throw EngineException.ofType(String.format("Argument %d can't be converted from %s to %s", i - start, e.source, e.target));
|
else throw EngineException.ofType(String.format("Argument %d can't be converted from %s to %s", i - start, e.source, e.target));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overload.variadic) {
|
if (overload.variadic) {
|
||||||
var type = overload.params[overload.params.length - 1].getComponentType();
|
var type = overload.params[overload.params.length - 1].getComponentType();
|
||||||
var n = Math.max(args.length - end + start, 0);
|
var n = Math.max(args.length - end + start, 0);
|
||||||
Object varArg = Array.newInstance(type, n);
|
Object varArg = Array.newInstance(type, n);
|
||||||
|
|
||||||
for (var i = 0; i < n; i++) {
|
for (var i = 0; i < n; i++) {
|
||||||
try {
|
try {
|
||||||
Array.set(varArg, i, Values.convert(ctx, args[i + end - start], type));
|
Array.set(varArg, i, Values.convert(ctx, args[i + end - start], type));
|
||||||
}
|
}
|
||||||
catch (ConvertException e) {
|
catch (ConvertException e) {
|
||||||
if (overloads.size() > 1) continue loop;
|
if (overloads.size() > 1) continue loop;
|
||||||
else throw EngineException.ofType(String.format("Element in variadic argument can't be converted from %s to %s", e.source, e.target));
|
else throw EngineException.ofType(String.format("Element in variadic argument can't be converted from %s to %s", e.source, e.target));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newArgs[newArgs.length - 1] = varArg;
|
newArgs[newArgs.length - 1] = varArg;
|
||||||
}
|
}
|
||||||
|
|
||||||
var thisArgType = overload.passThis ? overload.params[consumesEngine ? 1 : 0] : overload.thisArg;
|
var thisArgType = overload.passThis ? overload.params[consumesEngine ? 1 : 0] : overload.thisArg;
|
||||||
Object _this;
|
Object _this;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
_this = thisArgType == null ? null : Values.convert(ctx, thisArg, thisArgType);
|
_this = thisArgType == null ? null : Values.convert(ctx, thisArg, thisArgType);
|
||||||
}
|
}
|
||||||
catch (ConvertException e) {
|
catch (ConvertException e) {
|
||||||
if (overloads.size() > 1) continue loop;
|
if (overloads.size() > 1) continue loop;
|
||||||
else throw EngineException.ofType(String.format("This argument can't be converted from %s to %s", e.source, e.target));
|
else throw EngineException.ofType(String.format("This argument can't be converted from %s to %s", e.source, e.target));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (consumesEngine) newArgs[0] = ctx;
|
if (consumesEngine) newArgs[0] = ctx;
|
||||||
if (overload.passThis) {
|
if (overload.passThis) {
|
||||||
newArgs[consumesEngine ? 1 : 0] = _this;
|
newArgs[consumesEngine ? 1 : 0] = _this;
|
||||||
_this = null;
|
_this = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return Values.normalize(ctx, overload.runner.run(ctx, _this, newArgs));
|
return Values.normalize(ctx, overload.runner.run(ctx, _this, newArgs));
|
||||||
}
|
}
|
||||||
catch (InstantiationException e) { throw EngineException.ofError("The class may not be instantiated."); }
|
catch (InstantiationException e) { throw EngineException.ofError("The class may not be instantiated."); }
|
||||||
catch (IllegalAccessException | IllegalArgumentException e) { continue; }
|
catch (IllegalAccessException | IllegalArgumentException e) { continue; }
|
||||||
catch (InvocationTargetException e) {
|
catch (InvocationTargetException e) {
|
||||||
var loc = Location.INTERNAL;
|
var loc = Location.INTERNAL;
|
||||||
if (e.getTargetException() instanceof EngineException) {
|
if (e.getTargetException() instanceof EngineException) {
|
||||||
throw ((EngineException)e.getTargetException()).add(ctx, name, loc);
|
throw ((EngineException)e.getTargetException()).add(ctx, name, loc);
|
||||||
}
|
}
|
||||||
else if (e.getTargetException() instanceof NullPointerException) {
|
else if (e.getTargetException() instanceof NullPointerException) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
throw EngineException.ofType("Unexpected value of 'undefined'.").add(ctx, name, loc);
|
throw EngineException.ofType("Unexpected value of 'undefined'.").add(ctx, name, loc);
|
||||||
}
|
}
|
||||||
else if (e.getTargetException() instanceof InterruptException || e.getTargetException() instanceof InterruptedException) {
|
else if (e.getTargetException() instanceof InterruptException || e.getTargetException() instanceof InterruptedException) {
|
||||||
throw new InterruptException();
|
throw new InterruptException();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var target = e.getTargetException();
|
var target = e.getTargetException();
|
||||||
var targetClass = target.getClass();
|
var targetClass = target.getClass();
|
||||||
var err = new NativeWrapper(e.getTargetException());
|
var err = new NativeWrapper(e.getTargetException());
|
||||||
|
|
||||||
err.defineProperty(ctx, "message", target.getMessage());
|
err.defineProperty(ctx, "message", target.getMessage());
|
||||||
err.defineProperty(ctx, "name", NativeWrapperProvider.getName(targetClass));
|
err.defineProperty(ctx, "name", NativeWrapperProvider.getName(targetClass));
|
||||||
|
|
||||||
throw new EngineException(err).add(ctx, name, loc);
|
throw new EngineException(err).add(ctx, name, loc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (ReflectiveOperationException e) {
|
catch (ReflectiveOperationException e) {
|
||||||
throw EngineException.ofError(e.getMessage()).add(ctx, name, Location.INTERNAL);
|
throw EngineException.ofError(e.getMessage()).add(ctx, name, Location.INTERNAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw EngineException.ofType("No overload found for native method.");
|
throw EngineException.ofType("No overload found for native method.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public OverloadFunction add(Overload overload) {
|
public OverloadFunction add(Overload overload) {
|
||||||
this.overloads.add(overload);
|
this.overloads.add(overload);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OverloadFunction(String name) {
|
public OverloadFunction(String name) {
|
||||||
super(name, 0);
|
super(name, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static OverloadFunction of(String name, Overload overload) {
|
public static OverloadFunction of(String name, Overload overload) {
|
||||||
if (overload == null) return null;
|
if (overload == null) return null;
|
||||||
else return new OverloadFunction(name).add(overload);
|
else return new OverloadFunction(name).add(overload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,243 +1,243 @@
|
|||||||
package me.topchetoeu.jscript.json;
|
package me.topchetoeu.jscript.json;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Filename;
|
import me.topchetoeu.jscript.Filename;
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
||||||
import me.topchetoeu.jscript.parsing.Operator;
|
import me.topchetoeu.jscript.parsing.Operator;
|
||||||
import me.topchetoeu.jscript.parsing.ParseRes;
|
import me.topchetoeu.jscript.parsing.ParseRes;
|
||||||
import me.topchetoeu.jscript.parsing.Parsing;
|
import me.topchetoeu.jscript.parsing.Parsing;
|
||||||
import me.topchetoeu.jscript.parsing.Token;
|
import me.topchetoeu.jscript.parsing.Token;
|
||||||
|
|
||||||
public class JSON {
|
public class JSON {
|
||||||
public static Object toJs(JSONElement val) {
|
public static Object toJs(JSONElement val) {
|
||||||
if (val.isBoolean()) return val.bool();
|
if (val.isBoolean()) return val.bool();
|
||||||
if (val.isString()) return val.string();
|
if (val.isString()) return val.string();
|
||||||
if (val.isNumber()) return val.number();
|
if (val.isNumber()) return val.number();
|
||||||
if (val.isList()) return ArrayValue.of(null, val.list().stream().map(JSON::toJs).collect(Collectors.toList()));
|
if (val.isList()) return ArrayValue.of(null, val.list().stream().map(JSON::toJs).collect(Collectors.toList()));
|
||||||
if (val.isMap()) {
|
if (val.isMap()) {
|
||||||
var res = new ObjectValue();
|
var res = new ObjectValue();
|
||||||
for (var el : val.map().entrySet()) {
|
for (var el : val.map().entrySet()) {
|
||||||
res.defineProperty(null, el.getKey(), toJs(el.getValue()));
|
res.defineProperty(null, el.getKey(), toJs(el.getValue()));
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
if (val.isNull()) return Values.NULL;
|
if (val.isNull()) return Values.NULL;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
private static JSONElement fromJs(Context ctx, Object val, HashSet<Object> prev) {
|
private static JSONElement fromJs(Context ctx, Object val, HashSet<Object> prev) {
|
||||||
if (val instanceof Boolean) return JSONElement.bool((boolean)val);
|
if (val instanceof Boolean) return JSONElement.bool((boolean)val);
|
||||||
if (val instanceof Number) return JSONElement.number(((Number)val).doubleValue());
|
if (val instanceof Number) return JSONElement.number(((Number)val).doubleValue());
|
||||||
if (val instanceof String) return JSONElement.string((String)val);
|
if (val instanceof String) return JSONElement.string((String)val);
|
||||||
if (val == Values.NULL) return JSONElement.NULL;
|
if (val == Values.NULL) return JSONElement.NULL;
|
||||||
if (val instanceof ArrayValue) {
|
if (val instanceof ArrayValue) {
|
||||||
if (prev.contains(val)) throw new EngineException("Circular dependency in JSON.");
|
if (prev.contains(val)) throw new EngineException("Circular dependency in JSON.");
|
||||||
prev.add(val);
|
prev.add(val);
|
||||||
|
|
||||||
var res = new JSONList();
|
var res = new JSONList();
|
||||||
|
|
||||||
for (var el : ((ArrayValue)val).toArray()) {
|
for (var el : ((ArrayValue)val).toArray()) {
|
||||||
var jsonEl = fromJs(ctx, el, prev);
|
var jsonEl = fromJs(ctx, el, prev);
|
||||||
if (jsonEl == null) jsonEl = JSONElement.NULL;
|
if (jsonEl == null) jsonEl = JSONElement.NULL;
|
||||||
res.add(jsonEl);
|
res.add(jsonEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
prev.remove(val);
|
prev.remove(val);
|
||||||
return JSONElement.of(res);
|
return JSONElement.of(res);
|
||||||
}
|
}
|
||||||
if (val instanceof ObjectValue) {
|
if (val instanceof ObjectValue) {
|
||||||
if (prev.contains(val)) throw new EngineException("Circular dependency in JSON.");
|
if (prev.contains(val)) throw new EngineException("Circular dependency in JSON.");
|
||||||
prev.add(val);
|
prev.add(val);
|
||||||
|
|
||||||
var res = new JSONMap();
|
var res = new JSONMap();
|
||||||
|
|
||||||
for (var el : ((ObjectValue)val).keys(false)) {
|
for (var el : ((ObjectValue)val).keys(false)) {
|
||||||
var jsonEl = fromJs(ctx, ((ObjectValue)val).getMember(ctx, el), prev);
|
var jsonEl = fromJs(ctx, ((ObjectValue)val).getMember(ctx, el), prev);
|
||||||
if (jsonEl == null) continue;
|
if (jsonEl == null) continue;
|
||||||
if (el instanceof String || el instanceof Number) res.put(el.toString(), jsonEl);
|
if (el instanceof String || el instanceof Number) res.put(el.toString(), jsonEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
prev.remove(val);
|
prev.remove(val);
|
||||||
return JSONElement.of(res);
|
return JSONElement.of(res);
|
||||||
}
|
}
|
||||||
if (val == null) return null;
|
if (val == null) return null;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
public static JSONElement fromJs(Context ctx, Object val) {
|
public static JSONElement fromJs(Context ctx, Object val) {
|
||||||
return fromJs(ctx, val, new HashSet<>());
|
return fromJs(ctx, val, new HashSet<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ParseRes<String> parseIdentifier(List<Token> tokens, int i) {
|
public static ParseRes<String> parseIdentifier(List<Token> tokens, int i) {
|
||||||
return Parsing.parseIdentifier(tokens, i);
|
return Parsing.parseIdentifier(tokens, i);
|
||||||
}
|
}
|
||||||
public static ParseRes<String> parseString(Filename filename, List<Token> tokens, int i) {
|
public static ParseRes<String> parseString(Filename filename, List<Token> tokens, int i) {
|
||||||
var res = Parsing.parseString(filename, tokens, i);
|
var res = Parsing.parseString(filename, tokens, i);
|
||||||
if (res.isSuccess()) return ParseRes.res((String)res.result.value, res.n);
|
if (res.isSuccess()) return ParseRes.res((String)res.result.value, res.n);
|
||||||
else return res.transform();
|
else return res.transform();
|
||||||
}
|
}
|
||||||
public static ParseRes<Double> parseNumber(Filename filename, List<Token> tokens, int i) {
|
public static ParseRes<Double> parseNumber(Filename filename, List<Token> tokens, int i) {
|
||||||
var minus = Parsing.isOperator(tokens, i, Operator.SUBTRACT);
|
var minus = Parsing.isOperator(tokens, i, Operator.SUBTRACT);
|
||||||
if (minus) i++;
|
if (minus) i++;
|
||||||
|
|
||||||
var res = Parsing.parseNumber(filename, tokens, i);
|
var res = Parsing.parseNumber(filename, tokens, i);
|
||||||
if (res.isSuccess()) return ParseRes.res((minus ? -1 : 1) * (Double)res.result.value, res.n + (minus ? 1 : 0));
|
if (res.isSuccess()) return ParseRes.res((minus ? -1 : 1) * (Double)res.result.value, res.n + (minus ? 1 : 0));
|
||||||
else return res.transform();
|
else return res.transform();
|
||||||
}
|
}
|
||||||
public static ParseRes<Boolean> parseBool(Filename filename, List<Token> tokens, int i) {
|
public static ParseRes<Boolean> parseBool(Filename filename, List<Token> tokens, int i) {
|
||||||
var id = parseIdentifier(tokens, i);
|
var id = parseIdentifier(tokens, i);
|
||||||
|
|
||||||
if (!id.isSuccess()) return ParseRes.failed();
|
if (!id.isSuccess()) return ParseRes.failed();
|
||||||
else if (id.result.equals("true")) return ParseRes.res(true, 1);
|
else if (id.result.equals("true")) return ParseRes.res(true, 1);
|
||||||
else if (id.result.equals("false")) return ParseRes.res(false, 1);
|
else if (id.result.equals("false")) return ParseRes.res(false, 1);
|
||||||
else return ParseRes.failed();
|
else return ParseRes.failed();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ParseRes<?> parseValue(Filename filename, List<Token> tokens, int i) {
|
public static ParseRes<?> parseValue(Filename filename, List<Token> tokens, int i) {
|
||||||
return ParseRes.any(
|
return ParseRes.any(
|
||||||
parseString(filename, tokens, i),
|
parseString(filename, tokens, i),
|
||||||
parseNumber(filename, tokens, i),
|
parseNumber(filename, tokens, i),
|
||||||
parseBool(filename, tokens, i),
|
parseBool(filename, tokens, i),
|
||||||
parseMap(filename, tokens, i),
|
parseMap(filename, tokens, i),
|
||||||
parseList(filename, tokens, i)
|
parseList(filename, tokens, i)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ParseRes<JSONMap> parseMap(Filename filename, List<Token> tokens, int i) {
|
public static ParseRes<JSONMap> parseMap(Filename filename, List<Token> tokens, int i) {
|
||||||
int n = 0;
|
int n = 0;
|
||||||
if (!Parsing.isOperator(tokens, i + n++, Operator.BRACE_OPEN)) return ParseRes.failed();
|
if (!Parsing.isOperator(tokens, i + n++, Operator.BRACE_OPEN)) return ParseRes.failed();
|
||||||
|
|
||||||
var values = new JSONMap();
|
var values = new JSONMap();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) {
|
if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) {
|
||||||
n++;
|
n++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var name = ParseRes.any(
|
var name = ParseRes.any(
|
||||||
parseIdentifier(tokens, i + n),
|
parseIdentifier(tokens, i + n),
|
||||||
parseString(filename, tokens, i + n),
|
parseString(filename, tokens, i + n),
|
||||||
parseNumber(filename, tokens, i + n)
|
parseNumber(filename, tokens, i + n)
|
||||||
);
|
);
|
||||||
if (!name.isSuccess()) return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), "Expected an index.", name);
|
if (!name.isSuccess()) return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), "Expected an index.", name);
|
||||||
else n += name.n;
|
else n += name.n;
|
||||||
|
|
||||||
if (!Parsing.isOperator(tokens, i + n, Operator.COLON)) {
|
if (!Parsing.isOperator(tokens, i + n, Operator.COLON)) {
|
||||||
return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), "Expected a colon.", name);
|
return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), "Expected a colon.", name);
|
||||||
}
|
}
|
||||||
n++;
|
n++;
|
||||||
|
|
||||||
var res = parseValue(filename, tokens, i + n);
|
var res = parseValue(filename, tokens, i + n);
|
||||||
if (!res.isSuccess()) return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), "Expected a list element.", res);
|
if (!res.isSuccess()) return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), "Expected a list element.", res);
|
||||||
else n += res.n;
|
else n += res.n;
|
||||||
|
|
||||||
values.put(name.result.toString(), JSONElement.of(res.result));
|
values.put(name.result.toString(), JSONElement.of(res.result));
|
||||||
|
|
||||||
if (Parsing.isOperator(tokens, i + n, Operator.COMMA)) n++;
|
if (Parsing.isOperator(tokens, i + n, Operator.COMMA)) n++;
|
||||||
else if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) {
|
else if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) {
|
||||||
n++;
|
n++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ParseRes.res(values, n);
|
return ParseRes.res(values, n);
|
||||||
}
|
}
|
||||||
public static ParseRes<JSONList> parseList(Filename filename, List<Token> tokens, int i) {
|
public static ParseRes<JSONList> parseList(Filename filename, List<Token> tokens, int i) {
|
||||||
int n = 0;
|
int n = 0;
|
||||||
if (!Parsing.isOperator(tokens, i + n++, Operator.BRACKET_OPEN)) return ParseRes.failed();
|
if (!Parsing.isOperator(tokens, i + n++, Operator.BRACKET_OPEN)) return ParseRes.failed();
|
||||||
|
|
||||||
var values = new JSONList();
|
var values = new JSONList();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (Parsing.isOperator(tokens, i + n, Operator.BRACKET_CLOSE)) {
|
if (Parsing.isOperator(tokens, i + n, Operator.BRACKET_CLOSE)) {
|
||||||
n++;
|
n++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var res = parseValue(filename, tokens, i + n);
|
var res = parseValue(filename, tokens, i + n);
|
||||||
if (!res.isSuccess()) return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), "Expected a list element.", res);
|
if (!res.isSuccess()) return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), "Expected a list element.", res);
|
||||||
else n += res.n;
|
else n += res.n;
|
||||||
|
|
||||||
values.add(JSONElement.of(res.result));
|
values.add(JSONElement.of(res.result));
|
||||||
|
|
||||||
if (Parsing.isOperator(tokens, i + n, Operator.COMMA)) n++;
|
if (Parsing.isOperator(tokens, i + n, Operator.COMMA)) n++;
|
||||||
else if (Parsing.isOperator(tokens, i + n, Operator.BRACKET_CLOSE)) {
|
else if (Parsing.isOperator(tokens, i + n, Operator.BRACKET_CLOSE)) {
|
||||||
n++;
|
n++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ParseRes.res(values, n);
|
return ParseRes.res(values, n);
|
||||||
}
|
}
|
||||||
public static JSONElement parse(Filename filename, String raw) {
|
public static JSONElement parse(Filename filename, String raw) {
|
||||||
if (filename == null) filename = new Filename("jscript", "json");
|
if (filename == null) filename = new Filename("jscript", "json");
|
||||||
var res = parseValue(filename, Parsing.tokenize(filename, raw), 0);
|
var res = parseValue(filename, Parsing.tokenize(filename, raw), 0);
|
||||||
if (res.isFailed()) throw new SyntaxException(null, "Invalid JSON given.");
|
if (res.isFailed()) throw new SyntaxException(null, "Invalid JSON given.");
|
||||||
else if (res.isError()) throw new SyntaxException(null, res.error);
|
else if (res.isError()) throw new SyntaxException(null, res.error);
|
||||||
else return JSONElement.of(res.result);
|
else return JSONElement.of(res.result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String stringify(JSONElement el) {
|
public static String stringify(JSONElement el) {
|
||||||
if (el.isNumber()) return Double.toString(el.number());
|
if (el.isNumber()) return Double.toString(el.number());
|
||||||
if (el.isBoolean()) return el.bool() ? "true" : "false";
|
if (el.isBoolean()) return el.bool() ? "true" : "false";
|
||||||
if (el.isNull()) return "null";
|
if (el.isNull()) return "null";
|
||||||
if (el.isString()) {
|
if (el.isString()) {
|
||||||
var res = new StringBuilder("\"");
|
var res = new StringBuilder("\"");
|
||||||
var alphabet = "0123456789ABCDEF".toCharArray();
|
var alphabet = "0123456789ABCDEF".toCharArray();
|
||||||
|
|
||||||
for (var c : el.string().toCharArray()) {
|
for (var c : el.string().toCharArray()) {
|
||||||
if (c < 32 || c >= 127) {
|
if (c < 32 || c >= 127) {
|
||||||
res
|
res
|
||||||
.append("\\u")
|
.append("\\u")
|
||||||
.append(alphabet[(c >> 12) & 0xF])
|
.append(alphabet[(c >> 12) & 0xF])
|
||||||
.append(alphabet[(c >> 8) & 0xF])
|
.append(alphabet[(c >> 8) & 0xF])
|
||||||
.append(alphabet[(c >> 4) & 0xF])
|
.append(alphabet[(c >> 4) & 0xF])
|
||||||
.append(alphabet[(c >> 0) & 0xF]);
|
.append(alphabet[(c >> 0) & 0xF]);
|
||||||
}
|
}
|
||||||
else if (c == '\\')
|
else if (c == '\\')
|
||||||
res.append("\\\\");
|
res.append("\\\\");
|
||||||
else if (c == '"')
|
else if (c == '"')
|
||||||
res.append("\\\"");
|
res.append("\\\"");
|
||||||
else res.append(c);
|
else res.append(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.append('"').toString();
|
return res.append('"').toString();
|
||||||
}
|
}
|
||||||
if (el.isList()) {
|
if (el.isList()) {
|
||||||
var res = new StringBuilder().append("[");
|
var res = new StringBuilder().append("[");
|
||||||
for (int i = 0; i < el.list().size(); i++) {
|
for (int i = 0; i < el.list().size(); i++) {
|
||||||
if (i != 0) res.append(",");
|
if (i != 0) res.append(",");
|
||||||
res.append(stringify(el.list().get(i)));
|
res.append(stringify(el.list().get(i)));
|
||||||
}
|
}
|
||||||
res.append("]");
|
res.append("]");
|
||||||
return res.toString();
|
return res.toString();
|
||||||
}
|
}
|
||||||
if (el.isMap()) {
|
if (el.isMap()) {
|
||||||
var res = new StringBuilder().append("{");
|
var res = new StringBuilder().append("{");
|
||||||
var entries = el.map().entrySet().stream().collect(Collectors.toList());
|
var entries = el.map().entrySet().stream().collect(Collectors.toList());
|
||||||
|
|
||||||
for (int i = 0; i < entries.size(); i++) {
|
for (int i = 0; i < entries.size(); i++) {
|
||||||
if (i != 0) res.append(",");
|
if (i != 0) res.append(",");
|
||||||
res.append(stringify(JSONElement.string(entries.get(i).getKey())));
|
res.append(stringify(JSONElement.string(entries.get(i).getKey())));
|
||||||
res.append(":");
|
res.append(":");
|
||||||
res.append(stringify(entries.get(i).getValue()));
|
res.append(stringify(entries.get(i).getValue()));
|
||||||
}
|
}
|
||||||
res.append("}");
|
res.append("}");
|
||||||
return res.toString();
|
return res.toString();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
public static String stringify(JSONMap map) {
|
public static String stringify(JSONMap map) {
|
||||||
return stringify(JSONElement.of(map));
|
return stringify(JSONElement.of(map));
|
||||||
}
|
}
|
||||||
public static String stringify(JSONList list) {
|
public static String stringify(JSONList list) {
|
||||||
return stringify(JSONElement.of(list));
|
return stringify(JSONElement.of(list));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,88 +1,88 @@
|
|||||||
package me.topchetoeu.jscript.json;
|
package me.topchetoeu.jscript.json;
|
||||||
|
|
||||||
public class JSONElement {
|
public class JSONElement {
|
||||||
public static enum Type {
|
public static enum Type {
|
||||||
STRING,
|
STRING,
|
||||||
NUMBER,
|
NUMBER,
|
||||||
BOOLEAN,
|
BOOLEAN,
|
||||||
NULL,
|
NULL,
|
||||||
LIST,
|
LIST,
|
||||||
MAP,
|
MAP,
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final JSONElement NULL = new JSONElement(Type.NULL, null);
|
public static final JSONElement NULL = new JSONElement(Type.NULL, null);
|
||||||
|
|
||||||
public static JSONElement map(JSONMap val) {
|
public static JSONElement map(JSONMap val) {
|
||||||
return new JSONElement(Type.MAP, val);
|
return new JSONElement(Type.MAP, val);
|
||||||
}
|
}
|
||||||
public static JSONElement list(JSONList val) {
|
public static JSONElement list(JSONList val) {
|
||||||
return new JSONElement(Type.LIST, val);
|
return new JSONElement(Type.LIST, val);
|
||||||
}
|
}
|
||||||
public static JSONElement string(String val) {
|
public static JSONElement string(String val) {
|
||||||
return new JSONElement(Type.STRING, val);
|
return new JSONElement(Type.STRING, val);
|
||||||
}
|
}
|
||||||
public static JSONElement number(double val) {
|
public static JSONElement number(double val) {
|
||||||
return new JSONElement(Type.NUMBER, val);
|
return new JSONElement(Type.NUMBER, val);
|
||||||
}
|
}
|
||||||
public static JSONElement bool(boolean val) {
|
public static JSONElement bool(boolean val) {
|
||||||
return new JSONElement(Type.BOOLEAN, val);
|
return new JSONElement(Type.BOOLEAN, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JSONElement of(Object val) {
|
public static JSONElement of(Object val) {
|
||||||
if (val instanceof JSONMap) return map((JSONMap)val);
|
if (val instanceof JSONMap) return map((JSONMap)val);
|
||||||
else if (val instanceof JSONList) return list((JSONList)val);
|
else if (val instanceof JSONList) return list((JSONList)val);
|
||||||
else if (val instanceof String) return string((String)val);
|
else if (val instanceof String) return string((String)val);
|
||||||
else if (val instanceof Boolean) return bool((Boolean)val);
|
else if (val instanceof Boolean) return bool((Boolean)val);
|
||||||
else if (val instanceof Number) return number(((Number)val).doubleValue());
|
else if (val instanceof Number) return number(((Number)val).doubleValue());
|
||||||
else if (val == null) return NULL;
|
else if (val == null) return NULL;
|
||||||
else throw new IllegalArgumentException("val must be: String, Boolean, Number, JSONList or JSONMap.");
|
else throw new IllegalArgumentException("val must be: String, Boolean, Number, JSONList or JSONMap.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public final Type type;
|
public final Type type;
|
||||||
private final Object value;
|
private final Object value;
|
||||||
|
|
||||||
public boolean isMap() { return type == Type.MAP; }
|
public boolean isMap() { return type == Type.MAP; }
|
||||||
public boolean isList() { return type == Type.LIST; }
|
public boolean isList() { return type == Type.LIST; }
|
||||||
public boolean isString() { return type == Type.STRING; }
|
public boolean isString() { return type == Type.STRING; }
|
||||||
public boolean isNumber() { return type == Type.NUMBER; }
|
public boolean isNumber() { return type == Type.NUMBER; }
|
||||||
public boolean isBoolean() { return type == Type.BOOLEAN; }
|
public boolean isBoolean() { return type == Type.BOOLEAN; }
|
||||||
public boolean isNull() { return type == Type.NULL; }
|
public boolean isNull() { return type == Type.NULL; }
|
||||||
|
|
||||||
public JSONMap map() {
|
public JSONMap map() {
|
||||||
if (!isMap()) throw new IllegalStateException("Element is not a map.");
|
if (!isMap()) throw new IllegalStateException("Element is not a map.");
|
||||||
return (JSONMap)value;
|
return (JSONMap)value;
|
||||||
}
|
}
|
||||||
public JSONList list() {
|
public JSONList list() {
|
||||||
if (!isList()) throw new IllegalStateException("Element is not a map.");
|
if (!isList()) throw new IllegalStateException("Element is not a map.");
|
||||||
return (JSONList)value;
|
return (JSONList)value;
|
||||||
}
|
}
|
||||||
public String string() {
|
public String string() {
|
||||||
if (!isString()) throw new IllegalStateException("Element is not a string.");
|
if (!isString()) throw new IllegalStateException("Element is not a string.");
|
||||||
return (String)value;
|
return (String)value;
|
||||||
}
|
}
|
||||||
public double number() {
|
public double number() {
|
||||||
if (!isNumber()) throw new IllegalStateException("Element is not a number.");
|
if (!isNumber()) throw new IllegalStateException("Element is not a number.");
|
||||||
return (double)value;
|
return (double)value;
|
||||||
}
|
}
|
||||||
public boolean bool() {
|
public boolean bool() {
|
||||||
if (!isBoolean()) throw new IllegalStateException("Element is not a boolean.");
|
if (!isBoolean()) throw new IllegalStateException("Element is not a boolean.");
|
||||||
return (boolean)value;
|
return (boolean)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if (isMap()) return "{...}";
|
if (isMap()) return "{...}";
|
||||||
if (isList()) return "[...]";
|
if (isList()) return "[...]";
|
||||||
if (isString()) return (String)value;
|
if (isString()) return (String)value;
|
||||||
if (isString()) return (String)value;
|
if (isString()) return (String)value;
|
||||||
if (isNumber()) return (double)value + "";
|
if (isNumber()) return (double)value + "";
|
||||||
if (isBoolean()) return (boolean)value + "";
|
if (isBoolean()) return (boolean)value + "";
|
||||||
if (isNull()) return "null";
|
if (isNull()) return "null";
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
private JSONElement(Type type, Object val) {
|
private JSONElement(Type type, Object val) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.value = val;
|
this.value = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
package me.topchetoeu.jscript.json;
|
package me.topchetoeu.jscript.json;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class JSONList extends ArrayList<JSONElement> {
|
public class JSONList extends ArrayList<JSONElement> {
|
||||||
public JSONList() {}
|
public JSONList() {}
|
||||||
public JSONList(JSONElement ...els) {
|
public JSONList(JSONElement ...els) {
|
||||||
super(List.of(els));
|
super(List.of(els));
|
||||||
}
|
}
|
||||||
public JSONList(Collection<JSONElement> els) {
|
public JSONList(Collection<JSONElement> els) {
|
||||||
super(els);
|
super(els);
|
||||||
}
|
}
|
||||||
|
|
||||||
public JSONList addNull() { this.add(JSONElement.NULL); return this; }
|
public JSONList addNull() { this.add(JSONElement.NULL); return this; }
|
||||||
public JSONList add(String val) { this.add(JSONElement.of(val)); return this; }
|
public JSONList add(String val) { this.add(JSONElement.of(val)); return this; }
|
||||||
public JSONList add(double val) { this.add(JSONElement.of(val)); return this; }
|
public JSONList add(double val) { this.add(JSONElement.of(val)); return this; }
|
||||||
public JSONList add(boolean val) { this.add(JSONElement.of(val)); return this; }
|
public JSONList add(boolean val) { this.add(JSONElement.of(val)); return this; }
|
||||||
public JSONList add(Map<String, JSONElement> val) { this.add(JSONElement.of(val)); return this; }
|
public JSONList add(Map<String, JSONElement> val) { this.add(JSONElement.of(val)); return this; }
|
||||||
public JSONList add(Collection<JSONElement> val) { this.add(JSONElement.of(val)); return this; }
|
public JSONList add(Collection<JSONElement> val) { this.add(JSONElement.of(val)); return this; }
|
||||||
public JSONList add(JSONMap val) { this.add(JSONElement.of(val)); return this; }
|
public JSONList add(JSONMap val) { this.add(JSONElement.of(val)); return this; }
|
||||||
public JSONList add(JSONList val) { this.add(JSONElement.of(val)); return this; }
|
public JSONList add(JSONList val) { this.add(JSONElement.of(val)); return this; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,150 +1,150 @@
|
|||||||
package me.topchetoeu.jscript.json;
|
package me.topchetoeu.jscript.json;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class JSONMap implements Map<String, JSONElement> {
|
public class JSONMap implements Map<String, JSONElement> {
|
||||||
private Map<String, JSONElement> elements = new HashMap<>();
|
private Map<String, JSONElement> elements = new HashMap<>();
|
||||||
|
|
||||||
public JSONElement get(String path) {
|
public JSONElement get(String path) {
|
||||||
var curr = this;
|
var curr = this;
|
||||||
var segs = path.split("\\.");
|
var segs = path.split("\\.");
|
||||||
var i = 0;
|
var i = 0;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
var tmp = curr.elements.get(segs[i++]);
|
var tmp = curr.elements.get(segs[i++]);
|
||||||
if (i == segs.length) return tmp;
|
if (i == segs.length) return tmp;
|
||||||
if (!tmp.isMap()) return null;
|
if (!tmp.isMap()) return null;
|
||||||
curr = tmp.map();
|
curr = tmp.map();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isMap(String path) {
|
public boolean isMap(String path) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
return el != null && el.isMap();
|
return el != null && el.isMap();
|
||||||
}
|
}
|
||||||
public boolean isList(String path) {
|
public boolean isList(String path) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
return el != null && el.isList();
|
return el != null && el.isList();
|
||||||
}
|
}
|
||||||
public boolean isString(String path) {
|
public boolean isString(String path) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
return el != null && el.isString();
|
return el != null && el.isString();
|
||||||
}
|
}
|
||||||
public boolean isNumber(String path) {
|
public boolean isNumber(String path) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
return el != null && el.isNumber();
|
return el != null && el.isNumber();
|
||||||
}
|
}
|
||||||
public boolean isBoolean(String path) {
|
public boolean isBoolean(String path) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
return el != null && el.isBoolean();
|
return el != null && el.isBoolean();
|
||||||
}
|
}
|
||||||
public boolean isNull(String path) {
|
public boolean isNull(String path) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
return el != null && el.isNull();
|
return el != null && el.isNull();
|
||||||
}
|
}
|
||||||
public boolean contains(String path) {
|
public boolean contains(String path) {
|
||||||
return get(path) != null;
|
return get(path) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JSONMap map(String path) {
|
public JSONMap map(String path) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
||||||
return el.map();
|
return el.map();
|
||||||
}
|
}
|
||||||
public JSONMap map(String path, JSONMap defaultVal) {
|
public JSONMap map(String path, JSONMap defaultVal) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
if (el == null) return defaultVal;
|
if (el == null) return defaultVal;
|
||||||
if (el.isMap()) return el.map();
|
if (el.isMap()) return el.map();
|
||||||
return defaultVal;
|
return defaultVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JSONList list(String path) {
|
public JSONList list(String path) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
||||||
return el.list();
|
return el.list();
|
||||||
}
|
}
|
||||||
public JSONList list(String path, JSONList defaultVal) {
|
public JSONList list(String path, JSONList defaultVal) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
if (el == null) return defaultVal;
|
if (el == null) return defaultVal;
|
||||||
if (el.isList()) return el.list();
|
if (el.isList()) return el.list();
|
||||||
return defaultVal;
|
return defaultVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String string(String path) {
|
public String string(String path) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
||||||
return el.string();
|
return el.string();
|
||||||
}
|
}
|
||||||
public String string(String path, String defaultVal) {
|
public String string(String path, String defaultVal) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
if (el == null) return defaultVal;
|
if (el == null) return defaultVal;
|
||||||
if (el.isString()) return el.string();
|
if (el.isString()) return el.string();
|
||||||
return defaultVal;
|
return defaultVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double number(String path) {
|
public double number(String path) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
||||||
return el.number();
|
return el.number();
|
||||||
}
|
}
|
||||||
public double number(String path, double defaultVal) {
|
public double number(String path, double defaultVal) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
if (el == null) return defaultVal;
|
if (el == null) return defaultVal;
|
||||||
if (el.isNumber()) return el.number();
|
if (el.isNumber()) return el.number();
|
||||||
return defaultVal;
|
return defaultVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean bool(String path) {
|
public boolean bool(String path) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
||||||
return el.bool();
|
return el.bool();
|
||||||
}
|
}
|
||||||
public boolean bool(String path, boolean defaultVal) {
|
public boolean bool(String path, boolean defaultVal) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
if (el == null) return defaultVal;
|
if (el == null) return defaultVal;
|
||||||
if (el.isBoolean()) return el.bool();
|
if (el.isBoolean()) return el.bool();
|
||||||
return defaultVal;
|
return defaultVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JSONMap setNull(String key) { elements.put(key, JSONElement.NULL); return this; }
|
public JSONMap setNull(String key) { elements.put(key, JSONElement.NULL); return this; }
|
||||||
public JSONMap set(String key, String val) { elements.put(key, JSONElement.of(val)); return this; }
|
public JSONMap set(String key, String val) { elements.put(key, JSONElement.of(val)); return this; }
|
||||||
public JSONMap set(String key, double val) { elements.put(key, JSONElement.of(val)); return this; }
|
public JSONMap set(String key, double val) { elements.put(key, JSONElement.of(val)); return this; }
|
||||||
public JSONMap set(String key, boolean val) { elements.put(key, JSONElement.of(val)); return this; }
|
public JSONMap set(String key, boolean val) { elements.put(key, JSONElement.of(val)); return this; }
|
||||||
public JSONMap set(String key, Map<String, JSONElement> val) { elements.put(key, JSONElement.of(val)); return this; }
|
public JSONMap set(String key, Map<String, JSONElement> val) { elements.put(key, JSONElement.of(val)); return this; }
|
||||||
public JSONMap set(String key, Collection<JSONElement> val) { elements.put(key, JSONElement.of(val)); return this; }
|
public JSONMap set(String key, Collection<JSONElement> val) { elements.put(key, JSONElement.of(val)); return this; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size() { return elements.size(); }
|
public int size() { return elements.size(); }
|
||||||
@Override
|
@Override
|
||||||
public boolean isEmpty() { return elements.isEmpty(); }
|
public boolean isEmpty() { return elements.isEmpty(); }
|
||||||
@Override
|
@Override
|
||||||
public boolean containsKey(Object key) { return elements.containsKey(key); }
|
public boolean containsKey(Object key) { return elements.containsKey(key); }
|
||||||
@Override
|
@Override
|
||||||
public boolean containsValue(Object value) { return elements.containsValue(value); }
|
public boolean containsValue(Object value) { return elements.containsValue(value); }
|
||||||
@Override
|
@Override
|
||||||
public JSONElement get(Object key) { return elements.get(key); }
|
public JSONElement get(Object key) { return elements.get(key); }
|
||||||
@Override
|
@Override
|
||||||
public JSONElement put(String key, JSONElement value) { return elements.put(key, value); }
|
public JSONElement put(String key, JSONElement value) { return elements.put(key, value); }
|
||||||
@Override
|
@Override
|
||||||
public JSONElement remove(Object key) { return elements.remove(key); }
|
public JSONElement remove(Object key) { return elements.remove(key); }
|
||||||
@Override
|
@Override
|
||||||
public void putAll(Map<? extends String, ? extends JSONElement> m) { elements.putAll(m); }
|
public void putAll(Map<? extends String, ? extends JSONElement> m) { elements.putAll(m); }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clear() { elements.clear(); }
|
public void clear() { elements.clear(); }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<String> keySet() { return elements.keySet(); }
|
public Set<String> keySet() { return elements.keySet(); }
|
||||||
@Override
|
@Override
|
||||||
public Collection<JSONElement> values() { return elements.values(); }
|
public Collection<JSONElement> values() { return elements.values(); }
|
||||||
@Override
|
@Override
|
||||||
public Set<Entry<String, JSONElement>> entrySet() { return elements.entrySet(); }
|
public Set<Entry<String, JSONElement>> entrySet() { return elements.entrySet(); }
|
||||||
|
|
||||||
public JSONMap() { }
|
public JSONMap() { }
|
||||||
public JSONMap(Map<String, JSONElement> els) {
|
public JSONMap(Map<String, JSONElement> els) {
|
||||||
this.elements = new HashMap<>(els);
|
this.elements = new HashMap<>(els);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ import me.topchetoeu.jscript.interop.Native;
|
|||||||
private void next(Context ctx, Object inducedValue, Object inducedError) {
|
private void next(Context ctx, Object inducedValue, Object inducedError) {
|
||||||
Object res = null;
|
Object res = null;
|
||||||
ctx.pushFrame(frame);
|
ctx.pushFrame(frame);
|
||||||
ctx.pushEnv(frame.function.environment);
|
|
||||||
|
|
||||||
awaiting = false;
|
awaiting = false;
|
||||||
while (!awaiting) {
|
while (!awaiting) {
|
||||||
|
@ -15,7 +15,7 @@ public class FileLib {
|
|||||||
@NativeGetter public PromiseLib pointer(Context ctx) {
|
@NativeGetter public PromiseLib pointer(Context ctx) {
|
||||||
return PromiseLib.await(ctx, () -> {
|
return PromiseLib.await(ctx, () -> {
|
||||||
try {
|
try {
|
||||||
return file.getPtr();
|
return file.seek(0, 1);
|
||||||
}
|
}
|
||||||
catch (FilesystemException e) { throw e.toEngineException(); }
|
catch (FilesystemException e) { throw e.toEngineException(); }
|
||||||
});
|
});
|
||||||
@ -23,23 +23,14 @@ public class FileLib {
|
|||||||
@NativeGetter public PromiseLib length(Context ctx) {
|
@NativeGetter public PromiseLib length(Context ctx) {
|
||||||
return PromiseLib.await(ctx, () -> {
|
return PromiseLib.await(ctx, () -> {
|
||||||
try {
|
try {
|
||||||
long curr = file.getPtr();
|
long curr = file.seek(0, 1);
|
||||||
file.setPtr(0, 2);
|
long res = file.seek(0, 2);
|
||||||
long res = file.getPtr();
|
file.seek(curr, 0);
|
||||||
file.setPtr(curr, 0);
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
catch (FilesystemException e) { throw e.toEngineException(); }
|
catch (FilesystemException e) { throw e.toEngineException(); }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@NativeGetter public PromiseLib getMode(Context ctx) {
|
|
||||||
return PromiseLib.await(ctx, () -> {
|
|
||||||
try {
|
|
||||||
return file.mode().name;
|
|
||||||
}
|
|
||||||
catch (FilesystemException e) { throw e.toEngineException(); }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Native public PromiseLib read(Context ctx, int n) {
|
@Native public PromiseLib read(Context ctx, int n) {
|
||||||
return PromiseLib.await(ctx, () -> {
|
return PromiseLib.await(ctx, () -> {
|
||||||
@ -73,11 +64,10 @@ public class FileLib {
|
|||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@Native public PromiseLib setPointer(Context ctx, long ptr) {
|
@Native public PromiseLib seek(Context ctx, long ptr, int whence) {
|
||||||
return PromiseLib.await(ctx, () -> {
|
return PromiseLib.await(ctx, () -> {
|
||||||
try {
|
try {
|
||||||
file.setPtr(ptr, 0);
|
return file.seek(ptr, whence);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
catch (FilesystemException e) { throw e.toEngineException(); }
|
catch (FilesystemException e) { throw e.toEngineException(); }
|
||||||
});
|
});
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user