Compare commits

...

150 Commits

Author SHA1 Message Date
2fe5ce607a fix: multiply acting as subtract 2024-09-21 19:01:05 +03:00
d821a3a89b refactor: utilize inheritence in index.js 2024-09-21 18:46:22 +03:00
0064c74ac8 fix: don't allow execution of CALL_SUPER twice or in non-construct call 2024-09-21 18:46:02 +03:00
bd548c813a fix: null out thisArg only when constructing 2024-09-21 18:45:38 +03:00
78af69ec80 fix: parseStatementEnd behaving incorrectly when EOF 2024-09-21 18:44:08 +03:00
98e5299f9c fix: derived classes use the scope API incorrectly 2024-09-21 18:43:32 +03:00
797452c28f fix: tmp variables captured incorrectly 2024-09-21 18:43:15 +03:00
fee74dcba4 fix: infinite loop in class parser 2024-09-21 18:42:51 +03:00
9845a39e84 fix: operations polluting stack 2024-09-21 18:42:34 +03:00
ee78bdc1cb feat: implement derived classes 2024-09-21 18:06:03 +03:00
7fcb9ed19f fix: member field initializers should be iterable 2024-09-20 11:39:48 +03:00
8dee4353d4 feat: implement non-enumerable members in classes 2024-09-20 11:39:46 +03:00
59e6f34a01 refactor: clean up REPL stringification code 2024-09-20 11:39:40 +03:00
fdac93bf4d fix: scope offsets calculated incorrectly 2024-09-20 11:39:34 +03:00
06eae2eaf2 Merge pull request #29 from TopchetoEU/TopchetoEU/classes
Classes
2024-09-19 15:21:01 +00:00
d7b50fa45b refactor: use classes in index.js 2024-09-19 18:11:42 +03:00
077e8afff7 fix: some behavioral issues 2024-09-19 18:11:35 +03:00
631ef9db4a fix: differenciate between non-functions and non-invokables in messages 2024-09-19 18:10:50 +03:00
0258cc0a90 feat: implement classes (without inheritence) 2024-09-19 18:09:28 +03:00
0b3dca8b13 refactor: extract members into own classes 2024-09-19 18:08:11 +03:00
6d56660136 fix: stupid mistake with variable capturing 2024-09-19 14:22:21 +03:00
8a21873631 fix: retrofit patterns for bindings and check if var is init in runtime 2024-09-19 11:02:02 +03:00
fbbd26bf7d fix: remove unneeded comments 2024-09-14 22:08:33 +03:00
e2a8a382cc refactoring 2024-09-14 21:33:33 +03:00
0670ffcdd1 fix: int value not correctly recognized 2024-09-14 19:54:42 +03:00
9b957335bf optimization: keep StringValue instances tied to one String instance 2024-09-14 19:45:05 +03:00
e9f889576c feat: implement hidden integers 2024-09-14 19:38:30 +03:00
e11d182631 refactor: remove dead code 2024-09-14 18:52:07 +03:00
30674ee463 refactor: get rid of InterruptException 2024-09-14 18:46:47 +03:00
fab3e59910 feat: implement a byte array 2024-09-14 18:46:28 +03:00
d7e4e7a024 refactor: oops 2024-09-14 18:45:55 +03:00
e4166fe450 refactor: rewrite some code for java 8 compatibility 2024-09-14 18:45:20 +03:00
b5b7781136 Merge pull request #28 from TopchetoEU:TopchetoEU/destructing
TopchetoEU/destructing
2024-09-14 15:38:02 +03:00
f13bf584a5 feat: add some missing features in the polyfills 2024-09-14 15:25:34 +03:00
4e8b110fc4 feat: add assign shorthands 2024-09-14 14:33:09 +03:00
cb82f4cf32 feat: implement patterns 2024-09-14 14:23:46 +03:00
23ae2b2e46 todo 2024-09-14 14:23:35 +03:00
55613ef2c9 feat: extend the instruction set 2024-09-14 14:23:28 +03:00
0b34c68139 fix: unnecessary new line in toReadable 2024-09-14 14:22:51 +03:00
d87e53264d refactor: split array logic 2024-09-14 14:22:31 +03:00
1f42263051 clean up member logic 2024-09-14 13:53:58 +03:00
3e6816cb2c fix: properly hande variable collisions 2024-09-12 20:25:11 +03:00
2a01b3d86e some work losl 2024-09-07 21:06:08 +03:00
8e64d13c87 refactor: clean up assigning 2024-09-06 15:48:22 +03:00
5f88061ee7 refactor: rename callNew -> construct and call -> invoke 2024-09-06 15:48:07 +03:00
b9268518f6 fix: array statements broken when empty elements 2024-09-06 15:46:10 +03:00
63ccd5757e feat: implement spread_obj intrinsic 2024-09-06 10:03:55 +03:00
515011b3ef refactor: improve meber listing system 2024-09-06 10:03:37 +03:00
b6f04aa177 Merge pull request #27 from TopchetoEU/TopchetoEU/optimize-var-flatten
Optimize var flattening
2024-09-05 22:32:48 +03:00
6f548ce5ff feat: reflect scope optimizations in runtime 2024-09-05 22:30:28 +03:00
7c74df4d36 refactor: use new system to reorder variables that overlaps neighboring scopes 2024-09-05 21:25:39 +03:00
641d4d1863 Merge pull request #26 from TopchetoEU/ES6
ES6 Support Groundwork + Fixes
2024-09-05 17:26:07 +03:00
0004839f6f fix: realloc for declarations after each iteration 2024-09-05 17:15:26 +03:00
07411f62c8 feat: implement capturable locals realloc 2024-09-05 17:14:59 +03:00
4bfc062aaf fix: correctly flatten locals in control flow statements 2024-09-05 17:13:34 +03:00
9ec99def3f fix: variable declarations shouldn't collide with defined name of named function exp 2024-09-05 13:29:42 +03:00
8f13ff3e0b fix: scope gets polluted by arguments with named function expressions 2024-09-05 13:29:20 +03:00
d7353e19ed fix: treat "arguments" as a keyword (as per strict soecifications) 2024-09-05 13:18:53 +03:00
e509edc459 fix: wrong arguments when compilling function bodies 2024-09-05 13:03:49 +03:00
b6a90b108b fix: wrong check for var decl collisions 2024-09-05 13:03:36 +03:00
55caf1e206 refactor: remove unneeded old comments 2024-09-05 13:03:04 +03:00
eac4a3af23 fix: throw access before decl for const and let in runtime 2024-09-05 00:31:03 +03:00
5b4adf5286 a clusterfuck of fixes with let and const 2024-09-05 00:28:13 +03:00
807b3918fa motherfucker 2024-09-04 15:55:59 +03:00
9265a7d813 Merge branch 'master' into ES6 2024-09-04 15:52:03 +03:00
1589ef51b0 refactor: move src and resources to standard places 2024-09-04 15:32:54 +03:00
marregui
313b20a3b3 Add first test (#23)
* Add the first test and upgrade build.gradle to modern standards

1. gradle wrapper
2. ./gradlew run
3. manifest will look like
    Manifest-Version: 1.0
    Main-Class: me.topchetoeu.jscript.runtime.SimpleRepl
    Build-Timestamp: 2024-09-04T10:44:35.990+0200
    Build-Branch: ma/add-first-tests
    Build-Revision: 412edc0ebc
    Build-Jdk: 21.0.3 (Oracle Corporation 21.0.3+7-LTS-152)
    Build-Author: TopchetoEU
4. build/distributions contains a zip and a jar which contain jscript-0.9.41-beta.jar
5. unnecessary libs have been removed
6. gradle has been updated to 8.10
7. first test has been added

* fix: revert removal of Jabel (for support of Java 11)

---------

Co-authored-by: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com>
2024-09-04 15:29:17 +03:00
ce9b419757 reafactor: make globals initializer use ES6 features 2024-09-04 10:45:34 +03:00
6f8efe74c4 fix: print was returning java null, not JS undefined 2024-09-04 10:45:14 +03:00
bd503ed943 feat: impl new instructions 2024-09-04 10:44:58 +03:00
5359c54694 fix: rethrow SyntaxException from compilation as EngineException 2024-09-04 10:44:44 +03:00
93c246ad97 refactor: remove arg loading from frame 2024-09-04 10:44:24 +03:00
7c8efaf066 fix: incorrect printing of object-like values 2024-09-04 10:43:40 +03:00
546d663466 feat: add this arg capture 2024-09-04 10:42:21 +03:00
f929015f55 ammend to prev commit 2024-09-04 10:41:52 +03:00
2a5f6aa9aa fix: use _STR and _INT variants of member instructions 2024-09-04 10:41:17 +03:00
78d233a6bd refactor: remove ArgumentsNode 2024-09-04 10:39:11 +03:00
3f25868f19 fix: some scope bug fixes 2024-09-04 10:38:16 +03:00
e3f1bc0949 fix: incorrect compilation of if-else 2024-09-04 10:36:48 +03:00
506726fd76 feat: implement ES6 variables and rest args 2024-09-04 10:36:25 +03:00
4cbc108686 feat: implement optional arguments 2024-09-04 10:00:48 +03:00
c39c06b792 refactor: move away intrinsic logic to final methods for performance 2024-09-04 10:00:45 +03:00
7ab78b9cea fix: control flow nodes were making scopes instead of compound nodes 2024-09-04 10:00:43 +03:00
87e077d70d oops 2024-09-04 10:00:40 +03:00
52f7c15ac8 refactor: change how function scope keeps track of arguments 2024-09-04 10:00:38 +03:00
6932bea677 refactor: remove unused class 2024-09-04 10:00:36 +03:00
82d6f52a26 refactor: make some classes final for performance 2024-09-04 10:00:33 +03:00
1b87c2f5a6 fix: add for-of to statement list 2024-09-04 10:00:30 +03:00
163dfe7b6e feat: implement access to intrinsics 2024-09-04 10:00:25 +03:00
2b6d4a87ca fix: for in and for of not reading open paren 2024-09-04 10:00:22 +03:00
349d392269 major rewrite: clean up a lot of code and lay ground for ES6 support 2024-09-04 10:00:15 +03:00
6481e992fa feat: implement "has" function for scopes 2024-09-04 10:00:11 +03:00
4a5e5a71af feat: Create new scope system for ES6+ support 2024-09-04 10:00:02 +03:00
89ba921b4a refactor: rename statements to nodes 2024-09-04 10:00:00 +03:00
a45f4109d8 feat: add environment in Source 2024-09-04 09:59:57 +03:00
62aba62a41 refactor: make Environment more reusable 2024-09-04 09:59:54 +03:00
4048d6ef1c refactor: rename ES5 to JavaScript 2024-09-04 09:59:42 +03:00
d0ccf00f14 everything all at once 2024-09-04 09:59:35 +03:00
f09feae08f refactor: clean up parsing 2024-09-04 09:59:32 +03:00
ef0fc5a61d refactor: distribute parse functions in node classes 2024-09-04 09:59:28 +03:00
bab59d454f refactor: Transition to a Value class 2024-09-04 09:59:26 +03:00
3475e3a130 refactor: Remove environment-related bloat 2024-09-04 09:59:15 +03:00
49b52d90a7 fix: wrappers cache compare objects with .equals and not == 2024-04-21 11:03:00 +03:00
8a8de518a6 feat: make Function constructor 2024-04-20 23:44:02 +03:00
099201e4ad refactor: remove testing junk in REPL 2024-04-20 22:23:45 +03:00
f8553b79f9 fix: run module in an isolated context 2024-04-20 22:22:55 +03:00
ba6462458c fix: some fixes in the filesystem 2024-04-20 22:18:47 +03:00
e33cdbb172 fix: properties not applied to wrappers without constructor method 2024-04-13 01:03:34 +03:00
fc6ddf7d3c feat: allow interface proxy wrappers 2024-04-12 16:37:06 +03:00
7f275095a2 fix: continue statement compiled incorrectly 2024-04-07 12:50:58 +03:00
90d019f92a bump 2024-04-07 12:33:48 +03:00
6fb31be12c fix(debugger): handle all errors when generating description 2024-04-07 12:33:26 +03:00
d6ede0b404 fix: incorrect toFixed behavior 2024-04-03 15:52:01 +03:00
71b40240c0 feat: add Number.toFixed 2024-04-03 15:09:01 +03:00
a8775d212f fix: clean up extensions at some points 2024-04-03 14:52:29 +03:00
71872a8d64 fix 2024-04-03 14:25:14 +03:00
c707f880f7 fix: use Extensions instead of Environment 2024-04-03 14:21:23 +03:00
0d629a6e82 fix: use correct class instead of proxy 2024-04-03 12:27:15 +03:00
6eea342d04 fix: fuck 2024-04-02 18:24:43 +03:00
ece9cf68dc fix: correctly update proto chain 2024-04-02 18:19:05 +03:00
11ecd8c68f fix: exec debugger close logic on application exit 2024-04-02 18:05:49 +03:00
48bd304c6e fix: environment forks fixes 2024-04-02 18:05:20 +03:00
d8e46c3149 fix: clone environment correctly 2024-03-31 16:11:32 +03:00
5fc5eb08f8 fix: update breakpoints when removing bp 2024-03-30 12:52:44 +02:00
8acbc003c4 fix: properly resolve breakpoints 2024-03-30 12:13:04 +02:00
fda33112a7 fix: load maps when attaching debugger 2024-03-30 11:13:45 +02:00
67b2413d7c bump2 2024-03-30 10:36:55 +02:00
3a05416510 bump 2024-03-30 10:30:26 +02:00
c291328cc3 fix: detach debugger after close 2024-03-30 10:22:12 +02:00
7cb267b0d9 fix: some issues with debugger 2024-03-30 09:55:20 +02:00
4e31766665 fix: add new vscode debugger functions 2024-03-29 21:53:15 +02:00
b5b63c4342 fix: make global cache of native wrappers 2024-03-28 16:08:07 +02:00
18f70a0d58 fix: i hate wrappers 2024-03-28 15:10:21 +02:00
d38b600366 fix: some more wrapper issues 2024-03-28 14:52:49 +02:00
0ac7af2ea3 fix: take into account empty classes 2024-03-28 14:21:23 +02:00
5185c93663 fix: don't include non-exposing wrappers in proto chain
feat: allow adding custom wrappers
2024-03-28 00:57:09 +02:00
510422cab7 feat: implement logic for exposing non-static fields 2024-03-27 23:39:33 +02:00
79e1d1cfaf Merge branch 'master' of https://github.com/TopchetoEU/java-jscript 2024-03-27 23:08:25 +02:00
e0f3274a95 feat: add simple for-of loop (not intended for production usage) 2024-03-27 23:08:21 +02:00
ef5d29105f Update README.md 2024-03-10 02:17:18 +02:00
d8ea6557df fix: buildline expects tag to start with 'v' 2024-03-09 00:45:00 +02:00
5ba858545a fix: defer handles of async functions 2024-03-09 00:28:30 +02:00
446ecd8f2b fix: promise defers callback twice 2024-03-08 17:23:50 +02:00
fbf103439a bump 2024-03-08 16:55:46 +02:00
b30f94de8f refactor: move function pushMsg signatures in EventLoop 2024-03-08 16:53:47 +02:00
47b4dd3c15 refactor: rename code to runtime 2024-03-06 23:23:01 +02:00
0fb336373a fix: make fs calls synchronized 2024-03-06 12:50:57 +02:00
b33325a98d fix: clear buffer of line writer file 2024-03-05 17:10:06 +02:00
ccf75d6066 fix: don't use Context.NULL in global scope 2024-03-05 16:51:50 +02:00
662dcc1ac1 bump 2024-03-05 16:30:13 +02:00
298 changed files with 12307 additions and 15488 deletions

View File

@@ -3,7 +3,7 @@ name: "tagged-release"
on:
push:
tags:
- "v*"
- "*"
jobs:
tagged-release:

View File

@@ -23,7 +23,3 @@ engine.run(true);
// Get our result
System.out.println(awaitable.await());
```
## NOTE:
To setup the typescript bundle in your sources, run `node build.js init-ts`. This will download the latest version of typescript, minify it, and add it to your src folder. If you are going to work with the `node build.js debug|release` command, this is not a necessary step.

View File

@@ -1,21 +1,75 @@
import java.text.SimpleDateFormat
plugins {
id "application"
id 'application'
id 'net.nemerosa.versioning' version '2.15.0'
id 'org.ajoberstar.grgit' version '5.0.0-rc.3' // required by gradle
// TODO: figure out how to integrate proguard
// id "com.github.xaverkapeller.proguard-annotations"
}
base.archivesName = project.project_name
version = project.project_version
group = project.project_group
description = 'ES5-compliant JavaScript interpreter'
repositories {
mavenCentral()
}
dependencies {
annotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2'
compileOnly 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2'
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
toolchain.languageVersion = JavaLanguageVersion.of(11)
withSourcesJar()
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
configure([tasks.compileJava]) {
options.release = 8
}
jar {
manifest.attributes["Main-class"] = project.main_class
manifest {
attributes(
'Main-Class': project.main_class,
'Build-Timestamp': new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").format(new Date()),
'Build-Branch': versioning.info.branch,
'Build-Revision': versioning.info.commit,
'Build-Jdk': "${System.properties['java.version']} (${System.properties['java.vendor']} ${System.properties['java.vm.version']})",
'Build-Author': 'TopchetoEU'
)
}
}
sourceSets {
main.java.srcDirs = [ "src/java" ]
main.resources.srcDirs = [ "src/assets" ]
application {
mainClass = project.main_class
applicationDefaultJvmArgs = ['-Xmx2G', '-Xms2G', '-server', '-Dfile.encoding=UTF-8']
}
distZip {
eachFile { file ->
if (file.path.contains('bin')) {
file.exclude()
}
}
}
distTar {
eachFile { file ->
if (file.path.contains('bin')) {
file.exclude()
}
}
}
processResources {
@@ -27,6 +81,10 @@ processResources {
}
}
base.archivesName = project.project_name
version = project.project_version
group = project.project_group
test {
useJUnitPlatform()
}
wrapper {
gradleVersion = '8.10'
}

View File

@@ -1,4 +1,4 @@
project_group = me.topchetoeu
project_name = jscript
project_version = 0.8.6-beta
main_class = me.topchetoeu.jscript.utils.JScriptRepl
project_version = 0.9.41-beta
main_class = me.topchetoeu.jscript.runtime.SimpleRepl

View File

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

View File

@@ -1,5 +1,13 @@
pluginManagement {
repositories {
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0'
}
rootProject.name = properties.project_name

View File

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

View File

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

View File

@@ -1,101 +0,0 @@
package me.topchetoeu.jscript.common;
import java.util.ArrayList;
public class Location implements Comparable<Location> {
public static final Location INTERNAL = new Location(-1, -1, new Filename("jscript", "native"));
private int line;
private int start;
private Filename filename;
public int line() { return line; }
public int start() { return start; }
public Filename filename() { return filename; }
@Override
public String toString() {
var res = new ArrayList<String>();
if (filename != null) res.add(filename.toString());
if (line >= 0) res.add(line + "");
if (start >= 0) res.add(start + "");
return String.join(":", res);
}
public Location add(int n, boolean clone) {
if (clone) return new Location(line, start + n, filename);
this.start += n;
return this;
}
public Location add(int n) {
return add(n, false);
}
public Location nextLine() {
line++;
start = 0;
return this;
}
public Location clone() {
return new Location(line, start, filename);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + line;
result = prime * result + start;
result = prime * result + ((filename == null) ? 0 : filename.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
Location other = (Location) obj;
if (line != other.line) return false;
if (start != other.start) return false;
if (filename == null && other.filename != null) return false;
else if (!filename.equals(other.filename)) return false;
return true;
}
@Override
public int compareTo(Location other) {
int a = filename.toString().compareTo(other.filename.toString());
int b = Integer.compare(line, other.line);
int c = Integer.compare(start, other.start);
if (a != 0) return a;
if (b != 0) return b;
return c;
}
public Location(int line, int start, Filename filename) {
this.line = line;
this.start = start;
this.filename = filename;
}
public static Location parse(String raw) {
int i0 = -1, i1 = -1;
for (var i = raw.length() - 1; i >= 0; i--) {
if (raw.charAt(i) == ':') {
if (i1 == -1) i1 = i;
else if (i0 == -1) {
i0 = i;
break;
}
}
}
return new Location(
Integer.parseInt(raw.substring(i0 + 1, i1)),
Integer.parseInt(raw.substring(i1 + 1)),
Filename.parse(raw.substring(0, i0))
);
}
}

View File

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

View File

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

View File

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

View File

@@ -1,31 +0,0 @@
package me.topchetoeu.jscript.common.events;
public class DataNotifier<T> {
private Notifier notifier = new Notifier();
private boolean isErr;
private T val;
private RuntimeException err;
public void error(RuntimeException t) {
err = t;
isErr = true;
notifier.next();
}
public void next(T val) {
this.val = val;
isErr = false;
notifier.next();
}
public T await() {
notifier.await();
try {
if (isErr) throw err;
else return val;
}
finally {
this.err = null;
this.val = null;
}
}
}

View File

@@ -1,19 +0,0 @@
package me.topchetoeu.jscript.common.events;
import me.topchetoeu.jscript.core.exceptions.InterruptException;
public class Notifier {
private boolean ok = false;
public synchronized void next() {
ok = true;
notifyAll();
}
public synchronized void await() {
try {
while (!ok) wait();
ok = false;
}
catch (InterruptedException e) { throw new InterruptException(e); }
}
}

View File

@@ -1,243 +0,0 @@
package me.topchetoeu.jscript.common.json;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.compilation.parsing.Operator;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.core.Context;
import me.topchetoeu.jscript.core.values.ArrayValue;
import me.topchetoeu.jscript.core.values.ObjectValue;
import me.topchetoeu.jscript.core.values.Values;
import me.topchetoeu.jscript.core.exceptions.EngineException;
import me.topchetoeu.jscript.core.exceptions.SyntaxException;
public class JSON {
public static Object toJs(JSONElement val) {
if (val.isBoolean()) return val.bool();
if (val.isString()) return val.string();
if (val.isNumber()) return val.number();
if (val.isList()) return ArrayValue.of(null, val.list().stream().map(JSON::toJs).collect(Collectors.toList()));
if (val.isMap()) {
var res = new ObjectValue();
for (var el : val.map().entrySet()) {
res.defineProperty(null, el.getKey(), toJs(el.getValue()));
}
return res;
}
if (val.isNull()) return Values.NULL;
return null;
}
private static JSONElement fromJs(Context ctx, Object val, HashSet<Object> prev) {
if (val instanceof Boolean) return JSONElement.bool((boolean)val);
if (val instanceof Number) return JSONElement.number(((Number)val).doubleValue());
if (val instanceof String) return JSONElement.string((String)val);
if (val == Values.NULL) return JSONElement.NULL;
if (val instanceof ArrayValue) {
if (prev.contains(val)) throw new EngineException("Circular dependency in JSON.");
prev.add(val);
var res = new JSONList();
for (var el : ((ArrayValue)val).toArray()) {
var jsonEl = fromJs(ctx, el, prev);
if (jsonEl == null) jsonEl = JSONElement.NULL;
res.add(jsonEl);
}
prev.remove(val);
return JSONElement.of(res);
}
if (val instanceof ObjectValue) {
if (prev.contains(val)) throw new EngineException("Circular dependency in JSON.");
prev.add(val);
var res = new JSONMap();
for (var el : Values.getMembers(ctx, val, false, false)) {
var jsonEl = fromJs(ctx, Values.getMember(ctx, val, el), prev);
if (jsonEl == null) continue;
if (el instanceof String || el instanceof Number) res.put(el.toString(), jsonEl);
}
prev.remove(val);
return JSONElement.of(res);
}
if (val == null) return null;
return null;
}
public static JSONElement fromJs(Context ctx, Object val) {
return fromJs(ctx, val, new HashSet<>());
}
public static ParseRes<String> parseIdentifier(List<Token> tokens, int i) {
return Parsing.parseIdentifier(tokens, i);
}
public static ParseRes<String> parseString(Filename filename, List<Token> tokens, int i) {
var res = Parsing.parseString(filename, tokens, i);
if (res.isSuccess()) return ParseRes.res((String)res.result.value, res.n);
else return res.transform();
}
public static ParseRes<Double> parseNumber(Filename filename, List<Token> tokens, int i) {
var minus = Parsing.isOperator(tokens, i, Operator.SUBTRACT);
if (minus) 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));
else return res.transform();
}
public static ParseRes<Boolean> parseBool(Filename filename, List<Token> tokens, int i) {
var id = parseIdentifier(tokens, i);
if (!id.isSuccess()) return ParseRes.failed();
else if (id.result.equals("true")) return ParseRes.res(true, 1);
else if (id.result.equals("false")) return ParseRes.res(false, 1);
else return ParseRes.failed();
}
public static ParseRes<?> parseValue(Filename filename, List<Token> tokens, int i) {
return ParseRes.any(
parseString(filename, tokens, i),
parseNumber(filename, tokens, i),
parseBool(filename, tokens, i),
parseMap(filename, tokens, i),
parseList(filename, tokens, i)
);
}
public static ParseRes<JSONMap> parseMap(Filename filename, List<Token> tokens, int i) {
int n = 0;
if (!Parsing.isOperator(tokens, i + n++, Operator.BRACE_OPEN)) return ParseRes.failed();
var values = new JSONMap();
while (true) {
if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) {
n++;
break;
}
var name = ParseRes.any(
parseIdentifier(tokens, i + n),
parseString(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);
else n += name.n;
if (!Parsing.isOperator(tokens, i + n, Operator.COLON)) {
return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), "Expected a colon.", name);
}
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);
else n += res.n;
values.put(name.result.toString(), JSONElement.of(res.result));
if (Parsing.isOperator(tokens, i + n, Operator.COMMA)) n++;
else if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) {
n++;
break;
}
}
return ParseRes.res(values, n);
}
public static ParseRes<JSONList> parseList(Filename filename, List<Token> tokens, int i) {
int n = 0;
if (!Parsing.isOperator(tokens, i + n++, Operator.BRACKET_OPEN)) return ParseRes.failed();
var values = new JSONList();
while (true) {
if (Parsing.isOperator(tokens, i + n, Operator.BRACKET_CLOSE)) {
n++;
break;
}
var res = parseValue(filename, tokens, i + n);
if (!res.isSuccess()) return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), "Expected a list element.", res);
else n += res.n;
values.add(JSONElement.of(res.result));
if (Parsing.isOperator(tokens, i + n, Operator.COMMA)) n++;
else if (Parsing.isOperator(tokens, i + n, Operator.BRACKET_CLOSE)) {
n++;
break;
}
}
return ParseRes.res(values, n);
}
public static JSONElement parse(Filename filename, String raw) {
if (filename == null) filename = new Filename("jscript", "json");
var res = parseValue(filename, Parsing.tokenize(filename, raw), 0);
if (res.isFailed()) throw new SyntaxException(null, "Invalid JSON given.");
else if (res.isError()) throw new SyntaxException(null, res.error);
else return JSONElement.of(res.result);
}
public static String stringify(JSONElement el) {
if (el.isNumber()) return Double.toString(el.number());
if (el.isBoolean()) return el.bool() ? "true" : "false";
if (el.isNull()) return "null";
if (el.isString()) {
var res = new StringBuilder("\"");
var alphabet = "0123456789ABCDEF".toCharArray();
for (var c : el.string().toCharArray()) {
if (c < 32 || c >= 127) {
res
.append("\\u")
.append(alphabet[(c >> 12) & 0xF])
.append(alphabet[(c >> 8) & 0xF])
.append(alphabet[(c >> 4) & 0xF])
.append(alphabet[(c >> 0) & 0xF]);
}
else if (c == '\\')
res.append("\\\\");
else if (c == '"')
res.append("\\\"");
else res.append(c);
}
return res.append('"').toString();
}
if (el.isList()) {
var res = new StringBuilder().append("[");
for (int i = 0; i < el.list().size(); i++) {
if (i != 0) res.append(",");
res.append(stringify(el.list().get(i)));
}
res.append("]");
return res.toString();
}
if (el.isMap()) {
var res = new StringBuilder().append("{");
var entries = el.map().entrySet().stream().collect(Collectors.toList());
for (int i = 0; i < entries.size(); i++) {
if (i != 0) res.append(",");
res.append(stringify(JSONElement.string(entries.get(i).getKey())));
res.append(":");
res.append(stringify(entries.get(i).getValue()));
}
res.append("}");
return res.toString();
}
return null;
}
public static String stringify(JSONMap map) {
return stringify(JSONElement.of(map));
}
public static String stringify(JSONList list) {
return stringify(JSONElement.of(list));
}
}

View File

@@ -1,12 +0,0 @@
package me.topchetoeu.jscript.compilation;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Operation;
public abstract class AssignableStatement extends Statement {
public abstract Statement toAssign(Statement val, Operation operation);
protected AssignableStatement(Location loc) {
super(loc);
}
}

View File

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

View File

@@ -1,64 +0,0 @@
package me.topchetoeu.jscript.compilation;
import java.util.List;
import java.util.Vector;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.values.FunctionStatement;
public class CompoundStatement extends Statement {
public final Statement[] statements;
public final boolean separateFuncs;
public Location end;
@Override public boolean pure() {
for (var stm : statements) {
if (!stm.pure()) return false;
}
return true;
}
@Override
public void declare(CompileResult target) {
for (var stm : statements) stm.declare(target);
}
@Override
public void compile(CompileResult target, boolean pollute, BreakpointType type) {
List<Statement> statements = new Vector<Statement>();
if (separateFuncs) for (var stm : this.statements) {
if (stm instanceof FunctionStatement && ((FunctionStatement)stm).statement) {
stm.compile(target, false);
}
else statements.add(stm);
}
else statements = List.of(this.statements);
var polluted = false;
for (var i = 0; i < statements.size(); i++) {
var stm = statements.get(i);
if (i != statements.size() - 1) stm.compile(target, false, BreakpointType.STEP_OVER);
else stm.compile(target, polluted = pollute, BreakpointType.STEP_OVER);
}
if (!polluted && pollute) {
target.add(Instruction.pushUndefined());
}
}
public CompoundStatement setEnd(Location loc) {
this.end = loc;
return this;
}
public CompoundStatement(Location loc, boolean separateFuncs, Statement ...statements) {
super(loc);
this.separateFuncs = separateFuncs;
this.statements = statements;
}
}

View File

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

View File

@@ -1,52 +0,0 @@
package me.topchetoeu.jscript.compilation;
import java.util.List;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.values.FunctionStatement;
public class VariableDeclareStatement extends Statement {
public static class Pair {
public final String name;
public final Statement value;
public final Location location;
public Pair(String name, Statement value, Location location) {
this.name = name;
this.value = value;
this.location = location;
}
}
public final List<Pair> values;
@Override
public void declare(CompileResult target) {
for (var key : values) {
target.scope.define(key.name);
}
}
@Override
public void compile(CompileResult target, boolean pollute) {
for (var entry : values) {
if (entry.name == null) continue;
var key = target.scope.getKey(entry.name);
if (key instanceof String) target.add(Instruction.makeVar((String)key));
if (entry.value != null) {
FunctionStatement.compileWithName(entry.value, target, true, entry.name, BreakpointType.STEP_OVER);
target.add(Instruction.storeVar(key));
}
}
if (pollute) target.add(Instruction.pushUndefined());
}
public VariableDeclareStatement(Location loc, List<Pair> values) {
super(loc);
this.values = values;
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,36 +0,0 @@
package me.topchetoeu.jscript.compilation.control;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class DoWhileStatement extends Statement {
public final Statement condition, body;
public final String label;
@Override
public void declare(CompileResult target) {
body.declare(target);
}
@Override
public void compile(CompileResult target, boolean pollute) {
int start = target.size();
body.compile(target, false, BreakpointType.STEP_OVER);
int mid = target.size();
condition.compile(target, true, BreakpointType.STEP_OVER);
int end = target.size();
WhileStatement.replaceBreaks(target, label, start, mid - 1, mid, end + 1);
target.add(Instruction.jmpIf(start - end));
}
public DoWhileStatement(Location loc, String label, Statement condition, Statement body) {
super(loc);
this.label = label;
this.condition = condition;
this.body = body;
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,83 +0,0 @@
package me.topchetoeu.jscript.compilation.control;
import java.util.HashMap;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.Instruction.Type;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class SwitchStatement extends Statement {
public static class SwitchCase {
public final Statement value;
public final int statementI;
public SwitchCase(Statement value, int statementI) {
this.value = value;
this.statementI = statementI;
}
}
public final Statement value;
public final SwitchCase[] cases;
public final Statement[] body;
public final int defaultI;
@Override
public void declare(CompileResult target) {
for (var stm : body) stm.declare(target);
}
@Override
public void compile(CompileResult target, boolean pollute) {
var caseToStatement = new HashMap<Integer, Integer>();
var statementToIndex = new HashMap<Integer, Integer>();
value.compile(target, true, BreakpointType.STEP_OVER);
for (var ccase : cases) {
target.add(Instruction.dup());
ccase.value.compile(target, true);
target.add(Instruction.operation(Operation.EQUALS));
caseToStatement.put(target.temp(), ccase.statementI);
}
int start = target.temp();
for (var stm : body) {
statementToIndex.put(statementToIndex.size(), target.size());
stm.compile(target, false, BreakpointType.STEP_OVER);
}
int end = target.size();
target.add(Instruction.discard());
if (pollute) target.add(Instruction.pushUndefined());
if (defaultI < 0 || defaultI >= body.length) target.set(start, Instruction.jmp(end - start));
else target.set(start, Instruction.jmp(statementToIndex.get(defaultI) - start));
for (int i = start; i < end; i++) {
var instr = target.get(i);
if (instr.type == Type.NOP && instr.is(0, "break") && instr.get(1) == null) {
target.set(i, Instruction.jmp(end - i));
}
}
for (var el : caseToStatement.entrySet()) {
var i = statementToIndex.get(el.getValue());
if (i == null) i = end;
target.set(el.getKey(), Instruction.jmpIf(i - el.getKey()));
}
}
public SwitchStatement(Location loc, Statement value, int defaultI, SwitchCase[] cases, Statement[] body) {
super(loc);
this.value = value;
this.defaultI = defaultI;
this.cases = cases;
this.body = body;
}
}

View File

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

View File

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

View File

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

View File

@@ -1,113 +0,0 @@
package me.topchetoeu.jscript.compilation.parsing;
import java.util.HashMap;
import java.util.Map;
import me.topchetoeu.jscript.common.Operation;
public enum Operator {
MULTIPLY("*", Operation.MULTIPLY, 13),
DIVIDE("/", Operation.DIVIDE, 12),
MODULO("%", Operation.MODULO, 12),
SUBTRACT("-", Operation.SUBTRACT, 11),
ADD("+", Operation.ADD, 11),
SHIFT_RIGHT(">>", Operation.SHIFT_RIGHT, 10),
SHIFT_LEFT("<<", Operation.SHIFT_LEFT, 10),
USHIFT_RIGHT(">>>", Operation.USHIFT_RIGHT, 10),
GREATER(">", Operation.GREATER, 9),
LESS("<", Operation.LESS, 9),
GREATER_EQUALS(">=", Operation.GREATER_EQUALS, 9),
LESS_EQUALS("<=", Operation.LESS_EQUALS, 9),
NOT_EQUALS("!=", Operation.LOOSE_NOT_EQUALS, 8),
LOOSE_NOT_EQUALS("!==", Operation.NOT_EQUALS, 8),
EQUALS("==", Operation.LOOSE_EQUALS, 8),
LOOSE_EQUALS("===", Operation.EQUALS, 8),
AND("&", Operation.AND, 7),
XOR("^", Operation.XOR, 6),
OR("|", Operation.OR, 5),
LAZY_AND("&&", 4),
LAZY_OR("||", 3),
ASSIGN_SHIFT_LEFT("<<=", 2, true),
ASSIGN_SHIFT_RIGHT(">>=", 2, true),
ASSIGN_USHIFT_RIGHT(">>>=", 2, true),
ASSIGN_AND("&=", 2, true),
ASSIGN_OR("|=", 2, true),
ASSIGN_XOR("^=", 2, true),
ASSIGN_MODULO("%=", 2, true),
ASSIGN_DIVIDE("/=", 2, true),
ASSIGN_MULTIPLY("*=", 2, true),
ASSIGN_SUBTRACT("-=", 2, true),
ASSIGN_ADD("+=", 2, true),
ASSIGN("=", 2, true),
SEMICOLON(";"),
COLON(":"),
PAREN_OPEN("("),
PAREN_CLOSE(")"),
BRACKET_OPEN("["),
BRACKET_CLOSE("]"),
BRACE_OPEN("{"),
BRACE_CLOSE("}"),
DOT("."),
COMMA(","),
NOT("!"),
QUESTION("?"),
INVERSE("~"),
INCREASE("++"),
DECREASE("--");
public final String readable;
public final Operation operation;
public final int precedence;
public final boolean reverse;
private static final Map<String, Operator> ops = new HashMap<>();
static {
for (var el : Operator.values()) {
ops.put(el.readable, el);
}
}
public boolean isAssign() { return precedence == 2; }
public static Operator parse(String val) {
return ops.get(val);
}
private Operator() {
this.readable = null;
this.operation = null;
this.precedence = -1;
this.reverse = false;
}
private Operator(String value) {
this.readable = value;
this.operation = null;
this.precedence = -1;
this.reverse = false;
}
private Operator(String value, int precedence) {
this.readable = value;
this.operation = null;
this.precedence = precedence;
this.reverse = false;
}
private Operator(String value, int precedence, boolean reverse) {
this.readable = value;
this.operation = null;
this.precedence = precedence;
this.reverse = reverse;
}
private Operator(String value, Operation funcName, int precedence) {
this.readable = value;
this.operation = funcName;
this.precedence = precedence;
this.reverse = false;
}
private Operator(String value, Operation funcName, int precedence, boolean reverse) {
this.readable = value;
this.operation = funcName;
this.precedence = precedence;
this.reverse = reverse;
}
}

View File

@@ -1,100 +0,0 @@
package me.topchetoeu.jscript.compilation.parsing;
import java.util.List;
import java.util.Map;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.parsing.Parsing.Parser;
public class ParseRes<T> {
public static enum State {
SUCCESS,
FAILED,
ERROR;
public boolean isSuccess() { return this == SUCCESS; }
public boolean isFailed() { return this == FAILED; }
public boolean isError() { return this == ERROR; }
}
public final ParseRes.State state;
public final String error;
public final T result;
public final int n;
private ParseRes(ParseRes.State state, String error, T result, int readN) {
this.result = result;
this.n = readN;
this.state = state;
this.error = error;
}
public ParseRes<T> setN(int i) {
if (!state.isSuccess()) return this;
return new ParseRes<>(state, null, result, i);
}
public ParseRes<T> addN(int i) {
if (!state.isSuccess()) return this;
return new ParseRes<>(state, null, result, this.n + i);
}
public <T2> ParseRes<T2> transform() {
if (isSuccess()) throw new RuntimeException("Can't transform a ParseRes that hasn't failed.");
return new ParseRes<>(state, error, null, 0);
}
public TestRes toTest() {
if (isSuccess()) return TestRes.res(n);
else if (isError()) return TestRes.error(null, error);
else return TestRes.failed();
}
public boolean isSuccess() { return state.isSuccess(); }
public boolean isFailed() { return state.isFailed(); }
public boolean isError() { return state.isError(); }
public static <T> ParseRes<T> failed() {
return new ParseRes<T>(State.FAILED, null, null, 0);
}
public static <T> ParseRes<T> error(Location loc, String error) {
if (loc != null) error = loc + ": " + error;
return new ParseRes<>(State.ERROR, error, null, 0);
}
public static <T> ParseRes<T> error(Location loc, String error, ParseRes<?> other) {
if (loc != null) error = loc + ": " + error;
if (!other.isError()) return new ParseRes<>(State.ERROR, error, null, 0);
return new ParseRes<>(State.ERROR, other.error, null, 0);
}
public static <T> ParseRes<T> res(T val, int i) {
return new ParseRes<>(State.SUCCESS, null, val, i);
}
@SafeVarargs
public static <T> ParseRes<? extends T> any(ParseRes<? extends T> ...parsers) {
return any(List.of(parsers));
}
public static <T> ParseRes<? extends T> any(List<ParseRes<? extends T>> parsers) {
ParseRes<? extends T> best = null;
ParseRes<? extends T> error = ParseRes.failed();
for (var parser : parsers) {
if (parser.isSuccess()) {
if (best == null || best.n < parser.n) best = parser;
}
else if (parser.isError() && error.isFailed()) error = parser.transform();
}
if (best != null) return best;
else return error;
}
@SafeVarargs
public static <T> ParseRes<? extends T> first(String filename, List<Token> tokens, Map<String, Parser<T>> named, Parser<? extends T> ...parsers) {
ParseRes<? extends T> error = ParseRes.failed();
for (var parser : parsers) {
var res = parser.parse(null, tokens, 0);
if (res.isSuccess()) return res;
else if (res.isError() && error.isFailed()) error = res.transform();
}
return error;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,15 +0,0 @@
package me.topchetoeu.jscript.compilation.parsing;
public class RawToken {
public final String value;
public final TokenType type;
public final int line;
public final int start;
public RawToken(String value, TokenType type, int line, int start) {
this.value = value;
this.type = type;
this.line = line;
this.start = start;
}
}

View File

@@ -1,45 +0,0 @@
package me.topchetoeu.jscript.compilation.parsing;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.parsing.ParseRes.State;
public class TestRes {
public final State state;
public final String error;
public final int i;
private TestRes(ParseRes.State state, String error, int i) {
this.i = i;
this.state = state;
this.error = error;
}
public TestRes add(int n) {
return new TestRes(state, null, this.i + n);
}
public <T> ParseRes<T> transform() {
if (isSuccess()) throw new RuntimeException("Can't transform a TestRes that hasn't failed.");
else if (isError()) return ParseRes.error(null, error);
else return ParseRes.failed();
}
public boolean isSuccess() { return state.isSuccess(); }
public boolean isFailed() { return state.isFailed(); }
public boolean isError() { return state.isError(); }
public static TestRes failed() {
return new TestRes(State.FAILED, null, 0);
}
public static TestRes error(Location loc, String error) {
if (loc != null) error = loc + ": " + error;
return new TestRes(State.ERROR, error, 0);
}
public static TestRes error(Location loc, String error, TestRes other) {
if (loc != null) error = loc + ": " + error;
if (!other.isError()) return new TestRes(State.ERROR, error, 0);
return new TestRes(State.ERROR, other.error, 0);
}
public static TestRes res(int i) {
return new TestRes(State.SUCCESS, null, i);
}
}

View File

@@ -1,58 +0,0 @@
package me.topchetoeu.jscript.compilation.parsing;
public class Token {
public final Object value;
public final String rawValue;
public final boolean isString;
public final boolean isRegex;
public final int line;
public final int start;
private Token(int line, int start, Object value, String rawValue, boolean isString, boolean isRegex) {
this.value = value;
this.rawValue = rawValue;
this.line = line;
this.start = start;
this.isString = isString;
this.isRegex = isRegex;
}
private Token(int line, int start, Object value, String rawValue) {
this.value = value;
this.rawValue = rawValue;
this.line = line;
this.start = start;
this.isString = false;
this.isRegex = false;
}
public boolean isString() { return isString; }
public boolean isRegex() { return isRegex; }
public boolean isNumber() { return value instanceof Number; }
public boolean isIdentifier() { return !isString && !isRegex && value instanceof String; }
public boolean isOperator() { return value instanceof Operator; }
public boolean isIdentifier(String lit) { return !isString && !isRegex && value.equals(lit); }
public boolean isOperator(Operator op) { return value.equals(op); }
public String string() { return (String)value; }
public String regex() { return (String)value; }
public double number() { return (double)value; }
public String identifier() { return (String)value; }
public Operator operator() { return (Operator)value; }
public static Token regex(int line, int start, String val, String rawValue) {
return new Token(line, start, val, rawValue, false, true);
}
public static Token string(int line, int start, String val, String rawValue) {
return new Token(line, start, val, rawValue, true, false);
}
public static Token number(int line, int start, double val, String rawValue) {
return new Token(line, start, val, rawValue);
}
public static Token identifier(int line, int start, String val) {
return new Token(line, start, val, val);
}
public static Token operator(int line, int start, Operator val) {
return new Token(line, start, val, val.readable);
}
}

View File

@@ -1,9 +0,0 @@
package me.topchetoeu.jscript.compilation.parsing;
enum TokenType {
REGEX,
STRING,
NUMBER,
LITERAL,
OPERATOR,
}

View File

@@ -1,77 +0,0 @@
package me.topchetoeu.jscript.compilation.scope;
import java.util.ArrayList;
public class LocalScopeRecord implements ScopeRecord {
public final LocalScopeRecord parent;
private final ArrayList<String> captures = new ArrayList<>();
private final ArrayList<String> locals = new ArrayList<>();
public String[] captures() {
return captures.toArray(String[]::new);
}
public String[] locals() {
return locals.toArray(String[]::new);
}
public LocalScopeRecord child() {
return new LocalScopeRecord(this);
}
public int localsCount() {
return locals.size();
}
public int capturesCount() {
return captures.size();
}
public int[] getCaptures() {
var buff = new int[captures.size()];
var i = 0;
for (var name : captures) {
var index = parent.getKey(name);
if (index instanceof Integer) buff[i++] = (int)index;
}
var res = new int[i];
System.arraycopy(buff, 0, res, 0, i);
return res;
}
public Object getKey(String name) {
var capI = captures.indexOf(name);
var locI = locals.lastIndexOf(name);
if (locI >= 0) return locI;
if (capI >= 0) return ~capI;
if (parent != null) {
var res = parent.getKey(name);
if (res != null && res instanceof Integer) {
captures.add(name);
return -captures.size();
}
}
return name;
}
public Object define(String name, boolean force) {
if (!force && locals.contains(name)) return locals.indexOf(name);
locals.add(name);
return locals.size() - 1;
}
public Object define(String name) {
return define(name, false);
}
public void undefine() {
locals.remove(locals.size() - 1);
}
public LocalScopeRecord() {
this.parent = null;
}
public LocalScopeRecord(LocalScopeRecord parent) {
this.parent = parent;
}
}

View File

@@ -1,7 +0,0 @@
package me.topchetoeu.jscript.compilation.scope;
public interface ScopeRecord {
public Object getKey(String name);
public Object define(String name);
public LocalScopeRecord child();
}

View File

@@ -1,40 +0,0 @@
package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class ArrayStatement extends Statement {
public final Statement[] statements;
@Override public boolean pure() {
for (var stm : statements) {
if (!stm.pure()) return false;
}
return true;
}
@Override
public void compile(CompileResult target, boolean pollute) {
target.add(Instruction.loadArr(statements.length));
for (var i = 0; i < statements.length; i++) {
var el = statements[i];
if (el != null) {
target.add(Instruction.dup());
target.add(Instruction.pushValue(i));
el.compile(target, true);
target.add(Instruction.storeMember());
}
}
if (!pollute) target.add(Instruction.discard());
}
public ArrayStatement(Location loc, Statement[] statements) {
super(loc);
this.statements = statements;
}
}

View File

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

View File

@@ -1,31 +0,0 @@
package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.compilation.AssignableStatement;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class ChangeStatement extends Statement {
public final AssignableStatement value;
public final double addAmount;
public final boolean postfix;
@Override
public void compile(CompileResult target, boolean pollute) {
value.toAssign(new ConstantStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, true);
if (!pollute) target.add(Instruction.discard());
else if (postfix) {
target.add(Instruction.pushValue(addAmount));
target.add(Instruction.operation(Operation.SUBTRACT));
}
}
public ChangeStatement(Location loc, AssignableStatement value, double addAmount, boolean postfix) {
super(loc);
this.value = value;
this.addAmount = addAmount;
this.postfix = postfix;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,36 +0,0 @@
package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class OperationStatement extends Statement {
public final Statement[] args;
public final Operation operation;
@Override public boolean pure() {
for (var el : args) {
if (!el.pure()) return false;
}
return true;
}
@Override
public void compile(CompileResult target, boolean pollute) {
for (var arg : args) {
arg.compile(target, true);
}
if (pollute) target.add(Instruction.operation(operation));
else target.add(Instruction.discard());
}
public OperationStatement(Location loc, Operation operation, Statement ...args) {
super(loc);
this.operation = operation;
this.args = args;
}
}

View File

@@ -1,25 +0,0 @@
package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class RegexStatement extends Statement {
public final String pattern, flags;
// Not really pure, since a function is called, but can be ignored.
@Override public boolean pure() { return true; }
@Override
public void compile(CompileResult target, boolean pollute) {
target.add(Instruction.loadRegex(pattern, flags));
if (!pollute) target.add(Instruction.discard());
}
public RegexStatement(Location loc, String pattern, String flags) {
super(loc);
this.pattern = pattern;
this.flags = flags;
}
}

View File

@@ -1,32 +0,0 @@
package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class TypeofStatement extends Statement {
public final Statement value;
// Not really pure, since a variable from the global scope could be accessed,
// which could lead to code execution, that would get omitted
@Override public boolean pure() { return true; }
@Override
public void compile(CompileResult target, boolean pollute) {
if (value instanceof VariableStatement) {
var i = target.scope.getKey(((VariableStatement)value).name);
if (i instanceof String) {
target.add(Instruction.typeof((String)i));
return;
}
}
value.compile(target, pollute);
target.add(Instruction.typeof());
}
public TypeofStatement(Location loc, Statement value) {
super(loc);
this.value = value;
}
}

View File

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

View File

@@ -1,22 +0,0 @@
package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class VariableIndexStatement extends Statement {
public final int index;
@Override public boolean pure() { return true; }
@Override
public void compile(CompileResult target, boolean pollute) {
if (pollute) target.add(Instruction.loadVar(index));
}
public VariableIndexStatement(Location loc, int i) {
super(loc);
this.index = i;
}
}

View File

@@ -1,31 +0,0 @@
package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.compilation.AssignableStatement;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class VariableStatement extends AssignableStatement {
public final String name;
@Override public boolean pure() { return false; }
@Override
public Statement toAssign(Statement val, Operation operation) {
return new VariableAssignStatement(loc(), name, val, operation);
}
@Override
public void compile(CompileResult target, boolean pollute) {
var i = target.scope.getKey(name);
target.add(Instruction.loadVar(i));
if (!pollute) target.add(Instruction.discard());
}
public VariableStatement(Location loc, String name) {
super(loc);
this.name = name;
}
}

View File

@@ -1,23 +0,0 @@
package me.topchetoeu.jscript.core;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.FunctionBody;
import me.topchetoeu.jscript.core.exceptions.EngineException;
import me.topchetoeu.jscript.core.scope.ValueVariable;
import me.topchetoeu.jscript.core.values.CodeFunction;
public interface Compiler {
public Key<Compiler> KEY = new Key<>();
public FunctionBody compile(Filename filename, String source);
public static Compiler get(Extensions ext) {
return ext.get(KEY, (filename, src) -> {
throw EngineException.ofError("No compiler attached to engine.");
});
}
public static CodeFunction compile(Environment env, Filename filename, String raw) {
return new CodeFunction(env, filename.toString(), Compiler.get(env).compile(filename, raw), new ValueVariable[0]);
}
}

View File

@@ -1,109 +0,0 @@
package me.topchetoeu.jscript.core;
import java.util.Iterator;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.core.debug.DebugContext;
import me.topchetoeu.jscript.core.values.CodeFunction;
import me.topchetoeu.jscript.core.values.FunctionValue;
import me.topchetoeu.jscript.core.exceptions.EngineException;
import me.topchetoeu.jscript.core.scope.ValueVariable;
public class Context implements Extensions {
public static final Context NULL = new Context();
public final Context parent;
public final Environment environment;
public final Frame frame;
// public final Engine engine;
public final int stackSize;
@Override public <T> void add(Key<T> key, T obj) {
if (environment != null) environment.add(key, obj);
// else if (engine != null) engine.add(key, obj);
}
@Override public <T> T get(Key<T> key) {
if (environment != null && environment.has(key)) return environment.get(key);
// else if (engine != null && engine.has(key)) return engine.get(key);
return null;
}
@Override public boolean has(Key<?> key) {
return
environment != null && environment.has(key);
// engine != null && engine.has(key);
}
@Override public boolean remove(Key<?> key) {
var res = false;
if (environment != null) res |= environment.remove(key);
// else if (engine != null) res |= engine.remove(key);
return res;
}
@Override public Iterable<Key<?>> keys() {
if (environment == null) return List.of();
else return environment.keys();
// if (engine == null && environment == null) return List.of();
// if (engine == null) return environment.keys();
// if (environment == null) return engine.keys();
// return () -> Stream.concat(
// StreamSupport.stream(engine.keys().spliterator(), false),
// StreamSupport.stream(environment.keys().spliterator(), false)
// ).distinct().iterator();
}
public FunctionValue compile(Filename filename, String raw) {
DebugContext.get(this).onSource(filename, raw);
var result = new CodeFunction(environment, filename.toString(), Compiler.get(this).compile(filename, raw), new ValueVariable[0]);
return result;
}
public Context pushFrame(Frame frame) {
var res = new Context(this, frame.function.environment, frame, stackSize + 1);
return res;
}
public Iterable<Frame> frames() {
var self = this;
return () -> new Iterator<Frame>() {
private Context curr = self;
private void update() {
while (curr != null && curr.frame == null) curr = curr.parent;
}
@Override public boolean hasNext() {
update();
return curr != null;
}
@Override public Frame next() {
update();
var res = curr.frame;
curr = curr.parent;
return res;
}
};
}
private Context(Context parent, Environment environment, Frame frame, int stackSize) {
this.parent = parent;
this.environment = environment;
this.frame = frame;
this.stackSize = stackSize;
if (hasNotNull(Environment.MAX_STACK_COUNT) && stackSize > (int)get(Environment.MAX_STACK_COUNT)) {
throw EngineException.ofRange("Stack overflow!");
}
}
public Context() {
this(null, null, null, 0);
}
public Context(Environment env) {
this(null, env, null, 0);
}
}

View File

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

View File

@@ -1,92 +0,0 @@
package me.topchetoeu.jscript.core;
import java.util.HashMap;
import me.topchetoeu.jscript.core.scope.GlobalScope;
import me.topchetoeu.jscript.core.values.FunctionValue;
import me.topchetoeu.jscript.core.values.NativeFunction;
import me.topchetoeu.jscript.core.values.ObjectValue;
import me.topchetoeu.jscript.core.exceptions.EngineException;
import me.topchetoeu.jscript.utils.interop.NativeWrapperProvider;
@SuppressWarnings("unchecked")
public class Environment implements Extensions {
public static final Key<Compiler> COMPILE_FUNC = new Key<>();
public static final Key<FunctionValue> REGEX_CONSTR = new Key<>();
public static final Key<Integer> MAX_STACK_COUNT = new Key<>();
public static final Key<Boolean> HIDE_STACK = new Key<>();
public static final Key<ObjectValue> OBJECT_PROTO = new Key<>();
public static final Key<ObjectValue> FUNCTION_PROTO = new Key<>();
public static final Key<ObjectValue> ARRAY_PROTO = new Key<>();
public static final Key<ObjectValue> BOOL_PROTO = new Key<>();
public static final Key<ObjectValue> NUMBER_PROTO = new Key<>();
public static final Key<ObjectValue> STRING_PROTO = new Key<>();
public static final Key<ObjectValue> SYMBOL_PROTO = new Key<>();
public static final Key<ObjectValue> ERROR_PROTO = new Key<>();
public static final Key<ObjectValue> SYNTAX_ERR_PROTO = new Key<>();
public static final Key<ObjectValue> TYPE_ERR_PROTO = new Key<>();
public static final Key<ObjectValue> RANGE_ERR_PROTO = new Key<>();
private HashMap<Key<?>, Object> data = new HashMap<>();
public GlobalScope global;
public WrapperProvider wrappers;
@Override public <T> void add(Key<T> key, T obj) {
data.put(key, obj);
}
@Override public <T> T get(Key<T> key) {
return (T)data.get(key);
}
@Override public boolean remove(Key<?> key) {
if (data.containsKey(key)) {
data.remove(key);
return true;
}
return false;
}
@Override public boolean has(Key<?> key) {
return data.containsKey(key);
}
@Override public Iterable<Key<?>> keys() {
return data.keySet();
}
public static FunctionValue regexConstructor(Extensions ext) {
return ext.init(REGEX_CONSTR, new NativeFunction("RegExp", args -> {
throw EngineException.ofError("Regular expressions not supported.").setCtx(args.ctx);
}));
}
public Environment copy() {
var res = new Environment(null, global);
res.wrappers = wrappers.fork(res);
res.global = global;
res.data.putAll(data);
return res;
}
public Environment child() {
var res = copy();
res.global = res.global.globalChild();
return res;
}
public Context context() {
return new Context(this);
}
public Environment(WrapperProvider nativeConverter, GlobalScope global) {
if (nativeConverter == null) nativeConverter = new NativeWrapperProvider(this);
if (global == null) global = new GlobalScope();
this.wrappers = nativeConverter;
this.global = global;
}
public Environment() {
this(null, null);
}
}

View File

@@ -1,23 +0,0 @@
package me.topchetoeu.jscript.core;
import me.topchetoeu.jscript.common.ResultRunnable;
import me.topchetoeu.jscript.common.events.DataNotifier;
import me.topchetoeu.jscript.core.exceptions.EngineException;
public interface EventLoop {
public static final Key<EventLoop> KEY = new Key<>();
public static EventLoop get(Extensions ext) {
if (ext.hasNotNull(KEY)) return ext.get(KEY);
else return new EventLoop() {
@Override public <T> DataNotifier<T> pushMsg(ResultRunnable<T> runnable, boolean micro) {
throw EngineException.ofError("No event loop attached to environment.");
}
};
}
public <T> DataNotifier<T> pushMsg(ResultRunnable<T> runnable, boolean micro);
public default DataNotifier<Void> pushMsg(Runnable runnable, boolean micro) {
return pushMsg(() -> { runnable.run(); return null; }, micro);
}
}

View File

@@ -1,38 +0,0 @@
package me.topchetoeu.jscript.core;
public interface Extensions {
<T> T get(Key<T> key);
<T> void add(Key<T> key, T obj);
Iterable<Key<?>> keys();
boolean has(Key<?> key);
boolean remove(Key<?> key);
default void add(Key<Void> key) {
add(key, null);
}
default boolean hasNotNull(Key<?> key) {
return has(key) && get(key) != null;
}
default <T> T get(Key<T> key, T defaultVal) {
if (has(key)) return get(key);
else return defaultVal;
}
default <T> T init(Key<T> key, T val) {
if (has(key)) return get(key);
else {
add(key, val);
return val;
}
}
@SuppressWarnings("unchecked")
default void addAll(Extensions source) {
if (source == null) return;
for (var key : source.keys()) {
add((Key<Object>)key, (Object)source.get(key));
}
}
}

View File

@@ -1,329 +0,0 @@
package me.topchetoeu.jscript.core;
import java.util.List;
import java.util.Stack;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.core.debug.DebugContext;
import me.topchetoeu.jscript.core.scope.LocalScope;
import me.topchetoeu.jscript.core.scope.ValueVariable;
import me.topchetoeu.jscript.core.values.ArrayValue;
import me.topchetoeu.jscript.core.values.CodeFunction;
import me.topchetoeu.jscript.core.values.ObjectValue;
import me.topchetoeu.jscript.core.values.ScopeValue;
import me.topchetoeu.jscript.core.values.Values;
import me.topchetoeu.jscript.core.exceptions.EngineException;
import me.topchetoeu.jscript.core.exceptions.InterruptException;
public class Frame {
public static enum TryState {
TRY,
CATCH,
FINALLY,
}
public static class TryCtx {
public final int start, end, catchStart, finallyStart;
public final int restoreStackPtr;
public final TryState state;
public final EngineException error;
public final PendingResult result;
public boolean hasCatch() { return catchStart >= 0; }
public boolean hasFinally() { return finallyStart >= 0; }
public boolean inBounds(int ptr) {
return ptr >= start && ptr < end;
}
public void setCause(EngineException target) {
if (error != null) target.setCause(error);
}
public TryCtx _catch(EngineException e) {
return new TryCtx(TryState.CATCH, e, result, restoreStackPtr, start, end, -1, finallyStart);
}
public TryCtx _finally(PendingResult res) {
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) {
this.catchStart = catchStart;
this.finallyStart = finallyStart;
this.restoreStackPtr = stackPtr;
this.result = res == null ? PendingResult.ofNone() : res;
this.state = state;
this.start = start;
this.end = end;
this.error = err;
}
}
private static class PendingResult {
public final boolean isReturn, isJump, isThrow;
public final Object value;
public final EngineException error;
public final int ptr;
public final Instruction instruction;
private PendingResult(Instruction instr, boolean isReturn, boolean isJump, boolean isThrow, Object value, EngineException error, int ptr) {
this.instruction = instr;
this.isReturn = isReturn;
this.isJump = isJump;
this.isThrow = isThrow;
this.value = value;
this.error = error;
this.ptr = ptr;
}
public static PendingResult ofNone() {
return new PendingResult(null, false, false, false, null, null, 0);
}
public static PendingResult ofReturn(Object value, Instruction instr) {
return new PendingResult(instr, true, false, false, value, null, 0);
}
public static PendingResult ofThrow(EngineException error, Instruction instr) {
return new PendingResult(instr, false, false, true, null, error, 0);
}
public static PendingResult ofJump(int codePtr, Instruction instr) {
return new PendingResult(instr, false, true, false, null, null, codePtr);
}
}
public final LocalScope scope;
public final Object thisArg;
public final Object[] args;
public final Stack<TryCtx> tryStack = new Stack<>();
public final CodeFunction function;
public final Context ctx;
public Object[] stack = new Object[32];
public int stackPtr = 0;
public int codePtr = 0;
public boolean jumpFlag = false, popTryFlag = false;
public void addTry(int start, int end, int catchStart, int finallyStart) {
var err = tryStack.empty() ? null : tryStack.peek().error;
var res = new TryCtx(TryState.TRY, err, null, stackPtr, start, end, catchStart, finallyStart);
tryStack.add(res);
}
public Object peek() {
return peek(0);
}
public Object peek(int offset) {
if (stackPtr <= offset) return null;
else return stack[stackPtr - 1 - offset];
}
public Object pop() {
if (stackPtr == 0) return null;
return stack[--stackPtr];
}
public Object[] take(int n) {
int srcI = stackPtr - n;
if (srcI < 0) srcI = 0;
int dstI = n + srcI - stackPtr;
int copyN = stackPtr - srcI;
Object[] res = new Object[n];
System.arraycopy(stack, srcI, res, dstI, copyN);
stackPtr -= copyN;
return res;
}
public void push(Object val) {
if (stack.length <= stackPtr) {
var newStack = new Object[stack.length * 2];
System.arraycopy(stack, 0, newStack, 0, stack.length);
stack = newStack;
}
stack[stackPtr++] = Values.normalize(ctx, val);
}
public Object next(Object value, Object returnValue, EngineException error) {
if (value != Values.NO_RETURN) push(value);
Instruction instr = null;
if (codePtr >= 0 && codePtr < function.body.instructions.length) instr = function.body.instructions[codePtr];
if (returnValue == Values.NO_RETURN && error == null) {
try {
if (Thread.currentThread().isInterrupted()) throw new InterruptException();
if (instr == null) returnValue = null;
else {
DebugContext.get(ctx).onInstruction(ctx, this, instr, Values.NO_RETURN, null, false);
try {
this.jumpFlag = this.popTryFlag = false;
returnValue = InstructionRunner.exec(ctx, instr, this);
}
catch (EngineException e) {
error = e.add(ctx, function.name, DebugContext.get(ctx).getMapOrEmpty(function).toLocation(codePtr, true));
}
}
}
catch (EngineException e) { error = e; }
}
while (!tryStack.empty()) {
var tryCtx = tryStack.peek();
TryCtx newCtx = null;
if (error != null) {
tryCtx.setCause(error);
if (tryCtx.hasCatch()) newCtx = tryCtx._catch(error);
else if (tryCtx.hasFinally()) newCtx = tryCtx._finally(PendingResult.ofThrow(error, instr));
}
else if (returnValue != Values.NO_RETURN) {
if (tryCtx.hasFinally()) newCtx = tryCtx._finally(PendingResult.ofReturn(returnValue, instr));
}
else if (jumpFlag && !tryCtx.inBounds(codePtr)) {
if (tryCtx.hasFinally()) newCtx = tryCtx._finally(PendingResult.ofJump(codePtr, instr));
}
else if (!this.popTryFlag) newCtx = tryCtx;
if (newCtx != null) {
if (newCtx != tryCtx) {
switch (newCtx.state) {
case CATCH:
if (tryCtx.state != TryState.CATCH) scope.catchVars.add(new ValueVariable(false, error.value));
codePtr = tryCtx.catchStart;
stackPtr = tryCtx.restoreStackPtr;
break;
case FINALLY:
if (tryCtx.state == TryState.CATCH) scope.catchVars.remove(scope.catchVars.size() - 1);
codePtr = tryCtx.finallyStart;
stackPtr = tryCtx.restoreStackPtr;
default:
}
tryStack.pop();
tryStack.push(newCtx);
}
error = null;
returnValue = Values.NO_RETURN;
break;
}
else {
popTryFlag = false;
if (tryCtx.state == TryState.CATCH) scope.catchVars.remove(scope.catchVars.size() - 1);
if (tryCtx.state != TryState.FINALLY && tryCtx.hasFinally()) {
codePtr = tryCtx.finallyStart;
stackPtr = tryCtx.restoreStackPtr;
tryStack.pop();
tryStack.push(tryCtx._finally(null));
break;
}
else {
tryStack.pop();
codePtr = tryCtx.end;
if (tryCtx.result.instruction != null) instr = tryCtx.result.instruction;
if (!jumpFlag && returnValue == Values.NO_RETURN && error == null) {
if (tryCtx.result.isJump) {
codePtr = tryCtx.result.ptr;
jumpFlag = true;
}
if (tryCtx.result.isReturn) returnValue = tryCtx.result.value;
if (error == null && tryCtx.result.isThrow) {
error = tryCtx.result.error;
}
}
if (error != null) tryCtx.setCause(error);
continue;
}
}
}
if (error != null) {
var caught = false;
for (var frame : ctx.frames()) {
for (var tryCtx : frame.tryStack) {
if (tryCtx.state == TryState.TRY) caught = true;
}
}
DebugContext.get(ctx).onInstruction(ctx, this, instr, null, error, caught);
throw error;
}
if (returnValue != Values.NO_RETURN) {
DebugContext.get(ctx).onInstruction(ctx, this, instr, returnValue, null, false);
return returnValue;
}
return Values.NO_RETURN;
}
public void onPush() {
DebugContext.get(ctx).onFramePush(ctx, this);
}
public void onPop() {
DebugContext.get(ctx.parent).onFramePop(ctx.parent, this);
}
public ObjectValue getLocalScope() {
var names = new String[scope.locals.length];
var map = DebugContext.get(ctx).getMapOrEmpty(function);
for (int i = 0; i < scope.locals.length; i++) {
var name = "local_" + (i - 2);
if (i == 0) name = "this";
else if (i == 1) name = "arguments";
else if (i < map.localNames.length) name = map.localNames[i];
names[i] = name;
}
return new ScopeValue(scope.locals, names);
}
public ObjectValue getCaptureScope() {
var names = new String[scope.captures.length];
var map = DebugContext.get(ctx).getMapOrEmpty(function);
for (int i = 0; i < scope.captures.length; i++) {
var name = "capture_" + (i - 2);
if (i < map.captureNames.length) name = map.captureNames[i];
names[i] = name;
}
return new ScopeValue(scope.captures, names);
}
public ObjectValue getValStackScope() {
return new ObjectValue() {
@Override
protected Object getField(Context ctx, Object key) {
var i = (int)Values.toNumber(ctx, key);
if (i < 0 || i >= stackPtr) return null;
else return stack[i];
}
@Override
protected boolean hasField(Context ctx, Object key) {
return true;
}
@Override
public List<Object> keys(boolean includeNonEnumerable) {
var res = super.keys(includeNonEnumerable);
for (var i = 0; i < stackPtr; i++) res.add(i);
return res;
}
};
}
public Frame(Context ctx, Object thisArg, Object[] args, CodeFunction func) {
this.args = args.clone();
this.scope = new LocalScope(func.body.localsN, func.captures);
this.scope.get(0).set(null, thisArg);
var argsObj = new ArrayValue();
for (var i = 0; i < args.length; i++) {
argsObj.set(ctx, i, args[i]);
}
this.scope.get(1).value = argsObj;
this.thisArg = thisArg;
this.function = func;
this.ctx = ctx.pushFrame(this);
}
}

View File

@@ -1,329 +0,0 @@
package me.topchetoeu.jscript.core;
import java.util.Collections;
import me.topchetoeu.jscript.core.scope.ValueVariable;
import me.topchetoeu.jscript.core.values.ArrayValue;
import me.topchetoeu.jscript.core.values.CodeFunction;
import me.topchetoeu.jscript.core.values.FunctionValue;
import me.topchetoeu.jscript.core.values.ObjectValue;
import me.topchetoeu.jscript.core.values.Symbol;
import me.topchetoeu.jscript.core.values.Values;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.core.exceptions.EngineException;
public class InstructionRunner {
private static Object execReturn(Context ctx, Instruction instr, Frame frame) {
return frame.pop();
}
private static Object execThrow(Context ctx, Instruction instr, Frame frame) {
throw new EngineException(frame.pop());
}
private static Object execThrowSyntax(Context ctx, Instruction instr, Frame frame) {
throw EngineException.ofSyntax((String)instr.get(0));
}
private static Object execCall(Context ctx, Instruction instr, Frame frame) {
var callArgs = frame.take(instr.get(0));
var func = frame.pop();
var thisArg = frame.pop();
frame.push(Values.call(ctx, func, thisArg, callArgs));
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execCallNew(Context ctx, Instruction instr, Frame frame) {
var callArgs = frame.take(instr.get(0));
var funcObj = frame.pop();
frame.push(Values.callNew(ctx, funcObj, callArgs));
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execMakeVar(Context ctx, Instruction instr, Frame frame) {
var name = (String)instr.get(0);
ctx.environment.global.define(ctx, name);
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execDefProp(Context ctx, Instruction instr, Frame frame) {
var setter = frame.pop();
var getter = frame.pop();
var name = frame.pop();
var obj = frame.pop();
if (getter != null && !(getter instanceof FunctionValue)) throw EngineException.ofType("Getter must be a function or undefined.");
if (setter != null && !(setter instanceof FunctionValue)) throw EngineException.ofType("Setter must be a function or undefined.");
if (!(obj instanceof ObjectValue)) throw EngineException.ofType("Property apply target must be an object.");
((ObjectValue)obj).defineProperty(ctx, name, (FunctionValue)getter, (FunctionValue)setter, false, false);
frame.push(obj);
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execKeys(Context ctx, Instruction instr, Frame frame) {
var val = frame.pop();
var members = Values.getMembers(ctx, val, false, false);
Collections.reverse(members);
frame.push(null);
for (var el : members) {
if (el instanceof Symbol) continue;
var obj = new ObjectValue();
obj.defineProperty(ctx, "value", el);
frame.push(obj);
}
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execTryStart(Context ctx, Instruction instr, Frame frame) {
int start = frame.codePtr + 1;
int catchStart = (int)instr.get(0);
int finallyStart = (int)instr.get(1);
if (finallyStart >= 0) finallyStart += start;
if (catchStart >= 0) catchStart += start;
int end = (int)instr.get(2) + start;
frame.addTry(start, end, catchStart, finallyStart);
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execTryEnd(Context ctx, Instruction instr, Frame frame) {
frame.popTryFlag = true;
return Values.NO_RETURN;
}
private static Object execDup(Context ctx, Instruction instr, Frame frame) {
int count = instr.get(0);
for (var i = 0; i < count; i++) {
frame.push(frame.peek(count - 1));
}
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execLoadValue(Context ctx, Instruction instr, Frame frame) {
switch (instr.type) {
case PUSH_UNDEFINED: frame.push(null); break;
case PUSH_NULL: frame.push(Values.NULL); break;
default: frame.push(instr.get(0)); break;
}
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execLoadVar(Context ctx, Instruction instr, Frame frame) {
var i = instr.get(0);
if (i instanceof String) frame.push(ctx.environment.global.get(ctx, (String)i));
else frame.push(frame.scope.get((int)i).get(ctx));
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execLoadObj(Context ctx, Instruction instr, Frame frame) {
frame.push(new ObjectValue());
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execLoadGlob(Context ctx, Instruction instr, Frame frame) {
frame.push(ctx.environment.global.obj);
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execLoadArr(Context ctx, Instruction instr, Frame frame) {
var res = new ArrayValue();
res.setSize(instr.get(0));
frame.push(res);
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execLoadFunc(Context ctx, Instruction instr, Frame frame) {
int id = instr.get(0);
var captures = new ValueVariable[instr.params.length - 1];
for (var i = 1; i < instr.params.length; i++) {
captures[i - 1] = frame.scope.get(instr.get(i));
}
var func = new CodeFunction(ctx.environment, "", frame.function.body.children[id], captures);
frame.push(func);
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execLoadMember(Context ctx, Instruction instr, Frame frame) {
var key = frame.pop();
var obj = frame.pop();
try {
frame.push(Values.getMember(ctx, obj, key));
}
catch (IllegalArgumentException e) {
throw EngineException.ofType(e.getMessage());
}
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execLoadRegEx(Context ctx, Instruction instr, Frame frame) {
if (ctx.hasNotNull(Environment.REGEX_CONSTR)) {
frame.push(Values.callNew(ctx, ctx.get(Environment.REGEX_CONSTR), instr.get(0), instr.get(1)));
}
else {
throw EngineException.ofSyntax("Regex is not supported.");
}
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execDiscard(Context ctx, Instruction instr, Frame frame) {
frame.pop();
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execStoreMember(Context ctx, Instruction instr, Frame frame) {
var val = frame.pop();
var key = frame.pop();
var obj = frame.pop();
if (!Values.setMember(ctx, obj, key, val)) throw EngineException.ofSyntax("Can't set member '" + key + "'.");
if ((boolean)instr.get(0)) frame.push(val);
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execStoreVar(Context ctx, Instruction instr, Frame frame) {
var val = (boolean)instr.get(1) ? frame.peek() : frame.pop();
var i = instr.get(0);
if (i instanceof String) ctx.environment.global.set(ctx, (String)i, val);
else frame.scope.get((int)i).set(ctx, val);
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execStoreSelfFunc(Context ctx, Instruction instr, Frame frame) {
frame.scope.locals[(int)instr.get(0)].set(ctx, frame.function);
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execJmp(Context ctx, Instruction instr, Frame frame) {
frame.codePtr += (int)instr.get(0);
frame.jumpFlag = true;
return Values.NO_RETURN;
}
private static Object execJmpIf(Context ctx, Instruction instr, Frame frame) {
if (Values.toBoolean(frame.pop())) {
frame.codePtr += (int)instr.get(0);
frame.jumpFlag = true;
}
else frame.codePtr ++;
return Values.NO_RETURN;
}
private static Object execJmpIfNot(Context ctx, Instruction instr, Frame frame) {
if (Values.not(frame.pop())) {
frame.codePtr += (int)instr.get(0);
frame.jumpFlag = true;
}
else frame.codePtr ++;
return Values.NO_RETURN;
}
private static Object execTypeof(Context ctx, Instruction instr, Frame frame) {
String name = instr.get(0);
Object obj;
if (name != null) {
if (ctx.environment.global.has(ctx, name)) {
obj = ctx.environment.global.get(ctx, name);
}
else obj = null;
}
else obj = frame.pop();
frame.push(Values.type(obj));
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execNop(Context ctx, Instruction instr, Frame frame) {
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execDelete(Context ctx, Instruction instr, Frame frame) {
var key = frame.pop();
var val = frame.pop();
if (!Values.deleteMember(ctx, val, key)) throw EngineException.ofSyntax("Can't delete member '" + key + "'.");
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execOperation(Context ctx, Instruction instr, Frame frame) {
Operation op = instr.get(0);
var args = new Object[op.operands];
for (var i = op.operands - 1; i >= 0; i--) args[i] = frame.pop();
frame.push(Values.operation(ctx, op, args));
frame.codePtr++;
return Values.NO_RETURN;
}
public static Object exec(Context ctx, Instruction instr, Frame frame) {
switch (instr.type) {
case NOP: return execNop(ctx, instr, frame);
case RETURN: return execReturn(ctx, instr, frame);
case THROW: return execThrow(ctx, instr, frame);
case THROW_SYNTAX: return execThrowSyntax(ctx, instr, frame);
case CALL: return execCall(ctx, instr, frame);
case CALL_NEW: return execCallNew(ctx, instr, frame);
case TRY_START: return execTryStart(ctx, instr, frame);
case TRY_END: return execTryEnd(ctx, instr, frame);
case DUP: return execDup(ctx, instr, frame);
case PUSH_UNDEFINED:
case PUSH_NULL:
case PUSH_STRING:
case PUSH_NUMBER:
case PUSH_BOOL:
return execLoadValue(ctx, instr, frame);
case LOAD_VAR: return execLoadVar(ctx, instr, frame);
case LOAD_OBJ: return execLoadObj(ctx, instr, frame);
case LOAD_ARR: return execLoadArr(ctx, instr, frame);
case LOAD_FUNC: return execLoadFunc(ctx, instr, frame);
case LOAD_MEMBER: return execLoadMember(ctx, instr, frame);
case LOAD_REGEX: return execLoadRegEx(ctx, instr, frame);
case LOAD_GLOB: return execLoadGlob(ctx, instr, frame);
case DISCARD: return execDiscard(ctx, instr, frame);
case STORE_MEMBER: return execStoreMember(ctx, instr, frame);
case STORE_VAR: return execStoreVar(ctx, instr, frame);
case STORE_SELF_FUNC: return execStoreSelfFunc(ctx, instr, frame);
case MAKE_VAR: return execMakeVar(ctx, instr, frame);
case KEYS: return execKeys(ctx, instr, frame);
case DEF_PROP: return execDefProp(ctx, instr, frame);
case TYPEOF: return execTypeof(ctx, instr, frame);
case DELETE: return execDelete(ctx, instr, frame);
case JMP: return execJmp(ctx, instr, frame);
case JMP_IF: return execJmpIf(ctx, instr, frame);
case JMP_IFN: return execJmpIfNot(ctx, instr, frame);
case OPERATION: return execOperation(ctx, instr, frame);
default: throw EngineException.ofSyntax("Invalid instruction " + instr.type.name() + ".");
}
}
}

View File

@@ -1,5 +0,0 @@
package me.topchetoeu.jscript.core;
public class Key<T> {
}

View File

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

View File

@@ -1,11 +0,0 @@
package me.topchetoeu.jscript.core.exceptions;
public class ConvertException extends RuntimeException {
public final String source, target;
public ConvertException(String source, String target) {
super(String.format("Cannot convert '%s' to '%s'.", source, target));
this.source = source;
this.target = target;
}
}

View File

@@ -1,109 +0,0 @@
package me.topchetoeu.jscript.core.exceptions;
import java.util.ArrayList;
import java.util.List;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.Context;
import me.topchetoeu.jscript.core.Environment;
import me.topchetoeu.jscript.core.values.ObjectValue;
import me.topchetoeu.jscript.core.values.Values;
import me.topchetoeu.jscript.core.values.ObjectValue.PlaceholderProto;
public class EngineException extends RuntimeException {
public static class StackElement {
public final Location location;
public final String name;
public final Context ctx;
public boolean visible() {
return ctx == null || !ctx.get(Environment.HIDE_STACK, false);
}
public String toString() {
var res = "";
var loc = location;
if (loc != null) res += "at " + loc.toString() + " ";
if (name != null && !name.equals("")) res += "in " + name + " ";
return res.trim();
}
public StackElement(Context ctx, Location location, String name) {
if (name != null) name = name.trim();
if (name.equals("")) name = null;
if (ctx == null) this.ctx = null;
else this.ctx = new Context(ctx.environment);
this.location = location;
this.name = name;
}
}
public final Object value;
public EngineException cause;
public Environment env = null;
public final List<StackElement> stackTrace = new ArrayList<>();
public EngineException add(Context ctx, String name, Location location) {
var el = new StackElement(ctx, location, name);
if (el.name == null && el.location == null) return this;
setCtx(ctx);
stackTrace.add(el);
return this;
}
public EngineException setCause(EngineException cause) {
this.cause = cause;
return this;
}
public EngineException setCtx(Context ctx) {
if (this.env == null) this.env = ctx.environment;
return this;
}
public String toString(Context ctx) {
var ss = new StringBuilder();
try {
ss.append(Values.toString(ctx, value)).append('\n');
}
catch (EngineException e) {
ss.append("[Error while stringifying]\n");
}
for (var line : stackTrace) {
if (line.visible()) ss.append(" ").append(line.toString()).append("\n");
}
if (cause != null) ss.append("Caused by ").append(cause.toString(ctx)).append('\n');
ss.deleteCharAt(ss.length() - 1);
return ss.toString();
}
private static Object err(String name, String msg, PlaceholderProto proto) {
var res = new ObjectValue(proto);
if (name != null) res.defineProperty(null, "name", name);
res.defineProperty(null, "message", msg);
return res;
}
public EngineException(Object error) {
super(error == null ? "null" : error.toString());
this.value = error;
this.cause = null;
}
public static EngineException ofError(String name, String msg) {
return new EngineException(err(name, msg, PlaceholderProto.ERROR));
}
public static EngineException ofError(String msg) {
return new EngineException(err(null, msg, PlaceholderProto.ERROR));
}
public static EngineException ofSyntax(String msg) {
return new EngineException(err(null, msg, PlaceholderProto.SYNTAX_ERROR));
}
public static EngineException ofType(String msg) {
return new EngineException(err(null, msg, PlaceholderProto.TYPE_ERROR));
}
public static EngineException ofRange(String msg) {
return new EngineException(err(null, msg, PlaceholderProto.RANGE_ERROR));
}
}

View File

@@ -1,8 +0,0 @@
package me.topchetoeu.jscript.core.exceptions;
public class InterruptException extends RuntimeException {
public InterruptException() { }
public InterruptException(Throwable e) {
super(e);
}
}

View File

@@ -1,73 +0,0 @@
package me.topchetoeu.jscript.core.scope;
import java.util.HashSet;
import java.util.Set;
import me.topchetoeu.jscript.core.Context;
import me.topchetoeu.jscript.core.values.FunctionValue;
import me.topchetoeu.jscript.core.values.NativeFunction;
import me.topchetoeu.jscript.core.values.ObjectValue;
import me.topchetoeu.jscript.core.values.Values;
import me.topchetoeu.jscript.core.exceptions.EngineException;
public class GlobalScope {
public final ObjectValue obj;
public boolean has(Context ctx, String name) {
return Values.hasMember(null, obj, name, false);
}
public GlobalScope globalChild() {
var obj = new ObjectValue();
Values.setPrototype(null, obj, this.obj);
return new GlobalScope(obj);
}
public Object define(Context ctx, String name) {
if (Values.hasMember(Context.NULL, obj, name, false)) return name;
obj.defineProperty(Context.NULL, name, null);
return name;
}
public void define(Context ctx, String name, Variable val) {
obj.defineProperty(Context.NULL, name,
new NativeFunction("get " + name, args -> val.get(args.ctx)),
new NativeFunction("set " + name, args -> { val.set(args.ctx, args.get(0)); return null; }),
true, true
);
}
public void define(Context ctx, String name, boolean readonly, Object val) {
obj.defineProperty(ctx, name, val, readonly, true, true);
}
public void define(Context ctx, String ...names) {
for (var n : names) define(ctx, n);
}
public void define(Context ctx, boolean readonly, FunctionValue val) {
define(null, val.name, readonly, val);
}
public Object get(Context ctx, String name) {
if (!Values.hasMember(ctx, obj, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist.");
else return Values.getMember(ctx, obj, name);
}
public void set(Context ctx, String name, Object val) {
if (!Values.hasMember(ctx, obj, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist.");
if (!Values.setMember(ctx, obj, name, val)) throw EngineException.ofSyntax("The global '" + name + "' is readonly.");
}
public Set<String> keys() {
var res = new HashSet<String>();
for (var key : keys()) {
if (key instanceof String) res.add((String)key);
}
return res;
}
public GlobalScope() {
this.obj = new ObjectValue();
}
public GlobalScope(ObjectValue val) {
this.obj = val;
}
}

View File

@@ -1,29 +0,0 @@
package me.topchetoeu.jscript.core.scope;
import java.util.ArrayList;
public class LocalScope {
public final ValueVariable[] captures;
public final ValueVariable[] locals;
public final ArrayList<ValueVariable> catchVars = new ArrayList<>();
public ValueVariable get(int i) {
if (i >= locals.length) return catchVars.get(i - locals.length);
if (i >= 0) return locals[i];
else return captures[~i];
}
public int size() {
return captures.length + locals.length;
}
public LocalScope(int n, ValueVariable[] captures) {
locals = new ValueVariable[n];
this.captures = captures;
for (int i = 0; i < n; i++) {
locals[i] = new ValueVariable(false, null);
}
}
}

View File

@@ -1,28 +0,0 @@
package me.topchetoeu.jscript.core.scope;
import me.topchetoeu.jscript.core.Context;
import me.topchetoeu.jscript.core.values.Values;
public class ValueVariable implements Variable {
public boolean readonly;
public Object value;
@Override
public boolean readonly() { return readonly; }
@Override
public Object get(Context ctx) {
return value;
}
@Override
public void set(Context ctx, Object val) {
if (readonly) return;
this.value = Values.normalize(ctx, val);
}
public ValueVariable(boolean readonly, Object val) {
this.readonly = readonly;
this.value = val;
}
}

View File

@@ -1,9 +0,0 @@
package me.topchetoeu.jscript.core.scope;
import me.topchetoeu.jscript.core.Context;
public interface Variable {
Object get(Context ctx);
default boolean readonly() { return true; }
default void set(Context ctx, Object val) { }
}

View File

@@ -1,227 +0,0 @@
package me.topchetoeu.jscript.core.values;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import me.topchetoeu.jscript.core.Context;
// TODO: Make methods generic
public class ArrayValue extends ObjectValue implements Iterable<Object> {
private static final Object UNDEFINED = new Object();
private Object[] values;
private int size;
private Object[] alloc(int index) {
index++;
if (index < values.length) return values;
if (index < values.length * 2) index = values.length * 2;
var arr = new Object[index];
System.arraycopy(values, 0, arr, 0, values.length);
return arr;
}
public int size() { return size; }
public boolean setSize(int val) {
if (val < 0) return false;
if (size > val) shrink(size - val);
else {
values = alloc(val);
size = val;
}
return true;
}
public Object get(int i) {
if (i < 0 || i >= size) return null;
var res = values[i];
if (res == UNDEFINED) return null;
else return res;
}
public void set(Context ctx, int i, Object val) {
if (i < 0) return;
values = alloc(i);
val = Values.normalize(ctx, val);
if (val == null) val = UNDEFINED;
values[i] = val;
if (i >= size) size = i + 1;
}
public boolean has(int i) {
return i >= 0 && i < size && values[i] != null;
}
public void remove(int i) {
if (i < 0 || i >= values.length) return;
values[i] = null;
}
public void shrink(int n) {
if (n >= values.length) {
values = new Object[16];
size = 0;
}
else {
for (int i = 0; i < n; i++) {
values[--size] = null;
}
}
}
public Object[] toArray() {
Object[] res = new Object[size];
copyTo(res, 0, 0, size);
return res;
}
public void copyTo(Object[] arr, int sourceStart, int destStart, int count) {
for (var i = 0; i < count; i++) {
if (i + sourceStart < 0 || i + sourceStart >= size) arr[i + destStart] = null;
if (values[i + sourceStart] == UNDEFINED) arr[i + destStart] = null;
else arr[i + sourceStart] = values[i + destStart];
}
}
public void copyTo(ArrayValue arr, int sourceStart, int destStart, int count) {
if (arr == this) {
move(sourceStart, destStart, count);
return;
}
// Iterate in reverse to reallocate at most once
if (destStart + count > arr.size) arr.size = destStart + count;
for (var i = count - 1; i >= 0; i--) {
if (i + sourceStart < 0 || i + sourceStart >= size) arr.remove(i + destStart);
if (values[i + sourceStart] == UNDEFINED) arr.set(null, i + destStart, null);
else if (values[i + sourceStart] == null) arr.remove(i + destStart);
else arr.set(null, i + destStart, values[i + sourceStart]);
}
}
public void copyFrom(Context ctx, Object[] arr, int sourceStart, int destStart, int count) {
for (var i = 0; i < count; i++) {
set(ctx, i + destStart, arr[i + sourceStart]);
}
}
public void move(int srcI, int dstI, int n) {
values = alloc(dstI + n);
System.arraycopy(values, srcI, values, dstI, n);
if (dstI + n >= size) size = dstI + n;
}
public void sort(Comparator<Object> comparator) {
Arrays.sort(values, 0, size, (a, b) -> {
var _a = 0;
var _b = 0;
if (a == UNDEFINED) _a = 1;
if (a == null) _a = 2;
if (b == UNDEFINED) _b = 1;
if (b == null) _b = 2;
if (_a != 0 || _b != 0) return Integer.compare(_a, _b);
return comparator.compare(a, b);
});
}
@Override
protected Object getField(Context ctx, Object key) {
if (key instanceof Number) {
var i = ((Number)key).doubleValue();
if (i >= 0 && i - Math.floor(i) == 0) {
return get((int)i);
}
}
return super.getField(ctx, key);
}
@Override
protected boolean setField(Context ctx, Object key, Object val) {
if (key instanceof Number) {
var i = Values.number(key);
if (i >= 0 && i - Math.floor(i) == 0) {
set(ctx, (int)i, val);
return true;
}
}
return super.setField(ctx, key, val);
}
@Override
protected boolean hasField(Context ctx, Object key) {
if (key instanceof Number) {
var i = Values.number(key);
if (i >= 0 && i - Math.floor(i) == 0) {
return has((int)i);
}
}
return super.hasField(ctx, key);
}
@Override
protected void deleteField(Context ctx, Object key) {
if (key instanceof Number) {
var i = Values.number(key);
if (i >= 0 && i - Math.floor(i) == 0) {
remove((int)i);
return;
}
}
super.deleteField(ctx, key);
}
@Override
public List<Object> keys(boolean includeNonEnumerable) {
var res = super.keys(includeNonEnumerable);
for (var i = 0; i < size(); i++) {
if (has(i)) res.add(i);
}
return res;
}
@Override
public Iterator<Object> iterator() {
return new Iterator<Object>() {
private int i = 0;
@Override
public boolean hasNext() {
return i < size();
}
@Override
public Object next() {
if (!hasNext()) return null;
return get(i++);
}
};
}
public ArrayValue() {
super(PlaceholderProto.ARRAY);
values = new Object[16];
size = 0;
}
public ArrayValue(int cap) {
super(PlaceholderProto.ARRAY);
values = new Object[cap];
size = 0;
}
public ArrayValue(Context ctx, Object ...values) {
this();
this.values = new Object[values.length];
size = values.length;
for (var i = 0; i < size; i++) this.values[i] = Values.normalize(ctx, values[i]);
}
public static ArrayValue of(Context ctx, Collection<?> values) {
return new ArrayValue(ctx, values.toArray(Object[]::new));
}
}

View File

@@ -1,50 +0,0 @@
package me.topchetoeu.jscript.core.values;
import me.topchetoeu.jscript.common.FunctionBody;
import me.topchetoeu.jscript.core.Context;
import me.topchetoeu.jscript.core.Environment;
import me.topchetoeu.jscript.core.Frame;
import me.topchetoeu.jscript.core.scope.ValueVariable;
public class CodeFunction extends FunctionValue {
public final FunctionBody body;
public final ValueVariable[] captures;
public Environment environment;
// public Location loc() {
// for (var instr : body.instructions) {
// if (instr.location != null) return instr.location;
// }
// return null;
// }
// public String readable() {
// var loc = loc();
// if (loc == null) return name;
// else if (name.equals("")) return loc.toString();
// else return name + "@" + loc;
// }
@Override
public Object call(Context ctx, Object thisArg, Object ...args) {
var frame = new Frame(ctx, thisArg, args, this);
frame.onPush();
try {
while (true) {
var res = frame.next(Values.NO_RETURN, Values.NO_RETURN, null);
if (res != Values.NO_RETURN) return res;
}
}
finally {
frame.onPop();
}
}
public CodeFunction(Environment environment, String name, FunctionBody body, ValueVariable[] captures) {
super(name, body.argsN);
this.captures = captures;
this.environment = environment;
this.body = body;
}
}

View File

@@ -1,6 +0,0 @@
package me.topchetoeu.jscript.core.values;
public enum ConvertHint {
TOSTRING,
VALUEOF,
}

View File

@@ -1,69 +0,0 @@
package me.topchetoeu.jscript.core.values;
import java.util.List;
import me.topchetoeu.jscript.core.Context;
public abstract class FunctionValue extends ObjectValue {
public String name = "";
public int length;
@Override
public String toString() {
return String.format("function %s(...)", name);
}
public abstract Object call(Context ctx, Object thisArg, Object ...args);
public Object call(Context ctx) {
return call(ctx, null);
}
@Override
protected Object getField(Context ctx, Object key) {
if ("name".equals(key)) return name;
if ("length".equals(key)) return length;
return super.getField(ctx, key);
}
@Override
protected boolean setField(Context ctx, Object key, Object val) {
if ("name".equals(key)) name = Values.toString(ctx, val);
else if ("length".equals(key)) length = (int)Values.toNumber(ctx, val);
else return super.setField(ctx, key, val);
return true;
}
@Override
protected boolean hasField(Context ctx, Object key) {
if ("name".equals(key)) return true;
if ("length".equals(key)) return true;
return super.hasField(ctx, key);
}
@Override
public List<Object> keys(boolean includeNonEnumerable) {
var res = super.keys(includeNonEnumerable);
if (includeNonEnumerable) {
res.add("name");
res.add("length");
}
return res;
}
public FunctionValue(String name, int length) {
super(PlaceholderProto.FUNCTION);
if (name == null) name = "";
this.length = length;
this.name = name;
nonConfigurableSet.add("name");
nonEnumerableSet.add("name");
nonWritableSet.add("length");
nonConfigurableSet.add("length");
nonEnumerableSet.add("length");
var proto = new ObjectValue();
proto.defineProperty(null, "constructor", this, true, false, false);
this.defineProperty(null, "prototype", proto, true, false, false);
}
}

View File

@@ -1,26 +0,0 @@
package me.topchetoeu.jscript.core.values;
import me.topchetoeu.jscript.core.Context;
import me.topchetoeu.jscript.utils.interop.Arguments;
public class NativeFunction extends FunctionValue {
public static interface NativeFunctionRunner {
Object run(Arguments args);
}
public final NativeFunctionRunner action;
@Override
public Object call(Context ctx, Object thisArg, Object ...args) {
return action.run(new Arguments(ctx, thisArg, args));
}
public NativeFunction(String name, NativeFunctionRunner action) {
super(name, 0);
this.action = action;
}
public NativeFunction(NativeFunctionRunner action) {
super("", 0);
this.action = action;
}
}

View File

@@ -1,32 +0,0 @@
package me.topchetoeu.jscript.core.values;
import me.topchetoeu.jscript.core.Context;
public class NativeWrapper extends ObjectValue {
private static final Object NATIVE_PROTO = new Object();
public final Object wrapped;
@Override
public ObjectValue getPrototype(Context ctx) {
if (prototype == NATIVE_PROTO) return ctx.environment.wrappers.getProto(wrapped.getClass());
else return super.getPrototype(ctx);
}
@Override
public String toString() {
return wrapped.toString();
}
@Override
public boolean equals(Object obj) {
return wrapped.equals(obj);
}
@Override
public int hashCode() {
return wrapped.hashCode();
}
public NativeWrapper(Object wrapped) {
this.wrapped = wrapped;
prototype = NATIVE_PROTO;
}
}

View File

@@ -1,354 +0,0 @@
package me.topchetoeu.jscript.core.values;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import me.topchetoeu.jscript.core.Context;
import me.topchetoeu.jscript.core.Environment;
public class ObjectValue {
public static enum PlaceholderProto {
NONE,
OBJECT,
ARRAY,
FUNCTION,
ERROR,
SYNTAX_ERROR,
TYPE_ERROR,
RANGE_ERROR,
}
public static enum State {
NORMAL,
NO_EXTENSIONS,
SEALED,
FROZEN,
}
public static class Property {
public final FunctionValue getter;
public final FunctionValue setter;
public Property(FunctionValue getter, FunctionValue setter) {
this.getter = getter;
this.setter = setter;
}
}
private static final Object OBJ_PROTO = new Object();
private static final Object ARR_PROTO = new Object();
private static final Object FUNC_PROTO = new Object();
private static final Object 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 RANGE_ERR_PROTO = new Object();
protected Object prototype;
public State state = State.NORMAL;
public LinkedHashMap<Object, Object> values = new LinkedHashMap<>();
public LinkedHashMap<Object, Property> properties = new LinkedHashMap<>();
public LinkedHashSet<Object> nonWritableSet = new LinkedHashSet<>();
public LinkedHashSet<Object> nonConfigurableSet = new LinkedHashSet<>();
public LinkedHashSet<Object> nonEnumerableSet = new LinkedHashSet<>();
private Property getProperty(Context ctx, Object key) {
if (properties.containsKey(key)) return properties.get(key);
var proto = getPrototype(ctx);
if (proto != null) return proto.getProperty(ctx, key);
else return null;
}
public final boolean memberWritable(Object key) {
if (state == State.FROZEN) return false;
return !values.containsKey(key) || !nonWritableSet.contains(key);
}
public final boolean memberConfigurable(Object key) {
if (state == State.SEALED || state == State.FROZEN) return false;
return !nonConfigurableSet.contains(key);
}
public final boolean memberEnumerable(Object key) {
return !nonEnumerableSet.contains(key);
}
public final boolean extensible() {
return state == State.NORMAL;
}
public final void preventExtensions() {
if (state == State.NORMAL) state = State.NO_EXTENSIONS;
}
public final void seal() {
if (state == State.NORMAL || state == State.NO_EXTENSIONS) state = State.SEALED;
}
public final void freeze() {
state = State.FROZEN;
}
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);
boolean reconfigured =
writable != memberWritable(key) ||
configurable != memberConfigurable(key) ||
enumerable != memberEnumerable(key);
if (!reconfigured) {
if (!memberWritable(key)) {
var a = values.get(key);
var b = val;
if (a == null || b == null) return a == null && b == null;
return a == b || a.equals(b);
}
values.put(key, val);
return true;
}
if (
properties.containsKey(key) &&
values.get(key) == val &&
!reconfigured
) return true;
if (!extensible() && !values.containsKey(key) && !properties.containsKey(key)) return false;
if (!memberConfigurable(key)) return false;
nonWritableSet.remove(key);
nonEnumerableSet.remove(key);
properties.remove(key);
values.remove(key);
if (!writable) nonWritableSet.add(key);
if (!configurable) nonConfigurableSet.add(key);
if (!enumerable) nonEnumerableSet.add(key);
values.put(key, val);
return true;
}
public final boolean defineProperty(Context ctx, Object key, Object val) {
return defineProperty(ctx, key, val, true, true, true);
}
public final boolean defineProperty(Context ctx, Object key, FunctionValue getter, FunctionValue setter, boolean configurable, boolean enumerable) {
key = Values.normalize(ctx, key);
if (
properties.containsKey(key) &&
properties.get(key).getter == getter &&
properties.get(key).setter == setter &&
!configurable == nonConfigurableSet.contains(key) &&
!enumerable == nonEnumerableSet.contains(key)
) return true;
if (!extensible() && !values.containsKey(key) && !properties.containsKey(key)) return false;
if (!memberConfigurable(key)) return false;
nonWritableSet.remove(key);
nonEnumerableSet.remove(key);
properties.remove(key);
values.remove(key);
if (!configurable) nonConfigurableSet.add(key);
if (!enumerable) nonEnumerableSet.add(key);
properties.put(key, new Property(getter, setter));
return true;
}
public ObjectValue getPrototype(Context ctx) {
try {
if (prototype == OBJ_PROTO) return ctx.get(Environment.OBJECT_PROTO);
if (prototype == ARR_PROTO) return ctx.get(Environment.ARRAY_PROTO);
if (prototype == FUNC_PROTO) return ctx.get(Environment.FUNCTION_PROTO);
if (prototype == ERR_PROTO) return ctx.get(Environment.ERROR_PROTO);
if (prototype == RANGE_ERR_PROTO) return ctx.get(Environment.RANGE_ERR_PROTO);
if (prototype == SYNTAX_ERR_PROTO) return ctx.get(Environment.SYNTAX_ERR_PROTO);
if (prototype == TYPE_ERR_PROTO) return ctx.get(Environment.TYPE_ERR_PROTO);
}
catch (NullPointerException e) { return null; }
return (ObjectValue)prototype;
}
public final boolean setPrototype(PlaceholderProto val) {
if (!extensible()) return false;
switch (val) {
case OBJECT: prototype = OBJ_PROTO; break;
case FUNCTION: prototype = FUNC_PROTO; break;
case ARRAY: prototype = ARR_PROTO; break;
case ERROR: prototype = ERR_PROTO; break;
case SYNTAX_ERROR: prototype = SYNTAX_ERR_PROTO; break;
case TYPE_ERROR: prototype = TYPE_ERR_PROTO; break;
case RANGE_ERROR: prototype = RANGE_ERR_PROTO; break;
case NONE: prototype = null; break;
}
return true;
}
/**
* A method, used to get the value of a field. If a property is bound to
* this key, but not a field, this method should return null.
*/
protected Object getField(Context ctx, Object key) {
if (values.containsKey(key)) return values.get(key);
var proto = getPrototype(ctx);
if (proto != null) return proto.getField(ctx, key);
else return null;
}
/**
* Changes the value of a field, that is bound to the given key. If no field is
* bound to this key, a new field should be created with the given value
* @return Whether or not the operation was successful
*/
protected boolean setField(Context ctx, Object key, Object val) {
if (val instanceof FunctionValue && ((FunctionValue)val).name.equals("")) {
((FunctionValue)val).name = Values.toString(ctx, key);
}
values.put(key, val);
return true;
}
/**
* Deletes the field bound to the given key.
*/
protected void deleteField(Context ctx, Object key) {
values.remove(key);
}
/**
* Returns whether or not there is a field bound to the given key.
* This must ignore properties
*/
protected boolean hasField(Context ctx, Object key) {
return values.containsKey(key);
}
public final Object getMember(Context ctx, Object key, Object thisArg) {
key = Values.normalize(ctx, key);
if ("__proto__".equals(key)) {
var res = getPrototype(ctx);
return res == null ? Values.NULL : res;
}
var prop = getProperty(ctx, key);
if (prop != null) {
if (prop.getter == null) return null;
else return prop.getter.call(ctx, Values.normalize(ctx, thisArg));
}
else return getField(ctx, key);
}
public final boolean setMember(Context ctx, Object key, Object val, Object thisArg, boolean onlyProps) {
key = Values.normalize(ctx, key); val = Values.normalize(ctx, val);
var prop = getProperty(ctx, key);
if (prop != null) {
if (prop.setter == null) return false;
prop.setter.call(ctx, Values.normalize(ctx, thisArg), val);
return true;
}
else if (onlyProps) return false;
else if (!extensible() && !values.containsKey(key)) return false;
else if (key == null) {
values.put(key, val);
return true;
}
else if ("__proto__".equals(key)) return setPrototype(ctx, val);
else if (nonWritableSet.contains(key)) return false;
else return setField(ctx, key, val);
}
public final boolean hasMember(Context ctx, Object key, boolean own) {
key = Values.normalize(ctx, key);
if (key != null && "__proto__".equals(key)) return true;
if (hasField(ctx, key)) return true;
if (properties.containsKey(key)) return true;
if (own) return false;
var proto = getPrototype(ctx);
return proto != null && proto.hasMember(ctx, key, own);
}
public final boolean deleteMember(Context ctx, Object key) {
key = Values.normalize(ctx, key);
if (!memberConfigurable(key)) return false;
properties.remove(key);
nonWritableSet.remove(key);
nonEnumerableSet.remove(key);
deleteField(ctx, key);
return true;
}
public final boolean setPrototype(Context ctx, Object val) {
val = Values.normalize(ctx, val);
if (!extensible()) return false;
if (val == null || val == Values.NULL) {
prototype = null;
return true;
}
else if (val instanceof ObjectValue) {
var obj = (ObjectValue)val;
if (ctx != null) {
if (obj == ctx.get(Environment.OBJECT_PROTO)) prototype = OBJ_PROTO;
else if (obj == ctx.get(Environment.ARRAY_PROTO)) prototype = ARR_PROTO;
else if (obj == ctx.get(Environment.FUNCTION_PROTO)) prototype = FUNC_PROTO;
else if (obj == ctx.get(Environment.ERROR_PROTO)) prototype = ERR_PROTO;
else if (obj == ctx.get(Environment.SYNTAX_ERR_PROTO)) prototype = SYNTAX_ERR_PROTO;
else if (obj == ctx.get(Environment.TYPE_ERR_PROTO)) prototype = TYPE_ERR_PROTO;
else if (obj == ctx.get(Environment.RANGE_ERR_PROTO)) prototype = RANGE_ERR_PROTO;
else prototype = obj;
}
else prototype = obj;
return true;
}
return false;
}
public final ObjectValue getMemberDescriptor(Context ctx, Object key) {
key = Values.normalize(ctx, key);
var prop = properties.get(key);
var res = new ObjectValue();
res.defineProperty(ctx, "configurable", memberConfigurable(key));
res.defineProperty(ctx, "enumerable", memberEnumerable(key));
if (prop != null) {
res.defineProperty(ctx, "get", prop.getter);
res.defineProperty(ctx, "set", prop.setter);
}
else if (hasField(ctx, key)) {
res.defineProperty(ctx, "value", values.get(key));
res.defineProperty(ctx, "writable", memberWritable(key));
}
else return null;
return res;
}
public List<Object> keys(boolean includeNonEnumerable) {
var res = new ArrayList<Object>();
for (var key : values.keySet()) {
if (nonEnumerableSet.contains(key) && !includeNonEnumerable) continue;
res.add(key);
}
for (var key : properties.keySet()) {
if (nonEnumerableSet.contains(key) && !includeNonEnumerable) continue;
res.add(key);
}
return res;
}
public ObjectValue(Context ctx, Map<?, ?> values) {
this(PlaceholderProto.OBJECT);
for (var el : values.entrySet()) {
defineProperty(ctx, el.getKey(), el.getValue());
}
}
public ObjectValue(PlaceholderProto proto) {
nonConfigurableSet.add("__proto__");
nonEnumerableSet.add("__proto__");
setPrototype(proto);
}
public ObjectValue() {
this(PlaceholderProto.OBJECT);
}
}

View File

@@ -1,54 +0,0 @@
package me.topchetoeu.jscript.core.values;
import java.util.HashMap;
import java.util.List;
import me.topchetoeu.jscript.core.Context;
import me.topchetoeu.jscript.core.scope.ValueVariable;
public class ScopeValue extends ObjectValue {
public final ValueVariable[] variables;
public final HashMap<String, Integer> names = new HashMap<>();
@Override
protected Object getField(Context ctx, Object key) {
if (names.containsKey(key)) return variables[names.get(key)].get(ctx);
return super.getField(ctx, key);
}
@Override
protected boolean setField(Context ctx, Object key, Object val) {
if (names.containsKey(key)) {
variables[names.get(key)].set(ctx, val);
return true;
}
var proto = getPrototype(ctx);
if (proto != null && proto.hasMember(ctx, key, false) && proto.setField(ctx, key, val)) return true;
return super.setField(ctx, key, val);
}
@Override
protected void deleteField(Context ctx, Object key) {
if (names.containsKey(key)) return;
super.deleteField(ctx, key);
}
@Override
protected boolean hasField(Context ctx, Object key) {
if (names.containsKey(key)) return true;
return super.hasField(ctx, key);
}
@Override
public List<Object> keys(boolean includeNonEnumerable) {
var res = super.keys(includeNonEnumerable);
res.addAll(names.keySet());
return res;
}
public ScopeValue(ValueVariable[] variables, String[] names) {
this.variables = variables;
for (var i = 0; i < names.length && i < variables.length; i++) {
this.names.put(names[i], i);
this.nonConfigurableSet.add(names[i]);
}
}
}

View File

@@ -1,28 +0,0 @@
package me.topchetoeu.jscript.core.values;
import java.util.HashMap;
public final class Symbol {
private static final HashMap<String, Symbol> registry = new HashMap<>();
public final String value;
public Symbol(String value) {
this.value = value;
}
@Override
public String toString() {
if (value == null) return "Symbol";
else return "@@" + value;
}
public static Symbol get(String name) {
if (registry.containsKey(name)) return registry.get(name);
else {
var res = new Symbol(name);
registry.put(name, res);
return res;
}
}
}

View File

@@ -1,760 +0,0 @@
package me.topchetoeu.jscript.core.values;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.core.Context;
import me.topchetoeu.jscript.core.Environment;
import me.topchetoeu.jscript.core.debug.DebugContext;
import me.topchetoeu.jscript.core.exceptions.ConvertException;
import me.topchetoeu.jscript.core.exceptions.EngineException;
import me.topchetoeu.jscript.core.exceptions.SyntaxException;
import me.topchetoeu.jscript.lib.PromiseLib;
public class Values {
public static enum CompareResult {
NOT_EQUAL,
EQUAL,
LESS,
GREATER;
public boolean less() { return this == LESS; }
public boolean greater() { return this == GREATER; }
public boolean lessOrEqual() { return this == LESS || this == EQUAL; }
public boolean greaterOrEqual() { return this == GREATER || this == EQUAL; }
public static CompareResult from(int cmp) {
if (cmp < 0) return LESS;
if (cmp > 0) return GREATER;
return EQUAL;
}
}
public static final Object NULL = new Object();
public static final Object NO_RETURN = new Object();
public static boolean isWrapper(Object val) { return val instanceof NativeWrapper; }
public static boolean isWrapper(Object val, Class<?> clazz) {
if (!isWrapper(val)) return false;
var res = (NativeWrapper)val;
return res != null && clazz.isInstance(res.wrapped);
}
public static boolean isNan(Object val) { return val instanceof Number && Double.isNaN(number(val)); }
public static double number(Object val) {
if (val instanceof Number) return ((Number)val).doubleValue();
else return Double.NaN;
}
@SuppressWarnings("unchecked")
public static <T> T wrapper(Object val, Class<T> clazz) {
if (!isWrapper(val)) val = new NativeWrapper(val);
var res = (NativeWrapper)val;
if (res != null && clazz.isInstance(res.wrapped)) return (T)res.wrapped;
else return null;
}
public static String type(Object val) {
if (val == null) return "undefined";
if (val instanceof String) return "string";
if (val instanceof Number) return "number";
if (val instanceof Boolean) return "boolean";
if (val instanceof Symbol) return "symbol";
if (val instanceof FunctionValue) return "function";
return "object";
}
private static Object tryCallConvertFunc(Context ctx, Object obj, String name) {
var func = getMember(ctx, obj, name);
if (func instanceof FunctionValue) {
var res = Values.call(ctx, func, obj);
if (isPrimitive(res)) return res;
}
throw EngineException.ofType("Value couldn't be converted to a primitive.");
}
public static boolean isPrimitive(Object obj) {
return
obj instanceof Number ||
obj instanceof String ||
obj instanceof Boolean ||
obj instanceof Symbol ||
obj == null ||
obj == NULL;
}
public static Object toPrimitive(Context ctx, Object obj, ConvertHint hint) {
obj = normalize(ctx, obj);
if (isPrimitive(obj)) return obj;
var first = hint == ConvertHint.VALUEOF ? "valueOf" : "toString";
var second = hint == ConvertHint.VALUEOF ? "toString" : "valueOf";
if (ctx != null) {
try { return tryCallConvertFunc(ctx, obj, first); }
catch (EngineException unused) { return tryCallConvertFunc(ctx, obj, second); }
}
throw EngineException.ofType("Value couldn't be converted to a primitive.");
}
public static boolean toBoolean(Object obj) {
if (obj == NULL || obj == null) return false;
if (obj instanceof Number && (number(obj) == 0 || Double.isNaN(number(obj)))) return false;
if (obj instanceof String && ((String)obj).equals("")) return false;
if (obj instanceof Boolean) return (Boolean)obj;
return true;
}
public static double toNumber(Context ctx, Object obj) {
var val = toPrimitive(ctx, obj, ConvertHint.VALUEOF);
if (val instanceof Number) return number(val);
if (val instanceof Boolean) return ((Boolean)val) ? 1 : 0;
if (val instanceof String) {
try { return Double.parseDouble((String)val); }
catch (NumberFormatException e) { return Double.NaN; }
}
return Double.NaN;
}
public static String toString(Context ctx, Object obj) {
var val = toPrimitive(ctx, obj, ConvertHint.VALUEOF);
if (val == null) return "undefined";
if (val == NULL) return "null";
if (val instanceof Number) {
var d = number(val);
if (d == Double.NEGATIVE_INFINITY) return "-Infinity";
if (d == Double.POSITIVE_INFINITY) return "Infinity";
if (Double.isNaN(d)) return "NaN";
return BigDecimal.valueOf(d).stripTrailingZeros().toPlainString();
}
if (val instanceof Boolean) return (Boolean)val ? "true" : "false";
if (val instanceof String) return (String)val;
if (val instanceof Symbol) return val.toString();
return "Unknown value";
}
public static Object add(Context ctx, Object a, Object b) {
if (a instanceof String || b instanceof String) return toString(ctx, a) + toString(ctx, b);
else return toNumber(ctx, a) + toNumber(ctx, b);
}
public static double subtract(Context ctx, Object a, Object b) {
return toNumber(ctx, a) - toNumber(ctx, b);
}
public static double multiply(Context ctx, Object a, Object b) {
return toNumber(ctx, a) * toNumber(ctx, b);
}
public static double divide(Context ctx, Object a, Object b) {
return toNumber(ctx, a) / toNumber(ctx, b);
}
public static double modulo(Context ctx, Object a, Object b) {
return toNumber(ctx, a) % toNumber(ctx, b);
}
public static double negative(Context ctx, Object obj) {
return -toNumber(ctx, obj);
}
public static int and(Context ctx, Object a, Object b) {
return (int)toNumber(ctx, a) & (int)toNumber(ctx, b);
}
public static int or(Context ctx, Object a, Object b) {
return (int)toNumber(ctx, a) | (int)toNumber(ctx, b);
}
public static int xor(Context ctx, Object a, Object b) {
return (int)toNumber(ctx, a) ^ (int)toNumber(ctx, b);
}
public static int bitwiseNot(Context ctx, Object obj) {
return ~(int)toNumber(ctx, obj);
}
public static int shiftLeft(Context ctx, Object a, Object b) {
return (int)toNumber(ctx, a) << (int)toNumber(ctx, b);
}
public static int shiftRight(Context ctx, Object a, Object b) {
return (int)toNumber(ctx, a) >> (int)toNumber(ctx, b);
}
public static long unsignedShiftRight(Context ctx, Object a, Object b) {
long _a = (long)toNumber(ctx, a);
long _b = (long)toNumber(ctx, b);
if (_a < 0) _a += 0x100000000l;
if (_b < 0) _b += 0x100000000l;
return _a >>> _b;
}
public static CompareResult compare(Context ctx, Object a, Object b) {
a = toPrimitive(ctx, a, ConvertHint.VALUEOF);
b = toPrimitive(ctx, b, ConvertHint.VALUEOF);
if (a instanceof String && b instanceof String) CompareResult.from(((String)a).compareTo((String)b));
var _a = toNumber(ctx, a);
var _b = toNumber(ctx, b);
if (Double.isNaN(_a) || Double.isNaN(_b)) return CompareResult.NOT_EQUAL;
return CompareResult.from(Double.compare(_a, _b));
}
public static boolean not(Object obj) {
return !toBoolean(obj);
}
public static boolean isInstanceOf(Context ctx, Object obj, Object proto) {
if (obj == null || obj == NULL || proto == null || proto == NULL) return false;
var val = getPrototype(ctx, obj);
while (val != null) {
if (val.equals(proto)) return true;
val = val.getPrototype(ctx);
}
return false;
}
public static Object operation(Context ctx, Operation op, Object ...args) {
switch (op) {
case ADD: return add(ctx, args[0], args[1]);
case SUBTRACT: return subtract(ctx, args[0], args[1]);
case DIVIDE: return divide(ctx, args[0], args[1]);
case MULTIPLY: return multiply(ctx, args[0], args[1]);
case MODULO: return modulo(ctx, args[0], args[1]);
case AND: return and(ctx, args[0], args[1]);
case OR: return or(ctx, args[0], args[1]);
case XOR: return xor(ctx, args[0], args[1]);
case EQUALS: return strictEquals(ctx, args[0], args[1]);
case NOT_EQUALS: return !strictEquals(ctx, args[0], args[1]);
case LOOSE_EQUALS: return looseEqual(ctx, args[0], args[1]);
case LOOSE_NOT_EQUALS: return !looseEqual(ctx, args[0], args[1]);
case GREATER: return compare(ctx, args[0], args[1]).greater();
case GREATER_EQUALS: return compare(ctx, args[0], args[1]).greaterOrEqual();
case LESS: return compare(ctx, args[0], args[1]).less();
case LESS_EQUALS: return compare(ctx, args[0], args[1]).lessOrEqual();
case INVERSE: return bitwiseNot(ctx, args[0]);
case NOT: return not(args[0]);
case POS: return toNumber(ctx, args[0]);
case NEG: return negative(ctx, args[0]);
case SHIFT_LEFT: return shiftLeft(ctx, args[0], args[1]);
case SHIFT_RIGHT: return shiftRight(ctx, args[0], args[1]);
case USHIFT_RIGHT: return unsignedShiftRight(ctx, args[0], args[1]);
case IN: return hasMember(ctx, args[1], args[0], false);
case INSTANCEOF: {
var proto = getMember(ctx, args[1], "prototype");
return isInstanceOf(ctx, args[0], proto);
}
default: return null;
}
}
public static Object getMember(Context ctx, Object obj, Object key) {
obj = normalize(ctx, obj); key = normalize(ctx, key);
if (obj == null) throw new IllegalArgumentException("Tried to access member of undefined.");
if (obj == NULL) throw new IllegalArgumentException("Tried to access member of null.");
if (obj instanceof ObjectValue) return ((ObjectValue)obj).getMember(ctx, key, obj);
if (obj instanceof String && key instanceof Number) {
var i = number(key);
var s = (String)obj;
if (i >= 0 && i < s.length() && i - Math.floor(i) == 0) {
return s.charAt((int)i) + "";
}
}
var proto = getPrototype(ctx, obj);
if (proto == null) return "__proto__".equals(key) ? NULL : null;
else if (key != null && "__proto__".equals(key)) return proto;
else return proto.getMember(ctx, key, obj);
}
public static Object getMemberPath(Context ctx, Object obj, Object ...path) {
var res = obj;
for (var key : path) res = getMember(ctx, res, key);
return res;
}
public static boolean setMember(Context ctx, Object obj, Object key, Object val) {
obj = normalize(ctx, obj); key = normalize(ctx, key); val = normalize(ctx, val);
if (obj == null) throw EngineException.ofType("Tried to access member of undefined.");
if (obj == NULL) throw EngineException.ofType("Tried to access member of null.");
if (key != null && "__proto__".equals(key)) return setPrototype(ctx, obj, val);
if (obj instanceof ObjectValue) return ((ObjectValue)obj).setMember(ctx, key, val, obj, false);
var proto = getPrototype(ctx, obj);
return proto.setMember(ctx, key, val, obj, true);
}
public static boolean hasMember(Context ctx, Object obj, Object key, boolean own) {
if (obj == null || obj == NULL) return false;
obj = normalize(ctx, obj); key = normalize(ctx, key);
if ("__proto__".equals(key)) return true;
if (obj instanceof ObjectValue) return ((ObjectValue)obj).hasMember(ctx, key, own);
if (obj instanceof String && key instanceof Number) {
var i = number(key);
var s = (String)obj;
if (i >= 0 && i < s.length() && i - Math.floor(i) == 0) return true;
}
if (own) return false;
var proto = getPrototype(ctx, obj);
return proto != null && proto.hasMember(ctx, key, own);
}
public static boolean deleteMember(Context ctx, Object obj, Object key) {
if (obj == null || obj == NULL) return false;
obj = normalize(ctx, obj); key = normalize(ctx, key);
if (obj instanceof ObjectValue) return ((ObjectValue)obj).deleteMember(ctx, key);
else return false;
}
public static ObjectValue getPrototype(Context ctx, Object obj) {
if (obj == null || obj == NULL) return null;
obj = normalize(ctx, obj);
if (obj instanceof ObjectValue) return ((ObjectValue)obj).getPrototype(ctx);
if (ctx == null) return null;
if (obj instanceof String) return ctx.get(Environment.STRING_PROTO);
else if (obj instanceof Number) return ctx.get(Environment.NUMBER_PROTO);
else if (obj instanceof Boolean) return ctx.get(Environment.BOOL_PROTO);
else if (obj instanceof Symbol) return ctx.get(Environment.SYMBOL_PROTO);
return null;
}
public static boolean setPrototype(Context ctx, Object obj, Object proto) {
obj = normalize(ctx, obj);
return obj instanceof ObjectValue && ((ObjectValue)obj).setPrototype(ctx, proto);
}
public static void makePrototypeChain(Context ctx, Object... chain) {
for(var i = 1; i < chain.length; i++) {
setPrototype(ctx, chain[i], chain[i - 1]);
}
}
public static List<Object> getMembers(Context ctx, Object obj, boolean own, boolean includeNonEnumerable) {
List<Object> res = new ArrayList<>();
if (obj instanceof ObjectValue) res = ((ObjectValue)obj).keys(includeNonEnumerable);
if (obj instanceof String) {
for (var i = 0; i < ((String)obj).length(); i++) res.add((double)i);
}
if (!own) {
var proto = getPrototype(ctx, obj);
while (proto != null) {
res.addAll(proto.keys(includeNonEnumerable));
proto = getPrototype(ctx, proto);
}
}
return res;
}
public static ObjectValue getMemberDescriptor(Context ctx, Object obj, Object key) {
if (obj instanceof ObjectValue) return ((ObjectValue)obj).getMemberDescriptor(ctx, key);
else if (obj instanceof String && key instanceof Number) {
var i = ((Number)key).intValue();
var _i = ((Number)key).doubleValue();
if (i - _i != 0) return null;
if (i < 0 || i >= ((String)obj).length()) return null;
return new ObjectValue(ctx, Map.of(
"value", ((String)obj).charAt(i) + "",
"writable", false,
"enumerable", true,
"configurable", false
));
}
else return null;
}
public static Object call(Context ctx, Object func, Object thisArg, Object ...args) {
if (!(func instanceof FunctionValue)) throw EngineException.ofType("Tried to call a non-function value.");
return ((FunctionValue)func).call(ctx, thisArg, args);
}
public static Object callNew(Context ctx, Object func, Object ...args) {
var res = new ObjectValue();
try {
var proto = Values.getMember(ctx, func, "prototype");
setPrototype(ctx, res, proto);
var ret = call(ctx, func, res, args);
if (!isPrimitive(ret)) return ret;
return res;
}
catch (IllegalArgumentException e) {
throw EngineException.ofType("Tried to call new on an invalid constructor.");
}
}
public static boolean strictEquals(Context ctx, Object a, Object b) {
a = normalize(ctx, a); b = normalize(ctx, b);
if (a == null || b == null) return a == null && b == null;
if (isNan(a) || isNan(b)) return false;
if (a instanceof Number && number(a) == -0.) a = 0.;
if (b instanceof Number && number(b) == -0.) b = 0.;
return a == b || a.equals(b);
}
public static boolean looseEqual(Context ctx, Object a, Object b) {
a = normalize(ctx, a); b = normalize(ctx, b);
// In loose equality, null is equivalent to undefined
if (a == NULL) a = null;
if (b == NULL) b = null;
if (a == null || b == null) return a == null && b == null;
// If both are objects, just compare their references
if (!isPrimitive(a) && !isPrimitive(b)) return a == b;
// Convert values to primitives
a = toPrimitive(ctx, a, ConvertHint.VALUEOF);
b = toPrimitive(ctx, b, ConvertHint.VALUEOF);
// Compare symbols by reference
if (a instanceof Symbol || b instanceof Symbol) return a == b;
if (a instanceof Boolean || b instanceof Boolean) return toBoolean(a) == toBoolean(b);
if (a instanceof Number || b instanceof Number) return strictEquals(ctx, toNumber(ctx, a), toNumber(ctx, b));
// Default to strings
return toString(ctx, a).equals(toString(ctx, b));
}
public static Object normalize(Context ctx, Object val) {
if (val instanceof Number) return number(val);
if (isPrimitive(val) || val instanceof ObjectValue) return val;
if (val instanceof Character) return val + "";
if (val instanceof Map) {
var res = new ObjectValue();
for (var entry : ((Map<?, ?>)val).entrySet()) {
res.defineProperty(ctx, entry.getKey(), entry.getValue());
}
return res;
}
if (val instanceof Iterable) {
var res = new ArrayValue();
for (var entry : ((Iterable<?>)val)) {
res.set(ctx, res.size(), entry);
}
return res;
}
if (val instanceof Class) {
if (ctx == null) return null;
else return ctx.environment.wrappers.getConstr((Class<?>)val);
}
return new NativeWrapper(val);
}
@SuppressWarnings("unchecked")
public static <T> T convert(Context ctx, Object obj, Class<T> clazz) {
if (clazz == Void.class) return null;
if (obj instanceof NativeWrapper) {
var res = ((NativeWrapper)obj).wrapped;
if (clazz.isInstance(res)) return (T)res;
}
if (clazz == null || clazz == Object.class) return (T)obj;
if (obj instanceof ArrayValue) {
if (clazz.isAssignableFrom(ArrayList.class)) {
var raw = ((ArrayValue)obj).toArray();
var res = new ArrayList<>();
for (var i = 0; i < raw.length; i++) res.add(convert(ctx, raw[i], Object.class));
return (T)new ArrayList<>(res);
}
if (clazz.isAssignableFrom(HashSet.class)) {
var raw = ((ArrayValue)obj).toArray();
var res = new HashSet<>();
for (var i = 0; i < raw.length; i++) res.add(convert(ctx, raw[i], Object.class));
return (T)new HashSet<>(res);
}
if (clazz.isArray()) {
var raw = ((ArrayValue)obj).toArray();
Object res = Array.newInstance(clazz.getComponentType(), raw.length);
for (var i = 0; i < raw.length; i++) Array.set(res, i, convert(ctx, raw[i], Object.class));
return (T)res;
}
}
if (obj instanceof ObjectValue && clazz.isAssignableFrom(HashMap.class)) {
var res = new HashMap<>();
for (var el : ((ObjectValue)obj).values.entrySet()) res.put(
convert(ctx, el.getKey(), null),
convert(ctx, el.getValue(), null)
);
return (T)res;
}
if (clazz == String.class) return (T)toString(ctx, obj);
if (clazz == Boolean.class || clazz == Boolean.TYPE) return (T)(Boolean)toBoolean(obj);
if (clazz == Byte.class || clazz == byte.class) return (T)(Byte)(byte)toNumber(ctx, obj);
if (clazz == Integer.class || clazz == int.class) return (T)(Integer)(int)toNumber(ctx, obj);
if (clazz == Long.class || clazz == long.class) return (T)(Long)(long)toNumber(ctx, obj);
if (clazz == Short.class || clazz == short.class) return (T)(Short)(short)toNumber(ctx, obj);
if (clazz == Float.class || clazz == float.class) return (T)(Float)(float)toNumber(ctx, obj);
if (clazz == Double.class || clazz == double.class) return (T)(Double)toNumber(ctx, obj);
if (clazz == Character.class || clazz == char.class) {
if (obj instanceof Number) return (T)(Character)(char)number(obj);
else {
var res = toString(ctx, obj);
if (res.length() == 0) throw new ConvertException("\"\"", "Character");
else return (T)(Character)res.charAt(0);
}
}
if (obj == null) return null;
if (clazz.isInstance(obj)) return (T)obj;
if (clazz.isAssignableFrom(NativeWrapper.class)) {
return (T)new NativeWrapper(obj);
}
throw new ConvertException(type(obj), clazz.getSimpleName());
}
public static Iterable<Object> fromJSIterator(Context ctx, Object obj) {
return () -> {
try {
var symbol = Symbol.get("Symbol.iterator");
var iteratorFunc = getMember(ctx, obj, symbol);
if (!(iteratorFunc instanceof FunctionValue)) return Collections.emptyIterator();
var iterator = iteratorFunc instanceof FunctionValue ?
((FunctionValue)iteratorFunc).call(ctx, obj, obj) :
iteratorFunc;
var nextFunc = getMember(ctx, call(ctx, iteratorFunc, obj), "next");
if (!(nextFunc instanceof FunctionValue)) return Collections.emptyIterator();
return new Iterator<Object>() {
private Object value = null;
public boolean consumed = true;
private FunctionValue next = (FunctionValue)nextFunc;
private void loadNext() {
if (next == null) value = null;
else if (consumed) {
var curr = next.call(ctx, iterator);
if (curr == null) { next = null; value = null; }
if (toBoolean(Values.getMember(ctx, curr, "done"))) { next = null; value = null; }
else {
this.value = Values.getMember(ctx, curr, "value");
consumed = false;
}
}
}
@Override
public boolean hasNext() {
loadNext();
return next != null;
}
@Override
public Object next() {
loadNext();
var res = value;
value = null;
consumed = true;
return res;
}
};
}
catch (IllegalArgumentException | NullPointerException e) {
return Collections.emptyIterator();
}
};
}
public static ObjectValue toJSIterator(Context ctx, Iterator<?> it) {
var res = new ObjectValue();
try {
var key = getMember(ctx, getMember(ctx, ctx.get(Environment.SYMBOL_PROTO), "constructor"), "iterator");
res.defineProperty(ctx, key, new NativeFunction("", args -> args.self));
}
catch (IllegalArgumentException | NullPointerException e) { }
res.defineProperty(ctx, "next", new NativeFunction("", args -> {
if (!it.hasNext()) return new ObjectValue(ctx, Map.of("done", true));
else {
var obj = new ObjectValue();
obj.defineProperty(args.ctx, "value", it.next());
return obj;
}
}));
return res;
}
public static ObjectValue toJSIterator(Context ctx, Iterable<?> it) {
return toJSIterator(ctx, it.iterator());
}
public static ObjectValue toJSAsyncIterator(Context ctx, Iterator<?> it) {
var res = new ObjectValue();
try {
var key = getMemberPath(ctx, ctx.get(Environment.SYMBOL_PROTO), "constructor", "asyncIterator");
res.defineProperty(ctx, key, new NativeFunction("", args -> args.self));
}
catch (IllegalArgumentException | NullPointerException e) { }
res.defineProperty(ctx, "next", new NativeFunction("", args -> {
return PromiseLib.await(args.ctx, () -> {
if (!it.hasNext()) return new ObjectValue(ctx, Map.of("done", true));
else {
var obj = new ObjectValue();
obj.defineProperty(args.ctx, "value", it.next());
return obj;
}
});
}));
return res;
}
private static boolean isEmptyFunc(ObjectValue val) {
if (!(val instanceof FunctionValue)) return false;
if (!val.values.containsKey("prototype") || val.values.size() + val.properties.size() > 1) return false;
var proto = val.values.get("prototype");
if (!(proto instanceof ObjectValue)) return false;
var protoObj = (ObjectValue)proto;
if (protoObj.values.get("constructor") != val) return false;
if (protoObj.values.size() + protoObj.properties.size() != 1) return false;
return true;
}
private static String toReadable(Context ctx, Object val, HashSet<Object> passed, int tab) {
if (tab == 0 && val instanceof String) return (String)val;
if (passed.contains(val)) return "[circular]";
var printed = true;
var res = new StringBuilder();
var dbg = DebugContext.get(ctx);
if (val instanceof FunctionValue) {
res.append(val.toString());
var loc = val instanceof CodeFunction ? dbg.getMapOrEmpty((CodeFunction)val).start() : null;
if (loc != null) res.append(" @ " + loc);
}
else if (val instanceof ArrayValue) {
res.append("[");
var obj = ((ArrayValue)val);
for (int i = 0; i < obj.size(); i++) {
if (i != 0) res.append(", ");
else res.append(" ");
if (obj.has(i)) res.append(toReadable(ctx, obj.get(i), passed, tab));
else res.append("<empty>");
}
res.append(" ] ");
}
else if (val instanceof NativeWrapper) {
var obj = ((NativeWrapper)val).wrapped;
res.append("Native " + obj.toString() + " ");
}
else printed = false;
if (val instanceof ObjectValue) {
if (tab > 3) {
return "{...}";
}
passed.add(val);
var obj = (ObjectValue)val;
if (obj.values.size() + obj.properties.size() == 0 || isEmptyFunc(obj)) {
if (!printed) res.append("{}\n");
}
else {
res.append("{\n");
for (var el : obj.values.entrySet()) {
for (int i = 0; i < tab + 1; i++) res.append(" ");
res.append(toReadable(ctx, el.getKey(), passed, tab + 1));
res.append(": ");
res.append(toReadable(ctx, el.getValue(), passed, tab + 1));
res.append(",\n");
}
for (var el : obj.properties.entrySet()) {
for (int i = 0; i < tab + 1; i++) res.append(" ");
res.append(toReadable(ctx, el.getKey(), passed, tab + 1));
res.append(": [prop],\n");
}
for (int i = 0; i < tab; i++) res.append(" ");
res.append("}");
}
passed.remove(val);
}
else if (val == null) return "undefined";
else if (val == Values.NULL) return "null";
else if (val instanceof String) return "'" + val + "'";
else return Values.toString(ctx, val);
return res.toString();
}
public static String toReadable(Context ctx, Object val) {
return toReadable(ctx, val, new HashSet<>(), 0);
}
public static String errorToReadable(RuntimeException err, String prefix) {
prefix = prefix == null ? "Uncaught" : "Uncaught " + prefix;
if (err instanceof EngineException) {
var ee = ((EngineException)err);
try {
return prefix + " " + ee.toString(new Context(ee.env));
}
catch (EngineException ex) {
return prefix + " " + toReadable(new Context(ee.env), ee.value);
}
}
else if (err instanceof SyntaxException) {
return prefix + " SyntaxError " + ((SyntaxException)err).msg;
}
else if (err.getCause() instanceof InterruptedException) return "";
else {
var str = new ByteArrayOutputStream();
err.printStackTrace(new PrintStream(str));
return prefix + " internal error " + str.toString();
}
}
public static void printValue(Context ctx, Object val) {
System.out.print(toReadable(ctx, val));
}
public static void printError(RuntimeException err, String prefix) {
System.out.println(errorToReadable(err, prefix));
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,266 +0,0 @@
package me.topchetoeu.jscript.lib;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.ExposeConstructor;
import me.topchetoeu.jscript.utils.interop.ExposeTarget;
import me.topchetoeu.jscript.utils.interop.WrapperName;
@WrapperName("Date")
public class DateLib {
private Calendar normal;
private Calendar utc;
private void updateUTC() {
if (utc == null || normal == null) return;
utc.setTimeInMillis(normal.getTimeInMillis());
}
private void updateNormal() {
if (utc == null || normal == null) return;
normal.setTimeInMillis(utc.getTimeInMillis());
}
private void invalidate() {
normal = utc = null;
}
@Expose public double __getYear() {
if (normal == null) return Double.NaN;
return normal.get(Calendar.YEAR) - 1900;
}
@Expose public double __setYeard(Arguments args) {
var real = args.getDouble(0);
if (real >= 0 && real <= 99) real = real + 1900;
if (Double.isNaN(real)) invalidate();
else normal.set(Calendar.YEAR, (int)real);
updateUTC();
return __getTime();
}
@Expose public double __getFullYear() {
if (normal == null) return Double.NaN;
return normal.get(Calendar.YEAR);
}
@Expose public double __getMonth() {
if (normal == null) return Double.NaN;
return normal.get(Calendar.MONTH);
}
@Expose public double __getDate() {
if (normal == null) return Double.NaN;
return normal.get(Calendar.DAY_OF_MONTH);
}
@Expose public double __getDay() {
if (normal == null) return Double.NaN;
return normal.get(Calendar.DAY_OF_WEEK);
}
@Expose public double __getHours() {
if (normal == null) return Double.NaN;
return normal.get(Calendar.HOUR_OF_DAY);
}
@Expose public double __getMinutes() {
if (normal == null) return Double.NaN;
return normal.get(Calendar.MINUTE);
}
@Expose public double __getSeconds() {
if (normal == null) return Double.NaN;
return normal.get(Calendar.SECOND);
}
@Expose public double __getMilliseconds() {
if (normal == null) return Double.NaN;
return normal.get(Calendar.MILLISECOND);
}
@Expose public double __getUTCFullYear() {
if (utc == null) return Double.NaN;
return utc.get(Calendar.YEAR);
}
@Expose public double __getUTCMonth() {
if (utc == null) return Double.NaN;
return utc.get(Calendar.MONTH);
}
@Expose public double __getUTCDate() {
if (utc == null) return Double.NaN;
return utc.get(Calendar.DAY_OF_MONTH);
}
@Expose public double __getUTCDay() {
if (utc == null) return Double.NaN;
return utc.get(Calendar.DAY_OF_WEEK);
}
@Expose public double __getUTCHours() {
if (utc == null) return Double.NaN;
return utc.get(Calendar.HOUR_OF_DAY);
}
@Expose public double __getUTCMinutes() {
if (utc == null) return Double.NaN;
return utc.get(Calendar.MINUTE);
}
@Expose public double __getUTCSeconds() {
if (utc == null) return Double.NaN;
return utc.get(Calendar.SECOND);
}
@Expose public double __getUTCMilliseconds() {
if (utc == null) return Double.NaN;
return utc.get(Calendar.MILLISECOND);
}
@Expose public double __setFullYear(Arguments args) {
var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate();
else normal.set(Calendar.YEAR, (int)real);
updateUTC();
return __getTime();
}
@Expose public double __setMonthd(Arguments args) {
var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate();
else normal.set(Calendar.MONTH, (int)real);
updateUTC();
return __getTime();
}
@Expose public double __setDated(Arguments args) {
var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate();
else normal.set(Calendar.DAY_OF_MONTH, (int)real);
updateUTC();
return __getTime();
}
@Expose public double __setDayd(Arguments args) {
var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate();
else normal.set(Calendar.DAY_OF_WEEK, (int)real);
updateUTC();
return __getTime();
}
@Expose public double __setHoursd(Arguments args) {
var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate();
else normal.set(Calendar.HOUR_OF_DAY, (int)real);
updateUTC();
return __getTime();
}
@Expose public double __setMinutesd(Arguments args) {
var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate();
else normal.set(Calendar.MINUTE, (int)real);
updateUTC();
return __getTime();
}
@Expose public double __setSecondsd(Arguments args) {
var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate();
else normal.set(Calendar.SECOND, (int)real);
updateUTC();
return __getTime();
}
@Expose public double __setMillisecondsd(Arguments args) {
var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate();
else normal.set(Calendar.MILLISECOND, (int)real);
updateUTC();
return __getTime();
}
@Expose public double __setUTCFullYeard(Arguments args) {
var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate();
else utc.set(Calendar.YEAR, (int)real);
updateNormal();
return __getTime();
}
@Expose public double __setUTCMonthd(Arguments args) {
var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate();
else utc.set(Calendar.MONTH, (int)real);
updateNormal();
return __getTime();
}
@Expose public double __setUTCDated(Arguments args) {
var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate();
else utc.set(Calendar.DAY_OF_MONTH, (int)real);
updateNormal();
return __getTime();
}
@Expose public double __setUTCDayd(Arguments args) {
var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate();
else utc.set(Calendar.DAY_OF_WEEK, (int)real);
updateNormal();
return __getTime();
}
@Expose public double __setUTCHoursd(Arguments args) {
var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate();
else utc.set(Calendar.HOUR_OF_DAY, (int)real);
updateNormal();
return __getTime();
}
@Expose public double __setUTCMinutesd(Arguments args) {
var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate();
else utc.set(Calendar.MINUTE, (int)real);
updateNormal();
return __getTime();
}
@Expose public double __setUTCSecondsd(Arguments args) {
var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate();
else utc.set(Calendar.SECOND, (int)real);
updateNormal();
return __getTime();
}
@Expose public double __setUTCMillisecondsd(Arguments args) {
var real = args.getDouble(0);
if (Double.isNaN(real)) invalidate();
else utc.set(Calendar.MILLISECOND, (int)real);
updateNormal();
return __getTime();
}
@Expose public double __getTime() {
if (utc == null) return Double.NaN;
return utc.getTimeInMillis();
}
@Expose public double __getTimezoneOffset() {
if (normal == null) return Double.NaN;
return normal.getTimeZone().getRawOffset() / 60000;
}
@Expose public double __valueOf() {
if (normal == null) return Double.NaN;
else return normal.getTimeInMillis();
}
@Expose public String __toString() {
return normal.getTime().toString();
}
@Override public String toString() {
return __toString();
}
public DateLib(long timestamp) {
normal = Calendar.getInstance();
utc = Calendar.getInstance();
normal.setTimeInMillis(timestamp);
utc.setTimeZone(TimeZone.getTimeZone("UTC"));
utc.setTimeInMillis(timestamp);
}
public DateLib() {
this(new Date().getTime());
}
@ExposeConstructor public static DateLib init(Arguments args) {
if (args.has(0)) return new DateLib(args.getLong(0));
else return new DateLib();
}
@Expose(target = ExposeTarget.STATIC)
public static double __now() {
return new DateLib().__getTime();
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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