Compare commits
14 Commits
6355a48c6b
...
v0.10.8-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
f16d088646
|
|||
|
8b1c2a5e4e
|
|||
|
ee8268b144
|
|||
|
fffeac9bac
|
|||
|
166e9c0470
|
|||
|
6125772038
|
|||
|
1e982cd2ef
|
|||
|
4389d115b6
|
|||
|
208444381e
|
|||
|
eff076f6fe
|
|||
|
24a4a01d8e
|
|||
|
b9a397a7b9
|
|||
|
40f6cfe616
|
|||
|
f712fb09ae
|
6
.github/workflows/tagged-release.yml
vendored
6
.github/workflows/tagged-release.yml
vendored
@@ -25,10 +25,14 @@ jobs:
|
||||
gradle-version: "8.10"
|
||||
- name: Build
|
||||
run: gradle build
|
||||
- name: Publish
|
||||
run: gradle publish
|
||||
env:
|
||||
ACCESS_TOKEN: "${{secrets.PACKAGE_TOKEN}}"
|
||||
REPO_URL: "${{github.server_url}}/api/packages/${{github.repository_owner}}/maven"
|
||||
- name: Create release
|
||||
uses: "https://gitea.com/actions/gitea-release-action@main"
|
||||
with:
|
||||
# api_key: "${{secrets.TOKEN}}"
|
||||
files: |
|
||||
LICENSE
|
||||
build/libs/*.jar
|
||||
@@ -9,4 +9,7 @@ java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(17);
|
||||
}
|
||||
|
||||
withJavadocJar();
|
||||
withSourcesJar();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
plugins {
|
||||
id("java");
|
||||
id("maven-publish");
|
||||
}
|
||||
|
||||
version = rootProject.version;
|
||||
@@ -20,3 +21,27 @@ dependencies {
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.9.2");
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher");
|
||||
}
|
||||
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
maven {
|
||||
name = "Gitea";
|
||||
url = uri(System.getenv("REPO_URL") ?: "");
|
||||
|
||||
credentials(HttpHeaderCredentials::class) {
|
||||
name = "Authorization";
|
||||
value = "token ${System.getenv("ACCESS_TOKEN")}";
|
||||
}
|
||||
|
||||
authentication {
|
||||
create<HttpHeaderAuthentication>("header");
|
||||
}
|
||||
}
|
||||
}
|
||||
publications {
|
||||
create<MavenPublication>("maven") {
|
||||
from(components["java"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,199 +1,62 @@
|
||||
package me.topchetoeu.j2s.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NavigableSet;
|
||||
import java.util.Objects;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import me.topchetoeu.j2s.common.Instruction.BreakpointType;
|
||||
|
||||
public class FunctionMap {
|
||||
public static class FunctionMapBuilder {
|
||||
private Location first, last;
|
||||
private final TreeMap<Integer, Location> sourceMap = new TreeMap<>();
|
||||
private final HashMap<Location, BreakpointType> breakpoints = new HashMap<>();
|
||||
|
||||
public Location toLocation(int pc) {
|
||||
return sourceMap.headMap(pc, true).firstEntry().getValue();
|
||||
public interface FunctionMap {
|
||||
public static final FunctionMap EMPTY = new FunctionMap() {
|
||||
@Override public Location first() {
|
||||
return null;
|
||||
}
|
||||
@Override public Location last() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public FunctionMapBuilder setDebug(Location loc, BreakpointType type) {
|
||||
if (loc == null || type == null || type == BreakpointType.NONE) return this;
|
||||
breakpoints.put(loc, type);
|
||||
return this;
|
||||
}
|
||||
public FunctionMapBuilder setLocation(int i, Location loc) {
|
||||
if (loc == null || i < 0) return this;
|
||||
if (first == null || first.compareTo(loc) > 0) first = loc;
|
||||
if (last == null || last.compareTo(loc) < 0) last = loc;
|
||||
|
||||
sourceMap.put(i, loc);
|
||||
return this;
|
||||
}
|
||||
public FunctionMapBuilder setLocationAndDebug(int i, Location loc, BreakpointType type) {
|
||||
setDebug(loc, type);
|
||||
setLocation(i, loc);
|
||||
return this;
|
||||
@Override public Location toLocation(int i, boolean approximate) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Location first() {
|
||||
return first;
|
||||
@Override public BreakpointType getBreakpoint(int i) {
|
||||
return BreakpointType.NONE;
|
||||
}
|
||||
public Location last() {
|
||||
return last;
|
||||
@Override public Iterable<Location> breakpoints(Location start, Location end) {
|
||||
return Arrays.asList();
|
||||
}
|
||||
|
||||
public FunctionMapBuilder map(Function<Location, Location> mapper) {
|
||||
var newSourceMaps = new HashMap<Integer, Location>();
|
||||
var newBreakpoints = new HashMap<Location, BreakpointType>();
|
||||
|
||||
for (var key : sourceMap.keySet()) {
|
||||
var mapped = mapper.apply(sourceMap.get(key));
|
||||
if (mapped == null) continue;
|
||||
newSourceMaps.put(key, mapped);
|
||||
}
|
||||
for (var key : breakpoints.keySet()) {
|
||||
var mapped = mapper.apply(key);
|
||||
if (mapped == null) continue;
|
||||
newBreakpoints.put(mapped, breakpoints.get(key));
|
||||
}
|
||||
|
||||
sourceMap.clear();
|
||||
sourceMap.putAll(newSourceMaps);
|
||||
|
||||
breakpoints.clear();
|
||||
breakpoints.putAll(newBreakpoints);
|
||||
|
||||
return this;
|
||||
@Override public Location correctBreakpoint(Location i) {
|
||||
return null;
|
||||
}
|
||||
@Override public Iterable<Location> correctBreakpoint(Pattern filename, int line, int column) {
|
||||
return Arrays.asList();
|
||||
}
|
||||
|
||||
public FunctionMap build(String[] localNames, String[] capturableNames, String[] captureNames) {
|
||||
return new FunctionMap(sourceMap, breakpoints, first, last, localNames, capturableNames, captureNames);
|
||||
@Override public String[] localNames() {
|
||||
return null;
|
||||
}
|
||||
public FunctionMap build() {
|
||||
return new FunctionMap(sourceMap, breakpoints, first, last, new String[0], new String[0], new String[0]);
|
||||
@Override public String[] capturableNames() {
|
||||
return null;
|
||||
}
|
||||
|
||||
private FunctionMapBuilder() { }
|
||||
}
|
||||
|
||||
public static final FunctionMap EMPTY = new FunctionMap();
|
||||
|
||||
private final HashMap<Integer, BreakpointType> bps = new HashMap<>();
|
||||
private final HashMap<Filename, TreeSet<Location>> bpLocs = new HashMap<>();
|
||||
|
||||
private final TreeMap<Integer, Location> pcToLoc = new TreeMap<>();
|
||||
|
||||
public final String[] localNames, capturableNames, captureNames;
|
||||
public final Location first, last;
|
||||
|
||||
public Location toLocation(int pc, boolean approximate) {
|
||||
if (pcToLoc.size() == 0 || pc < 0 || pc > pcToLoc.lastKey()) return null;
|
||||
var res = pcToLoc.get(pc);
|
||||
if (!approximate || res != null) return res;
|
||||
var entry = pcToLoc.headMap(pc, true).lastEntry();
|
||||
if (entry == null) return null;
|
||||
else return entry.getValue();
|
||||
}
|
||||
public Location toLocation(int pc) {
|
||||
return toLocation(pc, false);
|
||||
}
|
||||
|
||||
public BreakpointType getBreakpoint(int pc) {
|
||||
return bps.getOrDefault(pc, BreakpointType.NONE);
|
||||
}
|
||||
public Location correctBreakpoint(Location loc) {
|
||||
var set = bpLocs.get(loc.filename());
|
||||
if (set == null) return null;
|
||||
else return set.ceiling(loc);
|
||||
}
|
||||
public List<Location> correctBreakpoint(Pattern filename, int line, int column) {
|
||||
var candidates = new HashMap<Filename, TreeSet<Location>>();
|
||||
|
||||
for (var name : bpLocs.keySet()) {
|
||||
if (filename.matcher(name.toString()).matches()) {
|
||||
candidates.put(name, bpLocs.get(name));
|
||||
}
|
||||
@Override public String[] captureNames() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
var res = new ArrayList<Location>(candidates.size());
|
||||
for (var candidate : candidates.entrySet()) {
|
||||
var val = correctBreakpoint(Location.of(candidate.getKey(), line, column));
|
||||
if (val == null) continue;
|
||||
res.add(val);
|
||||
}
|
||||
Location first();
|
||||
Location last();
|
||||
|
||||
return res;
|
||||
}
|
||||
public List<Location> breakpoints(Location start, Location end) {
|
||||
if (!Objects.equals(start.filename(), end.filename())) return Arrays.asList();
|
||||
NavigableSet<Location> set = bpLocs.get(start.filename());
|
||||
if (set == null) return Arrays.asList();
|
||||
|
||||
if (start != null) set = set.tailSet(start, true);
|
||||
if (end != null) set = set.headSet(end, true);
|
||||
|
||||
return set.stream().collect(Collectors.toList());
|
||||
Location toLocation(int i, boolean approximate);
|
||||
default Location toLocation(int i) {
|
||||
return toLocation(i, false);
|
||||
}
|
||||
|
||||
public Location start() {
|
||||
if (pcToLoc.size() == 0) return null;
|
||||
return pcToLoc.firstEntry().getValue();
|
||||
}
|
||||
public Location end() {
|
||||
if (pcToLoc.size() == 0) return null;
|
||||
return pcToLoc.lastEntry().getValue();
|
||||
}
|
||||
BreakpointType getBreakpoint(int i);
|
||||
Location correctBreakpoint(Location i);
|
||||
Iterable<Location> correctBreakpoint(Pattern filename, int line, int column);
|
||||
Iterable<Location> breakpoints(Location start, Location end);
|
||||
|
||||
public FunctionMap clone() {
|
||||
var res = new FunctionMap(new HashMap<>(), new HashMap<>(), first, last, localNames, capturableNames, captureNames);
|
||||
res.pcToLoc.putAll(this.pcToLoc);
|
||||
res.bps.putAll(bps);
|
||||
res.bpLocs.putAll(bpLocs);
|
||||
res.pcToLoc.putAll(pcToLoc);
|
||||
return res;
|
||||
}
|
||||
|
||||
public FunctionMap(Map<Integer, Location> map, Map<Location, BreakpointType> breakpoints, Location first, Location last, String[] localNames, String[] capturableNames, String[] captureNames) {
|
||||
var locToPc = new HashMap<Location, Integer>();
|
||||
|
||||
for (var el : map.entrySet()) {
|
||||
pcToLoc.put(el.getKey(), el.getValue());
|
||||
locToPc.putIfAbsent(el.getValue(), el.getKey());
|
||||
}
|
||||
|
||||
for (var el : breakpoints.entrySet()) {
|
||||
if (el.getValue() == null || el.getValue() == BreakpointType.NONE) continue;
|
||||
bps.put(locToPc.get(el.getKey()), el.getValue());
|
||||
|
||||
if (!bpLocs.containsKey(el.getKey().filename())) bpLocs.put(el.getKey().filename(), new TreeSet<>());
|
||||
bpLocs.get(el.getKey().filename()).add(el.getKey());
|
||||
}
|
||||
|
||||
this.localNames = localNames;
|
||||
this.captureNames = captureNames;
|
||||
this.capturableNames = capturableNames;
|
||||
|
||||
this.first = first;
|
||||
this.last = last;
|
||||
}
|
||||
private FunctionMap() {
|
||||
localNames = new String[0];
|
||||
captureNames = new String[0];
|
||||
capturableNames = new String[0];
|
||||
first = null;
|
||||
last = null;
|
||||
}
|
||||
|
||||
public static FunctionMapBuilder builder() {
|
||||
return new FunctionMapBuilder();
|
||||
}
|
||||
}
|
||||
String[] localNames();
|
||||
String[] capturableNames();
|
||||
String[] captureNames();
|
||||
}
|
||||
|
||||
@@ -51,12 +51,11 @@ public class Instruction {
|
||||
STORE_MEMBER_INT(0x4A),
|
||||
STORE_MEMBER_STR(0x4B),
|
||||
|
||||
TYPEOF(0x53),
|
||||
OPERATION(0x54),
|
||||
GLOB_GET(0x50),
|
||||
GLOB_SET(0x51),
|
||||
GLOB_DEF(0x52),
|
||||
|
||||
GLOB_GET(0x60),
|
||||
GLOB_SET(0x61),
|
||||
GLOB_DEF(0x62);
|
||||
OPERATION(0x56);
|
||||
|
||||
private static final HashMap<Integer, Type> types = new HashMap<>();
|
||||
public final int numeric;
|
||||
@@ -333,13 +332,6 @@ public class Instruction {
|
||||
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 operation(Operation op) {
|
||||
return new Instruction(Type.OPERATION, op);
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ public class Metadata {
|
||||
AUTHOR = value;
|
||||
break;
|
||||
case "name":
|
||||
NAME = name;
|
||||
NAME = value;
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException(String.format("%s:%s: Unexpected metadata key '%s'", file, line, name));
|
||||
@@ -44,15 +44,15 @@ public class Metadata {
|
||||
}
|
||||
|
||||
public static String version() {
|
||||
if (VERSION.equals("$" + "{VERSION}")) return "1337-devel";
|
||||
if (VERSION.equals("${VERSION}")) return "1337-devel";
|
||||
else return VERSION;
|
||||
}
|
||||
public static String author() {
|
||||
if (AUTHOR.equals("$" + "{AUTHOR}")) return "anonymous";
|
||||
if (AUTHOR.equals("${AUTHOR}")) return "anonymous";
|
||||
else return AUTHOR;
|
||||
}
|
||||
public static String name() {
|
||||
if (NAME.equals("$" + "{NAME}")) return "some-product";
|
||||
if (NAME.equals("${NAME}")) return "some-product";
|
||||
else return NAME;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,36 +3,37 @@ package me.topchetoeu.j2s.common;
|
||||
import java.util.HashMap;
|
||||
|
||||
public enum Operation {
|
||||
INSTANCEOF(1, 2),
|
||||
IN(2, 2),
|
||||
TYPEOF(0x10, 1),
|
||||
INSTANCEOF(0x11, 2),
|
||||
IN(0x12, 2),
|
||||
|
||||
MULTIPLY(3, 2),
|
||||
DIVIDE(4, 2),
|
||||
MODULO(5, 2),
|
||||
ADD(6, 2),
|
||||
SUBTRACT(7, 2),
|
||||
MULTIPLY(0x20, 2),
|
||||
DIVIDE(0x21, 2),
|
||||
MODULO(0x22, 2),
|
||||
ADD(0x23, 2),
|
||||
SUBTRACT(0x24, 2),
|
||||
|
||||
USHIFT_RIGHT(8, 2),
|
||||
SHIFT_RIGHT(9, 2),
|
||||
SHIFT_LEFT(10, 2),
|
||||
USHIFT_RIGHT(0x30, 2),
|
||||
SHIFT_RIGHT(0x31, 2),
|
||||
SHIFT_LEFT(0x32, 2),
|
||||
|
||||
GREATER(11, 2),
|
||||
LESS(12, 2),
|
||||
GREATER_EQUALS(13, 2),
|
||||
LESS_EQUALS(14, 2),
|
||||
LOOSE_EQUALS(15, 2),
|
||||
LOOSE_NOT_EQUALS(16, 2),
|
||||
EQUALS(17, 2),
|
||||
NOT_EQUALS(18, 2),
|
||||
GREATER(0x40, 2),
|
||||
LESS(0x41, 2),
|
||||
GREATER_EQUALS(0x42, 2),
|
||||
LESS_EQUALS(0x43, 2),
|
||||
LOOSE_EQUALS(0x44, 2),
|
||||
LOOSE_NOT_EQUALS(0x45, 2),
|
||||
EQUALS(0x46, 2),
|
||||
NOT_EQUALS(0x47, 2),
|
||||
|
||||
AND(19, 2),
|
||||
OR(20, 2),
|
||||
XOR(21, 2),
|
||||
AND(0x50, 2),
|
||||
OR(0x51, 2),
|
||||
XOR(0x52, 2),
|
||||
|
||||
NEG(23, 1),
|
||||
POS(24, 1),
|
||||
NOT(25, 1),
|
||||
INVERSE(26, 1);
|
||||
NEG(0x60, 1),
|
||||
POS(0x61, 1),
|
||||
NOT(0x62, 1),
|
||||
INVERSE(0x63, 1);
|
||||
|
||||
private static final HashMap<Integer, Operation> operations = new HashMap<>();
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package me.topchetoeu.j2s.common;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class TestFunctionMap {
|
||||
@Test public void createEmpty() {
|
||||
FunctionMap.builder().build(null, null, null);
|
||||
FunctionMap.builder().build();
|
||||
}
|
||||
@Test public void startOfEmpty() {
|
||||
var empty = FunctionMap.EMPTY;
|
||||
|
||||
assertEquals(null, empty.start());
|
||||
assertEquals(null, empty.end());
|
||||
}
|
||||
}
|
||||
@@ -4,15 +4,6 @@ plugins {
|
||||
|
||||
description = "A compiler of EcmaScript 5 code to J2S bytecode";
|
||||
|
||||
tasks.processResources {
|
||||
filesMatching("metadata.json", {
|
||||
expand(
|
||||
"version" to properties["project_version"],
|
||||
"name" to properties["project_name"],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
package me.topchetoeu.j2s.compilation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NavigableSet;
|
||||
import java.util.Objects;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import me.topchetoeu.j2s.common.Filename;
|
||||
import me.topchetoeu.j2s.common.FunctionMap;
|
||||
import me.topchetoeu.j2s.common.Location;
|
||||
import me.topchetoeu.j2s.common.Instruction.BreakpointType;
|
||||
|
||||
public final class CompilationFunctionMap implements FunctionMap {
|
||||
public static class FunctionMapBuilder {
|
||||
private Location first, last;
|
||||
private final TreeMap<Integer, Location> sourceMap = new TreeMap<>();
|
||||
private final HashMap<Location, BreakpointType> breakpoints = new HashMap<>();
|
||||
|
||||
public Location toLocation(int pc) {
|
||||
return sourceMap.headMap(pc, true).firstEntry().getValue();
|
||||
}
|
||||
|
||||
public FunctionMapBuilder setDebug(Location loc, BreakpointType type) {
|
||||
if (loc == null || type == null || type == BreakpointType.NONE) return this;
|
||||
breakpoints.put(loc, type);
|
||||
return this;
|
||||
}
|
||||
public FunctionMapBuilder setLocation(int i, Location loc) {
|
||||
if (loc == null || i < 0) return this;
|
||||
if (first == null || first.compareTo(loc) > 0) first = loc;
|
||||
if (last == null || last.compareTo(loc) < 0) last = loc;
|
||||
|
||||
sourceMap.put(i, loc);
|
||||
return this;
|
||||
}
|
||||
public FunctionMapBuilder setLocationAndDebug(int i, Location loc, BreakpointType type) {
|
||||
setDebug(loc, type);
|
||||
setLocation(i, loc);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Location first() {
|
||||
return first;
|
||||
}
|
||||
public Location last() {
|
||||
return last;
|
||||
}
|
||||
|
||||
public FunctionMapBuilder map(Function<Location, Location> mapper) {
|
||||
var newSourceMaps = new HashMap<Integer, Location>();
|
||||
var newBreakpoints = new HashMap<Location, BreakpointType>();
|
||||
|
||||
for (var key : sourceMap.keySet()) {
|
||||
var mapped = mapper.apply(sourceMap.get(key));
|
||||
if (mapped == null) continue;
|
||||
newSourceMaps.put(key, mapped);
|
||||
}
|
||||
for (var key : breakpoints.keySet()) {
|
||||
var mapped = mapper.apply(key);
|
||||
if (mapped == null) continue;
|
||||
newBreakpoints.put(mapped, breakpoints.get(key));
|
||||
}
|
||||
|
||||
sourceMap.clear();
|
||||
sourceMap.putAll(newSourceMaps);
|
||||
|
||||
breakpoints.clear();
|
||||
breakpoints.putAll(newBreakpoints);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public CompilationFunctionMap build(String[] localNames, String[] capturableNames, String[] captureNames) {
|
||||
return new CompilationFunctionMap(sourceMap, breakpoints, first, last, localNames, capturableNames, captureNames);
|
||||
}
|
||||
public CompilationFunctionMap build() {
|
||||
return new CompilationFunctionMap(sourceMap, breakpoints, first, last, new String[0], new String[0], new String[0]);
|
||||
}
|
||||
|
||||
private FunctionMapBuilder() { }
|
||||
}
|
||||
|
||||
public static final CompilationFunctionMap EMPTY = new CompilationFunctionMap();
|
||||
|
||||
private final HashMap<Integer, BreakpointType> bps = new HashMap<>();
|
||||
private final HashMap<Filename, TreeSet<Location>> bpLocs = new HashMap<>();
|
||||
|
||||
private final TreeMap<Integer, Location> pcToLoc = new TreeMap<>();
|
||||
|
||||
public final String[] localNames, capturableNames, captureNames;
|
||||
public final Location first, last;
|
||||
|
||||
@Override public Location toLocation(int pc, boolean approximate) {
|
||||
if (pcToLoc.size() == 0 || pc < 0 || pc > pcToLoc.lastKey()) return null;
|
||||
var res = pcToLoc.get(pc);
|
||||
if (!approximate || res != null) return res;
|
||||
var entry = pcToLoc.headMap(pc, true).lastEntry();
|
||||
if (entry == null) return null;
|
||||
else return entry.getValue();
|
||||
}
|
||||
@Override public Location toLocation(int pc) {
|
||||
return toLocation(pc, false);
|
||||
}
|
||||
|
||||
@Override public BreakpointType getBreakpoint(int pc) {
|
||||
return bps.getOrDefault(pc, BreakpointType.NONE);
|
||||
}
|
||||
@Override public Location correctBreakpoint(Location loc) {
|
||||
var set = bpLocs.get(loc.filename());
|
||||
if (set == null) return null;
|
||||
else return set.ceiling(loc);
|
||||
}
|
||||
@Override public List<Location> correctBreakpoint(Pattern filename, int line, int column) {
|
||||
var candidates = new HashMap<Filename, TreeSet<Location>>();
|
||||
|
||||
for (var name : bpLocs.keySet()) {
|
||||
if (filename.matcher(name.toString()).matches()) {
|
||||
candidates.put(name, bpLocs.get(name));
|
||||
}
|
||||
}
|
||||
|
||||
var res = new ArrayList<Location>(candidates.size());
|
||||
for (var candidate : candidates.entrySet()) {
|
||||
var val = correctBreakpoint(Location.of(candidate.getKey(), line, column));
|
||||
if (val == null) continue;
|
||||
res.add(val);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
@Override public List<Location> breakpoints(Location start, Location end) {
|
||||
if (!Objects.equals(start.filename(), end.filename())) return Arrays.asList();
|
||||
NavigableSet<Location> set = bpLocs.get(start.filename());
|
||||
if (set == null) return Arrays.asList();
|
||||
|
||||
if (start != null) set = set.tailSet(start, true);
|
||||
if (end != null) set = set.headSet(end, true);
|
||||
|
||||
return set.stream().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public Location start() {
|
||||
if (pcToLoc.size() == 0) return null;
|
||||
return pcToLoc.firstEntry().getValue();
|
||||
}
|
||||
public Location end() {
|
||||
if (pcToLoc.size() == 0) return null;
|
||||
return pcToLoc.lastEntry().getValue();
|
||||
}
|
||||
|
||||
@Override public Location first() {
|
||||
return first;
|
||||
}
|
||||
@Override public Location last() {
|
||||
return last;
|
||||
}
|
||||
|
||||
@Override public String[] capturableNames() {
|
||||
return capturableNames;
|
||||
}
|
||||
@Override public String[] captureNames() {
|
||||
return captureNames;
|
||||
}
|
||||
@Override public String[] localNames() {
|
||||
return localNames;
|
||||
}
|
||||
|
||||
public CompilationFunctionMap clone() {
|
||||
var res = new CompilationFunctionMap(new HashMap<>(), new HashMap<>(), first, last, localNames, capturableNames, captureNames);
|
||||
res.pcToLoc.putAll(this.pcToLoc);
|
||||
res.bps.putAll(bps);
|
||||
res.bpLocs.putAll(bpLocs);
|
||||
res.pcToLoc.putAll(pcToLoc);
|
||||
return res;
|
||||
}
|
||||
|
||||
public CompilationFunctionMap(Map<Integer, Location> map, Map<Location, BreakpointType> breakpoints, Location first, Location last, String[] localNames, String[] capturableNames, String[] captureNames) {
|
||||
var locToPc = new HashMap<Location, Integer>();
|
||||
|
||||
for (var el : map.entrySet()) {
|
||||
pcToLoc.put(el.getKey(), el.getValue());
|
||||
locToPc.putIfAbsent(el.getValue(), el.getKey());
|
||||
}
|
||||
|
||||
for (var el : breakpoints.entrySet()) {
|
||||
if (el.getValue() == null || el.getValue() == BreakpointType.NONE) continue;
|
||||
bps.put(locToPc.get(el.getKey()), el.getValue());
|
||||
|
||||
if (!bpLocs.containsKey(el.getKey().filename())) bpLocs.put(el.getKey().filename(), new TreeSet<>());
|
||||
bpLocs.get(el.getKey().filename()).add(el.getKey());
|
||||
}
|
||||
|
||||
this.localNames = localNames;
|
||||
this.captureNames = captureNames;
|
||||
this.capturableNames = capturableNames;
|
||||
|
||||
this.first = first;
|
||||
this.last = last;
|
||||
}
|
||||
private CompilationFunctionMap() {
|
||||
localNames = new String[0];
|
||||
captureNames = new String[0];
|
||||
capturableNames = new String[0];
|
||||
first = null;
|
||||
last = null;
|
||||
}
|
||||
|
||||
public static FunctionMapBuilder builder() {
|
||||
return new FunctionMapBuilder();
|
||||
}
|
||||
}
|
||||
@@ -7,12 +7,11 @@ import java.util.function.Function;
|
||||
|
||||
import me.topchetoeu.j2s.common.Environment;
|
||||
import me.topchetoeu.j2s.common.FunctionBody;
|
||||
import me.topchetoeu.j2s.common.FunctionMap;
|
||||
import me.topchetoeu.j2s.common.Instruction;
|
||||
import me.topchetoeu.j2s.common.Key;
|
||||
import me.topchetoeu.j2s.common.Location;
|
||||
import me.topchetoeu.j2s.common.FunctionMap.FunctionMapBuilder;
|
||||
import me.topchetoeu.j2s.common.Instruction.BreakpointType;
|
||||
import me.topchetoeu.j2s.compilation.CompilationFunctionMap.FunctionMapBuilder;
|
||||
import me.topchetoeu.j2s.compilation.control.TryNode;
|
||||
import me.topchetoeu.j2s.compilation.scope.FunctionScope;
|
||||
import me.topchetoeu.j2s.compilation.scope.Variable;
|
||||
@@ -104,10 +103,10 @@ public final class CompileResult {
|
||||
return instructions.toArray(new Instruction[0]);
|
||||
}
|
||||
|
||||
public FunctionMap map(Function<Location, Location> mapper) {
|
||||
public CompilationFunctionMap map(Function<Location, Location> mapper) {
|
||||
return map.map(mapper).build(scope.localNames(), scope.capturableNames(), scope.captureNames());
|
||||
}
|
||||
public FunctionMap map() {
|
||||
public CompilationFunctionMap map() {
|
||||
return map.build(scope.localNames(), scope.capturableNames(), scope.captureNames());
|
||||
}
|
||||
public FunctionBody body() {
|
||||
@@ -155,7 +154,7 @@ public final class CompileResult {
|
||||
this.scope = scope;
|
||||
this.instructions = new ArrayList<>();
|
||||
this.children = new LinkedList<>();
|
||||
this.map = FunctionMap.builder();
|
||||
this.map = CompilationFunctionMap.builder();
|
||||
this.env = env;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ public class ForInNode extends Node {
|
||||
end.set(endI + 1);
|
||||
LabelContext.popLoop(target.env, label);
|
||||
|
||||
target.add(Instruction.discard());
|
||||
if (pollute) target.add(Instruction.pushUndefined());
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ public final class FunctionScope {
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns If a variable with the same name exists, the old variable. Otherwise, the given variable
|
||||
* @return If a variable with the same name exists, the old variable. Otherwise, the given variable
|
||||
*/
|
||||
public Variable define(Variable var) {
|
||||
if (passthrough) return null;
|
||||
@@ -48,7 +48,7 @@ public final class FunctionScope {
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns A variable with the given name, or null if a global variable
|
||||
* @return A variable with the given name, or null if a global variable
|
||||
*/
|
||||
public Variable define(String name) {
|
||||
return define(new Variable(name, false));
|
||||
|
||||
@@ -175,7 +175,7 @@ public final class VariableList implements Iterable<Variable> {
|
||||
this.offset = () -> offset;
|
||||
}
|
||||
/**
|
||||
* @param offset Will offset the indices by the size of the given list
|
||||
* @param prev Will offset the indices by the size of the given list
|
||||
*/
|
||||
public VariableList(IndexType type, VariableList prev) {
|
||||
this.indexType = type;
|
||||
|
||||
@@ -2,6 +2,7 @@ package me.topchetoeu.j2s.compilation.values.operations;
|
||||
|
||||
import me.topchetoeu.j2s.common.Instruction;
|
||||
import me.topchetoeu.j2s.common.Location;
|
||||
import me.topchetoeu.j2s.common.Operation;
|
||||
import me.topchetoeu.j2s.compilation.CompileResult;
|
||||
import me.topchetoeu.j2s.compilation.JavaScript;
|
||||
import me.topchetoeu.j2s.compilation.Node;
|
||||
@@ -20,14 +21,14 @@ public class TypeofNode extends Node {
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
if (value instanceof VariableNode varNode) {
|
||||
target.add(VariableNode.toGet(target, varNode.loc(), varNode.name, true, true));
|
||||
if (pollute) target.add(Instruction.typeof());
|
||||
if (pollute) target.add(Instruction.operation(Operation.TYPEOF));
|
||||
else target.add(Instruction.discard());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
value.compile(target, pollute);
|
||||
if (pollute) target.add(Instruction.typeof());
|
||||
if (pollute) target.add(Instruction.operation(Operation.TYPEOF));
|
||||
}
|
||||
|
||||
public TypeofNode(Location loc, Node value) {
|
||||
|
||||
@@ -4,9 +4,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import me.topchetoeu.j2s.compilation.parsing.Parsing;
|
||||
import me.topchetoeu.j2s.compilation.parsing.Source;
|
||||
|
||||
public class TestParseString {
|
||||
@Test public void notAString() {
|
||||
var res = Parsing.parseString(new Source("var a = 10"), 0);
|
||||
|
||||
@@ -4,9 +4,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import me.topchetoeu.j2s.compilation.parsing.Parsing;
|
||||
import me.topchetoeu.j2s.compilation.parsing.Source;
|
||||
|
||||
public class TestSkipWhite {
|
||||
@Test public void shBang() {
|
||||
var res1 = Parsing.skipEmpty(new Source("#!my-shbang\n10"), 0);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
project_group = me.topchetoeu
|
||||
project_group = me.topchetoeu.j2s
|
||||
project_name = j2s
|
||||
project_version = 0.10.4-beta
|
||||
project_version = 0.10.8-beta
|
||||
main_class = me.topchetoeu.j2s.repl.SimpleRepl
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import com.github.gradle.node.npm.task.NpmTask;
|
||||
|
||||
plugins {
|
||||
id("common");
|
||||
id("common-java");
|
||||
id("com.github.node-gradle.node") version "5.0.0";
|
||||
}
|
||||
|
||||
tasks.compileJava {
|
||||
enabled = false;
|
||||
}
|
||||
tasks.classes {
|
||||
enabled = false;
|
||||
dependencies {
|
||||
implementation(project(":common"));
|
||||
implementation(project(":compilation"));
|
||||
implementation(project(":runtime"));
|
||||
}
|
||||
|
||||
node {
|
||||
@@ -27,16 +26,38 @@ tasks.register<NpmTask>("compileStdlib") {
|
||||
|
||||
args.set(listOf("run", "build-env"));
|
||||
}
|
||||
tasks.register<NpmTask>("compileTranspiler") {
|
||||
tasks.register<NpmTask>("compileBabel") {
|
||||
dependsOn("npmInstall");
|
||||
|
||||
inputs.files("rollup.config.js");
|
||||
inputs.dir("src/transpiler");
|
||||
outputs.files("build/js/transpiler.js");
|
||||
outputs.files("build/js/babel.js");
|
||||
// nom nom tasty ram
|
||||
environment.put("NODE_OPTIONS", "--max-old-space-size=4096");
|
||||
|
||||
args.set(listOf("run", "build-ts"));
|
||||
args.set(listOf("run", "build-babel"));
|
||||
}
|
||||
tasks.register<NpmTask>("compileTypescript") {
|
||||
dependsOn("npmInstall");
|
||||
|
||||
inputs.files("rollup.config.js");
|
||||
inputs.dir("src/transpiler");
|
||||
outputs.files("build/js/typescript.js");
|
||||
// nom nom tasty ram
|
||||
environment.put("NODE_OPTIONS", "--max-old-space-size=4096");
|
||||
|
||||
args.set(listOf("run", "build-typescript"));
|
||||
}
|
||||
tasks.register<NpmTask>("compileCoffee") {
|
||||
dependsOn("npmInstall");
|
||||
|
||||
inputs.files("rollup.config.js");
|
||||
inputs.dir("src/transpiler");
|
||||
outputs.files("build/js/coffee.js");
|
||||
// nom nom tasty ram
|
||||
environment.put("NODE_OPTIONS", "--max-old-space-size=4096");
|
||||
|
||||
args.set(listOf("run", "build-coffee"));
|
||||
}
|
||||
|
||||
tasks.jar {
|
||||
@@ -50,7 +71,9 @@ tasks.jar {
|
||||
|
||||
tasks.processResources {
|
||||
dependsOn("compileStdlib");
|
||||
dependsOn("compileTranspiler");
|
||||
dependsOn("compileTypescript");
|
||||
dependsOn("compileBabel");
|
||||
dependsOn("compileCoffee");
|
||||
|
||||
from("build/js") {
|
||||
into("lib");
|
||||
@@ -58,11 +81,4 @@ tasks.processResources {
|
||||
from("src/lib") {
|
||||
into("lib");
|
||||
}
|
||||
|
||||
filesMatching("metadata.json", {
|
||||
expand(
|
||||
"version" to properties["project_version"].toString(),
|
||||
"name" to properties["project_name"].toString(),
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"scripts": {
|
||||
"build-env": "rollup -c --environment INPUT:src/stdlib/_entry.ts,OUTPUT:build/js/stdlib.js,POLYFILLS:src/polyfills",
|
||||
"build-ts": "rollup -c --environment INPUT:src/transpiler/_entry.ts,OUTPUT:build/js/transpiler.js"
|
||||
"build-babel": "rollup -c --environment INPUT:src/transpiler/_entry-babel.ts,OUTPUT:build/js/babel.js",
|
||||
"build-coffee": "rollup -c --environment INPUT:src/transpiler/_entry-coffee.ts,OUTPUT:build/js/coffee.js",
|
||||
"build-typescript": "rollup -c --environment INPUT:src/transpiler/_entry-typescript.ts,OUTPUT:build/js/typescript.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.26.0",
|
||||
|
||||
2
lib/src/lib/values/string.d.ts
vendored
2
lib/src/lib/values/string.d.ts
vendored
@@ -10,6 +10,8 @@ declare interface String {
|
||||
charCodeAt(i: number): number;
|
||||
codePointAt(i: number): number;
|
||||
|
||||
startsWith(search: string): boolean;
|
||||
endsWith(search: string): boolean;
|
||||
includes(search: string, offset?: number): number;
|
||||
indexOf(search: string, offset?: number): number;
|
||||
lastIndexOf(search: string, offset?: number): number;
|
||||
|
||||
123
lib/src/main/java/me/topchetoeu/j2s/lib/Compilers.java
Normal file
123
lib/src/main/java/me/topchetoeu/j2s/lib/Compilers.java
Normal file
@@ -0,0 +1,123 @@
|
||||
package me.topchetoeu.j2s.lib;
|
||||
|
||||
import me.topchetoeu.j2s.common.Environment;
|
||||
import me.topchetoeu.j2s.common.Filename;
|
||||
import me.topchetoeu.j2s.common.Metadata;
|
||||
import me.topchetoeu.j2s.common.Reading;
|
||||
import me.topchetoeu.j2s.common.SyntaxException;
|
||||
import me.topchetoeu.j2s.compilation.JavaScript;
|
||||
import me.topchetoeu.j2s.runtime.Compiler;
|
||||
import me.topchetoeu.j2s.runtime.debug.DebugHandler;
|
||||
import me.topchetoeu.j2s.runtime.exceptions.EngineException;
|
||||
import me.topchetoeu.j2s.runtime.values.Value;
|
||||
import me.topchetoeu.j2s.runtime.values.functions.CodeFunction;
|
||||
import me.topchetoeu.j2s.runtime.values.functions.FunctionValue;
|
||||
import me.topchetoeu.j2s.runtime.values.functions.NativeFunction;
|
||||
import me.topchetoeu.j2s.runtime.values.primitives.StringValue;
|
||||
|
||||
public class Compilers {
|
||||
public static Compiler jsCompiler() {
|
||||
return (env, filename, raw, mapper) -> {
|
||||
try {
|
||||
var res = JavaScript.compile(env, filename, raw, true);
|
||||
var body = res.body();
|
||||
|
||||
DebugHandler.get(env).onSourceLoad(filename, raw);
|
||||
for (var el : res.all()) {
|
||||
DebugHandler.get(env).onFunctionLoad(el.body(), el.map(mapper));
|
||||
}
|
||||
|
||||
return new CodeFunction(env, filename.toString(), body, new Value[0][]);
|
||||
}
|
||||
catch (SyntaxException e) {
|
||||
var res = EngineException.ofSyntax(e.msg);
|
||||
res.add(env, e.loc.filename() + "", e.loc);
|
||||
throw res;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Compiler wrap(Compiler first, Environment compilerEnv, Environment targetEnv, FunctionValue factory) {
|
||||
var curr = new NativeFunction(args -> {
|
||||
var filename = Filename.parse(args.get(0).toString(args.env));
|
||||
var src = args.get(1).toString(args.env);
|
||||
var mapper = (FunctionValue)args.get(2);
|
||||
return first.compile(targetEnv, filename, src, NativeMapper.unwrap(args.env, mapper));
|
||||
});
|
||||
|
||||
var next = (FunctionValue)factory.apply(compilerEnv, Value.UNDEFINED, curr);
|
||||
|
||||
return (env, filename, source, map) -> {
|
||||
return (FunctionValue)next.apply(
|
||||
compilerEnv, Value.UNDEFINED,
|
||||
StringValue.of(filename.toString()),
|
||||
StringValue.of(source),
|
||||
new NativeMapper(map)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
public static Compiler transpilerFromSource(Compiler prev, Environment target, Filename compilerName, String compilerSrc) {
|
||||
var env = StdLib.apply(null);
|
||||
|
||||
var glob = Value.global(env);
|
||||
var compilerFactory = new FunctionValue[1];
|
||||
|
||||
glob.defineOwnField(env, "getResource", new NativeFunction(args -> {
|
||||
var name = args.get(0).toString(args.env);
|
||||
var src = Reading.resourceToString("lib/" + name);
|
||||
|
||||
if (src == null) return Value.UNDEFINED;
|
||||
else return StringValue.of(src);
|
||||
}));
|
||||
glob.defineOwnField(env, "register", new NativeFunction(args -> {
|
||||
var func = (FunctionValue)args.get(0);
|
||||
compilerFactory[0] = func;
|
||||
return Value.UNDEFINED;
|
||||
}));
|
||||
glob.defineOwnField(env, "registerSource", new NativeFunction(args -> {
|
||||
var filename = Filename.parse(args.get(0).toString(args.env));
|
||||
var src = args.get(1).toString(args.env);
|
||||
DebugHandler.get(target).onSourceLoad(filename, src);
|
||||
return Value.UNDEFINED;
|
||||
}));
|
||||
|
||||
var compiled = JavaScript.compile(compilerName, compilerSrc, false);
|
||||
new CodeFunction(env, "intializer", compiled.body(), new Value[0][]).apply(env, Value.UNDEFINED);
|
||||
|
||||
return wrap(prev, env, target, compilerFactory[0]);
|
||||
}
|
||||
|
||||
public static Compiler babelCompiler(Compiler prev, Environment target) {
|
||||
return transpilerFromSource(prev, target,
|
||||
new Filename(Metadata.name(), "babel.js"),
|
||||
Reading.resourceToString("lib/babel.js")
|
||||
);
|
||||
}
|
||||
public static Compiler typescriptCompiler(Compiler prev, Environment target) {
|
||||
return transpilerFromSource(prev, target,
|
||||
new Filename(Metadata.name(), "typescript.js"),
|
||||
Reading.resourceToString("lib/typescript.js")
|
||||
);
|
||||
}
|
||||
public static Compiler coffeescriptCompiler(Compiler prev, Environment target) {
|
||||
return transpilerFromSource(prev, target,
|
||||
new Filename(Metadata.name(), "coffee.js"),
|
||||
Reading.resourceToString("lib/coffee.js")
|
||||
);
|
||||
}
|
||||
|
||||
public static interface TranspilerFactory {
|
||||
Compiler create(Compiler prev, Environment target);
|
||||
}
|
||||
|
||||
public static Compiler chainTranspilers(Environment target, Compiler base, TranspilerFactory ...factories) {
|
||||
var res = base;
|
||||
|
||||
for (var el : factories) {
|
||||
res = el.create(res, target);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.topchetoeu.j2s.repl;
|
||||
package me.topchetoeu.j2s.lib;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.topchetoeu.j2s.repl.mapping;
|
||||
package me.topchetoeu.j2s.lib;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
694
lib/src/main/java/me/topchetoeu/j2s/lib/Primordials.java
Normal file
694
lib/src/main/java/me/topchetoeu/j2s/lib/Primordials.java
Normal file
@@ -0,0 +1,694 @@
|
||||
package me.topchetoeu.j2s.lib;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
import me.topchetoeu.j2s.runtime.Compiler;
|
||||
import me.topchetoeu.j2s.runtime.EventLoop;
|
||||
import me.topchetoeu.j2s.common.Environment;
|
||||
import me.topchetoeu.j2s.common.Filename;
|
||||
import me.topchetoeu.j2s.common.Key;
|
||||
import me.topchetoeu.j2s.common.Metadata;
|
||||
import me.topchetoeu.j2s.common.SyntaxException;
|
||||
import me.topchetoeu.j2s.compilation.json.JSON;
|
||||
import me.topchetoeu.j2s.compilation.parsing.Parsing;
|
||||
import me.topchetoeu.j2s.compilation.parsing.Source;
|
||||
import me.topchetoeu.j2s.lib.buffers.Int32ArrayValue;
|
||||
import me.topchetoeu.j2s.lib.buffers.Int8ArrayValue;
|
||||
import me.topchetoeu.j2s.lib.buffers.TypedArrayValue;
|
||||
import me.topchetoeu.j2s.lib.buffers.Uint8ArrayValue;
|
||||
import me.topchetoeu.j2s.runtime.ArgumentsValue;
|
||||
import me.topchetoeu.j2s.runtime.Frame;
|
||||
import me.topchetoeu.j2s.runtime.exceptions.EngineException;
|
||||
import me.topchetoeu.j2s.runtime.values.Value;
|
||||
import me.topchetoeu.j2s.runtime.values.functions.FunctionValue;
|
||||
import me.topchetoeu.j2s.runtime.values.functions.NativeFunction;
|
||||
import me.topchetoeu.j2s.runtime.values.objects.ArrayLikeValue;
|
||||
import me.topchetoeu.j2s.runtime.values.objects.ArrayValue;
|
||||
import me.topchetoeu.j2s.runtime.values.objects.ObjectValue;
|
||||
import me.topchetoeu.j2s.runtime.values.primitives.BoolValue;
|
||||
import me.topchetoeu.j2s.runtime.values.primitives.StringValue;
|
||||
import me.topchetoeu.j2s.runtime.values.primitives.SymbolValue;
|
||||
import me.topchetoeu.j2s.runtime.values.primitives.UserValue;
|
||||
import me.topchetoeu.j2s.runtime.values.primitives.VoidValue;
|
||||
import me.topchetoeu.j2s.runtime.values.primitives.numbers.NumberValue;
|
||||
|
||||
public class Primordials {
|
||||
@SuppressWarnings("unchecked")
|
||||
public static ObjectValue mapPrimordials(Environment env) {
|
||||
var res = new ObjectValue();
|
||||
res.setPrototype(null, null);
|
||||
|
||||
var prototype = new ObjectValue[1];
|
||||
NativeFunction mapConstr = new NativeFunction(args -> {
|
||||
var isWeak = args.get(0).toBoolean();
|
||||
return UserValue.of(isWeak ? new WeakHashMap<>() : new LinkedHashMap<>(), prototype[0]);
|
||||
});
|
||||
mapConstr.prototype.defineOwnField(env, "get", new NativeFunction(getArgs -> {
|
||||
var map = getArgs.self(Map.class);
|
||||
var key = getArgs.get(0);
|
||||
var val = map.get(key);
|
||||
return val == null ? Value.UNDEFINED : (Value)val;
|
||||
}));
|
||||
mapConstr.prototype.defineOwnField(env, "set", new NativeFunction(getArgs -> {
|
||||
var map = getArgs.self(Map.class);
|
||||
var key = getArgs.get(0);
|
||||
var val = getArgs.get(1);
|
||||
map.put(key, val);
|
||||
|
||||
return Value.UNDEFINED;
|
||||
}));
|
||||
mapConstr.prototype.defineOwnField(env, "has", new NativeFunction(getArgs -> {
|
||||
var map = getArgs.self(Map.class);
|
||||
var key = getArgs.get(0);
|
||||
return BoolValue.of(map.containsKey(key));
|
||||
}));
|
||||
mapConstr.prototype.defineOwnField(env, "delete", new NativeFunction(getArgs -> {
|
||||
var map = getArgs.self(Map.class);
|
||||
var key = getArgs.get(0);
|
||||
map.remove(key);
|
||||
return Value.UNDEFINED;
|
||||
}));
|
||||
mapConstr.prototype.defineOwnField(env, "keys", new NativeFunction(getArgs -> {
|
||||
var map = getArgs.self(Map.class);
|
||||
return ArrayValue.of(map.keySet());
|
||||
}));
|
||||
mapConstr.prototype.defineOwnField(env, "clear", new NativeFunction(getArgs -> {
|
||||
getArgs.self(Map.class).clear();
|
||||
return Value.UNDEFINED;
|
||||
}));
|
||||
mapConstr.prototype.defineOwnField(env, "size", new NativeFunction(getArgs -> {
|
||||
return NumberValue.of(getArgs.self(Map.class).size());
|
||||
}));
|
||||
prototype[0] = (ObjectValue)mapConstr.prototype;
|
||||
|
||||
return mapConstr;
|
||||
}
|
||||
|
||||
public static String processRegex(String src) {
|
||||
var n = 0;
|
||||
|
||||
var source = new StringBuilder();
|
||||
|
||||
StringBuilder bracesSource = null;
|
||||
StringBuilder bracketsSource = null;
|
||||
|
||||
while (true) {
|
||||
if (n >= src.length()) break;
|
||||
var c = src.charAt(n++);
|
||||
|
||||
if (c == '\\' && n + 1 < src.length() && src.charAt(n) == 'b') {
|
||||
c = '\b';
|
||||
n++;
|
||||
}
|
||||
|
||||
if (bracesSource != null) {
|
||||
var failed = true;
|
||||
|
||||
if (Character.isDigit(c)) {
|
||||
bracesSource.append(c);
|
||||
failed = false;
|
||||
}
|
||||
else if (c == ',' && bracesSource.indexOf(",") < 0) {
|
||||
bracesSource.append(c);
|
||||
failed = false;
|
||||
}
|
||||
else if (c == '}' && bracesSource.length() > 0) {
|
||||
bracesSource.append(c);
|
||||
source.append(bracesSource);
|
||||
bracesSource = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
source.append("\\");
|
||||
source.append(bracesSource);
|
||||
bracesSource = null;
|
||||
n--;
|
||||
}
|
||||
}
|
||||
else if (bracketsSource != null) {
|
||||
if (c == '[') bracketsSource.append("\\[");
|
||||
else if (c == ']') {
|
||||
var res = bracketsSource.append(']').toString();
|
||||
bracketsSource = null;
|
||||
if (res.equals("[^]")) res = "[\\s\\S]";
|
||||
else if (res.equals("[]")) res = "[^\\s\\S]";
|
||||
source.append(res);
|
||||
}
|
||||
else if (c == '\\') {
|
||||
if (n >= src.length()) break;
|
||||
bracketsSource.append(c).append(src.charAt(n++));
|
||||
}
|
||||
else bracketsSource.append(c);
|
||||
}
|
||||
else if (c == '\\') {
|
||||
if (n >= src.length()) throw new PatternSyntaxException("Unexpected end", src, n);
|
||||
c = src.charAt(n++);
|
||||
source.append('\\').append(c);
|
||||
}
|
||||
else if (c == '[') {
|
||||
bracketsSource = new StringBuilder("[");
|
||||
}
|
||||
else if (c == '{' && bracketsSource == null) {
|
||||
bracesSource = new StringBuilder("{");
|
||||
}
|
||||
else source.append(c);
|
||||
}
|
||||
|
||||
if (bracesSource != null) {
|
||||
source.append("\\");
|
||||
source.append(bracesSource);
|
||||
}
|
||||
if (bracketsSource != null) throw new PatternSyntaxException("Unmatched '['", src, n - bracketsSource.length());
|
||||
|
||||
return source.toString();
|
||||
}
|
||||
|
||||
public static ObjectValue regexPrimordials(Environment env) {
|
||||
var res = new ObjectValue();
|
||||
res.setPrototype(null, null);
|
||||
|
||||
var prototype = new ObjectValue[1];
|
||||
NativeFunction mapConstr = new NativeFunction(args -> {
|
||||
var flags = 0;
|
||||
if (args.get(1).toBoolean()) flags |= Pattern.MULTILINE;
|
||||
if (args.get(2).toBoolean()) flags |= Pattern.CASE_INSENSITIVE;
|
||||
if (args.get(3).toBoolean()) flags |= Pattern.DOTALL;
|
||||
if (args.get(4).toBoolean()) flags |= Pattern.UNICODE_CASE | Pattern.CANON_EQ;
|
||||
if (args.get(5).toBoolean()) flags |= Pattern.UNICODE_CHARACTER_CLASS;
|
||||
try {
|
||||
var pattern = Pattern.compile(processRegex(args.get(0).toString(args.env)), flags);
|
||||
return UserValue.of(pattern, prototype[0]);
|
||||
}
|
||||
catch (PatternSyntaxException e) {
|
||||
throw EngineException.ofSyntax("(regex):" + e.getIndex() + ": " + e.getDescription());
|
||||
}
|
||||
});
|
||||
mapConstr.prototype.defineOwnField(env, "exec", new NativeFunction(args -> {
|
||||
var pattern = args.self(Pattern.class);
|
||||
var target = args.get(0).toString(args.env);
|
||||
var offset = args.get(1).toNumber(args.env).getInt();
|
||||
var index = args.get(2).toBoolean();
|
||||
|
||||
if (offset > target.length()) return Value.NULL;
|
||||
|
||||
var matcher = pattern.matcher(target).region(offset, target.length());
|
||||
if (!matcher.find()) return Value.NULL;
|
||||
|
||||
var matchesArr = new ArrayValue(matcher.groupCount() + 1);
|
||||
for (var i = 0; i < matcher.groupCount() + 1; i++) {
|
||||
var group = matcher.group(i);
|
||||
if (group == null) continue;
|
||||
matchesArr.set(args.env, i, StringValue.of(group));
|
||||
}
|
||||
|
||||
matchesArr.defineOwnField(args.env, "index", NumberValue.of(matcher.start()));
|
||||
matchesArr.defineOwnField(args.env, "input", StringValue.of(target));
|
||||
if (index) {
|
||||
var indices = new ArrayValue();
|
||||
indices.setPrototype(args.env, null);
|
||||
for (var i = 0; i < matcher.groupCount(); i++) {
|
||||
matchesArr.set(args.env, i, ArrayValue.of(Arrays.asList(
|
||||
NumberValue.of(matcher.start(i)),
|
||||
NumberValue.of(matcher.end(i))
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
var obj = new ObjectValue();
|
||||
obj.defineOwnField(args.env, "matches", matchesArr);
|
||||
obj.defineOwnField(args.env, "end", NumberValue.of(matcher.end()));
|
||||
|
||||
return obj;
|
||||
// return val == null ? Value.UNDEFINED : (Value)val;
|
||||
}));
|
||||
mapConstr.prototype.defineOwnField(env, "groupCount", new NativeFunction(args -> {
|
||||
var pattern = args.self(Pattern.class);
|
||||
return NumberValue.of(pattern.matcher("").groupCount());
|
||||
}));
|
||||
prototype[0] = (ObjectValue)mapConstr.prototype;
|
||||
|
||||
return mapConstr;
|
||||
}
|
||||
|
||||
public static ObjectValue symbolPrimordials(Environment env) {
|
||||
var res = new ObjectValue();
|
||||
res.setPrototype(null, null);
|
||||
|
||||
res.defineOwnField(env, "makeSymbol", new NativeFunction(args -> new SymbolValue(args.get(0).toString(args.env))));
|
||||
res.defineOwnField(env, "getSymbol", new NativeFunction(args -> SymbolValue.get(args.get(0).toString(args.env))));
|
||||
res.defineOwnField(env, "getSymbolKey", new NativeFunction(args -> ((SymbolValue)args.get(0)).key()));
|
||||
res.defineOwnField(env, "getSymbolDescriptor", new NativeFunction(args -> StringValue.of(((SymbolValue)args.get(0)).value)));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public static ObjectValue numberPrimordials(Environment env) {
|
||||
var res = new ObjectValue();
|
||||
res.setPrototype(null, null);
|
||||
|
||||
res.defineOwnField(env, "parseInt", new NativeFunction(args -> {
|
||||
var nradix = args.get(1).toNumber(env);
|
||||
var radix = nradix.isInt() ? nradix.getInt() : 10;
|
||||
|
||||
if (radix != 10 && args.get(0) instanceof NumberValue num) {
|
||||
if (num.isInt()) return num;
|
||||
else return NumberValue.of(num.getDouble() - num.getDouble() % 1);
|
||||
}
|
||||
else {
|
||||
if (radix < 2 || radix > 36) return NumberValue.NAN;
|
||||
|
||||
var str = args.get(0).toString().trim();
|
||||
var numRes = Parsing.parseInt(new Source(str), 0, "0123456789abcdefghijklmnopqrstuvwxyz".substring(0, radix), true);
|
||||
if (numRes.isSuccess()) {
|
||||
if (numRes.n == str.length()) return NumberValue.of(numRes.result);
|
||||
}
|
||||
return NumberValue.NAN;
|
||||
}
|
||||
}));
|
||||
res.defineOwnField(env, "parseFloat", new NativeFunction(args -> {
|
||||
if (args.get(0) instanceof NumberValue) {
|
||||
return args.get(0);
|
||||
}
|
||||
else {
|
||||
var str = args.get(0).toString().trim();
|
||||
var numRes = Parsing.parseFloat(new Source(str), 0, true);
|
||||
if (numRes.isSuccess()) {
|
||||
if (numRes.n == str.length()) return NumberValue.of(numRes.result);
|
||||
}
|
||||
return NumberValue.NAN;
|
||||
}
|
||||
}));
|
||||
res.defineOwnField(env, "isNaN", new NativeFunction(args -> BoolValue.of(args.get(0).isNaN())));
|
||||
|
||||
res.defineOwnField(env, "pow", new NativeFunction(args -> {
|
||||
return NumberValue.of(Math.pow(args.get(0).toNumber(args.env).getDouble(), args.get(1).toNumber(args.env).getDouble()));
|
||||
}));
|
||||
res.defineOwnField(env, "log", new NativeFunction(args -> {
|
||||
return NumberValue.of(Math.log(args.get(0).toNumber(args.env).getDouble()));
|
||||
}));
|
||||
|
||||
res.defineOwnField(env, "NaN", NumberValue.NAN);
|
||||
res.defineOwnField(env, "Infinity", NumberValue.of(Double.POSITIVE_INFINITY));
|
||||
res.defineOwnField(env, "PI", NumberValue.of(Math.PI));
|
||||
res.defineOwnField(env, "E", NumberValue.of(Math.E));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public static ObjectValue stringPrimordials(Environment env) {
|
||||
var res = new ObjectValue();
|
||||
res.setPrototype(null, null);
|
||||
|
||||
res.defineOwnField(env, "stringBuild", new NativeFunction(args -> {
|
||||
var parts = ((ArrayValue)args.get(0)).toArray();
|
||||
var sb = new StringBuilder();
|
||||
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
sb.append(((StringValue)parts[i]).value);
|
||||
}
|
||||
|
||||
return StringValue.of(sb.toString());
|
||||
}));
|
||||
|
||||
res.defineOwnField(env, "fromCharCode", new NativeFunction(args -> {
|
||||
return StringValue.of(new String(new char[] { (char)args.get(0).toNumber(args.env).getInt() }));
|
||||
}));
|
||||
|
||||
res.defineOwnField(env, "toCharCode", new NativeFunction(args -> {
|
||||
return NumberValue.of(args.get(0).toString(args.env).charAt(0));
|
||||
}));
|
||||
res.defineOwnField(env, "toCodePoint", new NativeFunction(args -> {
|
||||
return NumberValue.of(args.get(0).toString(args.env).codePointAt(args.get(1).toNumber(args.env).getInt()));
|
||||
}));
|
||||
|
||||
res.defineOwnField(env, "substring", new NativeFunction(args -> {
|
||||
var str = args.get(0).toString(args.env);
|
||||
var start = args.get(1).toNumber(args.env).getInt();
|
||||
var end = args.get(2).toNumber(args.env).getInt();
|
||||
|
||||
if (end <= start) return StringValue.of("");
|
||||
|
||||
start = Math.max(Math.min(start, str.length()), 0);
|
||||
end = Math.max(Math.min(end, str.length()), 0);
|
||||
|
||||
return StringValue.of(str.substring(start, end));
|
||||
}));
|
||||
|
||||
res.defineOwnField(env, "indexOf", new NativeFunction(args -> {
|
||||
var str = args.get(0).toString(args.env);
|
||||
var search = args.get(1).toString(args.env);
|
||||
var start = args.get(2).toNumber(args.env).getInt();
|
||||
if (start > str.length()) return NumberValue.of(-1);
|
||||
var reverse = args.get(3).toBoolean();
|
||||
|
||||
if (reverse) return NumberValue.of(str.lastIndexOf(search, start));
|
||||
else return NumberValue.of(str.indexOf(search, start));
|
||||
}));
|
||||
|
||||
res.defineOwnField(env, "lower", new NativeFunction(args -> {
|
||||
return StringValue.of(args.get(0).toString(args.env).toLowerCase());
|
||||
}));
|
||||
res.defineOwnField(env, "upper", new NativeFunction(args -> {
|
||||
return StringValue.of(args.get(0).toString(args.env).toUpperCase());
|
||||
}));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public static ObjectValue objectPrimordials(Environment env) {
|
||||
var res = new ObjectValue();
|
||||
res.setPrototype(null, null);
|
||||
|
||||
res.defineOwnField(env, "defineField", new NativeFunction(args -> {
|
||||
var obj = (ObjectValue)args.get(0);
|
||||
var key = args.get(1);
|
||||
var desc = (ObjectValue)args.get(2);
|
||||
|
||||
var valField = desc.getOwnMember(env, "v");
|
||||
var writeField = desc.getOwnMember(env, "w");
|
||||
var configField = desc.getOwnMember(env, "c");
|
||||
var enumField = desc.getOwnMember(env, "e");
|
||||
|
||||
var enumerable = enumField == null ? null : enumField.get(env, desc).toBoolean();
|
||||
var configurable = configField == null ? null : configField.get(env, desc).toBoolean();
|
||||
var writable = writeField == null ? null : writeField.get(env, desc).toBoolean();
|
||||
var value = valField == null ? null : valField.get(env, desc);
|
||||
|
||||
return BoolValue.of(obj.defineOwnField(args.env, key, value, configurable, enumerable, writable));
|
||||
}));
|
||||
res.defineOwnField(env, "defineProperty", new NativeFunction(args -> {
|
||||
var obj = (ObjectValue)args.get(0);
|
||||
var key = args.get(1);
|
||||
var desc = args.get(2);
|
||||
|
||||
var configField = desc.getOwnMember(env, "c");
|
||||
var enumField = desc.getOwnMember(env, "e");
|
||||
var getField = desc.getOwnMember(env, "g");
|
||||
var setField = desc.getOwnMember(env, "s");
|
||||
|
||||
var enumerable = enumField == null ? null : enumField.get(env, desc).toBoolean();
|
||||
var configurable = configField == null ? null : configField.get(env, desc).toBoolean();
|
||||
Optional<FunctionValue> getter = null, setter = null;
|
||||
|
||||
if (getField != null) {
|
||||
var getVal = getField.get(env, desc);
|
||||
if (getVal == Value.UNDEFINED) getter = Optional.empty();
|
||||
else getter = Optional.of((FunctionValue)getVal);
|
||||
}
|
||||
if (setField != null) {
|
||||
var setVal = setField.get(env, desc);
|
||||
if (setVal == Value.UNDEFINED) setter = Optional.empty();
|
||||
else setter = Optional.of((FunctionValue)setVal);
|
||||
}
|
||||
|
||||
return BoolValue.of(obj.defineOwnProperty(args.env, key, getter, setter, configurable, enumerable));
|
||||
}));
|
||||
res.defineOwnField(env, "getPrototype", new NativeFunction(args -> {
|
||||
var proto = args.get(0).getPrototype(env);
|
||||
if (proto == null) return Value.NULL;
|
||||
else return proto;
|
||||
}));
|
||||
res.defineOwnField(env, "setPrototype", new NativeFunction(args -> {
|
||||
var proto = args.get(1) instanceof VoidValue ? null : (ObjectValue)args.get(1);
|
||||
args.get(0).setPrototype(env, proto);
|
||||
return args.get(0);
|
||||
}));
|
||||
res.defineOwnField(env, "getMembers", new NativeFunction(args -> {
|
||||
var val = new ArrayValue();
|
||||
|
||||
for (var key : args.get(0).getMembers(env, args.get(1).toBoolean(), args.get(2).toBoolean())) {
|
||||
val.set(args.env, val.size(), StringValue.of(key));
|
||||
}
|
||||
|
||||
return val;
|
||||
}));
|
||||
res.defineOwnField(env, "getSymbolMembers", new NativeFunction(args -> {
|
||||
return ArrayValue.of(args.get(0).getSymbolMembers(env, args.get(1).toBoolean(), args.get(2).toBoolean()));
|
||||
}));
|
||||
res.defineOwnField(env, "getOwnMember", new NativeFunction(args -> {
|
||||
var obj = args.get(0);
|
||||
var key = args.get(1);
|
||||
|
||||
var member = obj.getOwnMember(args.env, key);
|
||||
if (member == null) return Value.UNDEFINED;
|
||||
else return member.descriptor(args.env, obj);
|
||||
}));
|
||||
res.defineOwnField(env, "preventExt", new NativeFunction(args -> {
|
||||
args.get(0).preventExtensions();
|
||||
return VoidValue.UNDEFINED;
|
||||
}));
|
||||
res.defineOwnField(env, "seal", new NativeFunction(args -> {
|
||||
args.get(0).seal();
|
||||
return VoidValue.UNDEFINED;
|
||||
}));
|
||||
res.defineOwnField(env, "freeze", new NativeFunction(args -> {
|
||||
args.get(0).freeze();
|
||||
return VoidValue.UNDEFINED;
|
||||
}));
|
||||
res.defineOwnField(env, "memcpy", new NativeFunction(args -> {
|
||||
var src = (ArrayValue)args.get(0);
|
||||
var dst = (ArrayValue)args.get(1);
|
||||
var srcI = args.get(2).toNumber(args.env).getInt();
|
||||
var dstI = args.get(3).toNumber(args.env).getInt();
|
||||
var n = args.get(4).toNumber(args.env).getInt();
|
||||
|
||||
src.copyTo(dst, srcI, dstI, n);
|
||||
|
||||
return VoidValue.UNDEFINED;
|
||||
}));
|
||||
res.defineOwnField(env, "sort", new NativeFunction(args -> {
|
||||
var arr = (ArrayValue)args.get(0);
|
||||
var func = (FunctionValue)args.get(1);
|
||||
|
||||
arr.sort((a, b) -> {
|
||||
return func.apply(args.env, Value.UNDEFINED, a, b).toNumber(args.env).getInt();
|
||||
});
|
||||
|
||||
return arr;
|
||||
}));
|
||||
|
||||
res.defineOwnField(env, "isArray", new NativeFunction(args -> {
|
||||
return BoolValue.of(args.get(0) instanceof ArrayLikeValue);
|
||||
}));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public static ObjectValue bufferPrimordials(Environment env) {
|
||||
var buffProto = new ObjectValue();
|
||||
buffProto.defineOwnProperty(env, "length", Optional.of(new NativeFunction(args -> {
|
||||
return NumberValue.of(args.self(byte[].class).length);
|
||||
})), Optional.empty(), false, true);
|
||||
|
||||
var res = new ObjectValue();
|
||||
res.setPrototype(null, null);
|
||||
|
||||
res.defineOwnField(env, "buff", new NativeFunction(args -> {
|
||||
var size = args.get(0).toNumber(env).getInt();
|
||||
return TypedArrayValue.buffer(new byte[size], buffProto);
|
||||
}));
|
||||
|
||||
res.defineOwnField(env, "uint8", new NativeFunction(args -> {
|
||||
var buff = args.get(byte[].class, 0);
|
||||
var start = args.get(1).toNumber(env).getInt();
|
||||
var end = args.get(2).toNumber(env).getInt();
|
||||
return new Uint8ArrayValue(buff, start, end);
|
||||
}));
|
||||
res.defineOwnField(env, "int8", new NativeFunction(args -> {
|
||||
var buff = args.get(byte[].class, 0);
|
||||
var start = args.get(1).toNumber(env).getInt();
|
||||
var end = args.get(2).toNumber(env).getInt();
|
||||
return new Int8ArrayValue(buff, start, end);
|
||||
}));
|
||||
res.defineOwnField(env, "int32", new NativeFunction(args -> {
|
||||
var buff = args.get(byte[].class, 0);
|
||||
var start = args.get(1).toNumber(env).getInt();
|
||||
var end = args.get(2).toNumber(env).getInt();
|
||||
return new Int32ArrayValue(buff, start, end);
|
||||
}));
|
||||
|
||||
res.defineOwnField(env, "isUint8", new NativeFunction(args -> {
|
||||
return BoolValue.of(args.get(0) instanceof Uint8ArrayValue);
|
||||
}));
|
||||
res.defineOwnField(env, "isInt8", new NativeFunction(args -> {
|
||||
return BoolValue.of(args.get(0) instanceof Int8ArrayValue);
|
||||
}));
|
||||
res.defineOwnField(env, "isInt32", new NativeFunction(args -> {
|
||||
return BoolValue.of(args.get(0) instanceof Int32ArrayValue);
|
||||
}));
|
||||
|
||||
res.defineOwnField(env, "is", new NativeFunction(args -> {
|
||||
return BoolValue.of(args.get(0) instanceof TypedArrayValue);
|
||||
}));
|
||||
res.defineOwnField(env, "isBuff", new NativeFunction(args -> {
|
||||
return BoolValue.of(args.get(byte[].class, 0) != null);
|
||||
}));
|
||||
|
||||
res.defineOwnField(env, "backer", new NativeFunction(args -> {
|
||||
return TypedArrayValue.buffer(((TypedArrayValue)args.get(0)).buffer, buffProto);
|
||||
}));
|
||||
res.defineOwnField(env, "start", new NativeFunction(args -> {
|
||||
return NumberValue.of(((TypedArrayValue)args.get(0)).start);
|
||||
}));
|
||||
res.defineOwnField(env, "end", new NativeFunction(args -> {
|
||||
return NumberValue.of(((TypedArrayValue)args.get(0)).end);
|
||||
}));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public static ObjectValue functionPrimordials(Environment env) {
|
||||
var res = new ObjectValue();
|
||||
res.setPrototype(null, null);
|
||||
|
||||
res.defineOwnField(env, "setCallable", new NativeFunction(args -> {
|
||||
var func = (FunctionValue)args.get(0);
|
||||
func.enableApply = args.get(1).toBoolean();
|
||||
return Value.UNDEFINED;
|
||||
}));
|
||||
res.defineOwnField(env, "setConstructable", new NativeFunction(args -> {
|
||||
var func = (FunctionValue)args.get(0);
|
||||
func.enableConstruct = args.get(1).toBoolean();
|
||||
return Value.UNDEFINED;
|
||||
}));
|
||||
res.defineOwnField(env, "invokeType", new NativeFunction(args -> {
|
||||
if (((ArgumentsValue)args.get(0)).frame.isNew) return StringValue.of("new");
|
||||
else return StringValue.of("call");
|
||||
}));
|
||||
res.defineOwnField(env, "invokeTypeInfer", new NativeFunction(args -> {
|
||||
var frame = Frame.get(args.env, args.get(0).toNumber(args.env).getInt());
|
||||
if (frame.isNew) return StringValue.of("new");
|
||||
else return StringValue.of("call");
|
||||
}));
|
||||
res.defineOwnField(env, "target", new NativeFunction(args -> {
|
||||
var frame = Frame.get(args.env, args.get(0).toNumber(args.env).getInt());
|
||||
if (frame.target == null) return Value.UNDEFINED;
|
||||
else return frame.target;
|
||||
}));
|
||||
|
||||
res.defineOwnField(env, "invoke", new NativeFunction(args -> {
|
||||
var func = (FunctionValue)args.get(0);
|
||||
var self = args.get(1);
|
||||
var funcArgs = (ArrayLikeValue)args.get(2);
|
||||
|
||||
return func.apply(env, self, funcArgs.toArray());
|
||||
}));
|
||||
res.defineOwnField(env, "construct", new NativeFunction(args -> {
|
||||
var func = (FunctionValue)args.get(0);
|
||||
var target = args.get(1);
|
||||
var funcArgs = (ArrayLikeValue)args.get(2);
|
||||
|
||||
if (target == Value.UNDEFINED) return func.constructNoSelf(env, funcArgs.toArray());
|
||||
else return func.construct(env, target, funcArgs.toArray());
|
||||
}));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public static ObjectValue jsonPrimordials(Environment env) {
|
||||
var res = new ObjectValue();
|
||||
res.setPrototype(null, null);
|
||||
|
||||
res.defineOwnField(env, "stringify", new NativeFunction(args -> {
|
||||
return StringValue.of(JSON.stringify(JSONConverter.fromJs(env, args.get(0))));
|
||||
}));
|
||||
res.defineOwnField(env, "parse", new NativeFunction(args -> {
|
||||
try {
|
||||
return JSONConverter.toJs(JSON.parse(null, args.get(0).toString(env)));
|
||||
}
|
||||
catch (SyntaxException e) {
|
||||
throw EngineException.ofSyntax(e.msg).add(env, e.loc.filename() + "", e.loc);
|
||||
}
|
||||
}));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public static void setProto(Environment env, Environment target, Key<ObjectValue> key, ObjectValue repo, String name) {
|
||||
var val = repo.getMember(env, name);
|
||||
if (val instanceof ObjectValue obj) {
|
||||
target.add(key, obj);
|
||||
}
|
||||
}
|
||||
|
||||
public static ObjectValue create(Environment env) {
|
||||
var res = new ObjectValue();
|
||||
res.setPrototype(null, null);
|
||||
|
||||
res.defineOwnField(env, "symbol", symbolPrimordials(env));
|
||||
res.defineOwnField(env, "number", numberPrimordials(env));
|
||||
res.defineOwnField(env, "string", stringPrimordials(env));
|
||||
res.defineOwnField(env, "object", objectPrimordials(env));
|
||||
res.defineOwnField(env, "buffer", bufferPrimordials(env));
|
||||
res.defineOwnField(env, "function", functionPrimordials(env));
|
||||
res.defineOwnField(env, "json", jsonPrimordials(env));
|
||||
res.defineOwnField(env, "map", mapPrimordials(env));
|
||||
res.defineOwnField(env, "regex", regexPrimordials(env));
|
||||
|
||||
int[] i = new int[1];
|
||||
|
||||
res.defineOwnField(env, "setGlobalPrototypes", new NativeFunction(args -> {
|
||||
var obj = (ObjectValue)args.get(0);
|
||||
|
||||
setProto(args.env, env, Value.OBJECT_PROTO, obj, "object");
|
||||
setProto(args.env, env, Value.FUNCTION_PROTO, obj, "function");
|
||||
setProto(args.env, env, Value.ARRAY_PROTO, obj, "array");
|
||||
setProto(args.env, env, Value.BOOL_PROTO, obj, "boolean");
|
||||
setProto(args.env, env, Value.NUMBER_PROTO, obj, "number");
|
||||
setProto(args.env, env, Value.STRING_PROTO, obj, "string");
|
||||
setProto(args.env, env, Value.SYMBOL_PROTO, obj, "symbol");
|
||||
setProto(args.env, env, Value.ERROR_PROTO, obj, "error");
|
||||
setProto(args.env, env, Value.SYNTAX_ERR_PROTO, obj, "syntax");
|
||||
setProto(args.env, env, Value.TYPE_ERR_PROTO, obj, "type");
|
||||
setProto(args.env, env, Value.RANGE_ERR_PROTO, obj, "range");
|
||||
setProto(args.env, env, Value.UINT8_ARR_PROTO, obj, "uint8");
|
||||
setProto(args.env, env, Value.INT32_ARR_PROTO, obj, "int32");
|
||||
return Value.UNDEFINED;
|
||||
}));
|
||||
res.defineOwnField(env, "setIntrinsic", new NativeFunction(args -> {
|
||||
var name = args.get(0).toString(env);
|
||||
var val = args.get(1);
|
||||
|
||||
Value.intrinsics(env).put(name, val);
|
||||
|
||||
return Value.UNDEFINED;
|
||||
}));
|
||||
res.defineOwnField(env, "compile", new NativeFunction(args -> {
|
||||
var nameVal = args.get(1);
|
||||
var name = nameVal instanceof VoidValue ?
|
||||
new Filename(Metadata.name(), "func" + i[0]++ + ".js") :
|
||||
Filename.parse(nameVal.toString(args.env));
|
||||
|
||||
return Compiler.compileFunc(env, name, args.get(0).toString(env));
|
||||
}));
|
||||
res.defineOwnField(env, "now", new NativeFunction(args -> {
|
||||
return NumberValue.of(System.currentTimeMillis());
|
||||
}));
|
||||
res.defineOwnField(env, "next", new NativeFunction(args -> {
|
||||
var func = (FunctionValue)args.get(0);
|
||||
EventLoop.get(env).pushMsg(() -> {
|
||||
func.apply(env, Value.UNDEFINED);
|
||||
}, true);
|
||||
return Value.UNDEFINED;
|
||||
}));
|
||||
res.defineOwnField(env, "print", new NativeFunction(args -> {
|
||||
for (var el : args.args) {
|
||||
if (el instanceof StringValue) System.out.print(((StringValue)el).value + " \t");
|
||||
else System.out.print(el.toReadable(args.env) + " \t");
|
||||
}
|
||||
System.out.println();
|
||||
|
||||
return Value.UNDEFINED;
|
||||
}));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
29
lib/src/main/java/me/topchetoeu/j2s/lib/StdLib.java
Normal file
29
lib/src/main/java/me/topchetoeu/j2s/lib/StdLib.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package me.topchetoeu.j2s.lib;
|
||||
|
||||
import me.topchetoeu.j2s.common.Environment;
|
||||
import me.topchetoeu.j2s.common.Filename;
|
||||
import me.topchetoeu.j2s.common.Metadata;
|
||||
import me.topchetoeu.j2s.common.Reading;
|
||||
import me.topchetoeu.j2s.compilation.CompileResult;
|
||||
import me.topchetoeu.j2s.compilation.JavaScript;
|
||||
import me.topchetoeu.j2s.runtime.values.Value;
|
||||
import me.topchetoeu.j2s.runtime.values.functions.CodeFunction;
|
||||
|
||||
public class StdLib {
|
||||
private static final CompileResult RUNNER = JavaScript.compile(new Filename(Metadata.name(), "init.js"), Reading.resourceToString("lib/stdlib.js"), false);
|
||||
|
||||
public static Environment apply(Environment env) {
|
||||
if (env == null) {
|
||||
env = new Environment();
|
||||
}
|
||||
|
||||
var stubEnv = new Environment();
|
||||
Value.global(stubEnv).defineOwnField(stubEnv, "target", Value.global(env));
|
||||
Value.global(stubEnv).defineOwnField(stubEnv, "primordials", Primordials.create(env));
|
||||
|
||||
var func = new CodeFunction(stubEnv, "intializer", RUNNER.body(), new Value[0][]);
|
||||
func.apply(stubEnv, Value.UNDEFINED);
|
||||
|
||||
return env;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.topchetoeu.j2s.repl.buffers;
|
||||
package me.topchetoeu.j2s.lib.buffers;
|
||||
|
||||
public final class Int32ArrayValue extends TypedArrayValue {
|
||||
@Override protected int onGet(int i) {
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.topchetoeu.j2s.repl.buffers;
|
||||
package me.topchetoeu.j2s.lib.buffers;
|
||||
|
||||
public final class Int8ArrayValue extends TypedArrayValue {
|
||||
@Override protected int onGet(int i) {
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.topchetoeu.j2s.repl.buffers;
|
||||
package me.topchetoeu.j2s.lib.buffers;
|
||||
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.topchetoeu.j2s.repl.buffers;
|
||||
package me.topchetoeu.j2s.lib.buffers;
|
||||
|
||||
public final class Uint8ArrayValue extends TypedArrayValue {
|
||||
@Override protected int onGet(int i) {
|
||||
@@ -1,4 +1,4 @@
|
||||
import { object, setGlobalPrototypes, setIntrinsic, target } from "./primordials.ts";
|
||||
import { now, object, print, setGlobalPrototypes, setIntrinsic, target } from "./primordials.ts";
|
||||
import { Error, RangeError, SyntaxError, TypeError } from "./values/errors.ts";
|
||||
import { Boolean } from "./values/boolean.ts";
|
||||
import { Function } from "./values/function.ts";
|
||||
@@ -21,11 +21,6 @@ import { Uint8Array } from "./arrays/Uint8Array.ts";
|
||||
import { Int32Array } from "./arrays/Int32Array.ts";
|
||||
import { TypedArray } from "./arrays/TypedArray.ts";
|
||||
|
||||
declare global {
|
||||
function print(...args: any[]): void;
|
||||
function measure(func: Function): void;
|
||||
}
|
||||
|
||||
function fixup<T extends Function>(clazz: T) {
|
||||
object.setPrototype(clazz, Function.prototype);
|
||||
object.setPrototype(clazz.prototype as any, Object.prototype);
|
||||
@@ -72,6 +67,16 @@ target.NaN = Number.NaN;
|
||||
target.Infinity = Number.POSITIVE_INFINITY;
|
||||
target.encodeURI = encodeURI;
|
||||
target.encodeURIComponent = encodeURIComponent;
|
||||
target.print = print;
|
||||
target.measure = (func: () => void) => {
|
||||
const start = now();
|
||||
try {
|
||||
return func();
|
||||
}
|
||||
finally {
|
||||
print(`Took ${now() - start}ms`);
|
||||
}
|
||||
};
|
||||
|
||||
setGlobalPrototypes({
|
||||
string: String.prototype,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { func, json, object } from "../primordials.ts";
|
||||
import { func, object, print } from "../primordials.ts";
|
||||
|
||||
export const console = {};
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ export interface BufferPrimordials {
|
||||
export interface FunctionPrimordials {
|
||||
invokeType(args: IArguments, self: any): "new" | "call";
|
||||
invokeTypeInfer(): "new" | "call";
|
||||
target(): Function | null | undefined;
|
||||
target(level?: number): Function | null | undefined;
|
||||
setConstructable(func: Function, flag: boolean): void;
|
||||
setCallable(func: Function, flag: boolean): void;
|
||||
invoke(func: Function, self: any, args: any[]): any;
|
||||
@@ -105,12 +105,13 @@ export interface Primordials {
|
||||
exec(target: string, offset: number, indices: boolean): { matches: RegExpMatchArray, end: number } | null;
|
||||
groupCount(): number;
|
||||
};
|
||||
compile(src: string): Function;
|
||||
compile(src: string, filename?: string): Function;
|
||||
setGlobalPrototypes(prototype: Record<string, any>): void;
|
||||
now(): number;
|
||||
next(func: () => void): void;
|
||||
schedule(func: () => void, delay: number): () => void;
|
||||
setIntrinsic(key: string, val: any): void;
|
||||
print(...args: any[]): void;
|
||||
}
|
||||
|
||||
// prevent optimization to "undefined", which doesn't exist yet
|
||||
@@ -134,6 +135,7 @@ export const {
|
||||
next,
|
||||
schedule,
|
||||
setIntrinsic,
|
||||
print,
|
||||
} = primordials;
|
||||
|
||||
export type regex = InstanceType<typeof regex>;
|
||||
@@ -55,23 +55,12 @@ export const Function = (() => {
|
||||
return res;
|
||||
}
|
||||
|
||||
public static compile(src = "", { globals = [], wrap = false }: { globals?: string[], wrap?: boolean } = {}) {
|
||||
const parts = [];
|
||||
public static compile(src: string, filename?: string) {
|
||||
return compile(String(src), filename);
|
||||
}
|
||||
|
||||
if (wrap) parts[parts.length] = "return (function() {\n";
|
||||
if (globals.length > 0) {
|
||||
parts[parts.length] = "let {";
|
||||
for (let i = 0; i < globals.length; i++) {
|
||||
if (i > 0) parts[parts.length] = ",";
|
||||
parts[parts.length] = globals[i];
|
||||
}
|
||||
parts[parts.length] = "} = arguments[0];";
|
||||
}
|
||||
parts[parts.length] = src;
|
||||
if (wrap) parts[parts.length] = "\n})(arguments[0])";
|
||||
|
||||
const res = compile(string.stringBuild(parts));
|
||||
return res;
|
||||
public static newTarget() {
|
||||
return func.target(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
3
lib/src/transpiler/_entry-babel.ts
Normal file
3
lib/src/transpiler/_entry-babel.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import babel from "./babel.ts";
|
||||
|
||||
register(babel);
|
||||
3
lib/src/transpiler/_entry-coffee.ts
Normal file
3
lib/src/transpiler/_entry-coffee.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import coffee from "./coffeescript.ts";
|
||||
|
||||
register(coffee);
|
||||
3
lib/src/transpiler/_entry-typescript.ts
Normal file
3
lib/src/transpiler/_entry-typescript.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import ts from "./typescript.ts";
|
||||
|
||||
register(ts);
|
||||
@@ -1,4 +0,0 @@
|
||||
import coffeescript from "./coffeescript.ts";
|
||||
import babel from "./babel.ts";
|
||||
|
||||
register(v => coffeescript(babel(v)));
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SourceMap } from "./map.ts";
|
||||
import { transform, availablePresets } from "@babel/standalone";
|
||||
import { transform, availablePlugins } from "@babel/standalone";
|
||||
|
||||
export default function babel(next: Compiler): Compiler {
|
||||
print("Loaded babel!");
|
||||
@@ -8,7 +8,65 @@ export default function babel(next: Compiler): Compiler {
|
||||
const res = transform(code, {
|
||||
filename,
|
||||
sourceMaps: true,
|
||||
presets: [availablePresets.env],
|
||||
assumptions: {
|
||||
arrayLikeIsIterable: true,
|
||||
constantSuper: true,
|
||||
ignoreFunctionLength: true,
|
||||
ignoreToPrimitiveHint: true,
|
||||
mutableTemplateObject: true,
|
||||
noDocumentAll: true,
|
||||
noNewArrows: true,
|
||||
noUninitializedPrivateFieldAccess: true,
|
||||
privateFieldsAsSymbols: true,
|
||||
},
|
||||
plugins: [
|
||||
// ES2022
|
||||
availablePlugins["transform-class-properties"],
|
||||
availablePlugins["transform-class-static-block"],
|
||||
availablePlugins["transform-private-methods"],
|
||||
availablePlugins["transform-private-property-in-object"],
|
||||
// "syntax-top-level-await",
|
||||
|
||||
// ES2021
|
||||
availablePlugins["transform-logical-assignment-operators"],
|
||||
availablePlugins["transform-numeric-separator"],
|
||||
|
||||
// ES2020
|
||||
availablePlugins["transform-optional-chaining"],
|
||||
availablePlugins["transform-nullish-coalescing-operator"],
|
||||
|
||||
// ES2018
|
||||
availablePlugins["transform-async-generator-functions"],
|
||||
availablePlugins["transform-object-rest-spread"],
|
||||
availablePlugins["transform-unicode-property-regex"],
|
||||
|
||||
// ES2017
|
||||
availablePlugins["transform-async-to-generator"],
|
||||
|
||||
// ES2016
|
||||
availablePlugins["transform-exponentiation-operator"],
|
||||
|
||||
// ES2015
|
||||
availablePlugins["transform-regenerator"],
|
||||
availablePlugins["transform-arrow-functions"],
|
||||
availablePlugins["transform-block-scoping"],
|
||||
availablePlugins["transform-classes"],
|
||||
availablePlugins["transform-computed-properties"],
|
||||
availablePlugins["transform-destructuring"],
|
||||
availablePlugins["transform-duplicate-keys"],
|
||||
availablePlugins["transform-for-of"],
|
||||
availablePlugins["transform-function-name"],
|
||||
availablePlugins["transform-literals"],
|
||||
availablePlugins["transform-new-target"],
|
||||
availablePlugins["transform-object-super"],
|
||||
availablePlugins["transform-parameters"],
|
||||
availablePlugins["transform-shorthand-properties"],
|
||||
availablePlugins["transform-spread"],
|
||||
availablePlugins["transform-sticky-regex"],
|
||||
availablePlugins["transform-template-literals"],
|
||||
availablePlugins["transform-unicode-escapes"],
|
||||
availablePlugins["transform-unicode-regex"],
|
||||
],
|
||||
});
|
||||
|
||||
const map = SourceMap.parse({
|
||||
@@ -18,7 +76,9 @@ export default function babel(next: Compiler): Compiler {
|
||||
});
|
||||
|
||||
registerSource(filename, code);
|
||||
return next("babel-internal://" + filename, res.code!, SourceMap.chain(map, prevMap));
|
||||
const func = next("babel-internal://" + filename, res.code!, SourceMap.chain(map, prevMap));
|
||||
func.name = filename;
|
||||
return func;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,9 @@ export default function coffee(next: Compiler): Compiler {
|
||||
});
|
||||
|
||||
registerSource(filename, code);
|
||||
return next("coffee-internal://" + filename, result, SourceMap.chain(map, prevMap));
|
||||
const func = next("coffee-internal://" + filename, result, SourceMap.chain(map, prevMap));
|
||||
func.name = filename;
|
||||
return func;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -109,10 +109,12 @@ export default function typescript(next: Compiler): Compiler {
|
||||
registerSource(filename, code);
|
||||
const compiled = next("ts-internal://" + filename, result, SourceMap.chain(map, prevMap));
|
||||
|
||||
return function (this: any) {
|
||||
const func = function (this: any) {
|
||||
const res = compiled.apply(this, arguments);
|
||||
if (declaration !== '') files["/src." + declI++ + ".d.ts"] = ScriptSnapshot.fromString(declaration);
|
||||
return res;
|
||||
};
|
||||
func.name = filename;
|
||||
return func;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
{
|
||||
"include": ["**/*.ts"],
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "Bundler",
|
||||
"module": "ESNext",
|
||||
"target": "ESNext",
|
||||
"noLib": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"allowImportingTsExtensions": true,
|
||||
"noEmit": true
|
||||
}
|
||||
"include": ["**/*.ts"],
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "Bundler",
|
||||
"module": "ESNext",
|
||||
"target": "ESNext",
|
||||
"noLib": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"allowImportingTsExtensions": true,
|
||||
"noEmit": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,15 +5,6 @@ plugins {
|
||||
|
||||
description = "A simple REPL for the interpreter, can be used for simple prototyping";
|
||||
|
||||
tasks.processResources {
|
||||
filesMatching("metadata.json", {
|
||||
expand(
|
||||
"version" to properties["project_version"],
|
||||
"name" to properties["project_name"],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform();
|
||||
}
|
||||
|
||||
@@ -6,15 +6,8 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
import me.topchetoeu.j2s.common.Environment;
|
||||
import me.topchetoeu.j2s.common.Filename;
|
||||
@@ -22,62 +15,24 @@ import me.topchetoeu.j2s.common.Key;
|
||||
import me.topchetoeu.j2s.common.Metadata;
|
||||
import me.topchetoeu.j2s.common.Reading;
|
||||
import me.topchetoeu.j2s.common.SyntaxException;
|
||||
import me.topchetoeu.j2s.compilation.JavaScript;
|
||||
import me.topchetoeu.j2s.compilation.json.JSON;
|
||||
import me.topchetoeu.j2s.compilation.parsing.Parsing;
|
||||
import me.topchetoeu.j2s.compilation.parsing.Source;
|
||||
import me.topchetoeu.j2s.repl.buffers.Int32ArrayValue;
|
||||
import me.topchetoeu.j2s.repl.buffers.Int8ArrayValue;
|
||||
import me.topchetoeu.j2s.repl.buffers.TypedArrayValue;
|
||||
import me.topchetoeu.j2s.repl.buffers.Uint8ArrayValue;
|
||||
import me.topchetoeu.j2s.lib.Compilers;
|
||||
import me.topchetoeu.j2s.lib.StdLib;
|
||||
import me.topchetoeu.j2s.repl.debug.SimpleDebugHandler;
|
||||
import me.topchetoeu.j2s.repl.debug.DebugServer;
|
||||
import me.topchetoeu.j2s.repl.debug.Debugger;
|
||||
import me.topchetoeu.j2s.repl.debug.SimpleDebugger;
|
||||
import me.topchetoeu.j2s.repl.mapping.NativeMapper;
|
||||
import me.topchetoeu.j2s.runtime.ArgumentsValue;
|
||||
import me.topchetoeu.j2s.runtime.Compiler;
|
||||
import me.topchetoeu.j2s.runtime.Engine;
|
||||
import me.topchetoeu.j2s.runtime.EventLoop;
|
||||
import me.topchetoeu.j2s.runtime.Frame;
|
||||
import me.topchetoeu.j2s.runtime.debug.DebugContext;
|
||||
import me.topchetoeu.j2s.runtime.debug.DebugHandler;
|
||||
import me.topchetoeu.j2s.runtime.exceptions.EngineException;
|
||||
import me.topchetoeu.j2s.runtime.values.Value;
|
||||
import me.topchetoeu.j2s.runtime.values.functions.CodeFunction;
|
||||
import me.topchetoeu.j2s.runtime.values.functions.FunctionValue;
|
||||
import me.topchetoeu.j2s.runtime.values.functions.NativeFunction;
|
||||
import me.topchetoeu.j2s.runtime.values.objects.ArrayLikeValue;
|
||||
import me.topchetoeu.j2s.runtime.values.objects.ArrayValue;
|
||||
import me.topchetoeu.j2s.runtime.values.objects.ObjectValue;
|
||||
import me.topchetoeu.j2s.runtime.values.primitives.BoolValue;
|
||||
import me.topchetoeu.j2s.runtime.values.primitives.StringValue;
|
||||
import me.topchetoeu.j2s.runtime.values.primitives.SymbolValue;
|
||||
import me.topchetoeu.j2s.runtime.values.primitives.UserValue;
|
||||
import me.topchetoeu.j2s.runtime.values.primitives.VoidValue;
|
||||
import me.topchetoeu.j2s.runtime.values.primitives.numbers.NumberValue;
|
||||
|
||||
public class SimpleRepl {
|
||||
public static final Compiler DEFAULT_COMPILER = (env, filename, raw, mapper) -> {
|
||||
try {
|
||||
var res = JavaScript.compile(env, filename, raw, true);
|
||||
var body = res.body();
|
||||
|
||||
DebugContext.get(env).onSource(filename, raw);
|
||||
for (var el : res.all()) {
|
||||
DebugContext.get(env).onFunctionLoad(el.body(), el.map(mapper));
|
||||
}
|
||||
|
||||
return new CodeFunction(env, filename.toString(), body, new Value[0][]);
|
||||
}
|
||||
catch (SyntaxException e) {
|
||||
var res = EngineException.ofSyntax(e.msg);
|
||||
res.add(env, e.loc.filename() + "", e.loc);
|
||||
throw res;
|
||||
}
|
||||
};
|
||||
|
||||
static Thread engineTask, debugTask;
|
||||
static Engine engine = new Engine();
|
||||
static Environment environment = Environment.empty(), tsEnvironment;
|
||||
static Environment environment = Environment.empty();
|
||||
static DebugServer server;
|
||||
static Debugger debugger;
|
||||
static Key<OutputStream> STDOUT = new Key<>();
|
||||
@@ -89,28 +44,12 @@ public class SimpleRepl {
|
||||
|
||||
private static void reader() {
|
||||
try {
|
||||
try {
|
||||
environment = createESEnv();
|
||||
tsEnvironment = createESEnv();
|
||||
}
|
||||
catch (ExecutionException e) { throw e.getCause(); }
|
||||
|
||||
server = new DebugServer();
|
||||
debugTask = server.start(new InetSocketAddress("127.0.0.1", 9229), true);
|
||||
server.targets.put("default", (socket, req) -> new SimpleDebugger(socket)
|
||||
.attach(DebugContext.get(environment))
|
||||
.attach(DebugContext.get(tsEnvironment))
|
||||
.attach((SimpleDebugHandler)DebugHandler.get(environment))
|
||||
);
|
||||
|
||||
try {
|
||||
try { initGlobals(); } catch (ExecutionException e) { throw e.getCause(); }
|
||||
}
|
||||
catch (EngineException | SyntaxException e) {
|
||||
System.err.println("Failed to load stdlib. Falling back to barebones environment...");
|
||||
System.err.println(Value.errorToReadable(environment, e, null));
|
||||
}
|
||||
|
||||
|
||||
System.out.println(String.format("Running %s v%s by %s", Metadata.name(), Metadata.version(), Metadata.author()));
|
||||
|
||||
for (var arg : args) {
|
||||
@@ -164,686 +103,12 @@ public class SimpleRepl {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static ObjectValue mapPrimordials(Environment env) {
|
||||
var res = new ObjectValue();
|
||||
res.setPrototype(null, null);
|
||||
|
||||
var prototype = new ObjectValue[1];
|
||||
NativeFunction mapConstr = new NativeFunction(args -> {
|
||||
var isWeak = args.get(0).toBoolean();
|
||||
return UserValue.of(isWeak ? new WeakHashMap<>() : new LinkedHashMap<>(), prototype[0]);
|
||||
});
|
||||
mapConstr.prototype.defineOwnField(env, "get", new NativeFunction(getArgs -> {
|
||||
var map = getArgs.self(Map.class);
|
||||
var key = getArgs.get(0);
|
||||
var val = map.get(key);
|
||||
return val == null ? Value.UNDEFINED : (Value)val;
|
||||
}));
|
||||
mapConstr.prototype.defineOwnField(env, "set", new NativeFunction(getArgs -> {
|
||||
var map = getArgs.self(Map.class);
|
||||
var key = getArgs.get(0);
|
||||
var val = getArgs.get(1);
|
||||
map.put(key, val);
|
||||
|
||||
return Value.UNDEFINED;
|
||||
}));
|
||||
mapConstr.prototype.defineOwnField(env, "has", new NativeFunction(getArgs -> {
|
||||
var map = getArgs.self(Map.class);
|
||||
var key = getArgs.get(0);
|
||||
return BoolValue.of(map.containsKey(key));
|
||||
}));
|
||||
mapConstr.prototype.defineOwnField(env, "delete", new NativeFunction(getArgs -> {
|
||||
var map = getArgs.self(Map.class);
|
||||
var key = getArgs.get(0);
|
||||
map.remove(key);
|
||||
return Value.UNDEFINED;
|
||||
}));
|
||||
mapConstr.prototype.defineOwnField(env, "keys", new NativeFunction(getArgs -> {
|
||||
var map = getArgs.self(Map.class);
|
||||
return ArrayValue.of(map.keySet());
|
||||
}));
|
||||
mapConstr.prototype.defineOwnField(env, "clear", new NativeFunction(getArgs -> {
|
||||
getArgs.self(Map.class).clear();
|
||||
return Value.UNDEFINED;
|
||||
}));
|
||||
mapConstr.prototype.defineOwnField(env, "size", new NativeFunction(getArgs -> {
|
||||
return NumberValue.of(getArgs.self(Map.class).size());
|
||||
}));
|
||||
prototype[0] = (ObjectValue)mapConstr.prototype;
|
||||
|
||||
return mapConstr;
|
||||
}
|
||||
|
||||
public static String processRegex(String src) {
|
||||
var n = 0;
|
||||
|
||||
var source = new StringBuilder();
|
||||
|
||||
StringBuilder bracesSource = null;
|
||||
StringBuilder bracketsSource = null;
|
||||
|
||||
while (true) {
|
||||
if (n >= src.length()) break;
|
||||
var c = src.charAt(n++);
|
||||
|
||||
if (c == '\\' && n + 1 < src.length() && src.charAt(n) == 'b') {
|
||||
c = '\b';
|
||||
n++;
|
||||
}
|
||||
|
||||
if (bracesSource != null) {
|
||||
var failed = true;
|
||||
|
||||
if (Character.isDigit(c)) {
|
||||
bracesSource.append(c);
|
||||
failed = false;
|
||||
}
|
||||
else if (c == ',' && bracesSource.indexOf(",") < 0) {
|
||||
bracesSource.append(c);
|
||||
failed = false;
|
||||
}
|
||||
else if (c == '}' && bracesSource.length() > 0) {
|
||||
bracesSource.append(c);
|
||||
source.append(bracesSource);
|
||||
bracesSource = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
source.append("\\");
|
||||
source.append(bracesSource);
|
||||
bracesSource = null;
|
||||
n--;
|
||||
}
|
||||
}
|
||||
else if (bracketsSource != null) {
|
||||
if (c == '[') bracketsSource.append("\\[");
|
||||
else if (c == ']') {
|
||||
var res = bracketsSource.append(']').toString();
|
||||
bracketsSource = null;
|
||||
if (res.equals("[^]")) res = "[\\s\\S]";
|
||||
else if (res.equals("[]")) res = "[^\\s\\S]";
|
||||
source.append(res);
|
||||
}
|
||||
else if (c == '\\') {
|
||||
if (n >= src.length()) break;
|
||||
bracketsSource.append(c).append(src.charAt(n++));
|
||||
}
|
||||
else bracketsSource.append(c);
|
||||
}
|
||||
else if (c == '\\') {
|
||||
if (n >= src.length()) throw new PatternSyntaxException("Unexpected end", src, n);
|
||||
c = src.charAt(n++);
|
||||
source.append('\\').append(c);
|
||||
}
|
||||
else if (c == '[') {
|
||||
bracketsSource = new StringBuilder("[");
|
||||
}
|
||||
else if (c == '{' && bracketsSource == null) {
|
||||
bracesSource = new StringBuilder("{");
|
||||
}
|
||||
else source.append(c);
|
||||
}
|
||||
|
||||
if (bracesSource != null) {
|
||||
source.append("\\");
|
||||
source.append(bracesSource);
|
||||
}
|
||||
if (bracketsSource != null) throw new PatternSyntaxException("Unmatched '['", src, n - bracketsSource.length());
|
||||
|
||||
return source.toString();
|
||||
}
|
||||
|
||||
private static ObjectValue regexPrimordials(Environment env) {
|
||||
var res = new ObjectValue();
|
||||
res.setPrototype(null, null);
|
||||
|
||||
var prototype = new ObjectValue[1];
|
||||
NativeFunction mapConstr = new NativeFunction(args -> {
|
||||
var flags = 0;
|
||||
if (args.get(1).toBoolean()) flags |= Pattern.MULTILINE;
|
||||
if (args.get(2).toBoolean()) flags |= Pattern.CASE_INSENSITIVE;
|
||||
if (args.get(3).toBoolean()) flags |= Pattern.DOTALL;
|
||||
if (args.get(4).toBoolean()) flags |= Pattern.UNICODE_CASE | Pattern.CANON_EQ;
|
||||
if (args.get(5).toBoolean()) flags |= Pattern.UNICODE_CHARACTER_CLASS;
|
||||
try {
|
||||
var pattern = Pattern.compile(processRegex(args.get(0).toString(args.env)), flags);
|
||||
return UserValue.of(pattern, prototype[0]);
|
||||
}
|
||||
catch (PatternSyntaxException e) {
|
||||
throw EngineException.ofSyntax("(regex):" + e.getIndex() + ": " + e.getDescription());
|
||||
}
|
||||
});
|
||||
mapConstr.prototype.defineOwnField(env, "exec", new NativeFunction(args -> {
|
||||
var pattern = args.self(Pattern.class);
|
||||
var target = args.get(0).toString(args.env);
|
||||
var offset = args.get(1).toNumber(args.env).getInt();
|
||||
var index = args.get(2).toBoolean();
|
||||
|
||||
if (offset > target.length()) return Value.NULL;
|
||||
|
||||
var matcher = pattern.matcher(target).region(offset, target.length());
|
||||
if (!matcher.find()) return Value.NULL;
|
||||
|
||||
var matchesArr = new ArrayValue(matcher.groupCount() + 1);
|
||||
for (var i = 0; i < matcher.groupCount() + 1; i++) {
|
||||
var group = matcher.group(i);
|
||||
if (group == null) continue;
|
||||
matchesArr.set(args.env, i, StringValue.of(group));
|
||||
}
|
||||
|
||||
matchesArr.defineOwnField(args.env, "index", NumberValue.of(matcher.start()));
|
||||
matchesArr.defineOwnField(args.env, "input", StringValue.of(target));
|
||||
if (index) {
|
||||
var indices = new ArrayValue();
|
||||
indices.setPrototype(args.env, null);
|
||||
for (var i = 0; i < matcher.groupCount(); i++) {
|
||||
matchesArr.set(args.env, i, ArrayValue.of(Arrays.asList(
|
||||
NumberValue.of(matcher.start(i)),
|
||||
NumberValue.of(matcher.end(i))
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
var obj = new ObjectValue();
|
||||
obj.defineOwnField(args.env, "matches", matchesArr);
|
||||
obj.defineOwnField(args.env, "end", NumberValue.of(matcher.end()));
|
||||
|
||||
return obj;
|
||||
// return val == null ? Value.UNDEFINED : (Value)val;
|
||||
}));
|
||||
mapConstr.prototype.defineOwnField(env, "groupCount", new NativeFunction(args -> {
|
||||
var pattern = args.self(Pattern.class);
|
||||
return NumberValue.of(pattern.matcher("").groupCount());
|
||||
}));
|
||||
prototype[0] = (ObjectValue)mapConstr.prototype;
|
||||
|
||||
return mapConstr;
|
||||
}
|
||||
|
||||
private static ObjectValue symbolPrimordials(Environment env) {
|
||||
var res = new ObjectValue();
|
||||
res.setPrototype(null, null);
|
||||
|
||||
res.defineOwnField(env, "makeSymbol", new NativeFunction(args -> new SymbolValue(args.get(0).toString(args.env))));
|
||||
res.defineOwnField(env, "getSymbol", new NativeFunction(args -> SymbolValue.get(args.get(0).toString(args.env))));
|
||||
res.defineOwnField(env, "getSymbolKey", new NativeFunction(args -> ((SymbolValue)args.get(0)).key()));
|
||||
res.defineOwnField(env, "getSymbolDescriptor", new NativeFunction(args -> StringValue.of(((SymbolValue)args.get(0)).value)));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private static ObjectValue numberPrimordials(Environment env) {
|
||||
var res = new ObjectValue();
|
||||
res.setPrototype(null, null);
|
||||
|
||||
res.defineOwnField(env, "parseInt", new NativeFunction(args -> {
|
||||
var nradix = args.get(1).toNumber(env);
|
||||
var radix = nradix.isInt() ? nradix.getInt() : 10;
|
||||
|
||||
if (radix != 10 && args.get(0) instanceof NumberValue num) {
|
||||
if (num.isInt()) return num;
|
||||
else return NumberValue.of(num.getDouble() - num.getDouble() % 1);
|
||||
}
|
||||
else {
|
||||
if (radix < 2 || radix > 36) return NumberValue.NAN;
|
||||
|
||||
var str = args.get(0).toString().trim();
|
||||
var numRes = Parsing.parseInt(new Source(str), 0, "0123456789abcdefghijklmnopqrstuvwxyz".substring(0, radix), true);
|
||||
if (numRes.isSuccess()) {
|
||||
if (numRes.n == str.length()) return NumberValue.of(numRes.result);
|
||||
}
|
||||
return NumberValue.NAN;
|
||||
}
|
||||
}));
|
||||
res.defineOwnField(env, "parseFloat", new NativeFunction(args -> {
|
||||
if (args.get(0) instanceof NumberValue) {
|
||||
return args.get(0);
|
||||
}
|
||||
else {
|
||||
var str = args.get(0).toString().trim();
|
||||
var numRes = Parsing.parseFloat(new Source(str), 0, true);
|
||||
if (numRes.isSuccess()) {
|
||||
if (numRes.n == str.length()) return NumberValue.of(numRes.result);
|
||||
}
|
||||
return NumberValue.NAN;
|
||||
}
|
||||
}));
|
||||
res.defineOwnField(env, "isNaN", new NativeFunction(args -> BoolValue.of(args.get(0).isNaN())));
|
||||
|
||||
res.defineOwnField(env, "pow", new NativeFunction(args -> {
|
||||
return NumberValue.of(Math.pow(args.get(0).toNumber(args.env).getDouble(), args.get(1).toNumber(args.env).getDouble()));
|
||||
}));
|
||||
res.defineOwnField(env, "log", new NativeFunction(args -> {
|
||||
return NumberValue.of(Math.log(args.get(0).toNumber(args.env).getDouble()));
|
||||
}));
|
||||
|
||||
res.defineOwnField(env, "NaN", NumberValue.NAN);
|
||||
res.defineOwnField(env, "Infinity", NumberValue.of(Double.POSITIVE_INFINITY));
|
||||
res.defineOwnField(env, "PI", NumberValue.of(Math.PI));
|
||||
res.defineOwnField(env, "E", NumberValue.of(Math.E));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private static ObjectValue stringPrimordials(Environment env) {
|
||||
var res = new ObjectValue();
|
||||
res.setPrototype(null, null);
|
||||
|
||||
res.defineOwnField(env, "stringBuild", new NativeFunction(args -> {
|
||||
var parts = ((ArrayValue)args.get(0)).toArray();
|
||||
var sb = new StringBuilder();
|
||||
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
sb.append(((StringValue)parts[i]).value);
|
||||
}
|
||||
|
||||
return StringValue.of(sb.toString());
|
||||
}));
|
||||
|
||||
res.defineOwnField(env, "fromCharCode", new NativeFunction(args -> {
|
||||
return StringValue.of(new String(new char[] { (char)args.get(0).toNumber(args.env).getInt() }));
|
||||
}));
|
||||
|
||||
res.defineOwnField(env, "toCharCode", new NativeFunction(args -> {
|
||||
return NumberValue.of(args.get(0).toString(args.env).charAt(0));
|
||||
}));
|
||||
res.defineOwnField(env, "toCodePoint", new NativeFunction(args -> {
|
||||
return NumberValue.of(args.get(0).toString(args.env).codePointAt(args.get(1).toNumber(args.env).getInt()));
|
||||
}));
|
||||
|
||||
res.defineOwnField(env, "substring", new NativeFunction(args -> {
|
||||
var str = args.get(0).toString(args.env);
|
||||
var start = args.get(1).toNumber(args.env).getInt();
|
||||
var end = args.get(2).toNumber(args.env).getInt();
|
||||
|
||||
if (end <= start) return StringValue.of("");
|
||||
|
||||
start = Math.max(Math.min(start, str.length()), 0);
|
||||
end = Math.max(Math.min(end, str.length()), 0);
|
||||
|
||||
return StringValue.of(str.substring(start, end));
|
||||
}));
|
||||
|
||||
res.defineOwnField(env, "indexOf", new NativeFunction(args -> {
|
||||
var str = args.get(0).toString(args.env);
|
||||
var search = args.get(1).toString(args.env);
|
||||
var start = args.get(2).toNumber(args.env).getInt();
|
||||
if (start > str.length()) return NumberValue.of(-1);
|
||||
var reverse = args.get(3).toBoolean();
|
||||
|
||||
if (reverse) return NumberValue.of(str.lastIndexOf(search, start));
|
||||
else return NumberValue.of(str.indexOf(search, start));
|
||||
}));
|
||||
|
||||
res.defineOwnField(env, "lower", new NativeFunction(args -> {
|
||||
return StringValue.of(args.get(0).toString(args.env).toLowerCase());
|
||||
}));
|
||||
res.defineOwnField(env, "upper", new NativeFunction(args -> {
|
||||
return StringValue.of(args.get(0).toString(args.env).toUpperCase());
|
||||
}));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private static ObjectValue objectPrimordials(Environment env) {
|
||||
var res = new ObjectValue();
|
||||
res.setPrototype(null, null);
|
||||
|
||||
res.defineOwnField(env, "defineField", new NativeFunction(args -> {
|
||||
var obj = (ObjectValue)args.get(0);
|
||||
var key = args.get(1);
|
||||
var desc = (ObjectValue)args.get(2);
|
||||
|
||||
var valField = desc.getOwnMember(env, "v");
|
||||
var writeField = desc.getOwnMember(env, "w");
|
||||
var configField = desc.getOwnMember(env, "c");
|
||||
var enumField = desc.getOwnMember(env, "e");
|
||||
|
||||
var enumerable = enumField == null ? null : enumField.get(env, desc).toBoolean();
|
||||
var configurable = configField == null ? null : configField.get(env, desc).toBoolean();
|
||||
var writable = writeField == null ? null : writeField.get(env, desc).toBoolean();
|
||||
var value = valField == null ? null : valField.get(env, desc);
|
||||
|
||||
return BoolValue.of(obj.defineOwnField(args.env, key, value, configurable, enumerable, writable));
|
||||
}));
|
||||
res.defineOwnField(env, "defineProperty", new NativeFunction(args -> {
|
||||
var obj = (ObjectValue)args.get(0);
|
||||
var key = args.get(1);
|
||||
var desc = args.get(2);
|
||||
|
||||
var configField = desc.getOwnMember(env, "c");
|
||||
var enumField = desc.getOwnMember(env, "e");
|
||||
var getField = desc.getOwnMember(env, "g");
|
||||
var setField = desc.getOwnMember(env, "s");
|
||||
|
||||
var enumerable = enumField == null ? null : enumField.get(env, desc).toBoolean();
|
||||
var configurable = configField == null ? null : configField.get(env, desc).toBoolean();
|
||||
Optional<FunctionValue> getter = null, setter = null;
|
||||
|
||||
if (getField != null) {
|
||||
var getVal = getField.get(env, desc);
|
||||
if (getVal == Value.UNDEFINED) getter = Optional.empty();
|
||||
else getter = Optional.of((FunctionValue)getVal);
|
||||
}
|
||||
if (setField != null) {
|
||||
var setVal = setField.get(env, desc);
|
||||
if (setVal == Value.UNDEFINED) setter = Optional.empty();
|
||||
else setter = Optional.of((FunctionValue)setVal);
|
||||
}
|
||||
|
||||
return BoolValue.of(obj.defineOwnProperty(args.env, key, getter, setter, configurable, enumerable));
|
||||
}));
|
||||
res.defineOwnField(env, "getPrototype", new NativeFunction(args -> {
|
||||
var proto = args.get(0).getPrototype(env);
|
||||
if (proto == null) return Value.NULL;
|
||||
else return proto;
|
||||
}));
|
||||
res.defineOwnField(env, "setPrototype", new NativeFunction(args -> {
|
||||
var proto = args.get(1) instanceof VoidValue ? null : (ObjectValue)args.get(1);
|
||||
args.get(0).setPrototype(env, proto);
|
||||
return args.get(0);
|
||||
}));
|
||||
res.defineOwnField(env, "getMembers", new NativeFunction(args -> {
|
||||
var val = new ArrayValue();
|
||||
|
||||
for (var key : args.get(0).getMembers(env, args.get(1).toBoolean(), args.get(2).toBoolean())) {
|
||||
val.set(args.env, val.size(), StringValue.of(key));
|
||||
}
|
||||
|
||||
return val;
|
||||
}));
|
||||
res.defineOwnField(env, "getSymbolMembers", new NativeFunction(args -> {
|
||||
return ArrayValue.of(args.get(0).getSymbolMembers(env, args.get(1).toBoolean(), args.get(2).toBoolean()));
|
||||
}));
|
||||
res.defineOwnField(env, "getOwnMember", new NativeFunction(args -> {
|
||||
var obj = args.get(0);
|
||||
var key = args.get(1);
|
||||
|
||||
var member = obj.getOwnMember(args.env, key);
|
||||
if (member == null) return Value.UNDEFINED;
|
||||
else return member.descriptor(args.env, obj);
|
||||
}));
|
||||
res.defineOwnField(env, "preventExt", new NativeFunction(args -> {
|
||||
args.get(0).preventExtensions();
|
||||
return VoidValue.UNDEFINED;
|
||||
}));
|
||||
res.defineOwnField(env, "seal", new NativeFunction(args -> {
|
||||
args.get(0).seal();
|
||||
return VoidValue.UNDEFINED;
|
||||
}));
|
||||
res.defineOwnField(env, "freeze", new NativeFunction(args -> {
|
||||
args.get(0).freeze();
|
||||
return VoidValue.UNDEFINED;
|
||||
}));
|
||||
res.defineOwnField(env, "memcpy", new NativeFunction(args -> {
|
||||
var src = (ArrayValue)args.get(0);
|
||||
var dst = (ArrayValue)args.get(1);
|
||||
var srcI = args.get(2).toNumber(args.env).getInt();
|
||||
var dstI = args.get(3).toNumber(args.env).getInt();
|
||||
var n = args.get(4).toNumber(args.env).getInt();
|
||||
|
||||
src.copyTo(dst, srcI, dstI, n);
|
||||
|
||||
return VoidValue.UNDEFINED;
|
||||
}));
|
||||
res.defineOwnField(env, "sort", new NativeFunction(args -> {
|
||||
var arr = (ArrayValue)args.get(0);
|
||||
var func = (FunctionValue)args.get(1);
|
||||
|
||||
arr.sort((a, b) -> {
|
||||
return func.apply(args.env, Value.UNDEFINED, a, b).toNumber(args.env).getInt();
|
||||
});
|
||||
|
||||
return arr;
|
||||
}));
|
||||
|
||||
res.defineOwnField(env, "isArray", new NativeFunction(args -> {
|
||||
return BoolValue.of(args.get(0) instanceof ArrayLikeValue);
|
||||
}));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private static ObjectValue bufferPrimordials(Environment env) {
|
||||
var buffProto = new ObjectValue();
|
||||
buffProto.defineOwnProperty(env, "length", Optional.of(new NativeFunction(args -> {
|
||||
return NumberValue.of(args.self(byte[].class).length);
|
||||
})), Optional.empty(), false, true);
|
||||
|
||||
var res = new ObjectValue();
|
||||
res.setPrototype(null, null);
|
||||
|
||||
res.defineOwnField(env, "buff", new NativeFunction(args -> {
|
||||
var size = args.get(0).toNumber(env).getInt();
|
||||
return TypedArrayValue.buffer(new byte[size], buffProto);
|
||||
}));
|
||||
|
||||
res.defineOwnField(env, "uint8", new NativeFunction(args -> {
|
||||
var buff = args.get(byte[].class, 0);
|
||||
var start = args.get(1).toNumber(env).getInt();
|
||||
var end = args.get(2).toNumber(env).getInt();
|
||||
return new Uint8ArrayValue(buff, start, end);
|
||||
}));
|
||||
res.defineOwnField(env, "int8", new NativeFunction(args -> {
|
||||
var buff = args.get(byte[].class, 0);
|
||||
var start = args.get(1).toNumber(env).getInt();
|
||||
var end = args.get(2).toNumber(env).getInt();
|
||||
return new Int8ArrayValue(buff, start, end);
|
||||
}));
|
||||
res.defineOwnField(env, "int32", new NativeFunction(args -> {
|
||||
var buff = args.get(byte[].class, 0);
|
||||
var start = args.get(1).toNumber(env).getInt();
|
||||
var end = args.get(2).toNumber(env).getInt();
|
||||
return new Int32ArrayValue(buff, start, end);
|
||||
}));
|
||||
|
||||
res.defineOwnField(env, "isUint8", new NativeFunction(args -> {
|
||||
return BoolValue.of(args.get(0) instanceof Uint8ArrayValue);
|
||||
}));
|
||||
res.defineOwnField(env, "isInt8", new NativeFunction(args -> {
|
||||
return BoolValue.of(args.get(0) instanceof Int8ArrayValue);
|
||||
}));
|
||||
res.defineOwnField(env, "isInt32", new NativeFunction(args -> {
|
||||
return BoolValue.of(args.get(0) instanceof Int32ArrayValue);
|
||||
}));
|
||||
|
||||
res.defineOwnField(env, "is", new NativeFunction(args -> {
|
||||
return BoolValue.of(args.get(0) instanceof TypedArrayValue);
|
||||
}));
|
||||
res.defineOwnField(env, "isBuff", new NativeFunction(args -> {
|
||||
return BoolValue.of(args.get(byte[].class, 0) != null);
|
||||
}));
|
||||
|
||||
res.defineOwnField(env, "backer", new NativeFunction(args -> {
|
||||
return TypedArrayValue.buffer(((TypedArrayValue)args.get(0)).buffer, buffProto);
|
||||
}));
|
||||
res.defineOwnField(env, "start", new NativeFunction(args -> {
|
||||
return NumberValue.of(((TypedArrayValue)args.get(0)).start);
|
||||
}));
|
||||
res.defineOwnField(env, "end", new NativeFunction(args -> {
|
||||
return NumberValue.of(((TypedArrayValue)args.get(0)).end);
|
||||
}));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private static ObjectValue functionPrimordials(Environment env) {
|
||||
var res = new ObjectValue();
|
||||
res.setPrototype(null, null);
|
||||
|
||||
res.defineOwnField(env, "setCallable", new NativeFunction(args -> {
|
||||
var func = (FunctionValue)args.get(0);
|
||||
func.enableApply = args.get(1).toBoolean();
|
||||
return Value.UNDEFINED;
|
||||
}));
|
||||
res.defineOwnField(env, "setConstructable", new NativeFunction(args -> {
|
||||
var func = (FunctionValue)args.get(0);
|
||||
func.enableConstruct = args.get(1).toBoolean();
|
||||
return Value.UNDEFINED;
|
||||
}));
|
||||
res.defineOwnField(env, "invokeType", new NativeFunction(args -> {
|
||||
if (((ArgumentsValue)args.get(0)).frame.isNew) return StringValue.of("new");
|
||||
else return StringValue.of("call");
|
||||
}));
|
||||
res.defineOwnField(env, "invokeTypeInfer", new NativeFunction(args -> {
|
||||
var frame = Frame.get(args.env, args.get(0).toNumber(args.env).getInt());
|
||||
if (frame.isNew) return StringValue.of("new");
|
||||
else return StringValue.of("call");
|
||||
}));
|
||||
res.defineOwnField(env, "target", new NativeFunction(args -> {
|
||||
var frame = Frame.get(args.env, args.get(0).toNumber(args.env).getInt());
|
||||
if (frame.target == null) return Value.UNDEFINED;
|
||||
else return frame.target;
|
||||
}));
|
||||
|
||||
res.defineOwnField(env, "invoke", new NativeFunction(args -> {
|
||||
var func = (FunctionValue)args.get(0);
|
||||
var self = args.get(1);
|
||||
var funcArgs = (ArrayLikeValue)args.get(2);
|
||||
|
||||
return func.apply(env, self, funcArgs.toArray());
|
||||
}));
|
||||
res.defineOwnField(env, "construct", new NativeFunction(args -> {
|
||||
var func = (FunctionValue)args.get(0);
|
||||
var target = args.get(1);
|
||||
var funcArgs = (ArrayLikeValue)args.get(2);
|
||||
|
||||
if (target == Value.UNDEFINED) return func.constructNoSelf(env, funcArgs.toArray());
|
||||
else return func.construct(env, target, funcArgs.toArray());
|
||||
}));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private static ObjectValue jsonPrimordials(Environment env) {
|
||||
var res = new ObjectValue();
|
||||
res.setPrototype(null, null);
|
||||
|
||||
res.defineOwnField(env, "stringify", new NativeFunction(args -> {
|
||||
return StringValue.of(JSON.stringify(JSONConverter.fromJs(env, args.get(0))));
|
||||
}));
|
||||
res.defineOwnField(env, "parse", new NativeFunction(args -> {
|
||||
try {
|
||||
return JSONConverter.toJs(JSON.parse(null, args.get(0).toString(env)));
|
||||
}
|
||||
catch (SyntaxException e) {
|
||||
throw EngineException.ofSyntax(e.msg).add(env, e.loc.filename() + "", e.loc);
|
||||
}
|
||||
}));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private static void setProto(Environment env, Environment target, Key<ObjectValue> key, ObjectValue repo, String name) {
|
||||
var val = repo.getMember(env, name);
|
||||
if (val instanceof ObjectValue obj) {
|
||||
target.add(key, obj);
|
||||
}
|
||||
}
|
||||
|
||||
private static ObjectValue primordials(Environment env) {
|
||||
var res = new ObjectValue();
|
||||
res.setPrototype(null, null);
|
||||
|
||||
res.defineOwnField(env, "symbol", symbolPrimordials(env));
|
||||
res.defineOwnField(env, "number", numberPrimordials(env));
|
||||
res.defineOwnField(env, "string", stringPrimordials(env));
|
||||
res.defineOwnField(env, "object", objectPrimordials(env));
|
||||
res.defineOwnField(env, "buffer", bufferPrimordials(env));
|
||||
res.defineOwnField(env, "function", functionPrimordials(env));
|
||||
res.defineOwnField(env, "json", jsonPrimordials(env));
|
||||
res.defineOwnField(env, "map", mapPrimordials(env));
|
||||
res.defineOwnField(env, "regex", regexPrimordials(env));
|
||||
|
||||
int[] i = new int[1];
|
||||
|
||||
res.defineOwnField(env, "setGlobalPrototypes", new NativeFunction(args -> {
|
||||
var obj = (ObjectValue)args.get(0);
|
||||
|
||||
setProto(args.env, env, Value.OBJECT_PROTO, obj, "object");
|
||||
setProto(args.env, env, Value.FUNCTION_PROTO, obj, "function");
|
||||
setProto(args.env, env, Value.ARRAY_PROTO, obj, "array");
|
||||
setProto(args.env, env, Value.BOOL_PROTO, obj, "boolean");
|
||||
setProto(args.env, env, Value.NUMBER_PROTO, obj, "number");
|
||||
setProto(args.env, env, Value.STRING_PROTO, obj, "string");
|
||||
setProto(args.env, env, Value.SYMBOL_PROTO, obj, "symbol");
|
||||
setProto(args.env, env, Value.ERROR_PROTO, obj, "error");
|
||||
setProto(args.env, env, Value.SYNTAX_ERR_PROTO, obj, "syntax");
|
||||
setProto(args.env, env, Value.TYPE_ERR_PROTO, obj, "type");
|
||||
setProto(args.env, env, Value.RANGE_ERR_PROTO, obj, "range");
|
||||
setProto(args.env, env, Value.UINT8_ARR_PROTO, obj, "uint8");
|
||||
setProto(args.env, env, Value.INT32_ARR_PROTO, obj, "int32");
|
||||
return Value.UNDEFINED;
|
||||
}));
|
||||
res.defineOwnField(env, "setIntrinsic", new NativeFunction(args -> {
|
||||
var name = args.get(0).toString(env);
|
||||
var val = args.get(1);
|
||||
|
||||
Value.intrinsics(env).put(name, val);
|
||||
|
||||
return Value.UNDEFINED;
|
||||
}));
|
||||
res.defineOwnField(env, "compile", new NativeFunction(args -> {
|
||||
return Compiler.compileFunc(env, new Filename(Metadata.name(), "func" + i[0]++ + ".js"), args.get(0).toString(env));
|
||||
}));
|
||||
res.defineOwnField(env, "now", new NativeFunction(args -> {
|
||||
return NumberValue.of(System.currentTimeMillis());
|
||||
}));
|
||||
res.defineOwnField(env, "next", new NativeFunction(args -> {
|
||||
var func = (FunctionValue)args.get(0);
|
||||
EventLoop.get(env).pushMsg(() -> {
|
||||
func.apply(env, Value.UNDEFINED);
|
||||
}, true);
|
||||
return Value.UNDEFINED;
|
||||
}));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private static Environment createESEnv() throws InterruptedException, ExecutionException {
|
||||
var env = initEnv();
|
||||
var stubEnv = initEnv();
|
||||
Value.global(stubEnv).defineOwnField(stubEnv, "target", Value.global(env));
|
||||
Value.global(stubEnv).defineOwnField(stubEnv, "primordials", primordials(env));
|
||||
|
||||
EventLoop.get(stubEnv).pushMsg(
|
||||
false, stubEnv,
|
||||
new Filename(Metadata.name(), "init.js"), Reading.resourceToString("lib/stdlib.js"),
|
||||
Value.UNDEFINED
|
||||
).get();
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
private static Compiler wrap(Compiler first, Environment compilerEnv, Environment targetEnv, FunctionValue factory) {
|
||||
var curr = new NativeFunction(args -> {
|
||||
var filename = Filename.parse(args.get(0).toString(args.env));
|
||||
var src = args.get(1).toString(args.env);
|
||||
var mapper = (FunctionValue)args.get(2);
|
||||
return first.compile(targetEnv, filename, src, NativeMapper.unwrap(args.env, mapper));
|
||||
});
|
||||
|
||||
var next = (FunctionValue)factory.apply(compilerEnv, Value.UNDEFINED, curr);
|
||||
|
||||
return (env, filename, source, map) -> {
|
||||
return (FunctionValue)next.apply(
|
||||
compilerEnv, Value.UNDEFINED,
|
||||
StringValue.of(filename.toString()),
|
||||
StringValue.of(source),
|
||||
new NativeMapper(map)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private static Environment initEnv() {
|
||||
private static Environment createESEnv() {
|
||||
var env = new Environment();
|
||||
env.add(EventLoop.KEY, engine);
|
||||
env.add(DebugContext.KEY, new DebugContext());
|
||||
env.add(Compiler.KEY, DEFAULT_COMPILER);
|
||||
// env.add(CompileResult.DEBUG_LOG);
|
||||
env.add(DebugHandler.KEY, new SimpleDebugHandler());
|
||||
env.add(Compiler.KEY, Compilers.chainTranspilers(environment, Compilers.jsCompiler(), Compilers::babelCompiler, Compilers::coffeescriptCompiler));
|
||||
StdLib.apply(env);
|
||||
|
||||
var glob = Value.global(env);
|
||||
|
||||
@@ -851,76 +116,19 @@ public class SimpleRepl {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new CancellationException();
|
||||
}));
|
||||
glob.defineOwnField(null, "print", new NativeFunction("print", args -> {
|
||||
for (var el : args.args) {
|
||||
if (el instanceof StringValue) System.out.print(((StringValue)el).value + " \t");
|
||||
else System.out.print(el.toReadable(args.env) + " \t");
|
||||
}
|
||||
System.out.println();
|
||||
|
||||
return Value.UNDEFINED;
|
||||
}));
|
||||
glob.defineOwnField(null, "measure", new NativeFunction("measure", args -> {
|
||||
var start = System.nanoTime();
|
||||
|
||||
((FunctionValue)args.get(0)).apply(args.env, Value.UNDEFINED);
|
||||
|
||||
System.out.println(String.format("Finished in %sms", (System.nanoTime() - start) / 1000000.));
|
||||
|
||||
return Value.UNDEFINED;
|
||||
}));
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
private static void initEngine() {
|
||||
engineTask = engine.start();
|
||||
}
|
||||
private static void initGlobals() throws InterruptedException, ExecutionException {
|
||||
var res = new FunctionValue[1];
|
||||
var setter = new NativeFunction(args -> {
|
||||
res[0] = (FunctionValue)args.get(0);
|
||||
return Value.UNDEFINED;
|
||||
});
|
||||
|
||||
var tsGlob = Value.global(tsEnvironment);
|
||||
var tsCompilerFactory = new FunctionValue[1];
|
||||
|
||||
tsGlob.defineOwnField(tsEnvironment, "getResource", new NativeFunction(args -> {
|
||||
var name = args.get(0).toString(args.env);
|
||||
var src = Reading.resourceToString("lib/" + name);
|
||||
|
||||
if (src == null) return Value.UNDEFINED;
|
||||
else return StringValue.of(src);
|
||||
}));
|
||||
tsGlob.defineOwnField(tsEnvironment, "register", new NativeFunction(args -> {
|
||||
var func = (FunctionValue)args.get(0);
|
||||
tsCompilerFactory[0] = func;
|
||||
return Value.UNDEFINED;
|
||||
}));
|
||||
tsGlob.defineOwnField(tsEnvironment, "registerSource", new NativeFunction(args -> {
|
||||
var filename = Filename.parse(args.get(0).toString(args.env));
|
||||
var src = args.get(1).toString(args.env);
|
||||
DebugContext.get(environment).onSource(filename, src);
|
||||
return Value.UNDEFINED;
|
||||
}));
|
||||
|
||||
var ts = Reading.resourceToString("lib/transpiler.js");
|
||||
if (ts != null) EventLoop.get(tsEnvironment).pushMsg(
|
||||
false, tsEnvironment,
|
||||
new Filename(Metadata.name(), "transpiler.js"), ts,
|
||||
Value.UNDEFINED, setter
|
||||
).get();
|
||||
|
||||
var tsCompiler = wrap(Compiler.get(environment), tsEnvironment, environment, tsCompilerFactory[0]);
|
||||
environment.add(Compiler.KEY, tsCompiler);
|
||||
}
|
||||
|
||||
|
||||
public static void main(String args[]) throws InterruptedException {
|
||||
SimpleRepl.args = args;
|
||||
var reader = new Thread(SimpleRepl::reader);
|
||||
|
||||
environment = initEnv();
|
||||
environment = createESEnv();
|
||||
|
||||
initEngine();
|
||||
|
||||
|
||||
@@ -1,24 +1,19 @@
|
||||
package me.topchetoeu.j2s.runtime.debug;
|
||||
package me.topchetoeu.j2s.repl.debug;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import me.topchetoeu.j2s.common.Environment;
|
||||
import me.topchetoeu.j2s.common.Filename;
|
||||
import me.topchetoeu.j2s.common.Instruction;
|
||||
import me.topchetoeu.j2s.common.FunctionBody;
|
||||
import me.topchetoeu.j2s.common.FunctionMap;
|
||||
import me.topchetoeu.j2s.common.Instruction;
|
||||
import me.topchetoeu.j2s.common.Key;
|
||||
import me.topchetoeu.j2s.runtime.Frame;
|
||||
import me.topchetoeu.j2s.runtime.debug.DebugHandler;
|
||||
import me.topchetoeu.j2s.runtime.exceptions.EngineException;
|
||||
import me.topchetoeu.j2s.runtime.values.Value;
|
||||
import me.topchetoeu.j2s.runtime.values.functions.CodeFunction;
|
||||
import me.topchetoeu.j2s.runtime.values.functions.FunctionValue;
|
||||
|
||||
public class DebugContext {
|
||||
public static final Key<DebugContext> KEY = new Key<>();
|
||||
public static final Key<Void> IGNORE = new Key<>();
|
||||
|
||||
public class SimpleDebugHandler implements DebugHandler {
|
||||
private HashMap<Filename, String> sources;
|
||||
private WeakHashMap<FunctionBody, FunctionMap> maps;
|
||||
private DebugHandler debugger;
|
||||
@@ -49,24 +44,10 @@ public class DebugContext {
|
||||
return debugger;
|
||||
}
|
||||
|
||||
public FunctionMap getMap(FunctionBody func) {
|
||||
public FunctionMap getMap(Environment env, FunctionBody func) {
|
||||
if (maps == null) return null;
|
||||
return maps.get(func);
|
||||
}
|
||||
public FunctionMap getMap(FunctionValue func) {
|
||||
if (maps == null || !(func instanceof CodeFunction)) return null;
|
||||
return getMap(((CodeFunction)func).body);
|
||||
}
|
||||
public FunctionMap getMapOrEmpty(FunctionBody func) {
|
||||
if (maps == null) return FunctionMap.EMPTY;
|
||||
var res = maps.get(func);
|
||||
if (res == null) return FunctionMap.EMPTY;
|
||||
else return res;
|
||||
}
|
||||
public FunctionMap getMapOrEmpty(FunctionValue func) {
|
||||
if (maps == null || !(func instanceof CodeFunction)) return FunctionMap.EMPTY;
|
||||
return getMapOrEmpty(((CodeFunction)func).body);
|
||||
}
|
||||
|
||||
public void onFramePop(Environment env, Frame frame) {
|
||||
if (debugger != null) debugger.onFramePop(env, frame);
|
||||
@@ -75,39 +56,27 @@ public class DebugContext {
|
||||
if (debugger != null) debugger.onFramePush(env, frame);
|
||||
}
|
||||
|
||||
public boolean onInstruction(Environment env, Frame frame, Instruction instruction, Value returnVal, EngineException error, boolean caught) {
|
||||
@Override public boolean onInstruction(Environment env, Frame frame, Instruction instruction, Value returnVal, EngineException error, boolean caught) {
|
||||
if (debugger != null) return debugger.onInstruction(env, frame, instruction, returnVal, error, caught);
|
||||
else return false;
|
||||
}
|
||||
public boolean onInstruction(Environment env, Frame frame, Instruction instruction) {
|
||||
if (debugger != null) return debugger.onInstruction(env, frame, instruction, null, null, false);
|
||||
else return false;
|
||||
}
|
||||
public void onSource(Filename filename, String source) {
|
||||
@Override public void onSourceLoad(Filename filename, String source) {
|
||||
if (debugger != null) debugger.onSourceLoad(filename, source);
|
||||
if (sources != null) sources.put(filename, source);
|
||||
}
|
||||
public void onFunctionLoad(FunctionBody func, FunctionMap map) {
|
||||
@Override public void onFunctionLoad(FunctionBody func, FunctionMap map) {
|
||||
if (maps != null) maps.put(func, map);
|
||||
if (debugger != null) debugger.onFunctionLoad(func, map);
|
||||
}
|
||||
|
||||
private DebugContext(boolean enabled) {
|
||||
private SimpleDebugHandler(boolean enabled) {
|
||||
if (enabled) {
|
||||
sources = new HashMap<>();
|
||||
maps = new WeakHashMap<>();
|
||||
}
|
||||
}
|
||||
|
||||
public DebugContext() {
|
||||
public SimpleDebugHandler() {
|
||||
this(true);
|
||||
}
|
||||
|
||||
public static boolean enabled(Environment exts) {
|
||||
return exts != null && exts.hasNotNull(KEY) && !exts.has(IGNORE);
|
||||
}
|
||||
public static DebugContext get(Environment exts) {
|
||||
if (enabled(exts)) return exts.get(KEY);
|
||||
else return new DebugContext(false);
|
||||
}
|
||||
}
|
||||
@@ -24,13 +24,13 @@ import me.topchetoeu.j2s.compilation.json.JSON;
|
||||
import me.topchetoeu.j2s.compilation.json.JSONElement;
|
||||
import me.topchetoeu.j2s.compilation.json.JSONList;
|
||||
import me.topchetoeu.j2s.compilation.json.JSONMap;
|
||||
import me.topchetoeu.j2s.repl.JSONConverter;
|
||||
import me.topchetoeu.j2s.repl.SimpleRepl;
|
||||
import me.topchetoeu.j2s.lib.Compilers;
|
||||
import me.topchetoeu.j2s.lib.JSONConverter;
|
||||
import me.topchetoeu.j2s.runtime.Compiler;
|
||||
import me.topchetoeu.j2s.runtime.Engine;
|
||||
import me.topchetoeu.j2s.runtime.EventLoop;
|
||||
import me.topchetoeu.j2s.runtime.Frame;
|
||||
import me.topchetoeu.j2s.runtime.debug.DebugContext;
|
||||
import me.topchetoeu.j2s.runtime.debug.DebugHandler;
|
||||
import me.topchetoeu.j2s.runtime.exceptions.EngineException;
|
||||
import me.topchetoeu.j2s.runtime.values.Value;
|
||||
import me.topchetoeu.j2s.runtime.values.Member.FieldMember;
|
||||
@@ -201,11 +201,11 @@ public class SimpleDebugger implements Debugger {
|
||||
this.frame = frame;
|
||||
this.id = id;
|
||||
|
||||
var map = DebugContext.get(frame.env).getMapOrEmpty(frame.function);
|
||||
var map = DebugHandler.get(frame.env).getMapOrEmpty(frame.env, frame.function);
|
||||
this.globals = Value.global(frame.env);
|
||||
this.locals = ScopeObject.locals(frame, map.localNames);
|
||||
this.capturables = ScopeObject.capturables(frame, map.capturableNames);
|
||||
this.captures = ScopeObject.captures(frame, map.captureNames);
|
||||
this.locals = ScopeObject.locals(frame, map.localNames());
|
||||
this.capturables = ScopeObject.capturables(frame, map.capturableNames());
|
||||
this.captures = ScopeObject.captures(frame, map.captureNames());
|
||||
if (this.globals instanceof ObjectValue) {
|
||||
this.variables = ScopeObject.combine((ObjectValue)this.globals, locals, capturables, captures);
|
||||
}
|
||||
@@ -249,7 +249,7 @@ public class SimpleDebugger implements Debugger {
|
||||
|
||||
private ObjectValue emptyObject = new ObjectValue();
|
||||
|
||||
private WeakHashMap<DebugContext, DebugContext> contexts = new WeakHashMap<>();
|
||||
private WeakHashMap<SimpleDebugHandler, SimpleDebugHandler> contexts = new WeakHashMap<>();
|
||||
private WeakHashMap<FunctionBody, FunctionMap> mappings = new WeakHashMap<>();
|
||||
private HashMap<Location, HashSet<Breakpoint>> bpLocs = new HashMap<>();
|
||||
|
||||
@@ -617,25 +617,15 @@ public class SimpleDebugger implements Debugger {
|
||||
}
|
||||
}
|
||||
|
||||
// private Environment sanitizeEnvironment(Environment env) {
|
||||
// var res = env.child();
|
||||
|
||||
// res.remove(EventLoop.KEY);
|
||||
// res.remove(DebugContext.KEY);
|
||||
// res.add(DebugContext.IGNORE);
|
||||
|
||||
// return res;
|
||||
// }
|
||||
|
||||
private RunResult run(DebugFrame codeFrame, String code) {
|
||||
if (codeFrame == null) return new RunResult(null, null, EngineException.ofError("Invalid code frame!"));
|
||||
var engine = new Engine();
|
||||
var env = codeFrame.frame.env.child();
|
||||
|
||||
env.remove(DebugContext.KEY);
|
||||
env.remove(DebugHandler.KEY);
|
||||
env.remove(EventLoop.KEY);
|
||||
env.remove(Value.GLOBAL);
|
||||
env.add(Compiler.KEY, SimpleRepl.DEFAULT_COMPILER);
|
||||
env.add(Compiler.KEY, Compilers.jsCompiler());
|
||||
env.add(EventLoop.KEY, engine);
|
||||
env.add(Value.GLOBAL, codeFrame.variables);
|
||||
|
||||
@@ -1076,7 +1066,7 @@ public class SimpleDebugger implements Debugger {
|
||||
}
|
||||
|
||||
private boolean instructionLock;
|
||||
|
||||
|
||||
@Override public boolean onInstruction(Environment env, Frame cf, Instruction instruction, Value returnVal, EngineException error, boolean caught) {
|
||||
if (!enabled) return false;
|
||||
if (instructionLock) return false;
|
||||
@@ -1090,7 +1080,7 @@ public class SimpleDebugger implements Debugger {
|
||||
|
||||
frame = getFrame(cf);
|
||||
|
||||
var map = DebugContext.get(env).getMap(frame.frame.function);
|
||||
var map = DebugHandler.get(env).getMapOrEmpty(env, frame.frame.function);
|
||||
|
||||
frame.updateLoc(map.toLocation(frame.frame.codePtr));
|
||||
loc = frame.location;
|
||||
@@ -1197,7 +1187,11 @@ public class SimpleDebugger implements Debugger {
|
||||
}
|
||||
}
|
||||
|
||||
public SimpleDebugger attach(DebugContext ctx) {
|
||||
@Override public FunctionMap getMap(Environment env, FunctionBody func) {
|
||||
return mappings.get(func);
|
||||
}
|
||||
|
||||
public SimpleDebugger attach(SimpleDebugHandler ctx) {
|
||||
ctx.attachDebugger(this);
|
||||
contexts.put(ctx, ctx);
|
||||
return this;
|
||||
|
||||
@@ -4,15 +4,6 @@ plugins {
|
||||
|
||||
description = "The runtime of J2S, used to execute J2S bytecode";
|
||||
|
||||
tasks.processResources {
|
||||
filesMatching("metadata.json", {
|
||||
expand(
|
||||
"version" to properties["project_version"],
|
||||
"name" to properties["project_name"],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform();
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import java.util.concurrent.CancellationException;
|
||||
import me.topchetoeu.j2s.common.Environment;
|
||||
import me.topchetoeu.j2s.common.Instruction;
|
||||
import me.topchetoeu.j2s.common.Key;
|
||||
import me.topchetoeu.j2s.runtime.debug.DebugContext;
|
||||
import me.topchetoeu.j2s.runtime.debug.DebugHandler;
|
||||
import me.topchetoeu.j2s.runtime.exceptions.EngineException;
|
||||
import me.topchetoeu.j2s.runtime.values.Value;
|
||||
import me.topchetoeu.j2s.runtime.values.functions.CodeFunction;
|
||||
@@ -17,6 +17,9 @@ import me.topchetoeu.j2s.runtime.values.primitives.numbers.IntValue;
|
||||
|
||||
public final class Frame {
|
||||
public static final Key<Stack<Frame>> KEY = new Key<>();
|
||||
public static final Key<Integer> MAX_STACK_COUNT = new Key<>();
|
||||
public static final Key<Boolean> HIDE_STACK = new Key<>();
|
||||
|
||||
public static final EngineException STACK_OVERFLOW;
|
||||
static {
|
||||
STACK_OVERFLOW = EngineException.ofRange("Stack overflow!");
|
||||
@@ -120,7 +123,7 @@ public final class Frame {
|
||||
public final Stack<TryCtx> tryStack = new Stack<>();
|
||||
public final CodeFunction function;
|
||||
public final Environment env;
|
||||
private final DebugContext dbg;
|
||||
private final DebugHandler dbg;
|
||||
|
||||
public Value getVar(int i) {
|
||||
if (i < 0) return captures[~i][0];
|
||||
@@ -206,7 +209,7 @@ public final class Frame {
|
||||
returnValue = InstructionRunner.exec(env, instr, this);
|
||||
}
|
||||
catch (EngineException e) {
|
||||
error = e.add(env, function.name, dbg.getMapOrEmpty(function).toLocation(codePtr, true));
|
||||
error = e.add(env, function.name, dbg.getMapOrEmpty(env, function).toLocation(codePtr));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -214,7 +217,7 @@ public final class Frame {
|
||||
catch (EngineException e) { error = e; }
|
||||
catch (RuntimeException e) {
|
||||
if (!(e instanceof CancellationException)) {
|
||||
System.out.println(dbg.getMapOrEmpty(function).toLocation(codePtr, true));
|
||||
System.out.println(dbg.getMapOrEmpty(env, function).toLocation(codePtr));
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
@@ -352,10 +355,10 @@ public final class Frame {
|
||||
|
||||
public void onPush() {
|
||||
get(env).push(this);
|
||||
DebugContext.get(env).onFramePush(env, this);
|
||||
DebugHandler.get(env).onFramePush(env, this);
|
||||
}
|
||||
public void onPop() {
|
||||
DebugContext.get(env).onFramePop(env, this);
|
||||
DebugHandler.get(env).onFramePop(env, this);
|
||||
get(env).pop();
|
||||
}
|
||||
|
||||
@@ -376,7 +379,7 @@ public final class Frame {
|
||||
|
||||
public Frame(Environment env, boolean isNew, Value target, Value self, Value[] args, CodeFunction func) {
|
||||
this.env = env;
|
||||
this.dbg = DebugContext.get(env);
|
||||
this.dbg = DebugHandler.get(env);
|
||||
this.function = func;
|
||||
this.isNew = isNew;
|
||||
this.target = target;
|
||||
|
||||
@@ -14,6 +14,7 @@ import me.topchetoeu.j2s.runtime.values.primitives.numbers.NumberValue;
|
||||
|
||||
public class InstructionRunner {
|
||||
private static Value execReturn(Environment env, Instruction instr, Frame frame) {
|
||||
frame.codePtr++;
|
||||
return frame.pop();
|
||||
}
|
||||
private static Value execThrow(Environment env, Instruction instr, Frame frame) {
|
||||
@@ -235,18 +236,6 @@ public class InstructionRunner {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Value execTypeof(Environment env, Instruction instr, Frame frame) {
|
||||
String name = instr.get(0);
|
||||
Value obj;
|
||||
|
||||
if (name != null) obj = Value.global(env).getMember(env, name);
|
||||
else obj = frame.pop();
|
||||
|
||||
frame.push(obj.type());
|
||||
|
||||
frame.codePtr++;
|
||||
return null;
|
||||
}
|
||||
private static Value execNop(Environment env, Instruction instr, Frame frame) {
|
||||
frame.codePtr++;
|
||||
return null;
|
||||
@@ -355,6 +344,10 @@ public class InstructionRunner {
|
||||
case INSTANCEOF:
|
||||
res = BoolValue.of(stack[ptr - 1].isInstanceOf(env, stack[ptr].getMember(env, StringValue.of("prototype"))));
|
||||
break;
|
||||
case TYPEOF:
|
||||
res = stack[ptr++].type();
|
||||
frame.stackPtr++;
|
||||
break;
|
||||
|
||||
default: return null;
|
||||
}
|
||||
@@ -481,7 +474,6 @@ public class InstructionRunner {
|
||||
case STORE_MEMBER_INT: return execStoreMemberInt(env, instr, frame);
|
||||
case STORE_VAR: return execStoreVar(env, instr, frame);
|
||||
|
||||
case TYPEOF: return execTypeof(env, instr, frame);
|
||||
case DELETE: return execDelete(env, instr, frame);
|
||||
|
||||
case JMP: return execJmp(env, instr, frame);
|
||||
|
||||
@@ -2,32 +2,47 @@ package me.topchetoeu.j2s.runtime.debug;
|
||||
|
||||
import me.topchetoeu.j2s.common.Environment;
|
||||
import me.topchetoeu.j2s.common.Filename;
|
||||
import me.topchetoeu.j2s.common.Instruction;
|
||||
import me.topchetoeu.j2s.common.Key;
|
||||
import me.topchetoeu.j2s.common.FunctionBody;
|
||||
import me.topchetoeu.j2s.common.FunctionMap;
|
||||
import me.topchetoeu.j2s.common.Instruction;
|
||||
import me.topchetoeu.j2s.runtime.Frame;
|
||||
import me.topchetoeu.j2s.runtime.exceptions.EngineException;
|
||||
import me.topchetoeu.j2s.runtime.values.Value;
|
||||
import me.topchetoeu.j2s.runtime.values.functions.CodeFunction;
|
||||
import me.topchetoeu.j2s.runtime.values.functions.FunctionValue;
|
||||
|
||||
public interface DebugHandler {
|
||||
public static final Key<DebugHandler> KEY = new Key<>();
|
||||
public static final Key<Void> IGNORE = new Key<>();
|
||||
|
||||
public static DebugHandler EMPTY = new DebugHandler() {
|
||||
@Override public void onSourceLoad(Filename filename, String source) { }
|
||||
@Override public void onFunctionLoad(FunctionBody body, FunctionMap map) { }
|
||||
@Override public boolean onInstruction(
|
||||
Environment env, Frame frame, Instruction instruction, Value returnVal, EngineException error, boolean caught
|
||||
) { return false; }
|
||||
@Override public void onFramePush(Environment env, Frame frame) { }
|
||||
@Override public void onFramePop(Environment env, Frame frame) { }
|
||||
@Override public FunctionMap getMap(Environment env, FunctionBody body) { return null; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when a script has been loaded
|
||||
* @param filename The name of the source
|
||||
* @param source The name of the source
|
||||
* @param breakpoints A set of all the breakpointable locations in this source
|
||||
* @param map The source map associated with this file. null if this source map isn't mapped
|
||||
*/
|
||||
void onSourceLoad(Filename filename, String source);
|
||||
public void onSourceLoad(Filename filename, String source);
|
||||
|
||||
/**
|
||||
* Called when a function body has been loaded
|
||||
* @param body The body loaded
|
||||
* @param map The map of the function
|
||||
*/
|
||||
void onFunctionLoad(FunctionBody body, FunctionMap map);
|
||||
public void onFunctionLoad(FunctionBody body, FunctionMap map);
|
||||
|
||||
/**
|
||||
* Called immediatly before an instruction is executed, as well as after an instruction, if it has threw or returned.
|
||||
* Called immediately before an instruction is executed, as well as after an instruction, if it has threw or returned.
|
||||
* This function might pause in order to await debugging commands.
|
||||
* @param env The context of execution
|
||||
* @param frame The frame in which execution is occuring
|
||||
@@ -37,7 +52,19 @@ public interface DebugHandler {
|
||||
* @param caught Whether or not the error has been caught
|
||||
* @return Whether or not the frame should restart (currently does nothing)
|
||||
*/
|
||||
boolean onInstruction(Environment env, Frame frame, Instruction instruction, Value returnVal, EngineException error, boolean caught);
|
||||
public boolean onInstruction(Environment env, Frame frame, Instruction instruction, Value returnVal, EngineException error, boolean caught);
|
||||
|
||||
/**
|
||||
* Called immediately before an instruction is executed, as well as after an instruction, if it has threw or returned.
|
||||
* This function might pause in order to await debugging commands.
|
||||
* @param env The context of execution
|
||||
* @param frame The frame in which execution is occuring
|
||||
* @param instruction The instruction which was or will be executed
|
||||
* @return Whether or not the frame should restart (currently does nothing)
|
||||
*/
|
||||
public default boolean onInstruction(Environment env, Frame frame, Instruction instruction) {
|
||||
return onInstruction(env, frame, instruction, null, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called immediatly before a frame has been pushed on the frame stack.
|
||||
@@ -45,12 +72,36 @@ public interface DebugHandler {
|
||||
* @param env The context of execution
|
||||
* @param frame The code frame which was pushed
|
||||
*/
|
||||
void onFramePush(Environment env, Frame frame);
|
||||
public void onFramePush(Environment env, Frame frame);
|
||||
/**
|
||||
* Called immediatly after a frame has been popped out of the frame stack.
|
||||
* This function might pause in order to await debugging commands.
|
||||
* @param env The context of execution
|
||||
* @param frame The code frame which was popped out
|
||||
*/
|
||||
void onFramePop(Environment env, Frame frame);
|
||||
public void onFramePop(Environment env, Frame frame);
|
||||
|
||||
public FunctionMap getMap(Environment env, FunctionBody func);
|
||||
public default FunctionMap getMap(Environment env, FunctionValue func) {
|
||||
if (func instanceof CodeFunction codeFunc) return getMap(env, codeFunc.body);
|
||||
else return null;
|
||||
}
|
||||
public default FunctionMap getMapOrEmpty(Environment env, FunctionBody func) {
|
||||
var res = getMap(env, func);
|
||||
if (res == null) return FunctionMap.EMPTY;
|
||||
else return res;
|
||||
}
|
||||
public default FunctionMap getMapOrEmpty(Environment env, FunctionValue func) {
|
||||
if (func instanceof CodeFunction codeFunc) return getMapOrEmpty(env, codeFunc.body);
|
||||
else return null;
|
||||
}
|
||||
|
||||
public static DebugHandler get(Environment exts) {
|
||||
if (enabled(exts)) return exts.get(KEY);
|
||||
else return EMPTY;
|
||||
}
|
||||
|
||||
public static boolean enabled(Environment exts) {
|
||||
return exts != null && exts.hasNotNull(KEY) && !exts.has(IGNORE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.util.List;
|
||||
|
||||
import me.topchetoeu.j2s.common.Environment;
|
||||
import me.topchetoeu.j2s.common.Location;
|
||||
import me.topchetoeu.j2s.runtime.Frame;
|
||||
import me.topchetoeu.j2s.runtime.values.Value;
|
||||
import me.topchetoeu.j2s.runtime.values.objects.ObjectValue;
|
||||
import me.topchetoeu.j2s.runtime.values.objects.ObjectValue.PrototypeProvider;
|
||||
@@ -18,7 +19,7 @@ public class EngineException extends RuntimeException {
|
||||
public final Environment ext;
|
||||
|
||||
public boolean visible() {
|
||||
return ext == null || !ext.get(Value.HIDE_STACK, false);
|
||||
return ext == null || !ext.get(Frame.HIDE_STACK, false);
|
||||
}
|
||||
public String toString() {
|
||||
if (name == null && location == null) return "(skipped)";
|
||||
|
||||
@@ -48,9 +48,6 @@ public abstract class Value {
|
||||
}
|
||||
}
|
||||
|
||||
public static final Key<Integer> MAX_STACK_COUNT = new Key<>();
|
||||
public static final Key<Boolean> HIDE_STACK = 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<>();
|
||||
@@ -432,7 +429,6 @@ public abstract class Value {
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
public List<String> toReadableLines(Environment env, HashSet<ObjectValue> passed) {
|
||||
return Arrays.asList(toString(env));
|
||||
}
|
||||
|
||||
@@ -25,12 +25,12 @@ public final class CodeFunction extends FunctionValue {
|
||||
}
|
||||
}
|
||||
|
||||
@Override protected Value onApply(Environment ext, Value self, Value... args) {
|
||||
@Override protected Value onApply(Environment env, Value self, Value... args) {
|
||||
var frame = new Frame(env, false, null, self, args, this);
|
||||
var res = onCall(frame);
|
||||
return res;
|
||||
}
|
||||
@Override protected Value onConstruct(Environment ext, Value target, Value... args) {
|
||||
@Override protected Value onConstruct(Environment env, Value target, Value... args) {
|
||||
var self = new ObjectValue();
|
||||
|
||||
var proto = target.getMember(env, "prototype");
|
||||
|
||||
@@ -6,7 +6,7 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import me.topchetoeu.j2s.common.Environment;
|
||||
import me.topchetoeu.j2s.runtime.debug.DebugContext;
|
||||
import me.topchetoeu.j2s.runtime.debug.DebugHandler;
|
||||
import me.topchetoeu.j2s.runtime.exceptions.EngineException;
|
||||
import me.topchetoeu.j2s.runtime.values.KeyCache;
|
||||
import me.topchetoeu.j2s.runtime.values.Member;
|
||||
@@ -91,9 +91,9 @@ public abstract class FunctionValue extends ObjectValue {
|
||||
@Override public StringValue type() { return StringValue.of("function"); }
|
||||
|
||||
@Override public List<String> toReadableLines(Environment env, HashSet<ObjectValue> passed) {
|
||||
var dbg = DebugContext.get(env);
|
||||
var dbg = DebugHandler.get(env);
|
||||
var res = new StringBuilder(this.toString());
|
||||
var loc = dbg.getMapOrEmpty(this).start();
|
||||
var loc = dbg.getMapOrEmpty(env, this).first();
|
||||
|
||||
if (loc != null) res.append(" @ " + loc);
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ public class ObjectValue extends Value {
|
||||
private HashMap<SymbolValue, FieldMember> symbolFields = new HashMap<>();
|
||||
private HashMap<String, PropertyMember> properties = new HashMap<>();
|
||||
private HashMap<SymbolValue, PropertyMember> symbolProperties = new HashMap<>();
|
||||
private State state = State.NORMAL;
|
||||
|
||||
private LinkedHashMap<String, Boolean> keys = new LinkedHashMap<>();
|
||||
private LinkedHashMap<SymbolValue, Boolean> symbols = new LinkedHashMap<>();
|
||||
@@ -72,8 +73,6 @@ public class ObjectValue extends Value {
|
||||
@Override public NumberValue toNumber(Environment env) { return toPrimitive(env).toNumber(env); }
|
||||
@Override public StringValue type() { return StringValue.of("object"); }
|
||||
|
||||
private State state = State.NORMAL;
|
||||
|
||||
@Override public State getState() { return state; }
|
||||
|
||||
public final void preventExtensions() {
|
||||
|
||||
@@ -9,10 +9,10 @@ plugins {
|
||||
id("org.gradle.toolchains.foojay-resolver-convention") version "0.7.0";
|
||||
}
|
||||
|
||||
// rootProject.name = properties.project_name;
|
||||
rootProject.name = extra.properties["project_name"].toString();
|
||||
|
||||
include(":lib");
|
||||
include(":common");
|
||||
include(":repl");
|
||||
include(":runtime");
|
||||
include(":compilation");
|
||||
include(":compilation");
|
||||
|
||||
Reference in New Issue
Block a user