Compare commits
23 Commits
v0.10.5-be
...
master
Author | SHA1 | Date | |
---|---|---|---|
45292990b1 | |||
2dcfff689a | |||
2619e50e9b | |||
4bfda6b0a1 | |||
58d6110e1d | |||
120e59577d | |||
efe123b658 | |||
b190367681 | |||
e601749866 | |||
1670b64aaf | |||
1548938537 | |||
e14d85e7a8 | |||
4352550ae9 | |||
3c4d05abd4 | |||
f16d088646 | |||
8b1c2a5e4e | |||
ee8268b144 | |||
fffeac9bac | |||
166e9c0470 | |||
6125772038 | |||
1e982cd2ef | |||
4389d115b6 | |||
208444381e |
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
|
@ -3,5 +3,5 @@ repositories {
|
||||
}
|
||||
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
@ -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,11 +51,11 @@ public class Instruction {
|
||||
STORE_MEMBER_INT(0x4A),
|
||||
STORE_MEMBER_STR(0x4B),
|
||||
|
||||
OPERATION(0x50),
|
||||
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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -53,8 +53,8 @@ public abstract class FunctionNode extends Node {
|
||||
target.add(scope.define(param.name).index().toSet(false)).setLocation(param.loc());
|
||||
}
|
||||
if (hasSelf) {
|
||||
target.add(Instruction.loadCalled());
|
||||
target.add(scope.define(selfName).index().toSet(false));
|
||||
target.add(Instruction.loadCalled());
|
||||
target.add(scope.define(selfName).index().toSet(false));
|
||||
}
|
||||
|
||||
body.compile(target, lastReturn, BreakpointType.NONE);
|
||||
|
@ -286,40 +286,40 @@ public final class JavaScript {
|
||||
}
|
||||
|
||||
public static ParseRes<List<VariableNode>> parseParameters(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
|
||||
var openParen = Parsing.parseOperator(src, i + n, "(");
|
||||
if (!openParen.isSuccess()) return openParen.chainError(src.loc(i + n), "Expected a parameter list");
|
||||
n += openParen.n;
|
||||
var openParen = Parsing.parseOperator(src, i + n, "(");
|
||||
if (!openParen.isSuccess()) return openParen.chainError(src.loc(i + n), "Expected a parameter list");
|
||||
n += openParen.n;
|
||||
|
||||
var params = new ArrayList<VariableNode>();
|
||||
var params = new ArrayList<VariableNode>();
|
||||
|
||||
var closeParen = Parsing.parseOperator(src, i + n, ")");
|
||||
n += closeParen.n;
|
||||
var closeParen = Parsing.parseOperator(src, i + n, ")");
|
||||
n += closeParen.n;
|
||||
|
||||
if (!closeParen.isSuccess()) {
|
||||
while (true) {
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
if (!closeParen.isSuccess()) {
|
||||
while (true) {
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
var param = VariableNode.parse(src, i + n);
|
||||
if (!param.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a parameter or a closing brace");
|
||||
n += param.n;
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
var param = VariableNode.parse(src, i + n);
|
||||
if (!param.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a parameter or a closing brace");
|
||||
n += param.n;
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
params.add(param.result);
|
||||
params.add(param.result);
|
||||
|
||||
if (src.is(i + n, ",")) {
|
||||
n++;
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
}
|
||||
if (src.is(i + n, ",")) {
|
||||
n++;
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
}
|
||||
|
||||
if (src.is(i + n, ")")) {
|
||||
n++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (src.is(i + n, ")")) {
|
||||
n++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ParseRes.res(params, n);
|
||||
return ParseRes.res(params, n);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
9
doc/text/.gitignore
vendored
Normal file
9
doc/text/.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
/*
|
||||
!/img
|
||||
!/src
|
||||
!/.gitignore
|
||||
!/build
|
||||
!/document.md
|
||||
!/README.md
|
||||
!/requirements.md
|
||||
!/template.html
|
1
doc/text/README.md
Normal file
1
doc/text/README.md
Normal file
@ -0,0 +1 @@
|
||||
This is the main body of my thesis. **BE WARNED!** It is quite lengthy and written in Bulgarian.
|
42
doc/text/build
Executable file
42
doc/text/build
Executable file
@ -0,0 +1,42 @@
|
||||
#!/bin/luajit -lsrc.build
|
||||
|
||||
local config = require "config";
|
||||
|
||||
function profession(val)
|
||||
if val == "sys" then
|
||||
return {
|
||||
profession = "481020 „Системен програмист“",
|
||||
specialty = "4810201 „Системно програмиране“",
|
||||
};
|
||||
elseif val == "net" then
|
||||
return combine {
|
||||
profession = "523050 „Техник на компютърни системи“",
|
||||
specialty = "5230502 „Компютърни мрежи“",
|
||||
};
|
||||
end
|
||||
end
|
||||
|
||||
emit {
|
||||
target = "res.html",
|
||||
template {
|
||||
template = "template.html",
|
||||
year = os.date "%Y",
|
||||
prev_year = os.date "%Y" - 1,
|
||||
config,
|
||||
profession(config.profession),
|
||||
|
||||
build {
|
||||
"requirements.md",
|
||||
content = "requirements",
|
||||
},
|
||||
build {
|
||||
"document.md",
|
||||
content = "content",
|
||||
toc = "toc",
|
||||
ctx = {
|
||||
chapter_format = "Глава %s<br/>",
|
||||
chapter_format_plain = "Глава %s: ",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
1304
doc/text/document.md
Normal file
1304
doc/text/document.md
Normal file
File diff suppressed because it is too large
Load Diff
BIN
doc/text/img/debugging-example.png
Normal file
BIN
doc/text/img/debugging-example.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 96 KiB |
4
doc/text/img/dependencies.svg
Normal file
4
doc/text/img/dependencies.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 41 KiB |
BIN
doc/text/img/stack-arr.png
Normal file
BIN
doc/text/img/stack-arr.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
14
doc/text/requirements.md
Normal file
14
doc/text/requirements.md
Normal file
@ -0,0 +1,14 @@
|
||||
---
|
||||
title: Requirements
|
||||
---
|
||||
|
||||
1. Тема: Среда за изпълнение на „EcmaScript 5“ код за виртуалната машина на „Java“
|
||||
2. Изисквания
|
||||
- Покритие на „ECMA-262 5.1 Edition“ стандарта
|
||||
- Задоволително бързодействие
|
||||
- Възможност за лесно вграждане в други софтуерни продукти
|
||||
- Добри тестове, подробна документация и удобен и интуитивен публичен интерфейс за разработчици
|
||||
3. Съдържание
|
||||
1. Теоретична част
|
||||
2. Практическа част
|
||||
3. Приложение
|
728
doc/text/src/build.lua
Normal file
728
doc/text/src/build.lua
Normal file
@ -0,0 +1,728 @@
|
||||
---@diagnostic disable: undefined-field
|
||||
require "src.utils";
|
||||
local json = require "src.json";
|
||||
|
||||
local function reader(fd, ...)
|
||||
if type(fd) == "string" then
|
||||
fd = assert(io.open(fd, ... or "r"));
|
||||
elseif type(fd) == "function" then
|
||||
return fd;
|
||||
else
|
||||
assert(fd, ...);
|
||||
end
|
||||
|
||||
return function ()
|
||||
if fd == nil then error "File is closed" end
|
||||
local res = fd:read(1024);
|
||||
|
||||
if res == nil then
|
||||
fd:close();
|
||||
fd = nil;
|
||||
return nil;
|
||||
else
|
||||
return res;
|
||||
end
|
||||
end;
|
||||
end
|
||||
|
||||
local function read_to_str(fd, ...)
|
||||
local res = {};
|
||||
|
||||
for val in reader(fd, ...) do
|
||||
res[#res + 1] = val;
|
||||
end
|
||||
|
||||
return table.concat(res);
|
||||
end
|
||||
|
||||
local flag = {};
|
||||
function flag.has(name, ...)
|
||||
if name == ... then
|
||||
return true;
|
||||
elseif ... == nil then
|
||||
return false;
|
||||
else
|
||||
return flag.has(name, select(2, ...));
|
||||
end
|
||||
end
|
||||
function flag.add(name, ...)
|
||||
if flag.has(name, ...) then
|
||||
return ...;
|
||||
else
|
||||
return name, ...;
|
||||
end
|
||||
end
|
||||
|
||||
local converters = {};
|
||||
|
||||
local function sanitize(str)
|
||||
return (str
|
||||
:gsub("<", "<")
|
||||
:gsub(">", ">")
|
||||
);
|
||||
end
|
||||
|
||||
local function get_indent(ctx, n)
|
||||
return ("\t"):rep((ctx.indent or 0) + (n or 0));
|
||||
end
|
||||
local function indent(ctx)
|
||||
ctx.indent = (ctx.indent or 0) + 1;
|
||||
end
|
||||
local function undent(ctx)
|
||||
ctx.indent = (ctx.indent or 1) - 1;
|
||||
end
|
||||
|
||||
local function convert(data, ctx, ...)
|
||||
local func = converters[data.t];
|
||||
if func == nil then
|
||||
error(table.concat { "Unknown node '", data.t, "': ", to_readable(data) });
|
||||
else
|
||||
return func(data.c, ctx, ...);
|
||||
end
|
||||
end
|
||||
local function convert_all(arr, ctx, ...)
|
||||
local res = array {};
|
||||
local plain = array {};
|
||||
local inline = true;
|
||||
|
||||
for i = 1, #arr do
|
||||
local a, b, c = convert(arr[i], ctx, ...);
|
||||
res:append(a);
|
||||
plain:append(b);
|
||||
|
||||
if not c then
|
||||
inline = false;
|
||||
res:append("\n", get_indent(ctx));
|
||||
end
|
||||
end
|
||||
|
||||
return res, plain, inline;
|
||||
end
|
||||
|
||||
local function make_id(id, readable, ctx)
|
||||
ctx.ids = ctx.ids or {};
|
||||
local res;
|
||||
|
||||
if ctx.ids[id] ~= nil then
|
||||
ctx.ids[id] = ctx.ids[id] + 1;
|
||||
res = id .. "-" .. ctx.ids[id];
|
||||
else
|
||||
ctx.ids[id] = 0;
|
||||
res = id;
|
||||
end
|
||||
|
||||
if readable then
|
||||
ctx.citables = ctx.citables or {};
|
||||
ctx.citables[id] = readable;
|
||||
end
|
||||
|
||||
return res;
|
||||
end
|
||||
local function last_id(id, ctx)
|
||||
ctx.ids = ctx.ids or {};
|
||||
if ctx.ids[id] ~= nil and ctx.ids[id] > 0 then
|
||||
return id .. "-" .. ctx.ids[id];
|
||||
else
|
||||
return id;
|
||||
end
|
||||
end
|
||||
|
||||
local function count_headers(array)
|
||||
local res = 0;
|
||||
|
||||
for i = 1, #array do
|
||||
if not array[i].ignored then
|
||||
res = res + 1;
|
||||
end
|
||||
end
|
||||
|
||||
return res;
|
||||
end
|
||||
|
||||
local function read_attributes(data, readable, ctx, no_id)
|
||||
local res = {};
|
||||
|
||||
if data[1] ~= "" then
|
||||
res.id = data[1];
|
||||
end
|
||||
for i = 1, #data[2] do
|
||||
res[data[2][i]] = true;
|
||||
end
|
||||
for i = 1, #data[3] do
|
||||
local curr = data[3][i];
|
||||
res[curr[1]] = curr[2];
|
||||
end
|
||||
|
||||
if not no_id and res.id then
|
||||
res.id = make_id(res.id, readable, ctx);
|
||||
end
|
||||
|
||||
return res;
|
||||
end
|
||||
|
||||
function converters.Table(data, ctx)
|
||||
local options = data[3];
|
||||
local header = data[4];
|
||||
local body = data[5][1];
|
||||
|
||||
local text = array {};
|
||||
|
||||
local function convert_cell(cell, tag, options)
|
||||
local align = options[1].t;
|
||||
indent(ctx);
|
||||
text:push("<", tag);
|
||||
if align == "AlignRight" then
|
||||
text:push [[ class="right"]];
|
||||
elseif align == "AlignLeft" then
|
||||
text:push [[ class="left"]];
|
||||
elseif align == "AlignCenter" then
|
||||
text:push [[ class="center"]];
|
||||
end
|
||||
text:push(">\n", get_indent(ctx));
|
||||
text:append((convert_all(cell[5], ctx)));
|
||||
undent(ctx);
|
||||
text:push("\n", get_indent(ctx), "</" .. tag .. ">");
|
||||
end
|
||||
|
||||
local function convert_row(row, tag)
|
||||
text:push("<tr>");
|
||||
|
||||
for i = 1, #row[2] do
|
||||
local cell = row[2][i];
|
||||
|
||||
indent(ctx);
|
||||
text:push("\n", get_indent(ctx));
|
||||
convert_cell(cell, tag, options[i]);
|
||||
undent(ctx);
|
||||
end
|
||||
|
||||
text:push("\n", get_indent(ctx), "</tr>");
|
||||
end
|
||||
|
||||
text:push [[<table class="graphic-table">]];
|
||||
|
||||
indent(ctx);
|
||||
indent(ctx);
|
||||
|
||||
text:push("\n", get_indent(ctx, -1), "<thead>");
|
||||
text:push("\n", get_indent(ctx));
|
||||
convert_row(header[2][1], "th");
|
||||
text:push("\n", get_indent(ctx, -1), "</thead>");
|
||||
text:push("\n", get_indent(ctx, -1), "<tbody>");
|
||||
|
||||
for i = 1, #body[4] do
|
||||
text:push("\n", get_indent(ctx));
|
||||
convert_row(body[4][i], "td");
|
||||
end
|
||||
|
||||
text:push("\n", get_indent(ctx, -1), "</tbody>");
|
||||
|
||||
undent(ctx);
|
||||
undent(ctx);
|
||||
text:push("\n", get_indent(ctx), "</table>");
|
||||
|
||||
return text, array { "[table]" };
|
||||
end
|
||||
function converters.BulletList(data, ctx)
|
||||
local text = array { "<ul class=\"bullet\">" };
|
||||
local plain = array {};
|
||||
|
||||
indent(ctx);
|
||||
for i = 1, #data do
|
||||
local el_text, el_plain = convert_all(data[i], ctx);
|
||||
|
||||
text
|
||||
:push("\n", get_indent(ctx), "<li>")
|
||||
:append(el_text)
|
||||
:push("</li>");
|
||||
plain:push("* "):append(el_plain):push("\n");
|
||||
end
|
||||
undent(ctx);
|
||||
|
||||
text:push("\n", get_indent(ctx), "</ul>");
|
||||
|
||||
return text, plain;
|
||||
end
|
||||
function converters.OrderedList(data, ctx)
|
||||
local text = array { "<ol>" };
|
||||
local plain = array {};
|
||||
|
||||
indent(ctx);
|
||||
for i = 1, #data[2] do
|
||||
local el_text, el_plain = convert_all(data[2][i], ctx);
|
||||
|
||||
text
|
||||
:push("\n", get_indent(ctx), "<li value=\"", i, "\">")
|
||||
:append(el_text)
|
||||
:push("</li>");
|
||||
plain:push(i .. ") "):append(el_plain):push("\n");
|
||||
end
|
||||
undent(ctx);
|
||||
|
||||
text:push("\n", get_indent(ctx), "</ol>");
|
||||
|
||||
return text, plain;
|
||||
end
|
||||
|
||||
function converters.Code(data, ctx)
|
||||
local content = data[2];
|
||||
|
||||
return array { "<code>", sanitize(content), "</code>" }, array { content };
|
||||
end
|
||||
function converters.CodeBlock(data, ctx)
|
||||
local attribs = read_attributes(data[1], nil, ctx);
|
||||
local content = data[2];
|
||||
|
||||
local text = array { "<pre><code" };
|
||||
|
||||
local classes = array {};
|
||||
|
||||
for k, v in next, attribs do
|
||||
if v == true then
|
||||
classes:push("language-" .. k);
|
||||
end
|
||||
end
|
||||
|
||||
if #classes > 0 then
|
||||
text:push(" class=\"", classes:join " ", "\"");
|
||||
end
|
||||
|
||||
text:push(">", sanitize(content), "</code></pre>");
|
||||
|
||||
return text, array { content };
|
||||
end
|
||||
function converters.Image(data, ctx)
|
||||
local alt, alt_plain = convert_all(data[2], ctx);
|
||||
local url = data[3][1];
|
||||
local title = data[3][2];
|
||||
local attribs = read_attributes(data[1], title or alt_plain:join " ", ctx);
|
||||
|
||||
local classes = array {};
|
||||
if attribs.small then classes:push("small") end
|
||||
if attribs.big then classes:push("big") end
|
||||
|
||||
local text = array {}
|
||||
:push("<img title=\"", title, "\" src=\"", url, "\" alt=\"")
|
||||
:append(alt)
|
||||
:push("\"");
|
||||
|
||||
if #classes > 0 then
|
||||
text:push(" class=\"", classes:join " ", "\"");
|
||||
end
|
||||
|
||||
text:push("/>");
|
||||
|
||||
return text, title and { title } or alt_plain or url or "[picture]";
|
||||
end
|
||||
function converters.Figure(data, ctx)
|
||||
local chapter_i = count_headers(ctx.headers or {});
|
||||
ctx.figures_indices = ctx.figures_indices or {};
|
||||
ctx.figures_indices[chapter_i] = (ctx.figures_indices[chapter_i] or 0) + 1;
|
||||
local figure_i = ctx.figures_indices[chapter_i];
|
||||
|
||||
local prefix = ("%s.%s."):format(chapter_i, figure_i);
|
||||
|
||||
local attribs = read_attributes(data[1], prefix, ctx);
|
||||
local id = attribs.id;
|
||||
|
||||
indent(ctx);
|
||||
indent(ctx);
|
||||
|
||||
local name, name_plain = convert_all(data[2][1], ctx);
|
||||
local content, plain = convert_all(data[3], ctx);
|
||||
|
||||
undent(ctx);
|
||||
undent(ctx);
|
||||
local text = array { "<figure" };
|
||||
if id then text:push(" id=\"", id, "\"") end
|
||||
text:push(">");
|
||||
|
||||
text
|
||||
:push("\n", get_indent(ctx, 1), "<div class=\"fig-content\">", "\n", get_indent(ctx, 2))
|
||||
:append(content)
|
||||
:push("\n", get_indent(ctx, 1), "</div>", "\n", get_indent(ctx, 1), "<figcaption>", "\n", get_indent(ctx, 2))
|
||||
:push(prefix, " ")
|
||||
:append(name)
|
||||
:push("\n", get_indent(ctx, 1), "</figcaption>")
|
||||
:push("\n", get_indent(ctx), "</figure>");
|
||||
plain
|
||||
:push(" (", prefix, " ")
|
||||
:append(name_plain)
|
||||
:push(")");
|
||||
|
||||
ctx.citables = ctx.citables or {};
|
||||
if id then ctx.citables[id] = prefix end
|
||||
|
||||
return text, plain;
|
||||
end
|
||||
function converters.Div(data, ctx)
|
||||
local attribs = read_attributes(data[1], nil, ctx, true);
|
||||
|
||||
if attribs.figure then
|
||||
local separator_i = data[2]:find_i(function (v) return v.t == "HorizontalRule" end);
|
||||
|
||||
local content_data, title_data;
|
||||
|
||||
if separator_i == nil then
|
||||
content_data = array { data[2][1] };
|
||||
title_data = data[2]:slice(2);
|
||||
else
|
||||
content_data = data[2]:slice(1, separator_i - 1);
|
||||
title_data = data[2]:slice(separator_i + 1);
|
||||
end
|
||||
|
||||
if #title_data == 1 and title_data[1].t == "Para" then
|
||||
title_data = title_data[1].c
|
||||
end
|
||||
|
||||
return converters.Figure(array {
|
||||
data[1],
|
||||
array { title_data },
|
||||
content_data,
|
||||
}, ctx);
|
||||
else
|
||||
error "Divs are not supported";
|
||||
end
|
||||
end
|
||||
|
||||
function converters.Cite(data, ctx)
|
||||
local citation = data[1][1];
|
||||
local id = last_id(citation.citationId, ctx);
|
||||
|
||||
local function target()
|
||||
local res = (ctx.citables or {})[id];
|
||||
if res == nil then
|
||||
io.stderr:write("WARNING! Citation '" .. id .. "' doesn't exist!\n");
|
||||
res = id;
|
||||
end
|
||||
|
||||
return res;
|
||||
end
|
||||
|
||||
return array { "<a href=\"#", id, "\">", target, "</a>" }, array { target }, true;
|
||||
end
|
||||
function converters.Header(data, ctx)
|
||||
local level = data[1];
|
||||
local attribs = read_attributes(data[2], nil, ctx);
|
||||
|
||||
ctx.headers = ctx.headers or array {};
|
||||
ctx.header_stack = ctx.header_stack or array {};
|
||||
|
||||
local parent = ctx.header_stack[level - 1];
|
||||
if level > 1 and parent == nil then error "Heading hierarchy inconsistent" end
|
||||
|
||||
local text, plain = convert_all(data[3], ctx);
|
||||
|
||||
local header = {
|
||||
id = attribs.id,
|
||||
level = level,
|
||||
children = array {},
|
||||
ignored = attribs.nonum or false,
|
||||
};
|
||||
|
||||
local text_prefix, plain_prefix = "", "";
|
||||
|
||||
if level == 1 then
|
||||
ctx.headers:push(header);
|
||||
else
|
||||
parent.children:push(header);
|
||||
end
|
||||
|
||||
if not header.ignored then
|
||||
local prefix = array { count_headers(ctx.headers) };
|
||||
local text_format, plain_format;
|
||||
|
||||
for i = 1, level - 1 do
|
||||
--- @diagnostic disable-next-line: undefined-field
|
||||
prefix:push(count_headers(ctx.header_stack[i].children));
|
||||
end
|
||||
|
||||
header.prefix = prefix:join ".";
|
||||
|
||||
if level == 1 then
|
||||
text_format = ctx.chapter_format or "Chapter %s<br>";
|
||||
plain_format = ctx.chapter_format_plain or "Chapter %s: ";
|
||||
else
|
||||
text_format = "%s. ";
|
||||
plain_format = "%s. ";
|
||||
end
|
||||
|
||||
text_prefix = text_format:format(header.prefix);
|
||||
plain_prefix = plain_format:format(header.prefix);
|
||||
end
|
||||
|
||||
ctx.header_stack = ctx.header_stack:fill(nil, level + 1, #ctx.header_stack);
|
||||
ctx.header_stack[level] = header;
|
||||
|
||||
header.text = text_prefix .. text:join "";
|
||||
header.plain = plain_prefix .. plain:join "";
|
||||
|
||||
if attribs.id then
|
||||
ctx.citables = ctx.citables or {};
|
||||
ctx.citables[attribs.id] = header.plain;
|
||||
end
|
||||
|
||||
return
|
||||
arrays.concat({ ("<h%d id=%q>"):format(level, attribs.id), text_prefix }, text, { ("</h%d>"):format(level) }),
|
||||
array { plain_prefix }:append(plain);
|
||||
end
|
||||
function converters.Link(data, ctx)
|
||||
local content, content_plain = convert_all(data[2], ctx);
|
||||
|
||||
local url = data[3][1];
|
||||
local attribs = read_attributes(data[1], nil, ctx);
|
||||
local id = attribs.id or data[3][2];
|
||||
|
||||
if id == "" then id = nil end
|
||||
|
||||
|
||||
if id then
|
||||
ctx.link_i = (ctx.link_i or 0) + 1;
|
||||
local i = ctx.link_i;
|
||||
ctx.citables = ctx.citables or {};
|
||||
ctx.citables[id] = "[" .. i .. "]";
|
||||
end
|
||||
|
||||
local plain = array {};
|
||||
plain:push("<a href=\"", url, "\"");
|
||||
|
||||
if id ~= nil then
|
||||
plain:push(" id=\"", id, "\"");
|
||||
end
|
||||
|
||||
plain:push(">");
|
||||
plain:append(content);
|
||||
plain:push("</a>");
|
||||
|
||||
|
||||
return plain, content_plain:push(" (", url, ")"), true;
|
||||
end
|
||||
|
||||
function converters.Para(data, ctx)
|
||||
indent(ctx);
|
||||
local text, plain = convert_all(data, ctx);
|
||||
undent(ctx);
|
||||
return
|
||||
array {}
|
||||
:push "<p>"
|
||||
:append(text)
|
||||
:append "</p>",
|
||||
plain;
|
||||
end
|
||||
function converters.Emph(data, ctx)
|
||||
local text, plain = convert_all(data, ctx);
|
||||
return arrays.concat({ "<i>" }, text, { "</i>" }), plain, true;
|
||||
end
|
||||
function converters.Strong(data, ctx)
|
||||
local text, plain = convert_all(data, ctx);
|
||||
return arrays.concat({ "<strong>" }, text, { "</strong>" }), plain, true;
|
||||
end
|
||||
function converters.Str(data)
|
||||
return array { sanitize(data) }, array { data }, true;
|
||||
end
|
||||
function converters.Plain(data, ctx)
|
||||
return convert_all(data, ctx);
|
||||
end
|
||||
|
||||
function converters.RawBlock(data)
|
||||
return array {}, array {}, true;
|
||||
end
|
||||
function converters.MetaInlines(data, ctx)
|
||||
return convert_all(data, ctx);
|
||||
end
|
||||
|
||||
function converters.LineBreak()
|
||||
return array { "<br>" }, array { " " }, true;
|
||||
end
|
||||
function converters.SoftBreak()
|
||||
return array { "­" }, array { " " }, true;
|
||||
end
|
||||
function converters.HorizontalRule()
|
||||
return array { "<br class=\"pg-break\"/>" }, array { " " };
|
||||
end
|
||||
function converters.Space()
|
||||
return array { " " }, array { " " }, true;
|
||||
end
|
||||
|
||||
local function parse_meta(meta)
|
||||
local res = {};
|
||||
|
||||
for k, v in next, meta do
|
||||
local text, plain = convert(v, {});
|
||||
res[k] = text:concat "";
|
||||
end
|
||||
|
||||
return res;
|
||||
end
|
||||
|
||||
local function convert_headers(headers, ctx)
|
||||
ctx = ctx or { indent = 0 };
|
||||
indent(ctx);
|
||||
local res = arrays.concat(
|
||||
{ "<ul>" },
|
||||
headers:flat_map(function (child)
|
||||
return arrays.concat(
|
||||
{
|
||||
"\n", get_indent(ctx),
|
||||
"<li><a href=\"#",
|
||||
child.id,
|
||||
"\"><span class=\"name\">",
|
||||
child.plain,
|
||||
"</span><span class=\"page\">Page</span></a>"
|
||||
},
|
||||
#child.children > 0 and convert_headers(child.children, ctx) or { "" },
|
||||
{ "</li>" }
|
||||
);
|
||||
end),
|
||||
{
|
||||
"\n", get_indent(ctx, -1),
|
||||
"</ul>"
|
||||
}
|
||||
);
|
||||
undent(ctx);
|
||||
|
||||
return res;
|
||||
end
|
||||
|
||||
local function parse(...)
|
||||
local stream, err = io.popen("pandoc --highlight-style kate --from markdown-raw_html --to json -o - " .. array { ... }:join " ");
|
||||
local str = read_to_str(stream, err);
|
||||
local raw = json.parse(str);
|
||||
|
||||
return {
|
||||
metadata = parse_meta(raw.meta),
|
||||
content = raw.blocks or array {},
|
||||
};
|
||||
end
|
||||
|
||||
local function convert_page(data, ctx)
|
||||
ctx = ctx or {};
|
||||
ctx.headers = ctx.headers or array {};
|
||||
|
||||
local curr_page = array {};
|
||||
local pages = array {};
|
||||
|
||||
for i = 1, #data.content do
|
||||
local curr = data.content[i];
|
||||
if curr.t == "Header" then
|
||||
if curr.c[1] == 1 then
|
||||
if #pages > 0 or #curr_page > 0 then
|
||||
pages:append(curr_page);
|
||||
end
|
||||
curr_page = array {};
|
||||
end
|
||||
end
|
||||
|
||||
local text, _, inline = convert(curr, ctx);
|
||||
curr_page:append(text);
|
||||
if not inline then
|
||||
curr_page:push("\n");
|
||||
end
|
||||
end
|
||||
|
||||
pages:append(curr_page);
|
||||
|
||||
local function mapper(v)
|
||||
if type(v) == "function" then
|
||||
return v();
|
||||
else
|
||||
return v;
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
content = pages:map(mapper, true):join "",
|
||||
toc = convert_headers(ctx.headers):map(mapper, true):join "",
|
||||
};
|
||||
end
|
||||
|
||||
local function subst(template, variables)
|
||||
local replaces = array {};
|
||||
|
||||
for k, v in next, variables do
|
||||
local i = 1;
|
||||
local search = "{{" .. k .. "}}";
|
||||
|
||||
while true do
|
||||
local b, e = string.find(template, search, i, true);
|
||||
if b == nil then break end
|
||||
|
||||
replaces:push { b = b, e = e, v = v };
|
||||
i = e + 1;
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(replaces, function (a, b) return a.b < b.b end);
|
||||
|
||||
local parts = array {};
|
||||
local prev = 1;
|
||||
|
||||
for i = 1, #replaces do
|
||||
local curr = replaces[i];
|
||||
parts:push(template:sub(prev, curr.b - 1), curr.v);
|
||||
prev = curr.e + 1;
|
||||
end
|
||||
|
||||
parts:push(template:sub(prev));
|
||||
|
||||
return parts:join "";
|
||||
end
|
||||
|
||||
function build(options)
|
||||
local toc = options.toc;
|
||||
local content = options.content;
|
||||
local ctx = options.ctx or {};
|
||||
|
||||
print(table.concat { "Building ", array(options):join ", ", "..." });
|
||||
|
||||
local res = convert_page(parse(unpack(options)), ctx);
|
||||
|
||||
if toc == nil and content == nil then
|
||||
return res.content;
|
||||
end
|
||||
|
||||
local obj = {};
|
||||
if toc ~= nil then obj[toc] = res.toc end
|
||||
if content ~= nil then obj[content] = res.content end
|
||||
|
||||
return obj;
|
||||
end
|
||||
|
||||
function combine(objects)
|
||||
local res = {};
|
||||
|
||||
for i = 1, #objects do
|
||||
for k, v in next, objects[i] do
|
||||
res[k] = v;
|
||||
end
|
||||
end
|
||||
|
||||
return res;
|
||||
end
|
||||
|
||||
function template(options)
|
||||
print("Templating into " .. options.template .. "...");
|
||||
options = combine(array { options }:append(options));
|
||||
return subst(read_to_str(io.open(options.template, "r")), options);
|
||||
end
|
||||
|
||||
function emit(values)
|
||||
local f = io.stdout;
|
||||
|
||||
if values.target then
|
||||
f = assert(io.open(values.target, "w"));
|
||||
print("Emitting to " .. values.target .. "...");
|
||||
else
|
||||
print("Emitting to stdout...");
|
||||
end
|
||||
|
||||
for i = 1, #values do
|
||||
assert(f:write(values[i]));
|
||||
end
|
||||
|
||||
if f ~= io.stdout then
|
||||
f:close();
|
||||
end
|
||||
end
|
||||
|
||||
-- return main;
|
244
doc/text/src/json.lua
Normal file
244
doc/text/src/json.lua
Normal file
@ -0,0 +1,244 @@
|
||||
local json = { null = {} };
|
||||
|
||||
-- Internal functions.
|
||||
|
||||
local function kind_of(obj)
|
||||
if type(obj) ~= "table" then return type(obj) end
|
||||
|
||||
local i = 1;
|
||||
for _ in pairs(obj) do
|
||||
if obj[i] ~= nil then
|
||||
i = i + 1;
|
||||
else
|
||||
return "table";
|
||||
end
|
||||
end
|
||||
|
||||
if i == 1 then
|
||||
return "table";
|
||||
else
|
||||
return "array";
|
||||
end
|
||||
end
|
||||
|
||||
local function escape_str(s)
|
||||
local in_char = { "\\", "\"", "/", "\b", "\f", "\n", "\r", "\t" };
|
||||
local out_char = { "\\", "\"", "/", "b", "f", "n", "r", "t" };
|
||||
for i, c in ipairs(in_char) do
|
||||
s = s:gsub(c, "\\" .. out_char[i]);
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
-- Returns pos, did_find; there are two cases:
|
||||
-- 1. Delimiter found: pos = pos after leading space + delim; did_find = true.
|
||||
-- 2. Delimiter not found: pos = pos after leading space; did_find = false.
|
||||
-- This throws an error if err_if_missing is true and the delim is not found.
|
||||
local function skip_delim(str, pos, delim, err_if_missing)
|
||||
pos = string.find(str, "%S", pos) or pos;
|
||||
|
||||
if string.sub(str, pos, pos) ~= delim then
|
||||
if err_if_missing then
|
||||
error(table.concat { "Expected ", delim, " near position ", pos });
|
||||
end
|
||||
|
||||
return pos, false;
|
||||
end
|
||||
|
||||
return pos + 1, true;
|
||||
end
|
||||
|
||||
local esc_map = { b = "\b", f = "\f", n = "\n", r = "\r", t = "\t", v = "\v" };
|
||||
|
||||
-- Expects the given pos to be the first character after the opening quote.
|
||||
-- Returns val, pos; the returned pos is after the closing quote character.
|
||||
local function parse_str_val(str, pos, check)
|
||||
if pos > #str then error("End of input found while parsing string") end
|
||||
|
||||
if check then
|
||||
if string.sub(str, pos, pos) ~= "\"" then
|
||||
return nil, pos;
|
||||
else
|
||||
pos = pos + 1;
|
||||
end
|
||||
else
|
||||
pos = pos + 1;
|
||||
end
|
||||
|
||||
local res = {};
|
||||
|
||||
while true do
|
||||
local c = string.sub(str, pos, pos);
|
||||
if c == "\"" then
|
||||
return table.concat(res), pos + 1;
|
||||
elseif c == "\\" then
|
||||
c = string.sub(str, pos + 1, pos + 1);
|
||||
res[#res + 1] = esc_map[c] or c;
|
||||
pos = pos + 2;
|
||||
else
|
||||
res[#res + 1] = c;
|
||||
pos = pos + 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Returns val, pos; the returned pos is after the number's final character.
|
||||
local function parse_num_val(str, pos)
|
||||
local num_str = string.match(str, "^-?%d+%.?%d*[eE]?[+-]?%d*", pos);
|
||||
local val = tonumber(num_str);
|
||||
|
||||
if not val then error(table.concat { "Error parsing number at position ", pos, "." }) end
|
||||
return val, pos + #num_str;
|
||||
end
|
||||
|
||||
local json_end = { "eof" };
|
||||
|
||||
local function parse_impl(str, pos, end_delim)
|
||||
pos = pos or 1;
|
||||
if pos > #str then error("Reached unexpected end of input") end
|
||||
|
||||
pos = str:find("%S", pos) or pos;
|
||||
local c = str:sub(pos, pos);
|
||||
local delim_found;
|
||||
|
||||
if c == "{" then
|
||||
pos = pos + 1;
|
||||
|
||||
local key;
|
||||
local obj = {};
|
||||
|
||||
c = string.sub(str, pos, pos);
|
||||
if c == "}" then
|
||||
return obj, pos
|
||||
else
|
||||
while true do
|
||||
key, pos = parse_str_val(str, pos, true);
|
||||
if key == nil then error("Expected a string key") end
|
||||
|
||||
pos = skip_delim(str, pos, ":", true); -- true -> error if missing.
|
||||
obj[key], pos = parse_impl(str, pos);
|
||||
|
||||
pos, delim_found = skip_delim(str, pos, "}");
|
||||
if delim_found then return obj, pos end
|
||||
|
||||
pos, delim_found = skip_delim(str, pos, ",");
|
||||
if not delim_found then error("Expected semicolon or comma") end
|
||||
end
|
||||
end
|
||||
elseif c == "[" then
|
||||
pos = pos + 1
|
||||
|
||||
local arr = array {};
|
||||
local val;
|
||||
local delim_found = true;
|
||||
|
||||
while true do
|
||||
val, pos = parse_impl(str, pos, "]");
|
||||
if val == json_end then return arr, pos end
|
||||
if not delim_found then error("Comma missing between array items: " .. str:sub(pos, pos + 25)) end
|
||||
|
||||
arr[#arr + 1] = val;
|
||||
pos, delim_found = skip_delim(str, pos, ",");
|
||||
end
|
||||
elseif c == "\"" then -- Parse a string.
|
||||
return parse_str_val(str, pos, false);
|
||||
elseif c == "-" or c:find("%d") then -- Parse a number.
|
||||
return parse_num_val(str, pos);
|
||||
elseif c == end_delim then -- End of an object or array.
|
||||
return json_end, pos + 1, true;
|
||||
elseif str:sub(pos, pos + 3) == "null" then
|
||||
return nil, pos + 4;
|
||||
elseif str:sub(pos, pos + 3) == "true" then
|
||||
return true, pos + 4;
|
||||
elseif str:sub(pos, pos + 4) == "false" then
|
||||
return true, pos + 5;
|
||||
else
|
||||
error(table.concat { "Invalid json syntax starting at position ", pos, ": ", str:sub(pos, pos + 10) });
|
||||
end
|
||||
end
|
||||
|
||||
local function stringify_impl(obj, all, indent_str, n)
|
||||
local s = {}; -- We'll build the string as an array of strings to be concatenated.
|
||||
local kind = kind_of(obj); -- This is 'array' if it's an array or type(obj) otherwise.
|
||||
|
||||
if kind == "array" then
|
||||
for i, val in ipairs(obj) do
|
||||
s[i] = stringify_impl(val, all, indent_str, n + 1);
|
||||
end
|
||||
|
||||
if not indent_str then
|
||||
return "[" .. table.concat(s, ",") .. "]";
|
||||
elseif #s == 0 then
|
||||
return "[]";
|
||||
else
|
||||
local indent = "\n" .. string.rep(indent_str, n + 1);
|
||||
return table.concat {
|
||||
"[",
|
||||
indent, table.concat(s, "," .. indent),
|
||||
"\n", string.rep(indent_str, n), "]"
|
||||
};
|
||||
end
|
||||
elseif kind == "table" then
|
||||
for k, v in pairs(obj) do
|
||||
local sep = indent_str and ": " or ":";
|
||||
local val = stringify_impl(v, all, indent_str, n + 1);
|
||||
if val ~= nil then
|
||||
if type(k) == "string" then
|
||||
s[#s + 1] = stringify_impl(k) .. sep .. val;
|
||||
elseif type(k) == "number" then
|
||||
s[#s + 1] = "\"" .. k .. "\"" .. sep .. val;
|
||||
elseif type(k) == "boolean" then
|
||||
s[#s + 1] = "\"" .. k .. "\"" .. sep .. val;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not indent_str then
|
||||
return "{" .. table.concat(s, ",") .. "}";
|
||||
elseif #s == 0 then
|
||||
return "{}";
|
||||
else
|
||||
local indent = "\n" .. string.rep(indent_str, n + 1);
|
||||
return table.concat {
|
||||
"{",
|
||||
indent, table.concat(s, "," .. indent),
|
||||
"\n", string.rep(indent_str, n), "}"
|
||||
};
|
||||
end
|
||||
return "{" .. table.concat(s, ",") .. "}";
|
||||
elseif kind == "string" then
|
||||
return "\"" .. escape_str(obj) .. "\"";
|
||||
elseif kind == "number" then
|
||||
return tostring(obj);
|
||||
elseif kind == 'boolean' then
|
||||
return tostring(obj);
|
||||
elseif kind == "nil" then
|
||||
return "null";
|
||||
elseif all then
|
||||
return tostring(obj);
|
||||
else
|
||||
return nil;
|
||||
end
|
||||
end
|
||||
|
||||
-- local indent_str = " ";
|
||||
|
||||
function json.stringify(obj, indent_str)
|
||||
if indent_str == true then
|
||||
indent_str = " ";
|
||||
end
|
||||
return stringify_impl(obj, false, indent_str, 0);
|
||||
end
|
||||
function json.pretty(obj)
|
||||
return stringify_impl(obj, true, " ", 0);
|
||||
end
|
||||
json.null = {}; -- This is a one-off table to represent the null value.
|
||||
|
||||
---@param str string
|
||||
---@return unknown
|
||||
function json.parse(str)
|
||||
local obj = parse_impl(str, 1);
|
||||
return obj;
|
||||
end
|
||||
|
||||
return json
|
30
doc/text/src/perf.lua
Normal file
30
doc/text/src/perf.lua
Normal file
@ -0,0 +1,30 @@
|
||||
local ffi = require "ffi";
|
||||
|
||||
ffi.cdef [[
|
||||
typedef struct timeval {
|
||||
long tv_sec;
|
||||
long tv_usec;
|
||||
} timeval;
|
||||
|
||||
int gettimeofday(struct timeval* t, void* tzp);
|
||||
]];
|
||||
|
||||
local function now()
|
||||
local target = ffi.new "timeval";
|
||||
ffi.C.gettimeofday(target, nil);
|
||||
return tonumber(target.tv_sec) + tonumber(target.tv_usec) / 1000000;
|
||||
end
|
||||
|
||||
local function measure(func, ...)
|
||||
local start = now();
|
||||
|
||||
return (function(...)
|
||||
io.stderr:write(("Took %s seconds\n"):format(now() - start));
|
||||
return ...;
|
||||
end)(func(...));
|
||||
end
|
||||
|
||||
return {
|
||||
now = now,
|
||||
measure = measure,
|
||||
}
|
429
doc/text/src/utils.lua
Normal file
429
doc/text/src/utils.lua
Normal file
@ -0,0 +1,429 @@
|
||||
-- TILL - TopchetoEU's "immaculate" lua libs
|
||||
-- Some useful utilities every lua-er should use.
|
||||
-- Might break shit, don't use in production for crying out loud
|
||||
|
||||
-- Reimplement this for lua <5.1
|
||||
if table.move == nil then
|
||||
--- @diagnostic disable: duplicate-set-field
|
||||
function table.move(src, src_b, src_e, dst_b, dst)
|
||||
if dst == nil then dst = src end
|
||||
local offset = dst_b - src_b;
|
||||
|
||||
if dst_b < src_b then
|
||||
for i = src_e, src_b, -1 do
|
||||
dst[i + offset] = src[i];
|
||||
end
|
||||
else
|
||||
for i = src_b, src_e do
|
||||
dst[i + offset] = src[i];
|
||||
end
|
||||
end
|
||||
|
||||
return dst;
|
||||
end
|
||||
end
|
||||
|
||||
-- Why did we *remove* this from the spec again? Classical PUC
|
||||
unpack = table.unpack or unpack;
|
||||
table.unpack = unpack;
|
||||
|
||||
--- Prepares the object to be a class - puts an __index member in it pointing to the object itself
|
||||
--- @generic T
|
||||
--- @param obj T
|
||||
--- @return T | { __index: T }
|
||||
function class(obj)
|
||||
--- @diagnostic disable-next-line: inject-field
|
||||
obj.__index = obj;
|
||||
return obj;
|
||||
end
|
||||
|
||||
-- arrays
|
||||
|
||||
--- @alias array<T> T[] | arraylib
|
||||
|
||||
--- Converts the object to an array by putting "arrays" as its metatable
|
||||
--- @generic T: unknown
|
||||
--- @param obj T[]
|
||||
--- @return array<T>
|
||||
--- @overload fun(val: string): array<string>
|
||||
function array(obj)
|
||||
if type(obj) == "string" then
|
||||
return obj:split "";
|
||||
else
|
||||
return arrays.mk(obj);
|
||||
end
|
||||
end
|
||||
|
||||
--- @class arraylib
|
||||
arrays = class {};
|
||||
|
||||
--- Creates an array
|
||||
function arrays.mk(obj)
|
||||
return setmetatable(obj, arrays);
|
||||
end
|
||||
|
||||
--- Adds every element of every passed array to the end of this array
|
||||
function arrays:append(...)
|
||||
local res = self;
|
||||
local n = #res + 1;
|
||||
|
||||
for i = 1, select("#", ...) do
|
||||
local curr = select(i, ...);
|
||||
for j = 1, #curr do
|
||||
res[n] = curr[j];
|
||||
n = n + 1;
|
||||
end
|
||||
end
|
||||
|
||||
return res;
|
||||
end
|
||||
|
||||
--- Returns all the given arrays, concatenated to one
|
||||
function arrays.concat(...)
|
||||
--- @diagnostic disable-next-line: missing-fields
|
||||
return arrays.append(array {}, ...);
|
||||
end
|
||||
|
||||
--- Adds all the given elements to the end of this array
|
||||
function arrays:push(...)
|
||||
return self:append({ ... });
|
||||
end
|
||||
--- Removes the last element of the array and returns it
|
||||
--- @generic T
|
||||
--- @param self array<T>
|
||||
--- @return T val? The removed element, or nil if none
|
||||
function arrays:pop()
|
||||
local res = self[#self];
|
||||
self[#self] = nil;
|
||||
return res;
|
||||
end
|
||||
--- @generic T
|
||||
--- @param self array<T>
|
||||
--- @return T val? The last element of this array, or nil of none
|
||||
function arrays:peek()
|
||||
return self[#self];
|
||||
end
|
||||
|
||||
--- Returns the result of mapping the values in table t through the function f
|
||||
--- @param mutate boolean? If true, will operate directly on the given array
|
||||
function arrays:map(f, mutate)
|
||||
local out;
|
||||
|
||||
if mutate then
|
||||
out = self;
|
||||
else
|
||||
out = array {};
|
||||
end
|
||||
|
||||
for i = 1, #self do
|
||||
out[i] = f(self[i], i, self);
|
||||
end
|
||||
|
||||
return out;
|
||||
end
|
||||
|
||||
--- Finds the index of the given element, or nil if it doesn't exist
|
||||
function arrays:find_i(f)
|
||||
for i = 1, #self do
|
||||
if f(self[i], i, self) then
|
||||
return i;
|
||||
end
|
||||
end
|
||||
|
||||
return nil;
|
||||
end
|
||||
|
||||
--- Like arrays:map, but will expect the function to return arrays, and will :append them to the result array instead
|
||||
function arrays:flat_map(f)
|
||||
local out = array {};
|
||||
|
||||
for i = 1, #self do
|
||||
out:append(f(self[i], i, self));
|
||||
end
|
||||
|
||||
return out;
|
||||
end
|
||||
|
||||
--- Sets each value from b to e to val in the given array
|
||||
function arrays:fill(val, b, e)
|
||||
if b == nil then b = 1 end
|
||||
if e == nil then e = self end
|
||||
if b < 0 then b = #self + 1 - b end
|
||||
if e < 0 then e = #self + 1 - e end
|
||||
|
||||
for i = b, e do
|
||||
self[i] = val;
|
||||
end
|
||||
|
||||
return self;
|
||||
end
|
||||
|
||||
--- Every element from start to stop is removed from this array and are replaced with the given elements
|
||||
function arrays:splice(start, stop, ...)
|
||||
-- TODO: optimize
|
||||
if select("#") > 0 then
|
||||
local n = stop - start + 1;
|
||||
|
||||
while n > 0 do
|
||||
table.remove(self, start);
|
||||
n = n - 1;
|
||||
end
|
||||
|
||||
for i = 1, select("#") do
|
||||
table.insert(self, start, (select(i, ...)));
|
||||
end
|
||||
|
||||
return self;
|
||||
else
|
||||
local res = {};
|
||||
|
||||
for i = start, stop do
|
||||
table.insert(res, self[i]);
|
||||
end
|
||||
|
||||
return res;
|
||||
end
|
||||
end
|
||||
|
||||
--- Every element from start to stop is removed from this array and are replaced with the given elements
|
||||
function arrays:slice(start, stop)
|
||||
start = start or 1;
|
||||
stop = stop or #self;
|
||||
local res = array {};
|
||||
|
||||
for i = start, stop do
|
||||
res:push(self[i]);
|
||||
end
|
||||
|
||||
return res;
|
||||
end
|
||||
|
||||
--- Equivalent of table.concat { table.unpack(self) }
|
||||
--- @param sep string? Separator (defaults to empty)
|
||||
--- @param b number? First element to take (defaults to beginning)
|
||||
--- @param e number? Last element to take (defaults to end)
|
||||
function arrays:join(sep, b, e)
|
||||
return table.concat(self, sep, b, e);
|
||||
end
|
||||
|
||||
function arrays:__concat(other)
|
||||
return arrays.concat(self, other);
|
||||
end
|
||||
|
||||
-- strings
|
||||
|
||||
--- Splits the text into an array of separate lines.
|
||||
--- @param self string
|
||||
function string:split(sep)
|
||||
sep = sep or "";
|
||||
local lines = arrays {};
|
||||
local pos = 1;
|
||||
|
||||
if sep == "" then
|
||||
for i = 1, #self do
|
||||
lines:push(self:sub(1, 1));
|
||||
end
|
||||
else
|
||||
while true do
|
||||
local b, e = self:find(sep, pos);
|
||||
|
||||
if not b then
|
||||
table.insert(lines, self:sub(pos));
|
||||
break;
|
||||
else
|
||||
table.insert(lines, self:sub(pos, b - 1));
|
||||
pos = e + 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return lines;
|
||||
end
|
||||
--- Gets the nth character
|
||||
--- @param self string
|
||||
--- @param i number
|
||||
function string:at(i)
|
||||
return self:sub(i, i);
|
||||
end
|
||||
--- Performs a plain string replace
|
||||
--- @param self string
|
||||
--- @param old string
|
||||
--- @param new string
|
||||
function string:replace(old, new)
|
||||
local b, e = self:find(old, 1, true);
|
||||
|
||||
if b == nil then
|
||||
return self;
|
||||
else
|
||||
return self:sub(1, b - 1) .. new .. self:sub(e + 1);
|
||||
end
|
||||
end
|
||||
|
||||
local function escape_str(s)
|
||||
local in_char = { "\\", "\"", "/", "\b", "\f", "\n", "\r", "\t" };
|
||||
local out_char = { "\\", "\"", "/", "b", "f", "n", "r", "t" };
|
||||
for i, c in ipairs(in_char) do
|
||||
s = s:gsub(c, "\\" .. out_char[i]);
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
local function stringify_impl(obj, indent_str, n, passed)
|
||||
local s = {}; -- We'll build the string as an array of strings to be concatenated.
|
||||
local kind = type(obj); -- This is 'array' if it's an array or type(obj) otherwise.
|
||||
|
||||
if kind == "table" then
|
||||
if passed[obj] then return "<circular>" end
|
||||
passed[obj] = true;
|
||||
|
||||
local len = #obj;
|
||||
|
||||
for i = 1, len do
|
||||
s[i] = stringify_impl(obj[i], indent_str, n + 1, passed) .. ",";
|
||||
end
|
||||
|
||||
local keys = {};
|
||||
|
||||
for k, v in pairs(obj) do
|
||||
if type(k) ~= "number" or k > len then
|
||||
keys[#keys + 1] = { k, v };
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(keys, function (a, b)
|
||||
if type(a[1]) == "number" and type(b[1]) == "number" then
|
||||
return a[1] < b[1];
|
||||
elseif type(a[1]) == "string" and type(b[1]) == "string" then
|
||||
return a[1] < b[1];
|
||||
else
|
||||
return type(a[1]) < type(b[1]) or tostring(a[1]) < tostring(b[1]);
|
||||
end
|
||||
end);
|
||||
|
||||
for i = 1, #keys do
|
||||
local k = keys[i][1];
|
||||
local v = keys[i][2];
|
||||
|
||||
local val = stringify_impl(v, indent_str, n + 1, passed);
|
||||
if val ~= nil then
|
||||
if type(k) == "string" then
|
||||
s[#s + 1] = table.concat { k, ": ", val, "," };
|
||||
else
|
||||
s[#s + 1] = table.concat { "[", stringify_impl(k, indent_str, n + 1, passed), "]: ", val, "," };
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local meta = getmetatable(obj);
|
||||
if meta ~= nil and meta ~= arrays then
|
||||
s[#s + 1] = "<meta> = " .. stringify_impl(meta, indent_str, n + 1, passed) .. ",";
|
||||
end
|
||||
|
||||
passed[obj] = false;
|
||||
|
||||
if #s == 0 then
|
||||
if meta == arrays then
|
||||
return "[]";
|
||||
else
|
||||
return "{}";
|
||||
end
|
||||
end
|
||||
|
||||
local contents = table.concat(s, " "):sub(1, -2);
|
||||
if #contents > 80 then
|
||||
local indent = "\n" .. string.rep(indent_str, n + 1);
|
||||
|
||||
contents = table.concat {
|
||||
indent,
|
||||
table.concat(s, indent),
|
||||
"\n", string.rep(indent_str, n)
|
||||
};
|
||||
else
|
||||
contents = " " .. contents .. " ";
|
||||
end
|
||||
|
||||
if meta == arrays then
|
||||
return table.concat { "[", contents, "]" };
|
||||
else
|
||||
return table.concat { "{", contents, "}" };
|
||||
end
|
||||
elseif kind == "string" then
|
||||
return "\"" .. escape_str(obj) .. "\"";
|
||||
elseif kind == "function" then
|
||||
local data = debug.getinfo(obj, "S");
|
||||
return table.concat { tostring(obj), " @ ", data.short_src, ":", data.linedefined };
|
||||
elseif kind == "nil" then
|
||||
return "null";
|
||||
else
|
||||
return tostring(obj);
|
||||
end
|
||||
end
|
||||
|
||||
--- Turns the given value to a human-readable string.
|
||||
--- Should be used only for debugging and display purposes
|
||||
function to_readable(obj, indent_str)
|
||||
return stringify_impl(obj, indent_str or " ", 0, {});
|
||||
end
|
||||
|
||||
function print(...)
|
||||
for i = 1, select("#", ...) do
|
||||
if i > 1 then
|
||||
io.stderr:write("\t");
|
||||
end
|
||||
|
||||
io.stderr:write(tostring((select(i, ...))));
|
||||
end
|
||||
|
||||
io.stderr:write("\n");
|
||||
end
|
||||
|
||||
--- Prints the given values in a human-readable manner to stderr
|
||||
--- Should be used only for debugging
|
||||
function pprint(...)
|
||||
for i = 1, select("#", ...) do
|
||||
if i > 1 then
|
||||
io.stderr:write("\t");
|
||||
end
|
||||
|
||||
io.stderr:write(to_readable((select(i, ...))));
|
||||
end
|
||||
|
||||
io.stderr:write("\n");
|
||||
end
|
||||
|
||||
-- functions
|
||||
|
||||
--- @class functions
|
||||
functions = class {};
|
||||
|
||||
--- Constructs a function, such that it calls the first function with the passed arguments,
|
||||
--- the second function with the return of the first, and so on. The return value of the last function is returned
|
||||
---
|
||||
--- In short, does the following, if the passed functions are a, b and c: return c(b(a(...)))
|
||||
---
|
||||
--- Sometimes less cumbersome to write (a | b | c | d)(args...) than d(c(b(a(args...))))
|
||||
--- @param self function
|
||||
function functions:pipe(...)
|
||||
if ... == nil then
|
||||
return self;
|
||||
else
|
||||
local next = ...;
|
||||
|
||||
return functions.pipe(function (...)
|
||||
return next(self(...));
|
||||
end, select(2, ...));
|
||||
end
|
||||
end
|
||||
--- Calls pipe with a and b; Alternative syntax for older Lua installation
|
||||
function functions.__sub(a, b)
|
||||
return functions.pipe(a, b);
|
||||
end
|
||||
--- Calls pipe with a and b
|
||||
function functions.__bor(a, b)
|
||||
return functions.pipe(a, b);
|
||||
end
|
||||
|
||||
-- It's not vital to have this metatable, so we will just try our very best
|
||||
if debug then
|
||||
debug.setmetatable(load, functions);
|
||||
end
|
577
doc/text/template.html
Normal file
577
doc/text/template.html
Normal file
@ -0,0 +1,577 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Дипломна работа {{author}}</title>
|
||||
<style>
|
||||
@page {
|
||||
size: A4;
|
||||
}
|
||||
@page {
|
||||
@bottom-right-corner {
|
||||
text-align: center;
|
||||
content: counter(page);
|
||||
}
|
||||
}
|
||||
h1 {
|
||||
break-before: page;
|
||||
}
|
||||
.pg-break {
|
||||
break-after: page;
|
||||
}
|
||||
figure {
|
||||
break-inside: avoid-page;
|
||||
}
|
||||
.page h1, .page h2 {
|
||||
break-before: unset !important;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Georgia, 'Times New Roman', Times, serif;
|
||||
font-size: 14pt;
|
||||
line-height: 1.75;
|
||||
}
|
||||
|
||||
p::before {
|
||||
width: 3em;
|
||||
display: inline-block;
|
||||
content: ' ';
|
||||
}
|
||||
|
||||
.page {
|
||||
height: 100%;
|
||||
line-height: 1.25;
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
padding: 0;
|
||||
}
|
||||
li > ul {
|
||||
padding-left: 2em;
|
||||
}
|
||||
|
||||
nav {
|
||||
display: contents;
|
||||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
nav ul {
|
||||
list-style-type: none;
|
||||
}
|
||||
nav span {
|
||||
background-color: white;
|
||||
}
|
||||
nav a {
|
||||
line-height: 1.25;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
text-decoration: none;
|
||||
}
|
||||
nav a::after {
|
||||
z-index: -1;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
float: right;
|
||||
overflow: hidden;
|
||||
width: max-content;
|
||||
content:
|
||||
'. . . . . . . . . . . . . . . . . . '
|
||||
'. . . . . . . . . . . . . . . . . . '
|
||||
'. . . . . . . . . . . . . . . . . . '
|
||||
'. . . . . . . . . . . . . . . . . . '
|
||||
'. . . . . . . . . . . . . . . . . . '
|
||||
'. . . . . . . . . . . . . . . . . . '
|
||||
'. . . . . . . . . . . . . . . . . . '
|
||||
'. . . . . . . . . . . . . . . . . . '
|
||||
'. . . . . . . . . . . . . . . . . . '
|
||||
'. . . . . . . . . . . . . . . . . . ';
|
||||
}
|
||||
nav a span {
|
||||
z-index: 10000;
|
||||
}
|
||||
a {
|
||||
color: rgb(207, 37, 37);
|
||||
}
|
||||
|
||||
figure {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: .5em;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 1em 0;
|
||||
}
|
||||
figure .fig-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 1em;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
figcaption {
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
img.small {
|
||||
max-width: 50%;
|
||||
max-height: 25%;
|
||||
}
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-size: .65em;
|
||||
margin: .5em 0;
|
||||
}
|
||||
pre > code {
|
||||
font-size: inherit;
|
||||
line-height: 1.25;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.hljs-ln-code {
|
||||
padding-left: .5em !important;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.title-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: .5em;
|
||||
}
|
||||
|
||||
.hljs-ln-numbers {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
text-align: center;
|
||||
color: #ccc;
|
||||
border-right: 1px solid #CCC;
|
||||
vertical-align: top;
|
||||
padding-right: .5em !important;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.page {
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
.school-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
gap: 2em;
|
||||
font-size: 1.25em;
|
||||
text-align: right;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
.school-img {
|
||||
height: 6em;
|
||||
width: unset;
|
||||
}
|
||||
|
||||
.pg-break {
|
||||
break-after: page;
|
||||
}
|
||||
|
||||
.title-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
text-align: center;
|
||||
}
|
||||
.title-authors {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
padding: 0 3em;
|
||||
width: 100%;
|
||||
}
|
||||
.title-author {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.asm-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.asm-signature {
|
||||
display: block;
|
||||
text-align: right;
|
||||
/* flex-direction: column;
|
||||
text-align: right; */
|
||||
}
|
||||
.asm-title {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
text-align: center;
|
||||
}
|
||||
.asm-content {
|
||||
display: grid;
|
||||
padding-top: 5em;
|
||||
grid-template-rows: auto auto min-content;
|
||||
/* flex-direction: column;
|
||||
gap: .5em;
|
||||
justify-content: space-between; */
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.asm-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
ul {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.graphic-table {
|
||||
font-size: .9em;
|
||||
line-height: 1.25;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
.graphic-table tr:nth-child() {
|
||||
line-height: 1.25;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.graphic-table .left {
|
||||
text-align: left;
|
||||
}
|
||||
.graphic-table .right {
|
||||
text-align: right;
|
||||
}
|
||||
.graphic-table .center {
|
||||
text-align: center;
|
||||
}
|
||||
.graphic-table td, .graphic-table th {
|
||||
border: 1px solid black;
|
||||
padding: .25em;
|
||||
margin: 0;
|
||||
/* border */
|
||||
}
|
||||
.graphic-table thead tr {
|
||||
background-color: #dadada;
|
||||
}
|
||||
.graphic-table tbody tr {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.graphic-table tbody tr:nth-child(even) {
|
||||
background-color: #ececec;
|
||||
}
|
||||
</style>
|
||||
<style keep>
|
||||
@media print {
|
||||
nav a {
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/default.min.css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/highlightjs/styles/vs.css">
|
||||
<script src="https://unpkg.com/@highlightjs/cdn-assets/highlight.min.js"></script>
|
||||
<script src="https://unpkg.com/pagedjs/dist/paged.js"></script>
|
||||
<script src="https://unpkg.com/highlightjs-line-numbers.js/src/highlightjs-line-numbers.js"></script>
|
||||
<script>
|
||||
// This godless solution works, so it is fine
|
||||
class RepeatingTableHeadersHandler extends Paged.Handler {
|
||||
constructor(chunker, polisher, caller) {
|
||||
super(chunker, polisher, caller);
|
||||
this.splitTablesRefs = [];
|
||||
}
|
||||
|
||||
afterPageLayout(pageElement, page, breakToken, chunker) {
|
||||
this.chunker = chunker;
|
||||
this.splitTablesRefs = [];
|
||||
|
||||
if (breakToken) {
|
||||
const node = breakToken.node;
|
||||
const tables = this.findAllAncestors(node, "table");
|
||||
if (node.tagName === "TABLE") {
|
||||
tables.push(node);
|
||||
}
|
||||
|
||||
if (tables.length > 0) {
|
||||
this.splitTablesRefs = tables.map(t => t.dataset.ref);
|
||||
|
||||
//checks if split inside thead and if so, set breakToken to next sibling element
|
||||
let thead = node.tagName === "THEAD" ? node : this.findFirstAncestor(node, "thead");
|
||||
if (thead) {
|
||||
let lastTheadNode = thead.hasChildNodes() ? thead.lastChild : thead;
|
||||
breakToken.node = this.nodeAfter(lastTheadNode, chunker.source);
|
||||
}
|
||||
|
||||
this.hideEmptyTables(pageElement, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hideEmptyTables(pageElement, breakTokenNode) {
|
||||
this.splitTablesRefs.forEach(ref => {
|
||||
let table = pageElement.querySelector("[data-ref='" + ref + "']");
|
||||
if (table) {
|
||||
let sourceBody = table.querySelector("tbody > tr");
|
||||
if (!sourceBody || this.refEquals(sourceBody.firstElementChild, breakTokenNode)) {
|
||||
table.style.visibility = "hidden";
|
||||
table.style.position = "absolute";
|
||||
let lineSpacer = table.nextSibling;
|
||||
if (lineSpacer) {
|
||||
lineSpacer.style.visibility = "hidden";
|
||||
lineSpacer.style.position = "absolute";
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
refEquals(a, b) {
|
||||
return a && a.dataset && b && b.dataset && a.dataset.ref === b.dataset.ref;
|
||||
}
|
||||
|
||||
findFirstAncestor(element, selector) {
|
||||
while (element.parentNode && element.parentNode.nodeType === 1) {
|
||||
if (element.parentNode.matches(selector)) {
|
||||
return element.parentNode;
|
||||
}
|
||||
element = element.parentNode;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
findAllAncestors(element, selector) {
|
||||
const ancestors = [];
|
||||
while (element.parentNode && element.parentNode.nodeType === 1) {
|
||||
if (element.parentNode.matches(selector)) {
|
||||
ancestors.unshift(element.parentNode);
|
||||
}
|
||||
element = element.parentNode;
|
||||
}
|
||||
return ancestors;
|
||||
}
|
||||
|
||||
// The addition of repeating Table Headers is done here because this hook is triggered before overflow handling
|
||||
layout(rendered, layout) {
|
||||
this.splitTablesRefs.forEach(ref => {
|
||||
const renderedTable = rendered.querySelector("[data-ref='" + ref + "']");
|
||||
if (renderedTable) {
|
||||
// this event can be triggered multiple times
|
||||
// added a flag repeated-headers to control when table headers already repeated in current page.
|
||||
if (!renderedTable.getAttribute("repeated-headers")) {
|
||||
const sourceTable = this.chunker.source.querySelector("[data-ref='" + ref + "']");
|
||||
this.repeatColgroup(sourceTable, renderedTable);
|
||||
this.repeatTHead(sourceTable, renderedTable);
|
||||
renderedTable.setAttribute("repeated-headers", true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
repeatColgroup(sourceTable, renderedTable) {
|
||||
let colgroup = sourceTable.querySelectorAll("colgroup");
|
||||
let firstChild = renderedTable.firstChild;
|
||||
colgroup.forEach((colgroup) => {
|
||||
let clonedColgroup = colgroup.cloneNode(true);
|
||||
renderedTable.insertBefore(clonedColgroup, firstChild);
|
||||
});
|
||||
}
|
||||
|
||||
repeatTHead(sourceTable, renderedTable) {
|
||||
let thead = sourceTable.querySelector("thead");
|
||||
if (thead) {
|
||||
let clonedThead = thead.cloneNode(true);
|
||||
renderedTable.insertBefore(clonedThead, renderedTable.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
// the functions below are from pagedjs utils/dom.js
|
||||
nodeAfter(node, limiter) {
|
||||
if (limiter && node === limiter) {
|
||||
return;
|
||||
}
|
||||
let significantNode = this.nextSignificantNode(node);
|
||||
if (significantNode) {
|
||||
return significantNode;
|
||||
}
|
||||
if (node.parentNode) {
|
||||
while ((node = node.parentNode)) {
|
||||
if (limiter && node === limiter) {
|
||||
return;
|
||||
}
|
||||
significantNode = this.nextSignificantNode(node);
|
||||
if (significantNode) {
|
||||
return significantNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nextSignificantNode(sib) {
|
||||
while ((sib = sib.nextSibling)) {
|
||||
if (!this.isIgnorable(sib)) return sib;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
isIgnorable(node) {
|
||||
return (node.nodeType === 8) || // A comment node
|
||||
((node.nodeType === 3) && this.isAllWhitespace(node)); // a text node, all whitespace
|
||||
}
|
||||
|
||||
isAllWhitespace(node) {
|
||||
return !(/[^\t\n\r ]/.test(node.textContent));
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
Paged.registerHandlers(RepeatingTableHeadersHandler);
|
||||
|
||||
window.onload = async evn => {
|
||||
hljs.highlightAll();
|
||||
hljs.initLineNumbersOnLoad();
|
||||
|
||||
await new Promise(res => setTimeout(res, 50));
|
||||
const { Previewer, DOMContent } = Paged;
|
||||
|
||||
evn.preventDefault();
|
||||
const p = new Previewer();
|
||||
const styles = [...document.head.getElementsByTagName("style")]
|
||||
.filter(v => !v.hasAttribute("keep"))
|
||||
.map(v => {
|
||||
v.remove();
|
||||
return { [window.location.href]: v.textContent };
|
||||
});
|
||||
|
||||
const flow = await p.preview(DOMContent, styles, document.body);
|
||||
const pages = flow.pages.map(v => v.element);
|
||||
|
||||
for (const nav of document.getElementsByTagName("nav")) {
|
||||
|
||||
for (let el of nav.getElementsByTagName("li")) {
|
||||
el = el.children[0];
|
||||
if (!(el instanceof HTMLAnchorElement)) continue;
|
||||
const name = [...el.children].find(v => v.classList.contains("name"));
|
||||
const page = [...el.children].find(v => v.classList.contains("page"));
|
||||
|
||||
const href = decodeURIComponent(/#(.+)$/.exec(el.href)?.[1]);
|
||||
const target = document.getElementById(href);
|
||||
const i = pages.findIndex(v => v.contains(target));
|
||||
|
||||
page.innerText = i + 1;
|
||||
page.href = "#" + href;
|
||||
// console.log(target, name, i);
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
pages[i].getElementsByClassName("pagedjs_margin-bottom-right-corner-holder")[0].innerText = "";
|
||||
console.log(pages[i].getElementsByClassName("pagedjs_margin-bottom-right-corner-holder")[0]);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="page title-page">
|
||||
<div class="school-header">
|
||||
<img class="school-img" src="{{school_img}}"/>
|
||||
<h4>{{school_name}}</h4>
|
||||
</div>
|
||||
|
||||
<div class="title-content">
|
||||
<h2>ДИПЛОМНА РАБОТА</h2>
|
||||
<div>по професия код {{profession}}</div>
|
||||
<div>специалност код {{specialty}}</div>
|
||||
</div>
|
||||
<div class="title-content">Тема: {{topic}}</div>
|
||||
<div class="title-authors">
|
||||
<div class="title-author">
|
||||
<div class="author-type">Дипломант:</div>
|
||||
<div class="author-name">{{author}}</div>
|
||||
</div>
|
||||
<div class="title-author">
|
||||
<div class="author-type">Научен ръководител:</div>
|
||||
<div class="author-name">{{supervisor}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="title-end">СОФИЯ - {{year}}</div>
|
||||
</div>
|
||||
|
||||
<div class="page asm-page">
|
||||
<div class="school-header">
|
||||
<img class="school-img" src="{{school_img}}"/>
|
||||
<h4>{{school_name}}</h4>
|
||||
</div>
|
||||
<div class="asm-header">
|
||||
<div class="asm-header-dates">
|
||||
<div>Дата на заданието: 28.10.{{prev_year}} г.</div>
|
||||
<div>Дата на предаване: 28.01.{{year}} г.</div>
|
||||
</div>
|
||||
<div class="asm-signature">
|
||||
<div class="asm-signature-place">Утвърждавам: ..............................</div>
|
||||
<div class="asm-signature-person">/{{ensurer_name}}/ </div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="asm-content">
|
||||
<div class="asm-title">
|
||||
<h2>ЗАДАНИЕ<br/>за дипломна работа</h2>
|
||||
<h4>ДЪРЖАВЕН ИЗПИТ ЗА ПРИДОБИВАНЕ НА ТРЕТА СТЕПЕН НА ПРОФЕСИОНАЛНА КВАЛИФИКАЦИЯ</h4>
|
||||
<div>
|
||||
<div>по професия код {{profession}}</div>
|
||||
<div>специалност код {{specialty}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="asm-requirements">
|
||||
на ученика {{author}} от {{class}} клас<br/>
|
||||
{{requirements}}
|
||||
</div>
|
||||
<div class="asm-authors">
|
||||
<div class="asm-signature">
|
||||
<div class="author-type">Дипломант: ...........................................</div>
|
||||
<div class="author-name">/{{author}}/</div>
|
||||
</div>
|
||||
<div class="asm-signature">
|
||||
<div class="author-type">Ръководител: ...........................................</div>
|
||||
<div class="author-name">/{{supervisor}}/</div>
|
||||
</div>
|
||||
<div class="asm-signature">
|
||||
<div class="author-type">{{head_teacher_title}}: ...........................................</div>
|
||||
<div class="author-name">/{{head_teacher_name}}/</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page">
|
||||
<div>prazna str</div>
|
||||
</div>
|
||||
|
||||
{{content}}
|
||||
|
||||
<nav>
|
||||
{{toc}}
|
||||
</nav>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -1,4 +1,4 @@
|
||||
project_group = me.topchetoeu
|
||||
project_group = me.topchetoeu.j2s
|
||||
project_name = j2s
|
||||
project_version = 0.10.5-beta
|
||||
project_version = 0.10.11-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",
|
||||
@ -15,6 +17,7 @@
|
||||
"typescript": "^5.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||
"@babel/plugin-transform-class-properties": "^7.25.9",
|
||||
"@babel/plugin-transform-runtime": "^7.25.9",
|
||||
"@babel/plugin-transform-typescript": "^7.25.9",
|
||||
|
@ -34,7 +34,6 @@ const construct = (input, output) => defineConfig({
|
||||
optimizeConstEnums: true,
|
||||
allowDeclareFields: true,
|
||||
}],
|
||||
["@babel/plugin-transform-class-properties"],
|
||||
["@babel/plugin-transform-runtime", {
|
||||
moduleName: shouldPolyfill() ? "!polyfills:" : undefined,
|
||||
version: "^7.24.0",
|
||||
@ -48,6 +47,7 @@ const construct = (input, output) => defineConfig({
|
||||
assumptions: {
|
||||
ignoreToPrimitiveHint: true,
|
||||
noClassCalls: true,
|
||||
privateFieldsAsProperties: true,
|
||||
},
|
||||
|
||||
env: {
|
||||
@ -56,6 +56,7 @@ const construct = (input, output) => defineConfig({
|
||||
|
||||
babelHelpers: "runtime",
|
||||
plugins: [
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
"@babel/plugin-transform-arrow-functions",
|
||||
"@babel/plugin-transform-block-scoping",
|
||||
"@babel/plugin-transform-classes",
|
||||
@ -77,7 +78,7 @@ const construct = (input, output) => defineConfig({
|
||||
"@babel/plugin-transform-optional-chaining",
|
||||
"@babel/plugin-transform-logical-assignment-operators",
|
||||
"@babel/plugin-transform-numeric-separator",
|
||||
"@babel/plugin-transform-class-properties",
|
||||
"@babel/plugin-transform-private-methods",
|
||||
"@babel/plugin-transform-class-static-block",
|
||||
"@babel/plugin-transform-regenerator",
|
||||
|
||||
|
13
lib/src/lib/async.d.ts
vendored
13
lib/src/lib/async.d.ts
vendored
@ -22,3 +22,16 @@ declare interface PromiseConstructor {
|
||||
resolve<T>(val: T): Promise<Awaited<T>>;
|
||||
reject<T>(err: unknown): Promise<T>;
|
||||
}
|
||||
|
||||
declare interface AsyncIterator<T, Return = unknown, Next = unknown> {
|
||||
next(): Promise<IterationData<T, Return>>;
|
||||
next(val: Next): Promise<IterationData<T, Return>>;
|
||||
error?(err: unknown): Promise<IterationData<T, Return>>;
|
||||
return?(val: Return): Promise<IterationData<T, Return>>;
|
||||
}
|
||||
declare interface AsyncIterableIterator<T, Return = unknown, Next = unknown> extends AsyncIterator<T, Return, Next> {
|
||||
[Symbol.iterator](): this;
|
||||
}
|
||||
declare interface AsyncIterable<T> {
|
||||
[Symbol.iterator](): AsyncIterator<T>;
|
||||
}
|
||||
|
6
lib/src/lib/iterator.d.ts
vendored
6
lib/src/lib/iterator.d.ts
vendored
@ -1,12 +1,12 @@
|
||||
declare interface NormalIterationData<T> {
|
||||
value: T;
|
||||
done: true;
|
||||
done: false;
|
||||
}
|
||||
declare interface DoneIterationData<T> {
|
||||
value: T;
|
||||
done?: false;
|
||||
done?: true;
|
||||
}
|
||||
declare type IterationData<T, Return> = NormalIterationData<T> | DoneIterationData<Return>;
|
||||
declare type IterationData<T, Return = void> = NormalIterationData<T> | DoneIterationData<Return>;
|
||||
|
||||
declare interface Iterator<T, Return = unknown, Next = unknown> {
|
||||
next(): IterationData<T, Return>;
|
||||
|
135
lib/src/main/java/me/topchetoeu/j2s/lib/Compilers.java
Normal file
135
lib/src/main/java/me/topchetoeu/j2s/lib/Compilers.java
Normal file
@ -0,0 +1,135 @@
|
||||
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 handler = new SimpleDebugHandler();
|
||||
// env.add(DebugHandler.KEY, handler);
|
||||
|
||||
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);
|
||||
|
||||
// for (var el : compiled.all()) {
|
||||
// handler.onFunctionLoad(el.body(), el.map());
|
||||
// }
|
||||
|
||||
try {
|
||||
new CodeFunction(env, "intializer", compiled.body(), new Value[0][]).apply(env, Value.UNDEFINED);
|
||||
return wrap(prev, env, target, compilerFactory[0]);
|
||||
}
|
||||
catch (EngineException e) {
|
||||
System.out.println(Value.errorToReadable(env, e, "in transpiler initializer"));
|
||||
return prev;
|
||||
}
|
||||
}
|
||||
|
||||
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(Compiler base, Environment target, 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;
|
||||
|
693
lib/src/main/java/me/topchetoeu/j2s/lib/Primordials.java
Normal file
693
lib/src/main/java/me/topchetoeu/j2s/lib/Primordials.java
Normal file
@ -0,0 +1,693 @@
|
||||
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;
|
||||
}
|
||||
}
|
41
lib/src/main/java/me/topchetoeu/j2s/lib/StdLib.java
Normal file
41
lib/src/main/java/me/topchetoeu/j2s/lib/StdLib.java
Normal file
@ -0,0 +1,41 @@
|
||||
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.exceptions.EngineException;
|
||||
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, CompileResult body) {
|
||||
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", body.body(), new Value[0][]);
|
||||
try {
|
||||
func.apply(stubEnv, Value.UNDEFINED);
|
||||
}
|
||||
catch (EngineException e) {
|
||||
System.out.println(Value.errorToReadable(env, e, "in environment initializer"));
|
||||
}
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
public static Environment apply(Environment env) {
|
||||
if (env == null) env = new Environment();
|
||||
return apply(env, RUNNER);
|
||||
}
|
||||
}
|
@ -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 @@
|
||||
package me.topchetoeu.j2s.repl.debug;
|
||||
package me.topchetoeu.j2s.lib.debug;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
@ -15,7 +15,7 @@ import me.topchetoeu.j2s.common.SyntaxException;
|
||||
import me.topchetoeu.j2s.compilation.json.JSON;
|
||||
import me.topchetoeu.j2s.compilation.json.JSONList;
|
||||
import me.topchetoeu.j2s.compilation.json.JSONMap;
|
||||
import me.topchetoeu.j2s.repl.debug.WebSocketMessage.Type;
|
||||
import me.topchetoeu.j2s.lib.debug.WebSocketMessage.Type;
|
||||
|
||||
public class DebugServer {
|
||||
public static String browserDisplayName = Metadata.name() + "/" + Metadata.version();
|
37
lib/src/main/java/me/topchetoeu/j2s/lib/debug/Debugger.java
Normal file
37
lib/src/main/java/me/topchetoeu/j2s/lib/debug/Debugger.java
Normal file
@ -0,0 +1,37 @@
|
||||
package me.topchetoeu.j2s.lib.debug;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import me.topchetoeu.j2s.runtime.debug.DebugHandler;
|
||||
|
||||
public interface Debugger extends DebugHandler {
|
||||
void close();
|
||||
|
||||
void enable(V8Message msg) throws IOException;
|
||||
void disable(V8Message msg) throws IOException;
|
||||
|
||||
void setBreakpointByUrl(V8Message msg) throws IOException;
|
||||
void removeBreakpoint(V8Message msg) throws IOException;
|
||||
void continueToLocation(V8Message msg) throws IOException;
|
||||
|
||||
void getScriptSource(V8Message msg) throws IOException;
|
||||
void getPossibleBreakpoints(V8Message msg) throws IOException;
|
||||
|
||||
void resume(V8Message msg) throws IOException;
|
||||
void pause(V8Message msg) throws IOException;
|
||||
|
||||
void stepInto(V8Message msg) throws IOException;
|
||||
void stepOut(V8Message msg) throws IOException;
|
||||
void stepOver(V8Message msg) throws IOException;
|
||||
|
||||
void setPauseOnExceptions(V8Message msg) throws IOException;
|
||||
|
||||
void evaluateOnCallFrame(V8Message msg) throws IOException;
|
||||
|
||||
void getProperties(V8Message msg) throws IOException;
|
||||
void releaseObjectGroup(V8Message msg) throws IOException;
|
||||
void releaseObject(V8Message msg) throws IOException;
|
||||
void callFunctionOn(V8Message msg) throws IOException;
|
||||
|
||||
void runtimeEnable(V8Message msg) throws IOException;
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package me.topchetoeu.j2s.lib.debug;
|
||||
|
||||
public interface DebuggerProvider {
|
||||
Debugger getDebugger(WebSocket socket, HttpRequest req);
|
||||
}
|
100
lib/src/main/java/me/topchetoeu/j2s/lib/debug/HttpRequest.java
Normal file
100
lib/src/main/java/me/topchetoeu/j2s/lib/debug/HttpRequest.java
Normal file
@ -0,0 +1,100 @@
|
||||
package me.topchetoeu.j2s.lib.debug;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.util.HashMap;
|
||||
import java.util.IllegalFormatException;
|
||||
import java.util.Map;
|
||||
|
||||
import me.topchetoeu.j2s.common.Reading;
|
||||
|
||||
public class HttpRequest {
|
||||
public final String method;
|
||||
public final String path;
|
||||
public final Map<String, String> headers;
|
||||
public final OutputStream out;
|
||||
|
||||
public void writeCode(int code, String name) {
|
||||
try { out.write(("HTTP/1.1 " + code + " " + name + "\r\n").getBytes()); }
|
||||
catch (IOException e) { }
|
||||
}
|
||||
public void writeHeader(String name, String value) {
|
||||
try { out.write((name + ": " + value + "\r\n").getBytes()); }
|
||||
catch (IOException e) { }
|
||||
}
|
||||
public void writeLastHeader(String name, String value) {
|
||||
try { out.write((name + ": " + value + "\r\n\r\n").getBytes()); }
|
||||
catch (IOException e) { }
|
||||
}
|
||||
public void writeHeadersEnd() {
|
||||
try { out.write("\n".getBytes()); }
|
||||
catch (IOException e) { }
|
||||
}
|
||||
|
||||
public void writeResponse(int code, String name, String type, byte[] data) {
|
||||
writeCode(code, name);
|
||||
writeHeader("Content-Type", type);
|
||||
writeLastHeader("Content-Length", data.length + "");
|
||||
try {
|
||||
out.write(data);
|
||||
out.close();
|
||||
}
|
||||
catch (IOException e) { }
|
||||
}
|
||||
public void writeResponse(int code, String name, String type, InputStream data) {
|
||||
writeResponse(code, name, type, Reading.streamToBytes(data));
|
||||
}
|
||||
|
||||
public HttpRequest(String method, String path, Map<String, String> headers, OutputStream out) {
|
||||
this.method = method;
|
||||
this.path = path;
|
||||
this.headers = headers;
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
// We dont need no http library
|
||||
public static HttpRequest read(Socket socket) {
|
||||
try {
|
||||
var str = socket.getInputStream();
|
||||
var lines = new BufferedReader(new InputStreamReader(str));
|
||||
var line = lines.readLine();
|
||||
var i1 = line.indexOf(" ");
|
||||
var i2 = line.indexOf(" ", i1 + 1);
|
||||
|
||||
if (i1 < 0 || i2 < 0) {
|
||||
socket.close();
|
||||
return null;
|
||||
}
|
||||
|
||||
var method = line.substring(0, i1).trim().toUpperCase();
|
||||
var path = line.substring(i1 + 1, i2).trim();
|
||||
var headers = new HashMap<String, String>();
|
||||
|
||||
while (!(line = lines.readLine()).isEmpty()) {
|
||||
var i = line.indexOf(":");
|
||||
if (i < 0) continue;
|
||||
var name = line.substring(0, i).trim().toLowerCase();
|
||||
var value = line.substring(i + 1).trim();
|
||||
|
||||
if (name.length() == 0) continue;
|
||||
headers.put(name, value);
|
||||
}
|
||||
|
||||
if (headers.containsKey("content-length")) {
|
||||
try {
|
||||
var i = Integer.parseInt(headers.get("content-length"));
|
||||
str.skip(i);
|
||||
}
|
||||
catch (IllegalFormatException e) { /* ¯\_(ツ)_/¯ */ }
|
||||
}
|
||||
|
||||
return new HttpRequest(method, path, headers, socket.getOutputStream());
|
||||
}
|
||||
catch (IOException | NullPointerException e) { return null; }
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package me.topchetoeu.j2s.repl.debug;
|
||||
package me.topchetoeu.j2s.lib.debug;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@ -20,7 +20,7 @@ import me.topchetoeu.j2s.runtime.values.primitives.StringValue;
|
||||
import me.topchetoeu.j2s.runtime.values.primitives.SymbolValue;
|
||||
import me.topchetoeu.j2s.runtime.values.primitives.numbers.NumberValue;
|
||||
|
||||
public class ScopeObject extends Value {
|
||||
public class ScopeObject implements Value {
|
||||
public static final class ScopeMember extends FieldMember {
|
||||
public final Frame frame;
|
||||
public final int i;
|
@ -1,24 +1,19 @@
|
||||
package me.topchetoeu.j2s.runtime.debug;
|
||||
package me.topchetoeu.j2s.lib.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,21 @@ 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) {
|
||||
if (enabled) {
|
||||
sources = new HashMap<>();
|
||||
maps = new WeakHashMap<>();
|
||||
}
|
||||
}
|
||||
|
||||
public DebugContext() {
|
||||
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);
|
||||
public SimpleDebugHandler() {
|
||||
sources = new HashMap<>();
|
||||
maps = new WeakHashMap<>();
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package me.topchetoeu.j2s.repl.debug;
|
||||
package me.topchetoeu.j2s.lib.debug;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
@ -1,4 +1,4 @@
|
||||
package me.topchetoeu.j2s.repl.debug;
|
||||
package me.topchetoeu.j2s.lib.debug;
|
||||
|
||||
import me.topchetoeu.j2s.common.Environment;
|
||||
import me.topchetoeu.j2s.runtime.Frame;
|
19
lib/src/main/java/me/topchetoeu/j2s/lib/debug/V8Error.java
Normal file
19
lib/src/main/java/me/topchetoeu/j2s/lib/debug/V8Error.java
Normal file
@ -0,0 +1,19 @@
|
||||
package me.topchetoeu.j2s.lib.debug;
|
||||
|
||||
import me.topchetoeu.j2s.compilation.json.JSON;
|
||||
import me.topchetoeu.j2s.compilation.json.JSONMap;
|
||||
|
||||
public class V8Error {
|
||||
public final String message;
|
||||
|
||||
public V8Error(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.stringify(new JSONMap().set("error", new JSONMap()
|
||||
.set("message", message)
|
||||
));
|
||||
}
|
||||
}
|
22
lib/src/main/java/me/topchetoeu/j2s/lib/debug/V8Event.java
Normal file
22
lib/src/main/java/me/topchetoeu/j2s/lib/debug/V8Event.java
Normal file
@ -0,0 +1,22 @@
|
||||
package me.topchetoeu.j2s.lib.debug;
|
||||
|
||||
import me.topchetoeu.j2s.compilation.json.JSON;
|
||||
import me.topchetoeu.j2s.compilation.json.JSONMap;
|
||||
|
||||
public class V8Event {
|
||||
public final String name;
|
||||
public final JSONMap params;
|
||||
|
||||
public V8Event(String name, JSONMap params) {
|
||||
this.name = name;
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.stringify(new JSONMap()
|
||||
.set("method", name)
|
||||
.set("params", params)
|
||||
);
|
||||
}
|
||||
}
|
50
lib/src/main/java/me/topchetoeu/j2s/lib/debug/V8Message.java
Normal file
50
lib/src/main/java/me/topchetoeu/j2s/lib/debug/V8Message.java
Normal file
@ -0,0 +1,50 @@
|
||||
package me.topchetoeu.j2s.lib.debug;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import me.topchetoeu.j2s.compilation.json.JSON;
|
||||
import me.topchetoeu.j2s.compilation.json.JSONElement;
|
||||
import me.topchetoeu.j2s.compilation.json.JSONMap;
|
||||
|
||||
public class V8Message {
|
||||
public final String name;
|
||||
public final int id;
|
||||
public final JSONMap params;
|
||||
|
||||
public V8Message(String name, int id, Map<String, JSONElement> params) {
|
||||
this.name = name;
|
||||
this.params = new JSONMap(params);
|
||||
this.id = id;
|
||||
}
|
||||
public V8Result respond(JSONMap result) {
|
||||
return new V8Result(id, result);
|
||||
}
|
||||
public V8Result respond() {
|
||||
return new V8Result(id, new JSONMap());
|
||||
}
|
||||
|
||||
public V8Message(JSONMap raw) {
|
||||
if (!raw.isNumber("id")) throw new IllegalArgumentException("Expected number property 'id'.");
|
||||
if (!raw.isString("method")) throw new IllegalArgumentException("Expected string property 'method'.");
|
||||
|
||||
this.name = raw.string("method");
|
||||
this.id = (int)raw.number("id");
|
||||
this.params = raw.contains("params") ? raw.map("params") : new JSONMap();
|
||||
}
|
||||
public V8Message(String raw) {
|
||||
this(JSON.parse(null, raw).map());
|
||||
}
|
||||
|
||||
public JSONMap toMap() {
|
||||
var res = new JSONMap();
|
||||
return res;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.stringify(new JSONMap()
|
||||
.set("method", name)
|
||||
.set("params", params)
|
||||
.set("id", id)
|
||||
);
|
||||
}
|
||||
}
|
22
lib/src/main/java/me/topchetoeu/j2s/lib/debug/V8Result.java
Normal file
22
lib/src/main/java/me/topchetoeu/j2s/lib/debug/V8Result.java
Normal file
@ -0,0 +1,22 @@
|
||||
package me.topchetoeu.j2s.lib.debug;
|
||||
|
||||
import me.topchetoeu.j2s.compilation.json.JSON;
|
||||
import me.topchetoeu.j2s.compilation.json.JSONMap;
|
||||
|
||||
public class V8Result {
|
||||
public final int id;
|
||||
public final JSONMap result;
|
||||
|
||||
public V8Result(int id, JSONMap result) {
|
||||
this.id = id;
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.stringify(new JSONMap()
|
||||
.set("id", id)
|
||||
.set("result", result)
|
||||
);
|
||||
}
|
||||
}
|
186
lib/src/main/java/me/topchetoeu/j2s/lib/debug/WebSocket.java
Normal file
186
lib/src/main/java/me/topchetoeu/j2s/lib/debug/WebSocket.java
Normal file
@ -0,0 +1,186 @@
|
||||
package me.topchetoeu.j2s.lib.debug;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
|
||||
import me.topchetoeu.j2s.lib.debug.WebSocketMessage.Type;
|
||||
|
||||
public class WebSocket implements AutoCloseable {
|
||||
public long maxLength = 1 << 20;
|
||||
|
||||
private Socket socket;
|
||||
private boolean closed = false;
|
||||
|
||||
private OutputStream out() throws IOException {
|
||||
return socket.getOutputStream();
|
||||
}
|
||||
private InputStream in() throws IOException {
|
||||
return socket.getInputStream();
|
||||
}
|
||||
|
||||
private long readLen(int byteLen) throws IOException {
|
||||
long res = 0;
|
||||
|
||||
if (byteLen == 126) {
|
||||
res |= in().read() << 8;
|
||||
res |= in().read();
|
||||
return res;
|
||||
}
|
||||
else if (byteLen == 127) {
|
||||
res |= in().read() << 56;
|
||||
res |= in().read() << 48;
|
||||
res |= in().read() << 40;
|
||||
res |= in().read() << 32;
|
||||
res |= in().read() << 24;
|
||||
res |= in().read() << 16;
|
||||
res |= in().read() << 8;
|
||||
res |= in().read();
|
||||
return res;
|
||||
}
|
||||
else return byteLen;
|
||||
}
|
||||
private byte[] readMask(boolean has) throws IOException {
|
||||
if (has) {
|
||||
return new byte[] {
|
||||
(byte)in().read(),
|
||||
(byte)in().read(),
|
||||
(byte)in().read(),
|
||||
(byte)in().read()
|
||||
};
|
||||
}
|
||||
else return new byte[4];
|
||||
}
|
||||
|
||||
private void writeLength(int len) throws IOException {
|
||||
if (len < 126) {
|
||||
out().write((int)len);
|
||||
}
|
||||
else if (len <= 0xFFFF) {
|
||||
out().write(126);
|
||||
out().write((int)(len >> 8) & 0xFF);
|
||||
out().write((int)len & 0xFF);
|
||||
}
|
||||
else {
|
||||
out().write(127);
|
||||
out().write(0);
|
||||
out().write(0);
|
||||
out().write(0);
|
||||
out().write(0);
|
||||
out().write((len >> 24) & 0xFF);
|
||||
out().write((len >> 16) & 0xFF);
|
||||
out().write((len >> 8) & 0xFF);
|
||||
out().write(len & 0xFF);
|
||||
}
|
||||
}
|
||||
private synchronized void write(int type, byte[] data) throws IOException {
|
||||
out().write(type | 0x80);
|
||||
writeLength(data.length);
|
||||
out().write(data);
|
||||
}
|
||||
|
||||
public void send(String data) throws IOException {
|
||||
if (closed) throw new IllegalStateException("Websocket is closed.");
|
||||
write(1, data.getBytes());
|
||||
}
|
||||
public void send(byte[] data) throws IOException {
|
||||
if (closed) throw new IllegalStateException("Websocket is closed.");
|
||||
write(2, data);
|
||||
}
|
||||
public void send(WebSocketMessage msg) throws IOException {
|
||||
if (msg.type == Type.Binary) send(msg.binaryData());
|
||||
else send(msg.textData());
|
||||
}
|
||||
public void send(Object data) throws IOException {
|
||||
if (closed) throw new IllegalStateException("Websocket is closed.");
|
||||
write(1, data.toString().getBytes());
|
||||
}
|
||||
|
||||
public void close(String reason) {
|
||||
if (socket != null) {
|
||||
try {
|
||||
write(8, reason.getBytes());
|
||||
socket.close();
|
||||
}
|
||||
catch (Throwable e) { }
|
||||
}
|
||||
|
||||
socket = null;
|
||||
closed = true;
|
||||
}
|
||||
public void close() {
|
||||
close("");
|
||||
}
|
||||
|
||||
private WebSocketMessage fail(String reason) {
|
||||
System.out.println("WebSocket Error: " + reason);
|
||||
close(reason);
|
||||
return null;
|
||||
}
|
||||
|
||||
private byte[] readData() throws IOException {
|
||||
var maskLen = in().read();
|
||||
var hasMask = (maskLen & 0x80) != 0;
|
||||
var len = (int)readLen(maskLen & 0x7F);
|
||||
var mask = readMask(hasMask);
|
||||
|
||||
if (len > maxLength) fail("WebSocket Error: client exceeded configured max message size");
|
||||
else {
|
||||
var buff = new byte[len];
|
||||
|
||||
if (in().read(buff) < len) fail("WebSocket Error: payload too short");
|
||||
else {
|
||||
for (int i = 0; i < len; i++) {
|
||||
buff[i] ^= mask[(int)(i % 4)];
|
||||
}
|
||||
return buff;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public WebSocketMessage receive() throws IOException {
|
||||
var data = new ByteArrayOutputStream();
|
||||
var type = 0;
|
||||
|
||||
while (socket != null && !closed) {
|
||||
var finId = in().read();
|
||||
if (finId < 0) break;
|
||||
var fin = (finId & 0x80) != 0;
|
||||
int id = finId & 0x0F;
|
||||
|
||||
if (id == 0x8) { close(); return null; }
|
||||
if (id >= 0x8) {
|
||||
if (!fin) return fail("WebSocket Error: client-sent control frame was fragmented");
|
||||
if (id == 0x9) write(0xA, data.toByteArray());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type == 0) type = id;
|
||||
if (type == 0) return fail("WebSocket Error: client used opcode 0x00 for first fragment");
|
||||
|
||||
var buff = readData();
|
||||
if (buff == null) break;
|
||||
|
||||
if (data.size() + buff.length > maxLength) return fail("WebSocket Error: client exceeded configured max message size");
|
||||
data.write(buff);
|
||||
|
||||
if (!fin) continue;
|
||||
var raw = data.toByteArray();
|
||||
|
||||
if (type == 1) {
|
||||
return new WebSocketMessage(new String(raw));
|
||||
}
|
||||
else return new WebSocketMessage(raw);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public WebSocket(Socket socket) {
|
||||
this.socket = socket;
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package me.topchetoeu.j2s.lib.debug;
|
||||
|
||||
public class WebSocketMessage {
|
||||
public static enum Type {
|
||||
Text,
|
||||
Binary,
|
||||
}
|
||||
|
||||
public final Type type;
|
||||
private final Object data;
|
||||
|
||||
public final String textData() {
|
||||
if (type != Type.Text) throw new IllegalStateException("Message is not text.");
|
||||
return (String)data;
|
||||
}
|
||||
public final byte[] binaryData() {
|
||||
if (type != Type.Binary) throw new IllegalStateException("Message is not binary.");
|
||||
return (byte[])data;
|
||||
}
|
||||
|
||||
public WebSocketMessage(String data) {
|
||||
this.type = Type.Text;
|
||||
this.data = data;
|
||||
}
|
||||
public WebSocketMessage(byte[] data) {
|
||||
this.type = Type.Binary;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
3
lib/src/polyfills/classPrivateFieldLooseBase.js
Normal file
3
lib/src/polyfills/classPrivateFieldLooseBase.js
Normal file
@ -0,0 +1,3 @@
|
||||
export default function _classPrivateFieldLooseBase(obj) {
|
||||
return obj;
|
||||
}
|
13
lib/src/polyfills/classPrivateFieldLooseKey.js
Normal file
13
lib/src/polyfills/classPrivateFieldLooseKey.js
Normal file
@ -0,0 +1,13 @@
|
||||
import { print, self, symbol } from "../stdlib/primordials.ts";
|
||||
import { Object } from "../stdlib/values/object.ts";
|
||||
|
||||
self.Object = {
|
||||
defineProperty: function (obj, key, desc) {
|
||||
if (obj == null) return obj;
|
||||
Object.defineProperty(obj, key, desc);
|
||||
}
|
||||
};
|
||||
|
||||
export default function _classPrivateFieldLooseKey(key) {
|
||||
return symbol.makeSymbol(key);
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { object } from "../stdlib/primordials.ts";
|
||||
import { func, object, print } from "../stdlib/primordials.ts";
|
||||
|
||||
function _defineProperties(target, arr) {
|
||||
if (!arr) return;
|
||||
@ -31,5 +31,8 @@ export default function _createClass(clazz, instance, nonInstance) {
|
||||
_defineProperties(clazz.prototype, instance);
|
||||
_defineProperties(clazz, nonInstance);
|
||||
|
||||
func.setCallable(clazz, false);
|
||||
func.setConstructable(clazz, true);
|
||||
|
||||
return clazz;
|
||||
}
|
@ -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,25 +1,28 @@
|
||||
import { buffer, type InternalBuffer, map, symbol } from "../primordials.ts";
|
||||
|
||||
export const abs = new map(true);
|
||||
export const abKey: unique symbol = symbol.getSymbol("ArrayBuffer.impl") as any;
|
||||
|
||||
export class ArrayBuffer {
|
||||
public [abKey]!: InternalBuffer;
|
||||
#internal!: InternalBuffer;
|
||||
|
||||
public get byteLength() {
|
||||
return this[abKey].length;
|
||||
return this.#internal.length;
|
||||
}
|
||||
public get byteOffset() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public constructor(val: unknown) {
|
||||
if (buffer.isBuff(val)) this[abKey] = val;
|
||||
else this[abKey] = buffer.buff(Number(val));
|
||||
if (buffer.isBuff(val)) this.#internal = val;
|
||||
else this.#internal = buffer.buff(Number(val));
|
||||
}
|
||||
|
||||
public static unwrap(instance: ArrayBuffer) {
|
||||
return instance.#internal;
|
||||
}
|
||||
}
|
||||
|
||||
export function getAB(buff: InternalBuffer): ArrayBuffer {
|
||||
function wrapAB(buff: InternalBuffer): ArrayBuffer {
|
||||
let res = abs.get(buff);
|
||||
if (res == null) {
|
||||
res = new ArrayBuffer(buff);
|
||||
@ -28,3 +31,11 @@ export function getAB(buff: InternalBuffer): ArrayBuffer {
|
||||
|
||||
return res;
|
||||
}
|
||||
const unwrapAB = ArrayBuffer.unwrap;
|
||||
|
||||
delete (ArrayBuffer as any).unwrap;
|
||||
|
||||
|
||||
|
||||
export { wrapAB, unwrapAB };
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { buffer } from "../primordials.ts";
|
||||
import { abstractIgnore, TypedArray, typedArrayFuncs } from "./TypedArray.ts";
|
||||
import { token, TypedArray, typedArrayFuncs } from "./TypedArray.ts";
|
||||
|
||||
const factory = buffer.int32;
|
||||
const funcs = typedArrayFuncs(4, factory);
|
||||
@ -19,7 +19,7 @@ export class Int32Array extends TypedArray {
|
||||
}
|
||||
|
||||
public constructor(obj: any, start?: number, end?: number) {
|
||||
super(abstractIgnore);
|
||||
super(token);
|
||||
return funcs.construct(obj, start, end) as any;
|
||||
}
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
import { buffer, func, type InternalBuffer, object, string, symbol } from "../primordials.ts";
|
||||
import { symbols, wrapI } from "../utils.ts";
|
||||
import { Error, TypeError } from "../values/errors.ts";
|
||||
import { abKey, ArrayBuffer, getAB } from "./ArrayBuffer.ts";
|
||||
import { ArrayBuffer, unwrapAB, wrapAB } from "./ArrayBuffer.ts";
|
||||
|
||||
export const abstractIgnore = symbol.getSymbol("TypedArray.abstractIgnore");
|
||||
export const token = symbol.getSymbol("TypedArray.abstractIgnore");
|
||||
|
||||
export function typedArrayFuncs(perEl: number, constructor: (buff: InternalBuffer, start: number, end: number) => number[]) {
|
||||
return {
|
||||
@ -56,7 +56,7 @@ export function typedArrayFuncs(perEl: number, constructor: (buff: InternalBuffe
|
||||
return constructor(buffer.buff(self * perEl), 0, self);
|
||||
}
|
||||
if (self instanceof ArrayBuffer) {
|
||||
const internal = self[abKey];
|
||||
const internal = unwrapAB(self);
|
||||
if (start === undefined) start = 0;
|
||||
if (end === undefined) end = (internal.length / perEl) | 0;
|
||||
return constructor(internal, start, end);
|
||||
@ -90,7 +90,7 @@ export function typedArrayFuncs(perEl: number, constructor: (buff: InternalBuffe
|
||||
|
||||
export class TypedArray {
|
||||
public get buffer() {
|
||||
return getAB(buffer.backer(this as any));
|
||||
return wrapAB(buffer.backer(this as any));
|
||||
}
|
||||
public get byteOffset(): number {
|
||||
throw new Error("abstract");
|
||||
@ -212,8 +212,8 @@ export class TypedArray {
|
||||
return this;
|
||||
}
|
||||
|
||||
public constructor(token?: typeof abstractIgnore) {
|
||||
if (token !== abstractIgnore) {
|
||||
public constructor(_token?: typeof token) {
|
||||
if (_token !== token) {
|
||||
throw new TypeError("TypedArray constructor can't be called");
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { buffer } from "../primordials.ts";
|
||||
import { abstractIgnore, TypedArray, typedArrayFuncs } from "./TypedArray.ts";
|
||||
import { token, TypedArray, typedArrayFuncs } from "./TypedArray.ts";
|
||||
|
||||
const factory = buffer.uint8;
|
||||
const funcs = typedArrayFuncs(1, factory);
|
||||
@ -26,7 +26,7 @@ export class Uint8Array extends TypedArray {
|
||||
}
|
||||
|
||||
public constructor(obj: any, start?: number, end?: number) {
|
||||
super(abstractIgnore);
|
||||
super(token);
|
||||
return funcs.construct(obj, start, end) as any;
|
||||
}
|
||||
}
|
@ -1,13 +1,11 @@
|
||||
import { now, symbol } from "../primordials.ts";
|
||||
|
||||
const timeKey: unique symbol = symbol.makeSymbol("") as any;
|
||||
import { now, number, symbol } from "../primordials.ts";
|
||||
|
||||
export const Date = (() => {
|
||||
class Date {
|
||||
[timeKey]!: number;
|
||||
#time: number;
|
||||
|
||||
public constructor() {
|
||||
|
||||
this.#time = number.NaN;
|
||||
}
|
||||
|
||||
public static now() {
|
||||
|
@ -1,51 +1,49 @@
|
||||
import { Array } from "../values/array.ts";
|
||||
import { func, map, symbol } from "../primordials.ts";
|
||||
import { func, map } from "../primordials.ts";
|
||||
import { symbols } from "../utils.ts";
|
||||
|
||||
const mapKey: unique symbol = symbol.makeSymbol("Map.impl") as any;
|
||||
|
||||
export class Map<K, V> {
|
||||
private [mapKey]: InstanceType<typeof map>;
|
||||
#map: InstanceType<typeof map>;
|
||||
|
||||
public get size() {
|
||||
return this[mapKey].size();
|
||||
return this.#map.size();
|
||||
}
|
||||
|
||||
public get(key: K): V {
|
||||
return this[mapKey].get(key);
|
||||
return this.#map.get(key);
|
||||
}
|
||||
public has(key: K): boolean {
|
||||
return this[mapKey].has(key);
|
||||
return this.#map.has(key);
|
||||
}
|
||||
public set(key: K, val: V) {
|
||||
this[mapKey].set(key, val);
|
||||
this.#map.set(key, val);
|
||||
return this;
|
||||
}
|
||||
public delete(key: K): boolean {
|
||||
if (!this[mapKey].has(key)) return false;
|
||||
if (!this.#map.has(key)) return false;
|
||||
else {
|
||||
this[mapKey].delete(key);
|
||||
this.#map.delete(key);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public clear() {
|
||||
this[mapKey].clear();
|
||||
this.#map.clear();
|
||||
}
|
||||
|
||||
public keys(): K[] {
|
||||
return this[mapKey].keys();
|
||||
return this.#map.keys();
|
||||
}
|
||||
public values(): V[] {
|
||||
const res = this[mapKey].keys();
|
||||
const res = this.#map.keys();
|
||||
for (let i = 0; i < res.length; i++) {
|
||||
res[i] = this[mapKey].get(res[i]);
|
||||
res[i] = this.#map.get(res[i]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
public entries(): [K, V][] {
|
||||
const res = this[mapKey].keys();
|
||||
const res = this.#map.keys();
|
||||
for (let i = 0; i < res.length; i++) {
|
||||
res[i] = [res[i], this[mapKey].get(res[i])];
|
||||
res[i] = [res[i], this.#map.get(res[i])];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@ -62,7 +60,7 @@ export class Map<K, V> {
|
||||
}
|
||||
|
||||
public constructor(iterable?: Iterable<[K, V]>) {
|
||||
const _map = this[mapKey] = new map();
|
||||
const _map = this.#map = new map();
|
||||
|
||||
if (iterable != null) {
|
||||
if (Array.isArray(iterable)) {
|
||||
@ -81,31 +79,31 @@ export class Map<K, V> {
|
||||
}
|
||||
}
|
||||
export class WeakMap<K, V> {
|
||||
private [mapKey]: InstanceType<typeof map>;
|
||||
#map: InstanceType<typeof map>;
|
||||
|
||||
public get(key: K): V {
|
||||
return this[mapKey].get(key);
|
||||
return this.#map.get(key);
|
||||
}
|
||||
public has(key: K): boolean {
|
||||
return this[mapKey].has(key);
|
||||
return this.#map.has(key);
|
||||
}
|
||||
public set(key: K, val: V) {
|
||||
this[mapKey].set(key, val);
|
||||
this.#map.set(key, val);
|
||||
return this;
|
||||
}
|
||||
public delete(key: K): boolean {
|
||||
if (!this[mapKey].has(key)) return false;
|
||||
if (!this.#map.has(key)) return false;
|
||||
else {
|
||||
this[mapKey].delete(key);
|
||||
this.#map.delete(key);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public clear() {
|
||||
this[mapKey].clear();
|
||||
this.#map.clear();
|
||||
}
|
||||
|
||||
public constructor(iterable?: Iterable<[K, V]>) {
|
||||
const _map = this[mapKey] = new map(true);
|
||||
const _map = this.#map = new map(true);
|
||||
|
||||
if (iterable != null) {
|
||||
if (Array.isArray(iterable)) {
|
||||
@ -123,6 +121,3 @@ export class WeakMap<K, V> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func.setCallable(Map, false);
|
||||
func.setCallable(WeakMap, false);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { func, next, object, symbol } from "../primordials.ts";
|
||||
import { next } from "../primordials.ts";
|
||||
|
||||
enum PromiseState {
|
||||
Pending = "pend",
|
||||
@ -6,109 +6,106 @@ enum PromiseState {
|
||||
Rejected = "rej",
|
||||
}
|
||||
|
||||
const pState: unique symbol = symbol.makeSymbol("Promise.state") as any;
|
||||
const pValue: unique symbol = symbol.makeSymbol("Promise.value") as any;
|
||||
const pFulHandles: unique symbol = symbol.makeSymbol("Promise.fulfillHandles") as any;
|
||||
const pRejHandles: unique symbol = symbol.makeSymbol("Promise.rejectHandles") as any;
|
||||
|
||||
function makePromise<T>(): Promise<T> {
|
||||
return object.setPrototype({
|
||||
[pState]: PromiseState.Pending,
|
||||
[pFulHandles]: [],
|
||||
[pRejHandles]: [],
|
||||
}, Promise.prototype) as Promise<T>;
|
||||
}
|
||||
|
||||
function fulfill(self: Promise<any>, val: any) {
|
||||
if (self[pState] !== PromiseState.Pending) return;
|
||||
if (self === val) throw new Error("A promise may not be fulfilled with itself");
|
||||
|
||||
if (val != null && typeof val.then === "function") {
|
||||
val.then(
|
||||
(val: any) => fulfill(self, val),
|
||||
(err: any) => reject(self, err),
|
||||
);
|
||||
}
|
||||
else {
|
||||
self[pValue] = val;
|
||||
self[pState] = PromiseState.Fulfilled;
|
||||
|
||||
const handles = self[pFulHandles]!;
|
||||
|
||||
for (let i = 0; i < handles.length; i++) {
|
||||
handles[i](val);
|
||||
}
|
||||
|
||||
self[pFulHandles] = undefined;
|
||||
self[pRejHandles] = undefined;
|
||||
}
|
||||
}
|
||||
function reject(self: Promise<any>, val: any) {
|
||||
if (self[pState] !== PromiseState.Pending) return;
|
||||
if (self === val) throw new Error("A promise may not be rejected with itself");
|
||||
|
||||
if (val != null && typeof val.then === "function") {
|
||||
val.then(
|
||||
(val: any) => reject(self, val),
|
||||
(err: any) => reject(self, err),
|
||||
);
|
||||
}
|
||||
else {
|
||||
self[pValue] = val;
|
||||
self[pState] = PromiseState.Rejected;
|
||||
|
||||
const handles = self[pRejHandles]!;
|
||||
|
||||
for (let i = 0; i < handles.length; i++) {
|
||||
handles[i](val);
|
||||
}
|
||||
|
||||
self[pFulHandles] = undefined;
|
||||
self[pRejHandles] = undefined;
|
||||
}
|
||||
}
|
||||
function handle<T>(self: Promise<T>, ful?: (val: T) => void, rej?: (err: any) => void) {
|
||||
if (self[pState] === PromiseState.Pending) {
|
||||
if (ful != null) {
|
||||
self[pFulHandles]![self[pFulHandles]!.length] = ful;
|
||||
}
|
||||
if (rej != null) {
|
||||
self[pRejHandles]![self[pRejHandles]!.length] = rej;
|
||||
}
|
||||
}
|
||||
else if (self[pState] === PromiseState.Fulfilled) {
|
||||
if (ful != null) ful(self[pValue] as T);
|
||||
}
|
||||
else if (self[pState] === PromiseState.Rejected) {
|
||||
if (rej != null) rej(self[pValue]);
|
||||
}
|
||||
}
|
||||
|
||||
export class Promise<T> {
|
||||
public [pState]: PromiseState;
|
||||
public [pValue]?: T | unknown;
|
||||
public [pFulHandles]?: ((val: T) => void)[] = [];
|
||||
public [pRejHandles]?: ((val: T) => void)[] = [];
|
||||
static #InternalPromise = function <T>(this: Promise<T>) {
|
||||
this.#state = PromiseState.Pending;
|
||||
this.#fulHandles = [];
|
||||
this.#rejHandles = [];
|
||||
} as any as new <T>() => Promise<T>;
|
||||
|
||||
static {
|
||||
this.#InternalPromise.prototype = this.prototype;
|
||||
}
|
||||
|
||||
#state: PromiseState;
|
||||
#value?: T | unknown;
|
||||
#fulHandles?: ((val: T) => void)[] = [];
|
||||
#rejHandles?: ((val: T) => void)[] = [];
|
||||
|
||||
#fulfill(val: any) {
|
||||
if (this.#state !== PromiseState.Pending) return;
|
||||
if (this === val) throw new Error("A promise may not be fulfilled with itself");
|
||||
|
||||
if (val != null && typeof val.then === "function") {
|
||||
val.then(
|
||||
(val: any) => this.#fulfill(val),
|
||||
(err: any) => this.#reject(err),
|
||||
);
|
||||
}
|
||||
else {
|
||||
this.#value = val;
|
||||
this.#state = PromiseState.Fulfilled;
|
||||
|
||||
const handles = this.#fulHandles!;
|
||||
|
||||
for (let i = 0; i < handles.length; i++) {
|
||||
handles[i](val);
|
||||
}
|
||||
|
||||
this.#fulHandles = undefined;
|
||||
this.#rejHandles = undefined;
|
||||
}
|
||||
}
|
||||
#reject(val: any) {
|
||||
if (this.#state !== PromiseState.Pending) return;
|
||||
if (this === val) throw new Error("A promise may not be rejected with itself");
|
||||
|
||||
if (val != null && typeof val.then === "function") {
|
||||
val.then(
|
||||
(val: any) => this.#reject(val),
|
||||
(err: any) => this.#reject(err),
|
||||
);
|
||||
}
|
||||
else {
|
||||
this.#value = val;
|
||||
this.#state = PromiseState.Rejected;
|
||||
|
||||
const handles = this.#rejHandles!;
|
||||
|
||||
for (let i = 0; i < handles.length; i++) {
|
||||
handles[i](val);
|
||||
}
|
||||
|
||||
this.#fulHandles = undefined;
|
||||
this.#rejHandles = undefined;
|
||||
}
|
||||
}
|
||||
#handle(ful?: (val: T) => void, rej?: (err: any) => void) {
|
||||
if (this.#state === PromiseState.Pending) {
|
||||
if (ful != null) {
|
||||
this.#fulHandles![this.#fulHandles!.length] = ful;
|
||||
}
|
||||
if (rej != null) {
|
||||
this.#rejHandles![this.#rejHandles!.length] = rej;
|
||||
}
|
||||
}
|
||||
else if (this.#state === PromiseState.Fulfilled) {
|
||||
if (ful != null) ful(this.#value as T);
|
||||
}
|
||||
else if (this.#state === PromiseState.Rejected) {
|
||||
if (rej != null) rej(this.#value);
|
||||
}
|
||||
}
|
||||
|
||||
public then<Res>(ful?: (val: T) => Res, rej?: (err: any) => Res) {
|
||||
if (typeof ful !== "function") ful = undefined;
|
||||
if (typeof rej !== "function") rej = undefined;
|
||||
|
||||
const promise = makePromise<Res>();
|
||||
const promise = new Promise.#InternalPromise<Res>();
|
||||
|
||||
handle(this,
|
||||
this.#handle(
|
||||
val => next(() => {
|
||||
if (ful == null) fulfill(promise, val);
|
||||
if (ful == null) promise.#fulfill(val);
|
||||
else {
|
||||
try { fulfill(promise, ful(val)); }
|
||||
catch (e) { reject(promise, e); }
|
||||
try { promise.#fulfill(ful(val)); }
|
||||
catch (e) { promise.#reject(e); }
|
||||
}
|
||||
}),
|
||||
err => next(() => {
|
||||
if (rej == null) reject(promise, err);
|
||||
if (rej == null) promise.#reject(err);
|
||||
else {
|
||||
try { fulfill(promise, rej(err)); }
|
||||
catch (e) { reject(promise, e); }
|
||||
try { promise.#fulfill(rej(err)); }
|
||||
catch (e) { promise.#reject(e); }
|
||||
}
|
||||
}),
|
||||
);
|
||||
@ -134,21 +131,19 @@ export class Promise<T> {
|
||||
}
|
||||
|
||||
public constructor(fn: (fulfil: (val: T) => void, reject: (err: unknown) => void) => void) {
|
||||
this[pState] = PromiseState.Pending;
|
||||
this.#state = PromiseState.Pending;
|
||||
|
||||
fn(val => fulfill(this, val), err => reject(this, err));
|
||||
fn(val => this.#fulfill(val), err => this.#reject(err));
|
||||
}
|
||||
|
||||
public static resolve(val: any) {
|
||||
const res = makePromise();
|
||||
fulfill(res, val);
|
||||
const res = new this.#InternalPromise();
|
||||
res.#fulfill(val);
|
||||
return res;
|
||||
}
|
||||
public static reject(val: any) {
|
||||
const res = makePromise();
|
||||
reject(res, val);
|
||||
const res = new this.#InternalPromise();
|
||||
res.#reject(val);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
func.setCallable(Promise, false);
|
||||
|
@ -1,42 +1,40 @@
|
||||
import { Array } from "../values/array.ts";
|
||||
import { func, map, symbol } from "../primordials.ts";
|
||||
import { func, map } from "../primordials.ts";
|
||||
import { symbols } from "../utils.ts";
|
||||
|
||||
const mapKey: unique symbol = symbol.makeSymbol("Set.impl") as any;
|
||||
|
||||
export class Set<T> {
|
||||
private [mapKey]: InstanceType<typeof map>;
|
||||
#map: InstanceType<typeof map>;
|
||||
|
||||
public get size() {
|
||||
return this[mapKey].size();
|
||||
return this.#map.size();
|
||||
}
|
||||
|
||||
public has(key: T): boolean {
|
||||
return this[mapKey].has(key);
|
||||
return this.#map.has(key);
|
||||
}
|
||||
public add(val: T) {
|
||||
this[mapKey].set(val, true);
|
||||
this.#map.set(val, true);
|
||||
return this;
|
||||
}
|
||||
public delete(val: T): boolean {
|
||||
if (!this[mapKey].has(val)) return false;
|
||||
if (!this.#map.has(val)) return false;
|
||||
else {
|
||||
this[mapKey].delete(val);
|
||||
this.#map.delete(val);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public clear() {
|
||||
this[mapKey].clear();
|
||||
this.#map.clear();
|
||||
}
|
||||
|
||||
public keys(): T[] {
|
||||
return this[mapKey].keys();
|
||||
return this.#map.keys();
|
||||
}
|
||||
public values(): T[] {
|
||||
return this[mapKey].keys();
|
||||
return this.#map.keys();
|
||||
}
|
||||
public entries(): [T, T][] {
|
||||
const res = this[mapKey].keys();
|
||||
const res = this.#map.keys();
|
||||
|
||||
for (let i = 0; i < res.length; i++) {
|
||||
res[i] = [res[i], res[i]];
|
||||
@ -57,7 +55,7 @@ export class Set<T> {
|
||||
}
|
||||
|
||||
public constructor(iterable?: Iterable<T>) {
|
||||
const _map = this[mapKey] = new map();
|
||||
const _map = this.#map = new map();
|
||||
|
||||
if (iterable != null) {
|
||||
if (Array.isArray(iterable)) {
|
||||
@ -77,28 +75,28 @@ export class Set<T> {
|
||||
}
|
||||
|
||||
export class WeakSet<T> {
|
||||
private [mapKey]: InstanceType<typeof map>;
|
||||
#map: InstanceType<typeof map>;
|
||||
|
||||
public has(key: T): boolean {
|
||||
return this[mapKey].has(key);
|
||||
return this.#map.has(key);
|
||||
}
|
||||
public add(val: T) {
|
||||
this[mapKey].set(val, true);
|
||||
this.#map.set(val, true);
|
||||
return this;
|
||||
}
|
||||
public delete(val: T): boolean {
|
||||
if (!this[mapKey].has(val)) return false;
|
||||
if (!this.#map.has(val)) return false;
|
||||
else {
|
||||
this[mapKey].delete(val);
|
||||
this.#map.delete(val);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public clear() {
|
||||
this[mapKey].clear();
|
||||
this.#map.clear();
|
||||
}
|
||||
|
||||
public constructor(iterable?: Iterable<T>) {
|
||||
const _map = this[mapKey] = new map(true);
|
||||
const _map = this.#map = new map(true);
|
||||
|
||||
if (iterable != null) {
|
||||
if (Array.isArray(iterable)) {
|
||||
@ -116,6 +114,3 @@ export class WeakSet<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func.setCallable(Set, false);
|
||||
func.setCallable(WeakSet, false);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { func, json, object } from "../primordials.ts";
|
||||
import { func, object, print } from "../primordials.ts";
|
||||
|
||||
export const console = {};
|
||||
|
||||
|
@ -4,6 +4,14 @@ export interface InternalBuffer {
|
||||
length: number;
|
||||
[buffSymbol]: "buffer";
|
||||
}
|
||||
export interface InternalSocket {
|
||||
read(onRes: (data: Uint8Array) => void, onErr: (err: unknown) => void, onDone: () => void): void;
|
||||
write(data: Uint8Array, onRes: () => void, onErr: (err: unknown) => void): void;
|
||||
}
|
||||
export interface InternalServer {
|
||||
bind(address: string, onRes: () => void, onErr: (err: unknown) => void): void;
|
||||
next(onRes: (socket: InternalSocket) => void, onErr: (err: unknown) => void, onDone: () => void): void;
|
||||
}
|
||||
|
||||
export interface SymbolPrimordials {
|
||||
makeSymbol(name: string): symbol;
|
||||
@ -72,7 +80,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;
|
||||
@ -82,6 +90,9 @@ export interface JSONPrimordials {
|
||||
parse(data: string): any;
|
||||
stringify(data: any): string;
|
||||
}
|
||||
export interface NetPrimordials {
|
||||
server(): InternalServer;
|
||||
}
|
||||
|
||||
export interface Primordials {
|
||||
symbol: SymbolPrimordials;
|
||||
@ -91,6 +102,7 @@ export interface Primordials {
|
||||
function: FunctionPrimordials;
|
||||
json: JSONPrimordials;
|
||||
buffer: BufferPrimordials;
|
||||
net: NetPrimordials;
|
||||
map: new (weak?: boolean) => {
|
||||
get(key: any): any;
|
||||
has(key: any): boolean;
|
||||
@ -111,6 +123,7 @@ export interface Primordials {
|
||||
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
|
||||
@ -126,6 +139,7 @@ export const {
|
||||
buffer,
|
||||
function: func,
|
||||
json,
|
||||
net: socket,
|
||||
map,
|
||||
regex,
|
||||
setGlobalPrototypes,
|
||||
@ -134,6 +148,8 @@ export const {
|
||||
next,
|
||||
schedule,
|
||||
setIntrinsic,
|
||||
print,
|
||||
} = primordials;
|
||||
|
||||
export type regex = InstanceType<typeof regex>;
|
||||
export const self = (globalThis as any);
|
||||
|
63
lib/src/stdlib/sockets.ts
Normal file
63
lib/src/stdlib/sockets.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import { Promise as Promise } from "./classes/promise";
|
||||
import { func, InternalServer, InternalSocket, symbol } from "./primordials";
|
||||
import { Error } from "./values/errors";
|
||||
|
||||
const socketToken = symbol.makeSymbol("ServerSocket.token");
|
||||
|
||||
export class ServerSocket {
|
||||
#internal: InternalSocket;
|
||||
|
||||
public read() {
|
||||
return new Promise((ful, rej) => {
|
||||
this.#internal.read(ful, rej, ful as any);
|
||||
});
|
||||
}
|
||||
public write(data: Uint8Array) {
|
||||
return new Promise<void>((ful, rej) => {
|
||||
this.#internal.write(data, ful, rej);
|
||||
});
|
||||
}
|
||||
|
||||
public next() {
|
||||
return new Promise((ful, rej) => {
|
||||
this.#internal.read(
|
||||
data => ful({ value: data, done: false }),
|
||||
rej,
|
||||
() => ful({ value: undefined, done: true })
|
||||
);
|
||||
});
|
||||
}
|
||||
public [Symbol.iterator](): this {
|
||||
return this;
|
||||
}
|
||||
|
||||
public constructor(token: typeof socketToken, socket: InternalSocket) {
|
||||
if (token !== socketToken) throw new Error("Invalid token for creation");
|
||||
this.#internal = socket;
|
||||
}
|
||||
}
|
||||
|
||||
export class Server {
|
||||
#internal: InternalServer;
|
||||
|
||||
public bind(address: string) {
|
||||
return new Promise<void>((res, rej) => this.#internal.bind(address, res, rej));
|
||||
}
|
||||
|
||||
public next() {
|
||||
return new Promise((ful, rej) => {
|
||||
this.#internal.next(
|
||||
data => ful({ value: new ServerSocket(socketToken, data), done: false }),
|
||||
rej,
|
||||
() => ful({ value: undefined, done: true })
|
||||
);
|
||||
});
|
||||
}
|
||||
public [Symbol.iterator](): this {
|
||||
return this;
|
||||
}
|
||||
|
||||
public constructor(server: InternalServer) {
|
||||
this.#internal = server;
|
||||
}
|
||||
}
|
@ -58,6 +58,10 @@ export const Function = (() => {
|
||||
public static compile(src: string, filename?: string) {
|
||||
return compile(String(src), filename);
|
||||
}
|
||||
|
||||
public static newTarget() {
|
||||
return func.target(1);
|
||||
}
|
||||
}
|
||||
|
||||
func.setCallable(Function, true);
|
||||
|
@ -1,14 +1,12 @@
|
||||
import { func, regex, symbol } from "../primordials.ts";
|
||||
import { func, regex } from "../primordials.ts";
|
||||
import { String } from "./string.ts";
|
||||
import { type ReplaceRange } from "../utils.ts";
|
||||
import { applyReplaces } from "../utils.ts";
|
||||
import { applySplits } from "../utils.ts";
|
||||
import { symbols } from "../utils.ts";
|
||||
|
||||
const regexKey: unique symbol = symbol.makeSymbol("RegExp.impl") as any;
|
||||
|
||||
export class RegExp {
|
||||
private [regexKey]!: InstanceType<typeof regex>;
|
||||
#regex!: InstanceType<typeof regex>;
|
||||
|
||||
public readonly source!: string;
|
||||
public readonly flags!: string;
|
||||
@ -70,14 +68,14 @@ export class RegExp {
|
||||
this.unicodeSets = unicodeSets;
|
||||
this.sticky = sticky;
|
||||
|
||||
this[regexKey] = new regex(source, multiline, ignoreCase, dotall, unicode, unicodeSets);
|
||||
this.#regex = new regex(source, multiline, ignoreCase, dotall, unicode, unicodeSets);
|
||||
}
|
||||
|
||||
public exec(target: string) {
|
||||
const useLast = this.global || this.sticky;
|
||||
const start = useLast ? this.lastIndex : 0;
|
||||
|
||||
const match = this[regexKey].exec(target, start, this.indices);
|
||||
const match = this.#regex.exec(target, start, this.indices);
|
||||
if (match != null && !(this.sticky && match.matches.index !== start)) {
|
||||
if (useLast) this.lastIndex = match.end;
|
||||
return match.matches;
|
||||
@ -92,7 +90,7 @@ export class RegExp {
|
||||
|
||||
public [symbols.split](target: string, limit?: number) {
|
||||
return applySplits(target, limit, offset => {
|
||||
const val = this[regexKey].exec(target, offset, false);
|
||||
const val = this.#regex.exec(target, offset, false);
|
||||
if (val == null) return undefined;
|
||||
|
||||
return { start: val.matches.index!, end: val.end };
|
||||
@ -100,7 +98,7 @@ export class RegExp {
|
||||
}
|
||||
public [symbols.replace](target: string, replacer: any) {
|
||||
const matches: ReplaceRange[] = [];
|
||||
const regex = this[regexKey];
|
||||
const regex = this.#regex;
|
||||
|
||||
if (this.global) {
|
||||
let offset = 0;
|
||||
@ -136,3 +134,6 @@ export class RegExp {
|
||||
return applyReplaces(target, matches, replacer, regex.groupCount() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
func.setCallable(RegExp, true);
|
||||
func.setConstructable(RegExp, true);
|
||||
|
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)));
|
@ -47,6 +47,8 @@ export default function babel(next: Compiler): Compiler {
|
||||
availablePlugins["transform-exponentiation-operator"],
|
||||
|
||||
// ES2015
|
||||
availablePlugins["transform-regenerator"],
|
||||
availablePlugins["transform-arrow-functions"],
|
||||
availablePlugins["transform-block-scoping"],
|
||||
availablePlugins["transform-classes"],
|
||||
availablePlugins["transform-computed-properties"],
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -2,19 +2,13 @@ package me.topchetoeu.j2s.repl;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
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 +16,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.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.lib.Compilers;
|
||||
import me.topchetoeu.j2s.lib.StdLib;
|
||||
import me.topchetoeu.j2s.lib.debug.DebugServer;
|
||||
import me.topchetoeu.j2s.lib.debug.Debugger;
|
||||
import me.topchetoeu.j2s.lib.debug.SimpleDebugHandler;
|
||||
import me.topchetoeu.j2s.lib.debug.SimpleDebugger;
|
||||
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 +45,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,691 +104,11 @@ 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 -> {
|
||||
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;
|
||||
}));
|
||||
|
||||
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() {
|
||||
var env = new Environment();
|
||||
private static Environment createESEnv() {
|
||||
var env = StdLib.apply(null);
|
||||
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(Compilers.jsCompiler(), env, Compilers::babelCompiler));
|
||||
|
||||
var glob = Value.global(env);
|
||||
|
||||
@ -856,76 +116,31 @@ 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");
|
||||
glob.defineOwnField(null, "dofile", new NativeFunction("dofile", args -> {
|
||||
var file = args.get(0).toString(args.env);
|
||||
var filename = new Filename("file", new File(file).getAbsolutePath());
|
||||
|
||||
try {
|
||||
var src = Reading.streamToString(new FileInputStream(file));
|
||||
return Compiler.get(args.env).compile(args.env, filename, src, v -> v).apply(args.env, Value.UNDEFINED);
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
throw EngineException.ofError("IOException", e.getMessage());
|
||||
}
|
||||
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,19 +0,0 @@
|
||||
package me.topchetoeu.j2s.repl;
|
||||
|
||||
import me.topchetoeu.j2s.compilation.json.JSON;
|
||||
import me.topchetoeu.j2s.compilation.json.JSONMap;
|
||||
|
||||
public class V8Error {
|
||||
public final String message;
|
||||
|
||||
public V8Error(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.stringify(new JSONMap().set("error", new JSONMap()
|
||||
.set("message", message)
|
||||
));
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
package me.topchetoeu.j2s.repl.debug;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import me.topchetoeu.j2s.runtime.debug.DebugHandler;
|
||||
|
||||
public interface Debugger extends DebugHandler {
|
||||
void close();
|
||||
|
||||
void enable(V8Message msg) throws IOException;
|
||||
void disable(V8Message msg) throws IOException;
|
||||
|
||||
void setBreakpointByUrl(V8Message msg) throws IOException;
|
||||
void removeBreakpoint(V8Message msg) throws IOException;
|
||||
void continueToLocation(V8Message msg) throws IOException;
|
||||
|
||||
void getScriptSource(V8Message msg) throws IOException;
|
||||
void getPossibleBreakpoints(V8Message msg) throws IOException;
|
||||
|
||||
void resume(V8Message msg) throws IOException;
|
||||
void pause(V8Message msg) throws IOException;
|
||||
|
||||
void stepInto(V8Message msg) throws IOException;
|
||||
void stepOut(V8Message msg) throws IOException;
|
||||
void stepOver(V8Message msg) throws IOException;
|
||||
|
||||
void setPauseOnExceptions(V8Message msg) throws IOException;
|
||||
|
||||
void evaluateOnCallFrame(V8Message msg) throws IOException;
|
||||
|
||||
void getProperties(V8Message msg) throws IOException;
|
||||
void releaseObjectGroup(V8Message msg) throws IOException;
|
||||
void releaseObject(V8Message msg) throws IOException;
|
||||
void callFunctionOn(V8Message msg) throws IOException;
|
||||
|
||||
void runtimeEnable(V8Message msg) throws IOException;
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package me.topchetoeu.j2s.repl.debug;
|
||||
|
||||
public interface DebuggerProvider {
|
||||
Debugger getDebugger(WebSocket socket, HttpRequest req);
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
package me.topchetoeu.j2s.repl.debug;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.util.HashMap;
|
||||
import java.util.IllegalFormatException;
|
||||
import java.util.Map;
|
||||
|
||||
import me.topchetoeu.j2s.common.Reading;
|
||||
|
||||
public class HttpRequest {
|
||||
public final String method;
|
||||
public final String path;
|
||||
public final Map<String, String> headers;
|
||||
public final OutputStream out;
|
||||
|
||||
|
||||
public void writeCode(int code, String name) {
|
||||
try { out.write(("HTTP/1.1 " + code + " " + name + "\r\n").getBytes()); }
|
||||
catch (IOException e) { }
|
||||
}
|
||||
public void writeHeader(String name, String value) {
|
||||
try { out.write((name + ": " + value + "\r\n").getBytes()); }
|
||||
catch (IOException e) { }
|
||||
}
|
||||
public void writeLastHeader(String name, String value) {
|
||||
try { out.write((name + ": " + value + "\r\n\r\n").getBytes()); }
|
||||
catch (IOException e) { }
|
||||
}
|
||||
public void writeHeadersEnd() {
|
||||
try { out.write("\n".getBytes()); }
|
||||
catch (IOException e) { }
|
||||
}
|
||||
|
||||
public void writeResponse(int code, String name, String type, byte[] data) {
|
||||
writeCode(code, name);
|
||||
writeHeader("Content-Type", type);
|
||||
writeLastHeader("Content-Length", data.length + "");
|
||||
try {
|
||||
out.write(data);
|
||||
out.close();
|
||||
}
|
||||
catch (IOException e) { }
|
||||
}
|
||||
public void writeResponse(int code, String name, String type, InputStream data) {
|
||||
writeResponse(code, name, type, Reading.streamToBytes(data));
|
||||
}
|
||||
|
||||
public HttpRequest(String method, String path, Map<String, String> headers, OutputStream out) {
|
||||
this.method = method;
|
||||
this.path = path;
|
||||
this.headers = headers;
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
// We dont need no http library
|
||||
public static HttpRequest read(Socket socket) {
|
||||
try {
|
||||
var str = socket.getInputStream();
|
||||
var lines = new BufferedReader(new InputStreamReader(str));
|
||||
var line = lines.readLine();
|
||||
var i1 = line.indexOf(" ");
|
||||
var i2 = line.indexOf(" ", i1 + 1);
|
||||
|
||||
if (i1 < 0 || i2 < 0) {
|
||||
socket.close();
|
||||
return null;
|
||||
}
|
||||
|
||||
var method = line.substring(0, i1).trim().toUpperCase();
|
||||
var path = line.substring(i1 + 1, i2).trim();
|
||||
var headers = new HashMap<String, String>();
|
||||
|
||||
while (!(line = lines.readLine()).isEmpty()) {
|
||||
var i = line.indexOf(":");
|
||||
if (i < 0) continue;
|
||||
var name = line.substring(0, i).trim().toLowerCase();
|
||||
var value = line.substring(i + 1).trim();
|
||||
|
||||
if (name.length() == 0) continue;
|
||||
headers.put(name, value);
|
||||
}
|
||||
|
||||
if (headers.containsKey("content-length")) {
|
||||
try {
|
||||
var i = Integer.parseInt(headers.get("content-length"));
|
||||
str.skip(i);
|
||||
}
|
||||
catch (IllegalFormatException e) { /* ¯\_(ツ)_/¯ */ }
|
||||
}
|
||||
|
||||
return new HttpRequest(method, path, headers, socket.getOutputStream());
|
||||
}
|
||||
catch (IOException | NullPointerException e) { return null; }
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +0,0 @@
|
||||
package me.topchetoeu.j2s.repl.debug;
|
||||
|
||||
import me.topchetoeu.j2s.compilation.json.JSON;
|
||||
import me.topchetoeu.j2s.compilation.json.JSONMap;
|
||||
|
||||
public class V8Error {
|
||||
public final String message;
|
||||
|
||||
public V8Error(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.stringify(new JSONMap().set("error", new JSONMap()
|
||||
.set("message", message)
|
||||
));
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
package me.topchetoeu.j2s.repl.debug;
|
||||
|
||||
import me.topchetoeu.j2s.compilation.json.JSON;
|
||||
import me.topchetoeu.j2s.compilation.json.JSONMap;
|
||||
|
||||
public class V8Event {
|
||||
public final String name;
|
||||
public final JSONMap params;
|
||||
|
||||
public V8Event(String name, JSONMap params) {
|
||||
this.name = name;
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.stringify(new JSONMap()
|
||||
.set("method", name)
|
||||
.set("params", params)
|
||||
);
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
package me.topchetoeu.j2s.repl.debug;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import me.topchetoeu.j2s.compilation.json.JSON;
|
||||
import me.topchetoeu.j2s.compilation.json.JSONElement;
|
||||
import me.topchetoeu.j2s.compilation.json.JSONMap;
|
||||
|
||||
public class V8Message {
|
||||
public final String name;
|
||||
public final int id;
|
||||
public final JSONMap params;
|
||||
|
||||
public V8Message(String name, int id, Map<String, JSONElement> params) {
|
||||
this.name = name;
|
||||
this.params = new JSONMap(params);
|
||||
this.id = id;
|
||||
}
|
||||
public V8Result respond(JSONMap result) {
|
||||
return new V8Result(id, result);
|
||||
}
|
||||
public V8Result respond() {
|
||||
return new V8Result(id, new JSONMap());
|
||||
}
|
||||
|
||||
public V8Message(JSONMap raw) {
|
||||
if (!raw.isNumber("id")) throw new IllegalArgumentException("Expected number property 'id'.");
|
||||
if (!raw.isString("method")) throw new IllegalArgumentException("Expected string property 'method'.");
|
||||
|
||||
this.name = raw.string("method");
|
||||
this.id = (int)raw.number("id");
|
||||
this.params = raw.contains("params") ? raw.map("params") : new JSONMap();
|
||||
}
|
||||
public V8Message(String raw) {
|
||||
this(JSON.parse(null, raw).map());
|
||||
}
|
||||
|
||||
public JSONMap toMap() {
|
||||
var res = new JSONMap();
|
||||
return res;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.stringify(new JSONMap()
|
||||
.set("method", name)
|
||||
.set("params", params)
|
||||
.set("id", id)
|
||||
);
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
package me.topchetoeu.j2s.repl.debug;
|
||||
|
||||
import me.topchetoeu.j2s.compilation.json.JSON;
|
||||
import me.topchetoeu.j2s.compilation.json.JSONMap;
|
||||
|
||||
public class V8Result {
|
||||
public final int id;
|
||||
public final JSONMap result;
|
||||
|
||||
public V8Result(int id, JSONMap result) {
|
||||
this.id = id;
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.stringify(new JSONMap()
|
||||
.set("id", id)
|
||||
.set("result", result)
|
||||
);
|
||||
}
|
||||
}
|
@ -1,186 +0,0 @@
|
||||
package me.topchetoeu.j2s.repl.debug;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
|
||||
import me.topchetoeu.j2s.repl.debug.WebSocketMessage.Type;
|
||||
|
||||
public class WebSocket implements AutoCloseable {
|
||||
public long maxLength = 1 << 20;
|
||||
|
||||
private Socket socket;
|
||||
private boolean closed = false;
|
||||
|
||||
private OutputStream out() throws IOException {
|
||||
return socket.getOutputStream();
|
||||
}
|
||||
private InputStream in() throws IOException {
|
||||
return socket.getInputStream();
|
||||
}
|
||||
|
||||
private long readLen(int byteLen) throws IOException {
|
||||
long res = 0;
|
||||
|
||||
if (byteLen == 126) {
|
||||
res |= in().read() << 8;
|
||||
res |= in().read();
|
||||
return res;
|
||||
}
|
||||
else if (byteLen == 127) {
|
||||
res |= in().read() << 56;
|
||||
res |= in().read() << 48;
|
||||
res |= in().read() << 40;
|
||||
res |= in().read() << 32;
|
||||
res |= in().read() << 24;
|
||||
res |= in().read() << 16;
|
||||
res |= in().read() << 8;
|
||||
res |= in().read();
|
||||
return res;
|
||||
}
|
||||
else return byteLen;
|
||||
}
|
||||
private byte[] readMask(boolean has) throws IOException {
|
||||
if (has) {
|
||||
return new byte[] {
|
||||
(byte)in().read(),
|
||||
(byte)in().read(),
|
||||
(byte)in().read(),
|
||||
(byte)in().read()
|
||||
};
|
||||
}
|
||||
else return new byte[4];
|
||||
}
|
||||
|
||||
private void writeLength(int len) throws IOException {
|
||||
if (len < 126) {
|
||||
out().write((int)len);
|
||||
}
|
||||
else if (len <= 0xFFFF) {
|
||||
out().write(126);
|
||||
out().write((int)(len >> 8) & 0xFF);
|
||||
out().write((int)len & 0xFF);
|
||||
}
|
||||
else {
|
||||
out().write(127);
|
||||
out().write(0);
|
||||
out().write(0);
|
||||
out().write(0);
|
||||
out().write(0);
|
||||
out().write((len >> 24) & 0xFF);
|
||||
out().write((len >> 16) & 0xFF);
|
||||
out().write((len >> 8) & 0xFF);
|
||||
out().write(len & 0xFF);
|
||||
}
|
||||
}
|
||||
private synchronized void write(int type, byte[] data) throws IOException {
|
||||
out().write(type | 0x80);
|
||||
writeLength(data.length);
|
||||
out().write(data);
|
||||
}
|
||||
|
||||
public void send(String data) throws IOException {
|
||||
if (closed) throw new IllegalStateException("Websocket is closed.");
|
||||
write(1, data.getBytes());
|
||||
}
|
||||
public void send(byte[] data) throws IOException {
|
||||
if (closed) throw new IllegalStateException("Websocket is closed.");
|
||||
write(2, data);
|
||||
}
|
||||
public void send(WebSocketMessage msg) throws IOException {
|
||||
if (msg.type == Type.Binary) send(msg.binaryData());
|
||||
else send(msg.textData());
|
||||
}
|
||||
public void send(Object data) throws IOException {
|
||||
if (closed) throw new IllegalStateException("Websocket is closed.");
|
||||
write(1, data.toString().getBytes());
|
||||
}
|
||||
|
||||
public void close(String reason) {
|
||||
if (socket != null) {
|
||||
try {
|
||||
write(8, reason.getBytes());
|
||||
socket.close();
|
||||
}
|
||||
catch (Throwable e) { }
|
||||
}
|
||||
|
||||
socket = null;
|
||||
closed = true;
|
||||
}
|
||||
public void close() {
|
||||
close("");
|
||||
}
|
||||
|
||||
private WebSocketMessage fail(String reason) {
|
||||
System.out.println("WebSocket Error: " + reason);
|
||||
close(reason);
|
||||
return null;
|
||||
}
|
||||
|
||||
private byte[] readData() throws IOException {
|
||||
var maskLen = in().read();
|
||||
var hasMask = (maskLen & 0x80) != 0;
|
||||
var len = (int)readLen(maskLen & 0x7F);
|
||||
var mask = readMask(hasMask);
|
||||
|
||||
if (len > maxLength) fail("WebSocket Error: client exceeded configured max message size");
|
||||
else {
|
||||
var buff = new byte[len];
|
||||
|
||||
if (in().read(buff) < len) fail("WebSocket Error: payload too short");
|
||||
else {
|
||||
for (int i = 0; i < len; i++) {
|
||||
buff[i] ^= mask[(int)(i % 4)];
|
||||
}
|
||||
return buff;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public WebSocketMessage receive() throws IOException {
|
||||
var data = new ByteArrayOutputStream();
|
||||
var type = 0;
|
||||
|
||||
while (socket != null && !closed) {
|
||||
var finId = in().read();
|
||||
if (finId < 0) break;
|
||||
var fin = (finId & 0x80) != 0;
|
||||
int id = finId & 0x0F;
|
||||
|
||||
if (id == 0x8) { close(); return null; }
|
||||
if (id >= 0x8) {
|
||||
if (!fin) return fail("WebSocket Error: client-sent control frame was fragmented");
|
||||
if (id == 0x9) write(0xA, data.toByteArray());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type == 0) type = id;
|
||||
if (type == 0) return fail("WebSocket Error: client used opcode 0x00 for first fragment");
|
||||
|
||||
var buff = readData();
|
||||
if (buff == null) break;
|
||||
|
||||
if (data.size() + buff.length > maxLength) return fail("WebSocket Error: client exceeded configured max message size");
|
||||
data.write(buff);
|
||||
|
||||
if (!fin) continue;
|
||||
var raw = data.toByteArray();
|
||||
|
||||
if (type == 1) {
|
||||
return new WebSocketMessage(new String(raw));
|
||||
}
|
||||
else return new WebSocketMessage(raw);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public WebSocket(Socket socket) {
|
||||
this.socket = socket;
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
package me.topchetoeu.j2s.repl.debug;
|
||||
|
||||
public class WebSocketMessage {
|
||||
public static enum Type {
|
||||
Text,
|
||||
Binary,
|
||||
}
|
||||
|
||||
public final Type type;
|
||||
private final Object data;
|
||||
|
||||
public final String textData() {
|
||||
if (type != Type.Text) throw new IllegalStateException("Message is not text.");
|
||||
return (String)data;
|
||||
}
|
||||
public final byte[] binaryData() {
|
||||
if (type != Type.Binary) throw new IllegalStateException("Message is not binary.");
|
||||
return (byte[])data;
|
||||
}
|
||||
|
||||
public WebSocketMessage(String data) {
|
||||
this.type = Type.Text;
|
||||
this.data = data;
|
||||
}
|
||||
public WebSocketMessage(byte[] data) {
|
||||
this.type = Type.Binary;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
@ -1,30 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>J2S Debugger</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>J2S Debugger</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
This is the debugger of J2S. It implement the <a href="https://chromedevtools.github.io/devtools-protocol/1-2/">V8 Debugging protocol</a>,
|
||||
so you can use the devtools in chrome.<br>
|
||||
The debugger is still in early development, so please report any issues to
|
||||
<a href="https://git.topcheto.eu/topchetoeu/j2s/issues">the git repo</a>.
|
||||
</p>
|
||||
<p>
|
||||
This is the debugger of J2S. It implement the <a href="https://chromedevtools.github.io/devtools-protocol/1-2/">V8 Debugging protocol</a>,
|
||||
so you can use the devtools in chrome.<br>
|
||||
The debugger is still in early development, so please report any issues to
|
||||
<a href="https://git.topcheto.eu/topchetoeu/j2s/issues">the git repo</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Here are the available entrypoints:
|
||||
<ul>
|
||||
<li><a href="json/version">/json/version</a> - version and other stuff about the J2S engine</li>
|
||||
<li><a href="json/list">/json/list</a> - a list of all entrypoints</li>
|
||||
<li><a href="json/protocol">/json/protocol</a> - documentation of the implemented V8 protocol</li>
|
||||
<li>/(any target) - websocket entrypoints for debugging</li>
|
||||
</ul>
|
||||
</p>
|
||||
<p>
|
||||
Here are the available entrypoints:
|
||||
<ul>
|
||||
<li><a href="json/version">/json/version</a> - version and other stuff about the J2S engine</li>
|
||||
<li><a href="json/list">/json/list</a> - a list of all entrypoints</li>
|
||||
<li><a href="json/protocol">/json/protocol</a> - documentation of the implemented V8 protocol</li>
|
||||
<li>/(any target) - websocket entrypoints for debugging</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Running ${NAME} v${VERSION} by ${AUTHOR}
|
||||
</p>
|
||||
<p>
|
||||
Running ${NAME} v${VERSION} by ${AUTHOR}
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
File diff suppressed because it is too large
Load Diff
@ -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, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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) {
|
||||
|
@ -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)";
|
||||
|
@ -29,7 +29,7 @@ import me.topchetoeu.j2s.runtime.values.primitives.SymbolValue;
|
||||
import me.topchetoeu.j2s.runtime.values.primitives.VoidValue;
|
||||
import me.topchetoeu.j2s.runtime.values.primitives.numbers.NumberValue;
|
||||
|
||||
public abstract class Value {
|
||||
public interface Value {
|
||||
public static enum State {
|
||||
NORMAL(true, true, true),
|
||||
NON_EXTENDABLE(false, true, true),
|
||||
@ -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<>();
|
||||
@ -79,28 +76,43 @@ public abstract class Value {
|
||||
public abstract StringValue type();
|
||||
public abstract boolean isPrimitive();
|
||||
|
||||
public final boolean isNaN() {
|
||||
public default boolean isNaN() {
|
||||
return this == NumberValue.NAN || this instanceof NumberValue num && Double.isNaN(num.getDouble());
|
||||
}
|
||||
|
||||
public Value apply(Environment env, Value self, Value ...args) {
|
||||
public default Value apply(Environment env, Value self, Value ...args) {
|
||||
throw EngineException.ofType("Value is not a function");
|
||||
}
|
||||
public Value construct(Environment env, Value target, Value ...args) {
|
||||
public default Value construct(Environment env, Value target, Value ...args) {
|
||||
throw EngineException.ofType("Value is not a constructor");
|
||||
}
|
||||
|
||||
public final Value constructNoSelf(Environment env, Value ...args) {
|
||||
public default Value constructNoSelf(Environment env, Value ...args) {
|
||||
return this.construct(env, this, args);
|
||||
}
|
||||
|
||||
public Value toPrimitive(Environment env);
|
||||
public NumberValue toNumber(Environment env);
|
||||
public String toString(Environment env);
|
||||
public boolean toBoolean();
|
||||
|
||||
public abstract Value toPrimitive(Environment env);
|
||||
public abstract NumberValue toNumber(Environment env);
|
||||
public abstract String toString(Environment env);
|
||||
public abstract boolean toBoolean();
|
||||
public Member getOwnMember(Environment env, KeyCache key);
|
||||
public Set<String> getOwnMembers(Environment env, boolean onlyEnumerable);
|
||||
public Set<SymbolValue> getOwnSymbolMembers(Environment env, boolean onlyEnumerable);
|
||||
public boolean defineOwnField(Environment env, KeyCache key, Value val, Boolean writable, Boolean enumerable, Boolean configurable);
|
||||
public boolean defineOwnProperty(Environment env, KeyCache key, Optional<FunctionValue> get, Optional<FunctionValue> set, Boolean enumerable, Boolean configurable);
|
||||
public boolean deleteOwnMember(Environment env, KeyCache key);
|
||||
|
||||
public final boolean isInstanceOf(Environment env, Value proto) {
|
||||
public ObjectValue getPrototype(Environment env);
|
||||
public boolean setPrototype(Environment env, ObjectValue val);
|
||||
|
||||
public State getState();
|
||||
|
||||
public void preventExtensions();
|
||||
public void seal();
|
||||
public void freeze();
|
||||
|
||||
public default boolean isInstanceOf(Environment env, Value proto) {
|
||||
for (var val = getPrototype(env); val != null; val = val.getPrototype(env)) {
|
||||
if (val.equals(proto)) return true;
|
||||
}
|
||||
@ -108,91 +120,75 @@ public abstract class Value {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract Member getOwnMember(Environment env, KeyCache key);
|
||||
public abstract Set<String> getOwnMembers(Environment env, boolean onlyEnumerable);
|
||||
public abstract Set<SymbolValue> getOwnSymbolMembers(Environment env, boolean onlyEnumerable);
|
||||
public abstract boolean defineOwnField(Environment env, KeyCache key, Value val, Boolean writable, Boolean enumerable, Boolean configurable);
|
||||
public abstract boolean defineOwnProperty(Environment env, KeyCache key, Optional<FunctionValue> get, Optional<FunctionValue> set, Boolean enumerable, Boolean configurable);
|
||||
public abstract boolean deleteOwnMember(Environment env, KeyCache key);
|
||||
|
||||
public abstract ObjectValue getPrototype(Environment env);
|
||||
public abstract boolean setPrototype(Environment env, ObjectValue val);
|
||||
|
||||
public abstract State getState();
|
||||
|
||||
public abstract void preventExtensions();
|
||||
public abstract void seal();
|
||||
public abstract void freeze();
|
||||
|
||||
public final Member getOwnMember(Environment env, Value key) {
|
||||
public default Member getOwnMember(Environment env, Value key) {
|
||||
return getOwnMember(env, new KeyCache(key));
|
||||
}
|
||||
public final Member getOwnMember(Environment env, String key) {
|
||||
public default Member getOwnMember(Environment env, String key) {
|
||||
return getOwnMember(env, new KeyCache(key));
|
||||
}
|
||||
public final Member getOwnMember(Environment env, int key) {
|
||||
public default Member getOwnMember(Environment env, int key) {
|
||||
return getOwnMember(env, new KeyCache(key));
|
||||
}
|
||||
public final Member getOwnMember(Environment env, double key) {
|
||||
public default Member getOwnMember(Environment env, double key) {
|
||||
return getOwnMember(env, new KeyCache(key));
|
||||
}
|
||||
|
||||
public final boolean defineOwnProperty(Environment env, Value key, Optional<FunctionValue> get, Optional<FunctionValue> set, Boolean enumerable, Boolean configurable) {
|
||||
public default boolean defineOwnProperty(Environment env, Value key, Optional<FunctionValue> get, Optional<FunctionValue> set, Boolean enumerable, Boolean configurable) {
|
||||
return defineOwnProperty(env, new KeyCache(key), get, set, enumerable, configurable);
|
||||
}
|
||||
public final boolean defineOwnProperty(Environment env, String key, Optional<FunctionValue> get, Optional<FunctionValue> set, Boolean enumerable, Boolean configurable) {
|
||||
public default boolean defineOwnProperty(Environment env, String key, Optional<FunctionValue> get, Optional<FunctionValue> set, Boolean enumerable, Boolean configurable) {
|
||||
return defineOwnProperty(env, new KeyCache(key), get, set, enumerable, configurable);
|
||||
}
|
||||
public final boolean defineOwnProperty(Environment env, int key, Optional<FunctionValue> get, Optional<FunctionValue> set, Boolean enumerable, Boolean configurable) {
|
||||
public default boolean defineOwnProperty(Environment env, int key, Optional<FunctionValue> get, Optional<FunctionValue> set, Boolean enumerable, Boolean configurable) {
|
||||
return defineOwnProperty(env, new KeyCache(key), get, set, enumerable, configurable);
|
||||
}
|
||||
public final boolean defineOwnProperty(Environment env, double key, Optional<FunctionValue> get, Optional<FunctionValue> set, Boolean enumerable, Boolean configurable) {
|
||||
public default boolean defineOwnProperty(Environment env, double key, Optional<FunctionValue> get, Optional<FunctionValue> set, Boolean enumerable, Boolean configurable) {
|
||||
return defineOwnProperty(env, new KeyCache(key), get, set, enumerable, configurable);
|
||||
}
|
||||
|
||||
public final boolean defineOwnField(Environment env, Value key, Value val, Boolean writable, Boolean enumerable, Boolean configurable) {
|
||||
public default boolean defineOwnField(Environment env, Value key, Value val, Boolean writable, Boolean enumerable, Boolean configurable) {
|
||||
return defineOwnField(env, new KeyCache(key), val, writable, enumerable, configurable);
|
||||
}
|
||||
public final boolean defineOwnField(Environment env, String key, Value val, Boolean writable, Boolean enumerable, Boolean configurable) {
|
||||
public default boolean defineOwnField(Environment env, String key, Value val, Boolean writable, Boolean enumerable, Boolean configurable) {
|
||||
return defineOwnField(env, new KeyCache(key), val, writable, enumerable, configurable);
|
||||
}
|
||||
public final boolean defineOwnField(Environment env, int key, Value val, Boolean writable, Boolean enumerable, Boolean configurable) {
|
||||
public default boolean defineOwnField(Environment env, int key, Value val, Boolean writable, Boolean enumerable, Boolean configurable) {
|
||||
return defineOwnField(env, new KeyCache(key), val, writable, enumerable, configurable);
|
||||
}
|
||||
public final boolean defineOwnField(Environment env, double key, Value val, Boolean writable, Boolean enumerable, Boolean configurable) {
|
||||
public default boolean defineOwnField(Environment env, double key, Value val, Boolean writable, Boolean enumerable, Boolean configurable) {
|
||||
return defineOwnField(env, new KeyCache(key), val, writable, enumerable, configurable);
|
||||
}
|
||||
|
||||
public final boolean defineOwnField(Environment env, KeyCache key, Value val) {
|
||||
public default boolean defineOwnField(Environment env, KeyCache key, Value val) {
|
||||
return defineOwnField(env, key, val, true, true, true);
|
||||
}
|
||||
public final boolean defineOwnField(Environment env, Value key, Value val) {
|
||||
public default boolean defineOwnField(Environment env, Value key, Value val) {
|
||||
return defineOwnField(env, new KeyCache(key), val);
|
||||
}
|
||||
public final boolean defineOwnField(Environment env, String key, Value val) {
|
||||
public default boolean defineOwnField(Environment env, String key, Value val) {
|
||||
return defineOwnField(env, new KeyCache(key), val);
|
||||
}
|
||||
public final boolean defineOwnField(Environment env, int key, Value val) {
|
||||
public default boolean defineOwnField(Environment env, int key, Value val) {
|
||||
return defineOwnField(env, new KeyCache(key), val);
|
||||
}
|
||||
public final boolean defineOwnField(Environment env, double key, Value val) {
|
||||
public default boolean defineOwnField(Environment env, double key, Value val) {
|
||||
return defineOwnField(env, new KeyCache(key), val);
|
||||
}
|
||||
|
||||
public final boolean deleteOwnMember(Environment env, Value key) {
|
||||
public default boolean deleteOwnMember(Environment env, Value key) {
|
||||
return deleteOwnMember(env, new KeyCache(key));
|
||||
}
|
||||
public final boolean deleteOwnMember(Environment env, String key) {
|
||||
public default boolean deleteOwnMember(Environment env, String key) {
|
||||
return deleteOwnMember(env, new KeyCache(key));
|
||||
}
|
||||
public final boolean deleteOwnMember(Environment env, int key) {
|
||||
public default boolean deleteOwnMember(Environment env, int key) {
|
||||
return deleteOwnMember(env, new KeyCache(key));
|
||||
}
|
||||
public final boolean deleteOwnMember(Environment env, double key) {
|
||||
public default boolean deleteOwnMember(Environment env, double key) {
|
||||
return deleteOwnMember(env, new KeyCache(key));
|
||||
}
|
||||
|
||||
public final Value getMemberOrNull(Environment env, KeyCache key) {
|
||||
public default Value getMemberOrNull(Environment env, KeyCache key) {
|
||||
for (Value obj = this; obj != null; obj = obj.getPrototype(env)) {
|
||||
var member = obj.getOwnMember(env, key);
|
||||
if (member != null) return member.get(env, this);
|
||||
@ -200,38 +196,38 @@ public abstract class Value {
|
||||
|
||||
return null;
|
||||
}
|
||||
public final Value getMemberOrNull(Environment env, Value key) {
|
||||
public default Value getMemberOrNull(Environment env, Value key) {
|
||||
return getMemberOrNull(env, new KeyCache(key));
|
||||
}
|
||||
public final Value getMemberOrNull(Environment env, String key) {
|
||||
public default Value getMemberOrNull(Environment env, String key) {
|
||||
return getMemberOrNull(env, new KeyCache(key));
|
||||
}
|
||||
public final Value getMemberOrNull(Environment env, int key) {
|
||||
public default Value getMemberOrNull(Environment env, int key) {
|
||||
return getMemberOrNull(env, new KeyCache(key));
|
||||
}
|
||||
public final Value getMemberOrNull(Environment env, double key) {
|
||||
public default Value getMemberOrNull(Environment env, double key) {
|
||||
return getMemberOrNull(env, new KeyCache(key));
|
||||
}
|
||||
|
||||
public final Value getMember(Environment env, KeyCache key) {
|
||||
public default Value getMember(Environment env, KeyCache key) {
|
||||
var res = getMemberOrNull(env, key);
|
||||
if (res != null) return res;
|
||||
else return Value.UNDEFINED;
|
||||
}
|
||||
public final Value getMember(Environment env, Value key) {
|
||||
public default Value getMember(Environment env, Value key) {
|
||||
return getMember(env, new KeyCache(key));
|
||||
}
|
||||
public final Value getMember(Environment env, String key) {
|
||||
public default Value getMember(Environment env, String key) {
|
||||
return getMember(env, new KeyCache(key));
|
||||
}
|
||||
public final Value getMember(Environment env, int key) {
|
||||
public default Value getMember(Environment env, int key) {
|
||||
return getMember(env, new KeyCache(key));
|
||||
}
|
||||
public final Value getMember(Environment env, double key) {
|
||||
public default Value getMember(Environment env, double key) {
|
||||
return getMember(env, new KeyCache(key));
|
||||
}
|
||||
|
||||
public final boolean setMember(Environment env, KeyCache key, Value val) {
|
||||
public default boolean setMember(Environment env, KeyCache key, Value val) {
|
||||
for (Value obj = this; obj != null; obj = obj.getPrototype(env)) {
|
||||
var member = obj.getOwnMember(env, key);
|
||||
if (member != null && (member instanceof PropertyMember || obj == this)) {
|
||||
@ -252,20 +248,20 @@ public abstract class Value {
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
public final boolean setMember(Environment env, Value key, Value val) {
|
||||
public default boolean setMember(Environment env, Value key, Value val) {
|
||||
return setMember(env, new KeyCache(key), val);
|
||||
}
|
||||
public final boolean setMember(Environment env, String key, Value val) {
|
||||
public default boolean setMember(Environment env, String key, Value val) {
|
||||
return setMember(env, new KeyCache(key), val);
|
||||
}
|
||||
public final boolean setMember(Environment env, int key, Value val) {
|
||||
public default boolean setMember(Environment env, int key, Value val) {
|
||||
return setMember(env, new KeyCache(key), val);
|
||||
}
|
||||
public final boolean setMember(Environment env, double key, Value val) {
|
||||
public default boolean setMember(Environment env, double key, Value val) {
|
||||
return setMember(env, new KeyCache(key), val);
|
||||
}
|
||||
|
||||
public final boolean setMemberIfExists(Environment env, KeyCache key, Value val) {
|
||||
public default boolean setMemberIfExists(Environment env, KeyCache key, Value val) {
|
||||
for (Value obj = this; obj != null; obj = obj.getPrototype(env)) {
|
||||
var member = obj.getOwnMember(env, key);
|
||||
if (member != null) {
|
||||
@ -279,20 +275,20 @@ public abstract class Value {
|
||||
|
||||
return false;
|
||||
}
|
||||
public final boolean setMemberIfExists(Environment env, Value key, Value val) {
|
||||
public default boolean setMemberIfExists(Environment env, Value key, Value val) {
|
||||
return setMemberIfExists(env, new KeyCache(key), val);
|
||||
}
|
||||
public final boolean setMemberIfExists(Environment env, String key, Value val) {
|
||||
public default boolean setMemberIfExists(Environment env, String key, Value val) {
|
||||
return setMemberIfExists(env, new KeyCache(key), val);
|
||||
}
|
||||
public final boolean setMemberIfExists(Environment env, int key, Value val) {
|
||||
public default boolean setMemberIfExists(Environment env, int key, Value val) {
|
||||
return setMemberIfExists(env, new KeyCache(key), val);
|
||||
}
|
||||
public final boolean setMemberIfExists(Environment env, double key, Value val) {
|
||||
public default boolean setMemberIfExists(Environment env, double key, Value val) {
|
||||
return setMemberIfExists(env, new KeyCache(key), val);
|
||||
}
|
||||
|
||||
public final boolean hasMember(Environment env, KeyCache key, boolean own) {
|
||||
public default boolean hasMember(Environment env, KeyCache key, boolean own) {
|
||||
for (Value obj = this; obj != null; obj = obj.getPrototype(env)) {
|
||||
if (obj.getOwnMember(env, key) != null) return true;
|
||||
if (own) return false;
|
||||
@ -300,37 +296,37 @@ public abstract class Value {
|
||||
|
||||
return false;
|
||||
}
|
||||
public final boolean hasMember(Environment env, Value key, boolean own) {
|
||||
public default boolean hasMember(Environment env, Value key, boolean own) {
|
||||
return hasMember(env, new KeyCache(key), own);
|
||||
}
|
||||
public final boolean hasMember(Environment env, String key, boolean own) {
|
||||
public default boolean hasMember(Environment env, String key, boolean own) {
|
||||
return hasMember(env, new KeyCache(key), own);
|
||||
}
|
||||
public final boolean hasMember(Environment env, int key, boolean own) {
|
||||
public default boolean hasMember(Environment env, int key, boolean own) {
|
||||
return hasMember(env, new KeyCache(key), own);
|
||||
}
|
||||
public final boolean hasMember(Environment env, double key, boolean own) {
|
||||
public default boolean hasMember(Environment env, double key, boolean own) {
|
||||
return hasMember(env, new KeyCache(key), own);
|
||||
}
|
||||
|
||||
public final boolean deleteMember(Environment env, KeyCache key) {
|
||||
public default boolean deleteMember(Environment env, KeyCache key) {
|
||||
if (!hasMember(env, key, true)) return true;
|
||||
return deleteOwnMember(env, key);
|
||||
}
|
||||
public final boolean deleteMember(Environment env, Value key) {
|
||||
public default boolean deleteMember(Environment env, Value key) {
|
||||
return deleteMember(env, new KeyCache(key));
|
||||
}
|
||||
public final boolean deleteMember(Environment env, String key) {
|
||||
public default boolean deleteMember(Environment env, String key) {
|
||||
return deleteMember(env, new KeyCache(key));
|
||||
}
|
||||
public final boolean deleteMember(Environment env, int key) {
|
||||
public default boolean deleteMember(Environment env, int key) {
|
||||
return deleteMember(env, new KeyCache(key));
|
||||
}
|
||||
public final boolean deleteMember(Environment env, double key) {
|
||||
public default boolean deleteMember(Environment env, double key) {
|
||||
return deleteMember(env, new KeyCache(key));
|
||||
}
|
||||
|
||||
public final Set<String> getMembers(Environment env, boolean own, boolean onlyEnumerable) {
|
||||
public default Set<String> getMembers(Environment env, boolean own, boolean onlyEnumerable) {
|
||||
var res = new LinkedHashSet<String>();
|
||||
var protos = new ArrayList<Value>();
|
||||
|
||||
@ -347,7 +343,7 @@ public abstract class Value {
|
||||
|
||||
return res;
|
||||
}
|
||||
public final Set<SymbolValue> getSymbolMembers(Environment env, boolean own, boolean onlyEnumerable) {
|
||||
public default Set<SymbolValue> getSymbolMembers(Environment env, boolean own, boolean onlyEnumerable) {
|
||||
var res = new LinkedHashSet<SymbolValue>();
|
||||
var protos = new ArrayList<Value>();
|
||||
|
||||
@ -365,24 +361,24 @@ public abstract class Value {
|
||||
return res;
|
||||
}
|
||||
|
||||
public final Value getMemberPath(Environment env, String ...path) {
|
||||
public default Value getMemberPath(Environment env, String ...path) {
|
||||
var res = this;
|
||||
for (var key : path) res = res.getMember(env, key);
|
||||
return res;
|
||||
}
|
||||
public final Value getMemberPath(Environment env, Value ...path) {
|
||||
public default Value getMemberPath(Environment env, Value ...path) {
|
||||
var res = this;
|
||||
for (var key : path) res = res.getMember(env, key);
|
||||
return res;
|
||||
}
|
||||
public final ObjectValue getMemberDescriptor(Environment env, Value key) {
|
||||
public default ObjectValue getMemberDescriptor(Environment env, Value key) {
|
||||
var member = getOwnMember(env, new KeyCache(key));
|
||||
|
||||
if (member != null) return member.descriptor(env, this);
|
||||
else return null;
|
||||
}
|
||||
|
||||
public Iterable<Object> toIterable(Environment env) {
|
||||
public default Iterable<Object> toIterable(Environment env) {
|
||||
return () -> {
|
||||
if (!(this instanceof FunctionValue)) return Collections.emptyIterator();
|
||||
var func = (FunctionValue)this;
|
||||
@ -421,30 +417,29 @@ public abstract class Value {
|
||||
};
|
||||
}
|
||||
|
||||
public void callWith(Environment env, Iterable<? extends Value> it) {
|
||||
public default void callWith(Environment env, Iterable<? extends Value> it) {
|
||||
for (var el : it) {
|
||||
this.apply(env, Value.UNDEFINED, el);
|
||||
}
|
||||
}
|
||||
public void callWithAsync(Environment env, Iterable<? extends Value> it, boolean async) {
|
||||
public default void callWithAsync(Environment env, Iterable<? extends Value> it, boolean async) {
|
||||
for (var el : it) {
|
||||
env.get(EventLoop.KEY).pushMsg(() -> this.apply(env, Value.UNDEFINED, el), true);
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
public List<String> toReadableLines(Environment env, HashSet<ObjectValue> passed) {
|
||||
public default List<String> toReadableLines(Environment env, HashSet<ObjectValue> passed) {
|
||||
return Arrays.asList(toString(env));
|
||||
}
|
||||
|
||||
public final String toReadable(Environment ext) {
|
||||
public default String toReadable(Environment ext) {
|
||||
return String.join("\n", toReadableLines(ext, new HashSet<>()));
|
||||
}
|
||||
|
||||
public static final Value global(Environment env) {
|
||||
public static Value global(Environment env) {
|
||||
return env.initFrom(GLOBAL, () -> new ObjectValue());
|
||||
}
|
||||
public static final Map<String, Value> intrinsics(Environment env) {
|
||||
public static Map<String, Value> intrinsics(Environment env) {
|
||||
return env.initFrom(INTRINSICS, () -> new HashMap<>());
|
||||
}
|
||||
|
||||
@ -461,7 +456,7 @@ public abstract class Value {
|
||||
});
|
||||
}
|
||||
|
||||
public static final boolean lessOrEqual(Environment env, Value a, Value b) {
|
||||
public static boolean lessOrEqual(Environment env, Value a, Value b) {
|
||||
a = a.toPrimitive(env);
|
||||
b = b.toPrimitive(env);
|
||||
|
||||
@ -476,7 +471,7 @@ public abstract class Value {
|
||||
else return na.getDouble() <= nb.getDouble();
|
||||
}
|
||||
}
|
||||
public static final boolean greaterOrEqual(Environment env, Value a, Value b) {
|
||||
public static boolean greaterOrEqual(Environment env, Value a, Value b) {
|
||||
a = a.toPrimitive(env);
|
||||
b = b.toPrimitive(env);
|
||||
|
||||
@ -491,7 +486,7 @@ public abstract class Value {
|
||||
else return na.getDouble() >= nb.getDouble();
|
||||
}
|
||||
}
|
||||
public static final boolean less(Environment env, Value a, Value b) {
|
||||
public static boolean less(Environment env, Value a, Value b) {
|
||||
a = a.toPrimitive(env);
|
||||
b = b.toPrimitive(env);
|
||||
|
||||
@ -506,7 +501,7 @@ public abstract class Value {
|
||||
else return na.getDouble() < nb.getDouble();
|
||||
}
|
||||
}
|
||||
public static final boolean greater(Environment env, Value a, Value b) {
|
||||
public static boolean greater(Environment env, Value a, Value b) {
|
||||
a = a.toPrimitive(env);
|
||||
b = b.toPrimitive(env);
|
||||
|
||||
@ -522,7 +517,7 @@ public abstract class Value {
|
||||
}
|
||||
}
|
||||
|
||||
public static final Value add(Environment env, Value a, Value b) {
|
||||
public static Value add(Environment env, Value a, Value b) {
|
||||
a = a.toPrimitive(env);
|
||||
b = b.toPrimitive(env);
|
||||
|
||||
@ -538,21 +533,21 @@ public abstract class Value {
|
||||
}
|
||||
}
|
||||
|
||||
public static final NumberValue subtract(Environment env, Value a, Value b) {
|
||||
public static NumberValue subtract(Environment env, Value a, Value b) {
|
||||
var na = a.toNumber(env);
|
||||
var nb = b.toNumber(env);
|
||||
|
||||
if (na.isInt() && nb.isInt()) return NumberValue.of(na.getInt() - nb.getInt());
|
||||
else return NumberValue.of(na.getDouble() - nb.getDouble());
|
||||
}
|
||||
public static final NumberValue multiply(Environment env, Value a, Value b) {
|
||||
public static NumberValue multiply(Environment env, Value a, Value b) {
|
||||
var na = a.toNumber(env);
|
||||
var nb = b.toNumber(env);
|
||||
|
||||
if (na.isInt() && nb.isInt()) return NumberValue.of(na.getInt() * nb.getInt());
|
||||
else return NumberValue.of(na.getDouble() * nb.getDouble());
|
||||
}
|
||||
public static final NumberValue divide(Environment env, Value a, Value b) {
|
||||
public static NumberValue divide(Environment env, Value a, Value b) {
|
||||
var na = a.toNumber(env);
|
||||
var nb = b.toNumber(env);
|
||||
|
||||
@ -570,7 +565,7 @@ public abstract class Value {
|
||||
}
|
||||
else return NumberValue.of(na.getDouble() / nb.getDouble());
|
||||
}
|
||||
public static final NumberValue modulo(Environment env, Value a, Value b) {
|
||||
public static NumberValue modulo(Environment env, Value a, Value b) {
|
||||
var na = a.toNumber(env);
|
||||
var nb = b.toNumber(env);
|
||||
|
||||
@ -583,33 +578,33 @@ public abstract class Value {
|
||||
}
|
||||
else return NumberValue.of(na.getDouble() % nb.getDouble());
|
||||
}
|
||||
public static final NumberValue negative(Environment env, Value a) {
|
||||
public static NumberValue negative(Environment env, Value a) {
|
||||
var na = a.toNumber(env);
|
||||
|
||||
if (na.isInt()) return NumberValue.of(-na.getInt());
|
||||
else return NumberValue.of(-na.getDouble());
|
||||
}
|
||||
|
||||
public static final NumberValue and(Environment env, Value a, Value b) {
|
||||
public static NumberValue and(Environment env, Value a, Value b) {
|
||||
return NumberValue.of(a.toNumber(env).getInt() & b.toNumber(env).getInt());
|
||||
}
|
||||
public static final NumberValue or(Environment env, Value a, Value b) {
|
||||
public static NumberValue or(Environment env, Value a, Value b) {
|
||||
return NumberValue.of(a.toNumber(env).getInt() | b.toNumber(env).getInt());
|
||||
}
|
||||
public static final NumberValue xor(Environment env, Value a, Value b) {
|
||||
public static NumberValue xor(Environment env, Value a, Value b) {
|
||||
return NumberValue.of(a.toNumber(env).getInt() ^ b.toNumber(env).getInt());
|
||||
}
|
||||
public static final NumberValue bitwiseNot(Environment env, Value a) {
|
||||
public static NumberValue bitwiseNot(Environment env, Value a) {
|
||||
return NumberValue.of(~a.toNumber(env).getInt());
|
||||
}
|
||||
|
||||
public static final NumberValue shiftLeft(Environment env, Value a, Value b) {
|
||||
public static NumberValue shiftLeft(Environment env, Value a, Value b) {
|
||||
return NumberValue.of(a.toNumber(env).getInt() << b.toNumber(env).getInt());
|
||||
}
|
||||
public static final NumberValue shiftRight(Environment env, Value a, Value b) {
|
||||
public static NumberValue shiftRight(Environment env, Value a, Value b) {
|
||||
return NumberValue.of(a.toNumber(env).getInt() >> b.toNumber(env).getInt());
|
||||
}
|
||||
public static final NumberValue unsignedShiftRight(Environment env, Value a, Value b) {
|
||||
public static NumberValue unsignedShiftRight(Environment env, Value a, Value b) {
|
||||
long _a = a.toNumber(env).getLong() & 0xFFFFFFFF;
|
||||
long _b = b.toNumber(env).getLong() & 0xFFFFFFFF;
|
||||
|
||||
@ -619,7 +614,7 @@ public abstract class Value {
|
||||
return NumberValue.of(_a >>> _b);
|
||||
}
|
||||
|
||||
public static final boolean looseEqual(Environment env, Value a, Value b) {
|
||||
public static boolean looseEqual(Environment env, Value a, Value b) {
|
||||
// In loose equality, null is equivalent to undefined
|
||||
if (a instanceof VoidValue || b instanceof VoidValue) return a instanceof VoidValue && b instanceof VoidValue;
|
||||
|
||||
@ -641,7 +636,7 @@ public abstract class Value {
|
||||
return a.toString(env).equals(b.toString(env));
|
||||
}
|
||||
|
||||
public static final String errorToReadable(Environment env, RuntimeException err, String prefix) {
|
||||
public static String errorToReadable(Environment env, RuntimeException err, String prefix) {
|
||||
prefix = prefix == null ? "Uncaught" : "Uncaught " + prefix;
|
||||
if (err instanceof EngineException ee) {
|
||||
if (env == null) env = ee.env;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user