Compare commits

...

18 Commits

Author SHA1 Message Date
45292990b1
some thesis fixes 2025-01-29 14:12:57 +02:00
2dcfff689a
add page numbers to thesis 2025-01-28 14:33:34 +02:00
2619e50e9b
fix typos in thesis 2025-01-28 13:25:09 +02:00
4bfda6b0a1
bump
All checks were successful
tagged-release / Tagged Release (push) Successful in 5m38s
2025-01-28 13:11:29 +02:00
58d6110e1d
fix: stack overflow!! 2025-01-28 13:10:58 +02:00
120e59577d
bump
All checks were successful
tagged-release / Tagged Release (push) Successful in 5m45s
2025-01-28 12:53:14 +02:00
efe123b658
wrong build files 2025-01-28 12:52:53 +02:00
b190367681
add thesis text 2025-01-26 18:07:13 +02:00
e601749866
refactor: make Value interface (again?) 2025-01-24 23:04:05 +02:00
1670b64aaf
bump
Some checks failed
tagged-release / Tagged Release (push) Failing after 3m54s
2025-01-24 22:48:34 +02:00
1548938537
small fixes
Some checks failed
tagged-release / Tagged Release (push) Has been cancelled
2025-01-24 22:46:51 +02:00
e14d85e7a8
whitespaces 2025-01-24 22:45:14 +02:00
4352550ae9
move debugging to lib 2025-01-24 22:42:33 +02:00
3c4d05abd4
restructuring of stdlibs 2025-01-24 22:37:52 +02:00
f16d088646
bump
All checks were successful
tagged-release / Tagged Release (push) Successful in 5m44s
2025-01-24 05:57:33 +02:00
8b1c2a5e4e
separate more stuff into lib project 2025-01-24 05:57:15 +02:00
ee8268b144
fix: for-in not managing stack correctly 2025-01-24 05:47:44 +02:00
fffeac9bac
fix: name was always 'name'
All checks were successful
tagged-release / Tagged Release (push) Successful in 4m14s
2025-01-24 04:18:35 +02:00
96 changed files with 6303 additions and 2754 deletions

View File

@ -3,5 +3,5 @@ repositories {
} }
plugins { plugins {
`kotlin-dsl` `kotlin-dsl`
} }

View File

@ -31,7 +31,7 @@ public class Metadata {
AUTHOR = value; AUTHOR = value;
break; break;
case "name": case "name":
NAME = name; NAME = value;
break; break;
default: default:
throw new RuntimeException(String.format("%s:%s: Unexpected metadata key '%s'", file, line, name)); 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() { public static String version() {
if (VERSION.equals("$" + "{VERSION}")) return "1337-devel"; if (VERSION.equals("${VERSION}")) return "1337-devel";
else return VERSION; else return VERSION;
} }
public static String author() { public static String author() {
if (AUTHOR.equals("$" + "{AUTHOR}")) return "anonymous"; if (AUTHOR.equals("${AUTHOR}")) return "anonymous";
else return AUTHOR; else return AUTHOR;
} }
public static String name() { public static String name() {
if (NAME.equals("$" + "{NAME}")) return "some-product"; if (NAME.equals("${NAME}")) return "some-product";
else return NAME; else return NAME;
} }
} }

View File

@ -4,15 +4,6 @@ plugins {
description = "A compiler of EcmaScript 5 code to J2S bytecode"; 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 { tasks.test {
useJUnitPlatform(); useJUnitPlatform();
} }

View File

@ -53,8 +53,8 @@ public abstract class FunctionNode extends Node {
target.add(scope.define(param.name).index().toSet(false)).setLocation(param.loc()); target.add(scope.define(param.name).index().toSet(false)).setLocation(param.loc());
} }
if (hasSelf) { if (hasSelf) {
target.add(Instruction.loadCalled()); target.add(Instruction.loadCalled());
target.add(scope.define(selfName).index().toSet(false)); target.add(scope.define(selfName).index().toSet(false));
} }
body.compile(target, lastReturn, BreakpointType.NONE); body.compile(target, lastReturn, BreakpointType.NONE);

View File

@ -286,40 +286,40 @@ public final class JavaScript {
} }
public static ParseRes<List<VariableNode>> parseParameters(Source src, int i) { 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, "("); var openParen = Parsing.parseOperator(src, i + n, "(");
if (!openParen.isSuccess()) return openParen.chainError(src.loc(i + n), "Expected a parameter list"); if (!openParen.isSuccess()) return openParen.chainError(src.loc(i + n), "Expected a parameter list");
n += openParen.n; n += openParen.n;
var params = new ArrayList<VariableNode>(); var params = new ArrayList<VariableNode>();
var closeParen = Parsing.parseOperator(src, i + n, ")"); var closeParen = Parsing.parseOperator(src, i + n, ")");
n += closeParen.n; n += closeParen.n;
if (!closeParen.isSuccess()) { if (!closeParen.isSuccess()) {
while (true) { while (true) {
n += Parsing.skipEmpty(src, i + n); n += Parsing.skipEmpty(src, i + n);
var param = VariableNode.parse(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"); if (!param.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a parameter or a closing brace");
n += param.n; n += param.n;
n += Parsing.skipEmpty(src, i + n); n += Parsing.skipEmpty(src, i + n);
params.add(param.result); params.add(param.result);
if (src.is(i + n, ",")) { if (src.is(i + n, ",")) {
n++; n++;
n += Parsing.skipEmpty(src, i + n); n += Parsing.skipEmpty(src, i + n);
} }
if (src.is(i + n, ")")) { if (src.is(i + n, ")")) {
n++; n++;
break; break;
} }
} }
} }
return ParseRes.res(params, n); return ParseRes.res(params, n);
} }
} }

View File

@ -62,6 +62,7 @@ public class ForInNode extends Node {
end.set(endI + 1); end.set(endI + 1);
LabelContext.popLoop(target.env, label); LabelContext.popLoop(target.env, label);
target.add(Instruction.discard());
if (pollute) target.add(Instruction.pushUndefined()); if (pollute) target.add(Instruction.pushUndefined());
} }

9
doc/text/.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
/*
!/img
!/src
!/.gitignore
!/build
!/document.md
!/README.md
!/requirements.md
!/template.html

1
doc/text/README.md Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

14
doc/text/requirements.md Normal file
View 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
View 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("<", "&lt")
:gsub(">", "&gt")
);
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 { "&shy;" }, 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
View 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
View 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
View 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
View 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>

View File

@ -1,4 +1,4 @@
project_group = me.topchetoeu.j2s project_group = me.topchetoeu.j2s
project_name = j2s project_name = j2s
project_version = 0.10.6-beta project_version = 0.10.11-beta
main_class = me.topchetoeu.j2s.repl.SimpleRepl main_class = me.topchetoeu.j2s.repl.SimpleRepl

View File

@ -1,15 +1,14 @@
import com.github.gradle.node.npm.task.NpmTask; import com.github.gradle.node.npm.task.NpmTask;
plugins { plugins {
id("common"); id("common-java");
id("com.github.node-gradle.node") version "5.0.0"; id("com.github.node-gradle.node") version "5.0.0";
} }
tasks.compileJava { dependencies {
enabled = false; implementation(project(":common"));
} implementation(project(":compilation"));
tasks.classes { implementation(project(":runtime"));
enabled = false;
} }
node { node {
@ -27,16 +26,38 @@ tasks.register<NpmTask>("compileStdlib") {
args.set(listOf("run", "build-env")); args.set(listOf("run", "build-env"));
} }
tasks.register<NpmTask>("compileTranspiler") { tasks.register<NpmTask>("compileBabel") {
dependsOn("npmInstall"); dependsOn("npmInstall");
inputs.files("rollup.config.js"); inputs.files("rollup.config.js");
inputs.dir("src/transpiler"); inputs.dir("src/transpiler");
outputs.files("build/js/transpiler.js"); outputs.files("build/js/babel.js");
// nom nom tasty ram // nom nom tasty ram
environment.put("NODE_OPTIONS", "--max-old-space-size=4096"); 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 { tasks.jar {
@ -50,7 +71,9 @@ tasks.jar {
tasks.processResources { tasks.processResources {
dependsOn("compileStdlib"); dependsOn("compileStdlib");
dependsOn("compileTranspiler"); dependsOn("compileTypescript");
dependsOn("compileBabel");
dependsOn("compileCoffee");
from("build/js") { from("build/js") {
into("lib"); into("lib");
@ -58,11 +81,4 @@ tasks.processResources {
from("src/lib") { from("src/lib") {
into("lib"); into("lib");
} }
filesMatching("metadata.json", {
expand(
"version" to properties["project_version"].toString(),
"name" to properties["project_name"].toString(),
);
})
} }

View File

@ -1,7 +1,9 @@
{ {
"scripts": { "scripts": {
"build-env": "rollup -c --environment INPUT:src/stdlib/_entry.ts,OUTPUT:build/js/stdlib.js,POLYFILLS:src/polyfills", "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": { "dependencies": {
"@babel/core": "^7.26.0", "@babel/core": "^7.26.0",
@ -15,6 +17,7 @@
"typescript": "^5.7.2" "typescript": "^5.7.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/plugin-transform-class-properties": "^7.25.9", "@babel/plugin-transform-class-properties": "^7.25.9",
"@babel/plugin-transform-runtime": "^7.25.9", "@babel/plugin-transform-runtime": "^7.25.9",
"@babel/plugin-transform-typescript": "^7.25.9", "@babel/plugin-transform-typescript": "^7.25.9",

View File

@ -34,7 +34,6 @@ const construct = (input, output) => defineConfig({
optimizeConstEnums: true, optimizeConstEnums: true,
allowDeclareFields: true, allowDeclareFields: true,
}], }],
["@babel/plugin-transform-class-properties"],
["@babel/plugin-transform-runtime", { ["@babel/plugin-transform-runtime", {
moduleName: shouldPolyfill() ? "!polyfills:" : undefined, moduleName: shouldPolyfill() ? "!polyfills:" : undefined,
version: "^7.24.0", version: "^7.24.0",
@ -48,6 +47,7 @@ const construct = (input, output) => defineConfig({
assumptions: { assumptions: {
ignoreToPrimitiveHint: true, ignoreToPrimitiveHint: true,
noClassCalls: true, noClassCalls: true,
privateFieldsAsProperties: true,
}, },
env: { env: {
@ -56,6 +56,7 @@ const construct = (input, output) => defineConfig({
babelHelpers: "runtime", babelHelpers: "runtime",
plugins: [ plugins: [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-transform-arrow-functions", "@babel/plugin-transform-arrow-functions",
"@babel/plugin-transform-block-scoping", "@babel/plugin-transform-block-scoping",
"@babel/plugin-transform-classes", "@babel/plugin-transform-classes",
@ -77,7 +78,7 @@ const construct = (input, output) => defineConfig({
"@babel/plugin-transform-optional-chaining", "@babel/plugin-transform-optional-chaining",
"@babel/plugin-transform-logical-assignment-operators", "@babel/plugin-transform-logical-assignment-operators",
"@babel/plugin-transform-numeric-separator", "@babel/plugin-transform-numeric-separator",
"@babel/plugin-transform-class-properties", "@babel/plugin-transform-private-methods",
"@babel/plugin-transform-class-static-block", "@babel/plugin-transform-class-static-block",
"@babel/plugin-transform-regenerator", "@babel/plugin-transform-regenerator",

View File

@ -22,3 +22,16 @@ declare interface PromiseConstructor {
resolve<T>(val: T): Promise<Awaited<T>>; resolve<T>(val: T): Promise<Awaited<T>>;
reject<T>(err: unknown): Promise<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>;
}

View File

@ -1,12 +1,12 @@
declare interface NormalIterationData<T> { declare interface NormalIterationData<T> {
value: T; value: T;
done: true; done: false;
} }
declare interface DoneIterationData<T> { declare interface DoneIterationData<T> {
value: 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> { declare interface Iterator<T, Return = unknown, Next = unknown> {
next(): IterationData<T, Return>; next(): IterationData<T, Return>;

View 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;
}
}

View File

@ -1,4 +1,4 @@
package me.topchetoeu.j2s.repl; package me.topchetoeu.j2s.lib;
import java.util.HashSet; import java.util.HashSet;
import java.util.stream.Collectors; import java.util.stream.Collectors;

View File

@ -1,4 +1,4 @@
package me.topchetoeu.j2s.repl.mapping; package me.topchetoeu.j2s.lib;
import java.util.function.Function; import java.util.function.Function;

View 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;
}
}

View 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);
}
}

View File

@ -1,4 +1,4 @@
package me.topchetoeu.j2s.repl.buffers; package me.topchetoeu.j2s.lib.buffers;
public final class Int32ArrayValue extends TypedArrayValue { public final class Int32ArrayValue extends TypedArrayValue {
@Override protected int onGet(int i) { @Override protected int onGet(int i) {

View File

@ -1,4 +1,4 @@
package me.topchetoeu.j2s.repl.buffers; package me.topchetoeu.j2s.lib.buffers;
public final class Int8ArrayValue extends TypedArrayValue { public final class Int8ArrayValue extends TypedArrayValue {
@Override protected int onGet(int i) { @Override protected int onGet(int i) {

View File

@ -1,4 +1,4 @@
package me.topchetoeu.j2s.repl.buffers; package me.topchetoeu.j2s.lib.buffers;
import java.util.WeakHashMap; import java.util.WeakHashMap;

View File

@ -1,4 +1,4 @@
package me.topchetoeu.j2s.repl.buffers; package me.topchetoeu.j2s.lib.buffers;
public final class Uint8ArrayValue extends TypedArrayValue { public final class Uint8ArrayValue extends TypedArrayValue {
@Override protected int onGet(int i) { @Override protected int onGet(int i) {

View File

@ -1,4 +1,4 @@
package me.topchetoeu.j2s.repl.debug; package me.topchetoeu.j2s.lib.debug;
import java.io.IOException; import java.io.IOException;
import java.io.UncheckedIOException; 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.JSON;
import me.topchetoeu.j2s.compilation.json.JSONList; import me.topchetoeu.j2s.compilation.json.JSONList;
import me.topchetoeu.j2s.compilation.json.JSONMap; 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 class DebugServer {
public static String browserDisplayName = Metadata.name() + "/" + Metadata.version(); public static String browserDisplayName = Metadata.name() + "/" + Metadata.version();

View 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;
}

View File

@ -0,0 +1,5 @@
package me.topchetoeu.j2s.lib.debug;
public interface DebuggerProvider {
Debugger getDebugger(WebSocket socket, HttpRequest req);
}

View 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; }
}
}

View File

@ -1,4 +1,4 @@
package me.topchetoeu.j2s.repl.debug; package me.topchetoeu.j2s.lib.debug;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; 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.SymbolValue;
import me.topchetoeu.j2s.runtime.values.primitives.numbers.NumberValue; 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 static final class ScopeMember extends FieldMember {
public final Frame frame; public final Frame frame;
public final int i; public final int i;

View File

@ -1,4 +1,4 @@
package me.topchetoeu.j2s.repl.debug; package me.topchetoeu.j2s.lib.debug;
import java.util.HashMap; import java.util.HashMap;
import java.util.WeakHashMap; import java.util.WeakHashMap;
@ -69,14 +69,8 @@ public class SimpleDebugHandler implements DebugHandler {
if (debugger != null) debugger.onFunctionLoad(func, map); if (debugger != null) debugger.onFunctionLoad(func, map);
} }
private SimpleDebugHandler(boolean enabled) {
if (enabled) {
sources = new HashMap<>();
maps = new WeakHashMap<>();
}
}
public SimpleDebugHandler() { public SimpleDebugHandler() {
this(true); sources = new HashMap<>();
maps = new WeakHashMap<>();
} }
} }

View File

@ -1,4 +1,4 @@
package me.topchetoeu.j2s.repl.debug; package me.topchetoeu.j2s.lib.debug;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@ -24,8 +24,8 @@ import me.topchetoeu.j2s.compilation.json.JSON;
import me.topchetoeu.j2s.compilation.json.JSONElement; import me.topchetoeu.j2s.compilation.json.JSONElement;
import me.topchetoeu.j2s.compilation.json.JSONList; import me.topchetoeu.j2s.compilation.json.JSONList;
import me.topchetoeu.j2s.compilation.json.JSONMap; import me.topchetoeu.j2s.compilation.json.JSONMap;
import me.topchetoeu.j2s.repl.JSONConverter; import me.topchetoeu.j2s.lib.Compilers;
import me.topchetoeu.j2s.repl.SimpleRepl; import me.topchetoeu.j2s.lib.JSONConverter;
import me.topchetoeu.j2s.runtime.Compiler; import me.topchetoeu.j2s.runtime.Compiler;
import me.topchetoeu.j2s.runtime.Engine; import me.topchetoeu.j2s.runtime.Engine;
import me.topchetoeu.j2s.runtime.EventLoop; import me.topchetoeu.j2s.runtime.EventLoop;
@ -625,7 +625,7 @@ public class SimpleDebugger implements Debugger {
env.remove(DebugHandler.KEY); env.remove(DebugHandler.KEY);
env.remove(EventLoop.KEY); env.remove(EventLoop.KEY);
env.remove(Value.GLOBAL); env.remove(Value.GLOBAL);
env.add(Compiler.KEY, SimpleRepl.DEFAULT_COMPILER); env.add(Compiler.KEY, Compilers.jsCompiler());
env.add(EventLoop.KEY, engine); env.add(EventLoop.KEY, engine);
env.add(Value.GLOBAL, codeFrame.variables); env.add(Value.GLOBAL, codeFrame.variables);
@ -1080,7 +1080,7 @@ public class SimpleDebugger implements Debugger {
frame = getFrame(cf); frame = getFrame(cf);
var map = DebugHandler.get(env).getMap(env, frame.frame.function); var map = DebugHandler.get(env).getMapOrEmpty(env, frame.frame.function);
frame.updateLoc(map.toLocation(frame.frame.codePtr)); frame.updateLoc(map.toLocation(frame.frame.codePtr));
loc = frame.location; loc = frame.location;

View File

@ -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.common.Environment;
import me.topchetoeu.j2s.runtime.Frame; import me.topchetoeu.j2s.runtime.Frame;

View 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)
));
}
}

View 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)
);
}
}

View 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)
);
}
}

View 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)
);
}
}

View 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;
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,3 @@
export default function _classPrivateFieldLooseBase(obj) {
return obj;
}

View 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);
}

View File

@ -1,4 +1,4 @@
import { object } from "../stdlib/primordials.ts"; import { func, object, print } from "../stdlib/primordials.ts";
function _defineProperties(target, arr) { function _defineProperties(target, arr) {
if (!arr) return; if (!arr) return;
@ -31,5 +31,8 @@ export default function _createClass(clazz, instance, nonInstance) {
_defineProperties(clazz.prototype, instance); _defineProperties(clazz.prototype, instance);
_defineProperties(clazz, nonInstance); _defineProperties(clazz, nonInstance);
func.setCallable(clazz, false);
func.setConstructable(clazz, true);
return clazz; return clazz;
} }

View File

@ -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 { Error, RangeError, SyntaxError, TypeError } from "./values/errors.ts";
import { Boolean } from "./values/boolean.ts"; import { Boolean } from "./values/boolean.ts";
import { Function } from "./values/function.ts"; import { Function } from "./values/function.ts";
@ -21,11 +21,6 @@ import { Uint8Array } from "./arrays/Uint8Array.ts";
import { Int32Array } from "./arrays/Int32Array.ts"; import { Int32Array } from "./arrays/Int32Array.ts";
import { TypedArray } from "./arrays/TypedArray.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) { function fixup<T extends Function>(clazz: T) {
object.setPrototype(clazz, Function.prototype); object.setPrototype(clazz, Function.prototype);
object.setPrototype(clazz.prototype as any, Object.prototype); object.setPrototype(clazz.prototype as any, Object.prototype);
@ -72,6 +67,16 @@ target.NaN = Number.NaN;
target.Infinity = Number.POSITIVE_INFINITY; target.Infinity = Number.POSITIVE_INFINITY;
target.encodeURI = encodeURI; target.encodeURI = encodeURI;
target.encodeURIComponent = encodeURIComponent; target.encodeURIComponent = encodeURIComponent;
target.print = print;
target.measure = (func: () => void) => {
const start = now();
try {
return func();
}
finally {
print(`Took ${now() - start}ms`);
}
};
setGlobalPrototypes({ setGlobalPrototypes({
string: String.prototype, string: String.prototype,

View File

@ -1,25 +1,28 @@
import { buffer, type InternalBuffer, map, symbol } from "../primordials.ts"; import { buffer, type InternalBuffer, map, symbol } from "../primordials.ts";
export const abs = new map(true); export const abs = new map(true);
export const abKey: unique symbol = symbol.getSymbol("ArrayBuffer.impl") as any;
export class ArrayBuffer { export class ArrayBuffer {
public [abKey]!: InternalBuffer; #internal!: InternalBuffer;
public get byteLength() { public get byteLength() {
return this[abKey].length; return this.#internal.length;
} }
public get byteOffset() { public get byteOffset() {
return 0; return 0;
} }
public constructor(val: unknown) { public constructor(val: unknown) {
if (buffer.isBuff(val)) this[abKey] = val; if (buffer.isBuff(val)) this.#internal = val;
else this[abKey] = buffer.buff(Number(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); let res = abs.get(buff);
if (res == null) { if (res == null) {
res = new ArrayBuffer(buff); res = new ArrayBuffer(buff);
@ -28,3 +31,11 @@ export function getAB(buff: InternalBuffer): ArrayBuffer {
return res; return res;
} }
const unwrapAB = ArrayBuffer.unwrap;
delete (ArrayBuffer as any).unwrap;
export { wrapAB, unwrapAB };

View File

@ -1,5 +1,5 @@
import { buffer } from "../primordials.ts"; import { buffer } from "../primordials.ts";
import { abstractIgnore, TypedArray, typedArrayFuncs } from "./TypedArray.ts"; import { token, TypedArray, typedArrayFuncs } from "./TypedArray.ts";
const factory = buffer.int32; const factory = buffer.int32;
const funcs = typedArrayFuncs(4, factory); const funcs = typedArrayFuncs(4, factory);
@ -19,7 +19,7 @@ export class Int32Array extends TypedArray {
} }
public constructor(obj: any, start?: number, end?: number) { public constructor(obj: any, start?: number, end?: number) {
super(abstractIgnore); super(token);
return funcs.construct(obj, start, end) as any; return funcs.construct(obj, start, end) as any;
} }
} }

View File

@ -1,9 +1,9 @@
import { buffer, func, type InternalBuffer, object, string, symbol } from "../primordials.ts"; import { buffer, func, type InternalBuffer, object, string, symbol } from "../primordials.ts";
import { symbols, wrapI } from "../utils.ts"; import { symbols, wrapI } from "../utils.ts";
import { Error, TypeError } from "../values/errors.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[]) { export function typedArrayFuncs(perEl: number, constructor: (buff: InternalBuffer, start: number, end: number) => number[]) {
return { return {
@ -56,7 +56,7 @@ export function typedArrayFuncs(perEl: number, constructor: (buff: InternalBuffe
return constructor(buffer.buff(self * perEl), 0, self); return constructor(buffer.buff(self * perEl), 0, self);
} }
if (self instanceof ArrayBuffer) { if (self instanceof ArrayBuffer) {
const internal = self[abKey]; const internal = unwrapAB(self);
if (start === undefined) start = 0; if (start === undefined) start = 0;
if (end === undefined) end = (internal.length / perEl) | 0; if (end === undefined) end = (internal.length / perEl) | 0;
return constructor(internal, start, end); return constructor(internal, start, end);
@ -90,7 +90,7 @@ export function typedArrayFuncs(perEl: number, constructor: (buff: InternalBuffe
export class TypedArray { export class TypedArray {
public get buffer() { public get buffer() {
return getAB(buffer.backer(this as any)); return wrapAB(buffer.backer(this as any));
} }
public get byteOffset(): number { public get byteOffset(): number {
throw new Error("abstract"); throw new Error("abstract");
@ -212,8 +212,8 @@ export class TypedArray {
return this; return this;
} }
public constructor(token?: typeof abstractIgnore) { public constructor(_token?: typeof token) {
if (token !== abstractIgnore) { if (_token !== token) {
throw new TypeError("TypedArray constructor can't be called"); throw new TypeError("TypedArray constructor can't be called");
} }
} }

View File

@ -1,5 +1,5 @@
import { buffer } from "../primordials.ts"; import { buffer } from "../primordials.ts";
import { abstractIgnore, TypedArray, typedArrayFuncs } from "./TypedArray.ts"; import { token, TypedArray, typedArrayFuncs } from "./TypedArray.ts";
const factory = buffer.uint8; const factory = buffer.uint8;
const funcs = typedArrayFuncs(1, factory); const funcs = typedArrayFuncs(1, factory);
@ -26,7 +26,7 @@ export class Uint8Array extends TypedArray {
} }
public constructor(obj: any, start?: number, end?: number) { public constructor(obj: any, start?: number, end?: number) {
super(abstractIgnore); super(token);
return funcs.construct(obj, start, end) as any; return funcs.construct(obj, start, end) as any;
} }
} }

View File

@ -1,13 +1,11 @@
import { now, symbol } from "../primordials.ts"; import { now, number, symbol } from "../primordials.ts";
const timeKey: unique symbol = symbol.makeSymbol("") as any;
export const Date = (() => { export const Date = (() => {
class Date { class Date {
[timeKey]!: number; #time: number;
public constructor() { public constructor() {
this.#time = number.NaN;
} }
public static now() { public static now() {

View File

@ -1,51 +1,49 @@
import { Array } from "../values/array.ts"; import { Array } from "../values/array.ts";
import { func, map, symbol } from "../primordials.ts"; import { func, map } from "../primordials.ts";
import { symbols } from "../utils.ts"; import { symbols } from "../utils.ts";
const mapKey: unique symbol = symbol.makeSymbol("Map.impl") as any;
export class Map<K, V> { export class Map<K, V> {
private [mapKey]: InstanceType<typeof map>; #map: InstanceType<typeof map>;
public get size() { public get size() {
return this[mapKey].size(); return this.#map.size();
} }
public get(key: K): V { public get(key: K): V {
return this[mapKey].get(key); return this.#map.get(key);
} }
public has(key: K): boolean { public has(key: K): boolean {
return this[mapKey].has(key); return this.#map.has(key);
} }
public set(key: K, val: V) { public set(key: K, val: V) {
this[mapKey].set(key, val); this.#map.set(key, val);
return this; return this;
} }
public delete(key: K): boolean { public delete(key: K): boolean {
if (!this[mapKey].has(key)) return false; if (!this.#map.has(key)) return false;
else { else {
this[mapKey].delete(key); this.#map.delete(key);
return true; return true;
} }
} }
public clear() { public clear() {
this[mapKey].clear(); this.#map.clear();
} }
public keys(): K[] { public keys(): K[] {
return this[mapKey].keys(); return this.#map.keys();
} }
public values(): V[] { public values(): V[] {
const res = this[mapKey].keys(); const res = this.#map.keys();
for (let i = 0; i < res.length; i++) { for (let i = 0; i < res.length; i++) {
res[i] = this[mapKey].get(res[i]); res[i] = this.#map.get(res[i]);
} }
return res; return res;
} }
public entries(): [K, V][] { public entries(): [K, V][] {
const res = this[mapKey].keys(); const res = this.#map.keys();
for (let i = 0; i < res.length; i++) { 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; return res;
} }
@ -62,7 +60,7 @@ export class Map<K, V> {
} }
public constructor(iterable?: Iterable<[K, V]>) { public constructor(iterable?: Iterable<[K, V]>) {
const _map = this[mapKey] = new map(); const _map = this.#map = new map();
if (iterable != null) { if (iterable != null) {
if (Array.isArray(iterable)) { if (Array.isArray(iterable)) {
@ -81,31 +79,31 @@ export class Map<K, V> {
} }
} }
export class WeakMap<K, V> { export class WeakMap<K, V> {
private [mapKey]: InstanceType<typeof map>; #map: InstanceType<typeof map>;
public get(key: K): V { public get(key: K): V {
return this[mapKey].get(key); return this.#map.get(key);
} }
public has(key: K): boolean { public has(key: K): boolean {
return this[mapKey].has(key); return this.#map.has(key);
} }
public set(key: K, val: V) { public set(key: K, val: V) {
this[mapKey].set(key, val); this.#map.set(key, val);
return this; return this;
} }
public delete(key: K): boolean { public delete(key: K): boolean {
if (!this[mapKey].has(key)) return false; if (!this.#map.has(key)) return false;
else { else {
this[mapKey].delete(key); this.#map.delete(key);
return true; return true;
} }
} }
public clear() { public clear() {
this[mapKey].clear(); this.#map.clear();
} }
public constructor(iterable?: Iterable<[K, V]>) { public constructor(iterable?: Iterable<[K, V]>) {
const _map = this[mapKey] = new map(true); const _map = this.#map = new map(true);
if (iterable != null) { if (iterable != null) {
if (Array.isArray(iterable)) { if (Array.isArray(iterable)) {
@ -123,6 +121,3 @@ export class WeakMap<K, V> {
} }
} }
} }
func.setCallable(Map, false);
func.setCallable(WeakMap, false);

View File

@ -1,4 +1,4 @@
import { func, next, object, symbol } from "../primordials.ts"; import { next } from "../primordials.ts";
enum PromiseState { enum PromiseState {
Pending = "pend", Pending = "pend",
@ -6,109 +6,106 @@ enum PromiseState {
Rejected = "rej", 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> { export class Promise<T> {
public [pState]: PromiseState; static #InternalPromise = function <T>(this: Promise<T>) {
public [pValue]?: T | unknown; this.#state = PromiseState.Pending;
public [pFulHandles]?: ((val: T) => void)[] = []; this.#fulHandles = [];
public [pRejHandles]?: ((val: T) => void)[] = []; 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) { public then<Res>(ful?: (val: T) => Res, rej?: (err: any) => Res) {
if (typeof ful !== "function") ful = undefined; if (typeof ful !== "function") ful = undefined;
if (typeof rej !== "function") rej = undefined; if (typeof rej !== "function") rej = undefined;
const promise = makePromise<Res>(); const promise = new Promise.#InternalPromise<Res>();
handle(this, this.#handle(
val => next(() => { val => next(() => {
if (ful == null) fulfill(promise, val); if (ful == null) promise.#fulfill(val);
else { else {
try { fulfill(promise, ful(val)); } try { promise.#fulfill(ful(val)); }
catch (e) { reject(promise, e); } catch (e) { promise.#reject(e); }
} }
}), }),
err => next(() => { err => next(() => {
if (rej == null) reject(promise, err); if (rej == null) promise.#reject(err);
else { else {
try { fulfill(promise, rej(err)); } try { promise.#fulfill(rej(err)); }
catch (e) { reject(promise, e); } 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) { 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) { public static resolve(val: any) {
const res = makePromise(); const res = new this.#InternalPromise();
fulfill(res, val); res.#fulfill(val);
return res; return res;
} }
public static reject(val: any) { public static reject(val: any) {
const res = makePromise(); const res = new this.#InternalPromise();
reject(res, val); res.#reject(val);
return res; return res;
} }
} }
func.setCallable(Promise, false);

View File

@ -1,42 +1,40 @@
import { Array } from "../values/array.ts"; import { Array } from "../values/array.ts";
import { func, map, symbol } from "../primordials.ts"; import { func, map } from "../primordials.ts";
import { symbols } from "../utils.ts"; import { symbols } from "../utils.ts";
const mapKey: unique symbol = symbol.makeSymbol("Set.impl") as any;
export class Set<T> { export class Set<T> {
private [mapKey]: InstanceType<typeof map>; #map: InstanceType<typeof map>;
public get size() { public get size() {
return this[mapKey].size(); return this.#map.size();
} }
public has(key: T): boolean { public has(key: T): boolean {
return this[mapKey].has(key); return this.#map.has(key);
} }
public add(val: T) { public add(val: T) {
this[mapKey].set(val, true); this.#map.set(val, true);
return this; return this;
} }
public delete(val: T): boolean { public delete(val: T): boolean {
if (!this[mapKey].has(val)) return false; if (!this.#map.has(val)) return false;
else { else {
this[mapKey].delete(val); this.#map.delete(val);
return true; return true;
} }
} }
public clear() { public clear() {
this[mapKey].clear(); this.#map.clear();
} }
public keys(): T[] { public keys(): T[] {
return this[mapKey].keys(); return this.#map.keys();
} }
public values(): T[] { public values(): T[] {
return this[mapKey].keys(); return this.#map.keys();
} }
public entries(): [T, T][] { public entries(): [T, T][] {
const res = this[mapKey].keys(); const res = this.#map.keys();
for (let i = 0; i < res.length; i++) { for (let i = 0; i < res.length; i++) {
res[i] = [res[i], res[i]]; res[i] = [res[i], res[i]];
@ -57,7 +55,7 @@ export class Set<T> {
} }
public constructor(iterable?: Iterable<T>) { public constructor(iterable?: Iterable<T>) {
const _map = this[mapKey] = new map(); const _map = this.#map = new map();
if (iterable != null) { if (iterable != null) {
if (Array.isArray(iterable)) { if (Array.isArray(iterable)) {
@ -77,28 +75,28 @@ export class Set<T> {
} }
export class WeakSet<T> { export class WeakSet<T> {
private [mapKey]: InstanceType<typeof map>; #map: InstanceType<typeof map>;
public has(key: T): boolean { public has(key: T): boolean {
return this[mapKey].has(key); return this.#map.has(key);
} }
public add(val: T) { public add(val: T) {
this[mapKey].set(val, true); this.#map.set(val, true);
return this; return this;
} }
public delete(val: T): boolean { public delete(val: T): boolean {
if (!this[mapKey].has(val)) return false; if (!this.#map.has(val)) return false;
else { else {
this[mapKey].delete(val); this.#map.delete(val);
return true; return true;
} }
} }
public clear() { public clear() {
this[mapKey].clear(); this.#map.clear();
} }
public constructor(iterable?: Iterable<T>) { public constructor(iterable?: Iterable<T>) {
const _map = this[mapKey] = new map(true); const _map = this.#map = new map(true);
if (iterable != null) { if (iterable != null) {
if (Array.isArray(iterable)) { if (Array.isArray(iterable)) {
@ -116,6 +114,3 @@ export class WeakSet<T> {
} }
} }
} }
func.setCallable(Set, false);
func.setCallable(WeakSet, false);

View File

@ -1,4 +1,4 @@
import { func, json, object } from "../primordials.ts"; import { func, object, print } from "../primordials.ts";
export const console = {}; export const console = {};

View File

@ -4,6 +4,14 @@ export interface InternalBuffer {
length: number; length: number;
[buffSymbol]: "buffer"; [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 { export interface SymbolPrimordials {
makeSymbol(name: string): symbol; makeSymbol(name: string): symbol;
@ -82,6 +90,9 @@ export interface JSONPrimordials {
parse(data: string): any; parse(data: string): any;
stringify(data: any): string; stringify(data: any): string;
} }
export interface NetPrimordials {
server(): InternalServer;
}
export interface Primordials { export interface Primordials {
symbol: SymbolPrimordials; symbol: SymbolPrimordials;
@ -91,6 +102,7 @@ export interface Primordials {
function: FunctionPrimordials; function: FunctionPrimordials;
json: JSONPrimordials; json: JSONPrimordials;
buffer: BufferPrimordials; buffer: BufferPrimordials;
net: NetPrimordials;
map: new (weak?: boolean) => { map: new (weak?: boolean) => {
get(key: any): any; get(key: any): any;
has(key: any): boolean; has(key: any): boolean;
@ -111,6 +123,7 @@ export interface Primordials {
next(func: () => void): void; next(func: () => void): void;
schedule(func: () => void, delay: number): () => void; schedule(func: () => void, delay: number): () => void;
setIntrinsic(key: string, val: any): void; setIntrinsic(key: string, val: any): void;
print(...args: any[]): void;
} }
// prevent optimization to "undefined", which doesn't exist yet // prevent optimization to "undefined", which doesn't exist yet
@ -126,6 +139,7 @@ export const {
buffer, buffer,
function: func, function: func,
json, json,
net: socket,
map, map,
regex, regex,
setGlobalPrototypes, setGlobalPrototypes,
@ -134,6 +148,8 @@ export const {
next, next,
schedule, schedule,
setIntrinsic, setIntrinsic,
print,
} = primordials; } = primordials;
export type regex = InstanceType<typeof regex>; export type regex = InstanceType<typeof regex>;
export const self = (globalThis as any);

63
lib/src/stdlib/sockets.ts Normal file
View 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;
}
}

View File

@ -1,14 +1,12 @@
import { func, regex, symbol } from "../primordials.ts"; import { func, regex } from "../primordials.ts";
import { String } from "./string.ts"; import { String } from "./string.ts";
import { type ReplaceRange } from "../utils.ts"; import { type ReplaceRange } from "../utils.ts";
import { applyReplaces } from "../utils.ts"; import { applyReplaces } from "../utils.ts";
import { applySplits } from "../utils.ts"; import { applySplits } from "../utils.ts";
import { symbols } from "../utils.ts"; import { symbols } from "../utils.ts";
const regexKey: unique symbol = symbol.makeSymbol("RegExp.impl") as any;
export class RegExp { export class RegExp {
private [regexKey]!: InstanceType<typeof regex>; #regex!: InstanceType<typeof regex>;
public readonly source!: string; public readonly source!: string;
public readonly flags!: string; public readonly flags!: string;
@ -70,14 +68,14 @@ export class RegExp {
this.unicodeSets = unicodeSets; this.unicodeSets = unicodeSets;
this.sticky = sticky; 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) { public exec(target: string) {
const useLast = this.global || this.sticky; const useLast = this.global || this.sticky;
const start = useLast ? this.lastIndex : 0; 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 (match != null && !(this.sticky && match.matches.index !== start)) {
if (useLast) this.lastIndex = match.end; if (useLast) this.lastIndex = match.end;
return match.matches; return match.matches;
@ -92,7 +90,7 @@ export class RegExp {
public [symbols.split](target: string, limit?: number) { public [symbols.split](target: string, limit?: number) {
return applySplits(target, limit, offset => { 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; if (val == null) return undefined;
return { start: val.matches.index!, end: val.end }; return { start: val.matches.index!, end: val.end };
@ -100,7 +98,7 @@ export class RegExp {
} }
public [symbols.replace](target: string, replacer: any) { public [symbols.replace](target: string, replacer: any) {
const matches: ReplaceRange[] = []; const matches: ReplaceRange[] = [];
const regex = this[regexKey]; const regex = this.#regex;
if (this.global) { if (this.global) {
let offset = 0; let offset = 0;
@ -136,3 +134,6 @@ export class RegExp {
return applyReplaces(target, matches, replacer, regex.groupCount() + 1); return applyReplaces(target, matches, replacer, regex.groupCount() + 1);
} }
} }
func.setCallable(RegExp, true);
func.setConstructable(RegExp, true);

View File

@ -0,0 +1,3 @@
import coffee from "./coffeescript.ts";
register(coffee);

View File

@ -0,0 +1,3 @@
import ts from "./typescript.ts";
register(ts);

View File

@ -47,6 +47,7 @@ export default function babel(next: Compiler): Compiler {
availablePlugins["transform-exponentiation-operator"], availablePlugins["transform-exponentiation-operator"],
// ES2015 // ES2015
availablePlugins["transform-regenerator"],
availablePlugins["transform-arrow-functions"], availablePlugins["transform-arrow-functions"],
availablePlugins["transform-block-scoping"], availablePlugins["transform-block-scoping"],
availablePlugins["transform-classes"], availablePlugins["transform-classes"],

View File

@ -5,15 +5,6 @@ plugins {
description = "A simple REPL for the interpreter, can be used for simple prototyping"; 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 { tasks.test {
useJUnitPlatform(); useJUnitPlatform();
} }

View File

@ -2,19 +2,13 @@ package me.topchetoeu.j2s.repl;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.InetSocketAddress; 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.CancellationException;
import java.util.concurrent.ExecutionException; 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.Environment;
import me.topchetoeu.j2s.common.Filename; import me.topchetoeu.j2s.common.Filename;
@ -22,63 +16,24 @@ import me.topchetoeu.j2s.common.Key;
import me.topchetoeu.j2s.common.Metadata; import me.topchetoeu.j2s.common.Metadata;
import me.topchetoeu.j2s.common.Reading; import me.topchetoeu.j2s.common.Reading;
import me.topchetoeu.j2s.common.SyntaxException; import me.topchetoeu.j2s.common.SyntaxException;
import me.topchetoeu.j2s.compilation.JavaScript; import me.topchetoeu.j2s.lib.Compilers;
import me.topchetoeu.j2s.compilation.json.JSON; import me.topchetoeu.j2s.lib.StdLib;
import me.topchetoeu.j2s.compilation.parsing.Parsing; import me.topchetoeu.j2s.lib.debug.DebugServer;
import me.topchetoeu.j2s.compilation.parsing.Source; import me.topchetoeu.j2s.lib.debug.Debugger;
import me.topchetoeu.j2s.repl.buffers.Int32ArrayValue; import me.topchetoeu.j2s.lib.debug.SimpleDebugHandler;
import me.topchetoeu.j2s.repl.buffers.Int8ArrayValue; import me.topchetoeu.j2s.lib.debug.SimpleDebugger;
import me.topchetoeu.j2s.repl.buffers.TypedArrayValue;
import me.topchetoeu.j2s.repl.buffers.Uint8ArrayValue;
import me.topchetoeu.j2s.repl.debug.SimpleDebugHandler;
import me.topchetoeu.j2s.repl.debug.DebugServer;
import me.topchetoeu.j2s.repl.debug.Debugger;
import me.topchetoeu.j2s.repl.debug.SimpleDebugger;
import me.topchetoeu.j2s.repl.mapping.NativeMapper;
import me.topchetoeu.j2s.runtime.ArgumentsValue;
import me.topchetoeu.j2s.runtime.Compiler; import me.topchetoeu.j2s.runtime.Compiler;
import me.topchetoeu.j2s.runtime.Engine; import me.topchetoeu.j2s.runtime.Engine;
import me.topchetoeu.j2s.runtime.EventLoop; import me.topchetoeu.j2s.runtime.EventLoop;
import me.topchetoeu.j2s.runtime.Frame;
import me.topchetoeu.j2s.runtime.debug.DebugHandler; import me.topchetoeu.j2s.runtime.debug.DebugHandler;
import me.topchetoeu.j2s.runtime.exceptions.EngineException; import me.topchetoeu.j2s.runtime.exceptions.EngineException;
import me.topchetoeu.j2s.runtime.values.Value; 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.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 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();
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;
}
};
static Thread engineTask, debugTask; static Thread engineTask, debugTask;
static Engine engine = new Engine(); static Engine engine = new Engine();
static Environment environment = Environment.empty(), tsEnvironment; static Environment environment = Environment.empty();
static DebugServer server; static DebugServer server;
static Debugger debugger; static Debugger debugger;
static Key<OutputStream> STDOUT = new Key<>(); static Key<OutputStream> STDOUT = new Key<>();
@ -90,28 +45,12 @@ public class SimpleRepl {
private static void reader() { private static void reader() {
try { try {
try {
environment = createESEnv();
tsEnvironment = createESEnv();
}
catch (ExecutionException e) { throw e.getCause(); }
server = new DebugServer(); server = new DebugServer();
debugTask = server.start(new InetSocketAddress("127.0.0.1", 9229), true); debugTask = server.start(new InetSocketAddress("127.0.0.1", 9229), true);
server.targets.put("default", (socket, req) -> new SimpleDebugger(socket) server.targets.put("default", (socket, req) -> new SimpleDebugger(socket)
.attach((SimpleDebugHandler)DebugHandler.get(environment)) .attach((SimpleDebugHandler)DebugHandler.get(environment))
.attach((SimpleDebugHandler)DebugHandler.get(tsEnvironment))
); );
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())); System.out.println(String.format("Running %s v%s by %s", Metadata.name(), Metadata.version(), Metadata.author()));
for (var arg : args) { for (var arg : args) {
@ -165,691 +104,11 @@ public class SimpleRepl {
} }
} }
@SuppressWarnings("unchecked") private static Environment createESEnv() {
private static ObjectValue mapPrimordials(Environment env) { var env = StdLib.apply(null);
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();
env.add(EventLoop.KEY, engine); env.add(EventLoop.KEY, engine);
env.add(DebugHandler.KEY, new SimpleDebugHandler()); env.add(DebugHandler.KEY, new SimpleDebugHandler());
env.add(Compiler.KEY, DEFAULT_COMPILER); env.add(Compiler.KEY, Compilers.chainTranspilers(Compilers.jsCompiler(), env, Compilers::babelCompiler));
// env.add(CompileResult.DEBUG_LOG);
var glob = Value.global(env); var glob = Value.global(env);
@ -857,76 +116,31 @@ public class SimpleRepl {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
throw new CancellationException(); throw new CancellationException();
})); }));
glob.defineOwnField(null, "print", new NativeFunction("print", args -> { glob.defineOwnField(null, "dofile", new NativeFunction("dofile", args -> {
for (var el : args.args) { var file = args.get(0).toString(args.env);
if (el instanceof StringValue) System.out.print(((StringValue)el).value + " \t"); var filename = new Filename("file", new File(file).getAbsolutePath());
else System.out.print(el.toReadable(args.env) + " \t");
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; return env;
} }
private static void initEngine() { private static void initEngine() {
engineTask = engine.start(); 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);
DebugHandler.get(environment).onSourceLoad(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 { public static void main(String args[]) throws InterruptedException {
SimpleRepl.args = args; SimpleRepl.args = args;
var reader = new Thread(SimpleRepl::reader); var reader = new Thread(SimpleRepl::reader);
environment = initEnv(); environment = createESEnv();
initEngine(); initEngine();

View File

@ -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)
));
}
}

View File

@ -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;
}

View File

@ -1,5 +0,0 @@
package me.topchetoeu.j2s.repl.debug;
public interface DebuggerProvider {
Debugger getDebugger(WebSocket socket, HttpRequest req);
}

View File

@ -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; }
}
}

View File

@ -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)
));
}
}

View File

@ -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)
);
}
}

View File

@ -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)
);
}
}

View File

@ -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)
);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -1,30 +1,30 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>J2S Debugger</title> <title>J2S Debugger</title>
</head> </head>
<body> <body>
<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>, 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> so you can use the devtools in chrome.<br>
The debugger is still in early development, so please report any issues to 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>. <a href="https://git.topcheto.eu/topchetoeu/j2s/issues">the git repo</a>.
</p> </p>
<p> <p>
Here are the available entrypoints: Here are the available entrypoints:
<ul> <ul>
<li><a href="json/version">/json/version</a> - version and other stuff about the J2S engine</li> <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/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><a href="json/protocol">/json/protocol</a> - documentation of the implemented V8 protocol</li>
<li>/(any target) - websocket entrypoints for debugging</li> <li>/(any target) - websocket entrypoints for debugging</li>
</ul> </ul>
</p> </p>
<p> <p>
Running ${NAME} v${VERSION} by ${AUTHOR} Running ${NAME} v${VERSION} by ${AUTHOR}
</p> </p>
</body> </body>
</html> </html>

File diff suppressed because it is too large Load Diff

View File

@ -4,15 +4,6 @@ plugins {
description = "The runtime of J2S, used to execute J2S bytecode"; 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 { tasks.test {
useJUnitPlatform(); useJUnitPlatform();
} }

View File

@ -209,7 +209,7 @@ public final class Frame {
returnValue = InstructionRunner.exec(env, instr, this); returnValue = InstructionRunner.exec(env, instr, this);
} }
catch (EngineException e) { catch (EngineException e) {
error = e.add(env, function.name, dbg.getMapOrEmpty(env, function).toLocation(codePtr)); error = e.add(env, function.name, dbg.getMapOrEmpty(env, function).toLocation(codePtr, true));
} }
} }
} }

View File

@ -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.VoidValue;
import me.topchetoeu.j2s.runtime.values.primitives.numbers.NumberValue; import me.topchetoeu.j2s.runtime.values.primitives.numbers.NumberValue;
public abstract class Value { public interface Value {
public static enum State { public static enum State {
NORMAL(true, true, true), NORMAL(true, true, true),
NON_EXTENDABLE(false, true, true), NON_EXTENDABLE(false, true, true),
@ -76,28 +76,43 @@ public abstract class Value {
public abstract StringValue type(); public abstract StringValue type();
public abstract boolean isPrimitive(); public abstract boolean isPrimitive();
public final boolean isNaN() { public default boolean isNaN() {
return this == NumberValue.NAN || this instanceof NumberValue num && Double.isNaN(num.getDouble()); 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"); 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"); 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); 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 Member getOwnMember(Environment env, KeyCache key);
public abstract NumberValue toNumber(Environment env); public Set<String> getOwnMembers(Environment env, boolean onlyEnumerable);
public abstract String toString(Environment env); public Set<SymbolValue> getOwnSymbolMembers(Environment env, boolean onlyEnumerable);
public abstract boolean toBoolean(); 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)) { for (var val = getPrototype(env); val != null; val = val.getPrototype(env)) {
if (val.equals(proto)) return true; if (val.equals(proto)) return true;
} }
@ -105,91 +120,75 @@ public abstract class Value {
return false; return false;
} }
public abstract Member getOwnMember(Environment env, KeyCache key); public default Member getOwnMember(Environment env, Value 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) {
return getOwnMember(env, new KeyCache(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)); 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)); 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)); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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)); 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)); 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)); 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)); 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)) { for (Value obj = this; obj != null; obj = obj.getPrototype(env)) {
var member = obj.getOwnMember(env, key); var member = obj.getOwnMember(env, key);
if (member != null) return member.get(env, this); if (member != null) return member.get(env, this);
@ -197,38 +196,38 @@ public abstract class Value {
return null; return null;
} }
public final Value getMemberOrNull(Environment env, Value key) { public default Value getMemberOrNull(Environment env, Value key) {
return getMemberOrNull(env, new KeyCache(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)); 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)); 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)); 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); var res = getMemberOrNull(env, key);
if (res != null) return res; if (res != null) return res;
else return Value.UNDEFINED; 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)); 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)); 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)); 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)); 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)) { for (Value obj = this; obj != null; obj = obj.getPrototype(env)) {
var member = obj.getOwnMember(env, key); var member = obj.getOwnMember(env, key);
if (member != null && (member instanceof PropertyMember || obj == this)) { if (member != null && (member instanceof PropertyMember || obj == this)) {
@ -249,20 +248,20 @@ public abstract class Value {
} }
else return false; 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); 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); 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); 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); 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)) { for (Value obj = this; obj != null; obj = obj.getPrototype(env)) {
var member = obj.getOwnMember(env, key); var member = obj.getOwnMember(env, key);
if (member != null) { if (member != null) {
@ -276,20 +275,20 @@ public abstract class Value {
return false; 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); 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); 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); 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); 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)) { for (Value obj = this; obj != null; obj = obj.getPrototype(env)) {
if (obj.getOwnMember(env, key) != null) return true; if (obj.getOwnMember(env, key) != null) return true;
if (own) return false; if (own) return false;
@ -297,37 +296,37 @@ public abstract class Value {
return false; 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); 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); 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); 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); 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; if (!hasMember(env, key, true)) return true;
return deleteOwnMember(env, key); 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)); 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)); 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)); 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)); 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 res = new LinkedHashSet<String>();
var protos = new ArrayList<Value>(); var protos = new ArrayList<Value>();
@ -344,7 +343,7 @@ public abstract class Value {
return res; 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 res = new LinkedHashSet<SymbolValue>();
var protos = new ArrayList<Value>(); var protos = new ArrayList<Value>();
@ -362,24 +361,24 @@ public abstract class Value {
return res; return res;
} }
public final Value getMemberPath(Environment env, String ...path) { public default Value getMemberPath(Environment env, String ...path) {
var res = this; var res = this;
for (var key : path) res = res.getMember(env, key); for (var key : path) res = res.getMember(env, key);
return res; return res;
} }
public final Value getMemberPath(Environment env, Value ...path) { public default Value getMemberPath(Environment env, Value ...path) {
var res = this; var res = this;
for (var key : path) res = res.getMember(env, key); for (var key : path) res = res.getMember(env, key);
return res; 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)); var member = getOwnMember(env, new KeyCache(key));
if (member != null) return member.descriptor(env, this); if (member != null) return member.descriptor(env, this);
else return null; else return null;
} }
public Iterable<Object> toIterable(Environment env) { public default Iterable<Object> toIterable(Environment env) {
return () -> { return () -> {
if (!(this instanceof FunctionValue)) return Collections.emptyIterator(); if (!(this instanceof FunctionValue)) return Collections.emptyIterator();
var func = (FunctionValue)this; var func = (FunctionValue)this;
@ -418,29 +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) { for (var el : it) {
this.apply(env, Value.UNDEFINED, el); 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) { for (var el : it) {
env.get(EventLoop.KEY).pushMsg(() -> this.apply(env, Value.UNDEFINED, el), true); env.get(EventLoop.KEY).pushMsg(() -> this.apply(env, Value.UNDEFINED, el), true);
} }
} }
public List<String> toReadableLines(Environment env, HashSet<ObjectValue> passed) { public default List<String> toReadableLines(Environment env, HashSet<ObjectValue> passed) {
return Arrays.asList(toString(env)); 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<>())); 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()); 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<>()); return env.initFrom(INTRINSICS, () -> new HashMap<>());
} }
@ -457,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); a = a.toPrimitive(env);
b = b.toPrimitive(env); b = b.toPrimitive(env);
@ -472,7 +471,7 @@ public abstract class Value {
else return na.getDouble() <= nb.getDouble(); 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); a = a.toPrimitive(env);
b = b.toPrimitive(env); b = b.toPrimitive(env);
@ -487,7 +486,7 @@ public abstract class Value {
else return na.getDouble() >= nb.getDouble(); 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); a = a.toPrimitive(env);
b = b.toPrimitive(env); b = b.toPrimitive(env);
@ -502,7 +501,7 @@ public abstract class Value {
else return na.getDouble() < nb.getDouble(); 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); a = a.toPrimitive(env);
b = b.toPrimitive(env); b = b.toPrimitive(env);
@ -518,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); a = a.toPrimitive(env);
b = b.toPrimitive(env); b = b.toPrimitive(env);
@ -534,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 na = a.toNumber(env);
var nb = b.toNumber(env); var nb = b.toNumber(env);
if (na.isInt() && nb.isInt()) return NumberValue.of(na.getInt() - nb.getInt()); if (na.isInt() && nb.isInt()) return NumberValue.of(na.getInt() - nb.getInt());
else return NumberValue.of(na.getDouble() - nb.getDouble()); 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 na = a.toNumber(env);
var nb = b.toNumber(env); var nb = b.toNumber(env);
if (na.isInt() && nb.isInt()) return NumberValue.of(na.getInt() * nb.getInt()); if (na.isInt() && nb.isInt()) return NumberValue.of(na.getInt() * nb.getInt());
else return NumberValue.of(na.getDouble() * nb.getDouble()); 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 na = a.toNumber(env);
var nb = b.toNumber(env); var nb = b.toNumber(env);
@ -566,7 +565,7 @@ public abstract class Value {
} }
else return NumberValue.of(na.getDouble() / nb.getDouble()); 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 na = a.toNumber(env);
var nb = b.toNumber(env); var nb = b.toNumber(env);
@ -579,33 +578,33 @@ public abstract class Value {
} }
else return NumberValue.of(na.getDouble() % nb.getDouble()); 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); var na = a.toNumber(env);
if (na.isInt()) return NumberValue.of(-na.getInt()); if (na.isInt()) return NumberValue.of(-na.getInt());
else return NumberValue.of(-na.getDouble()); 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()); 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()); 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()); 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()); 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()); 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()); 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 _a = a.toNumber(env).getLong() & 0xFFFFFFFF;
long _b = b.toNumber(env).getLong() & 0xFFFFFFFF; long _b = b.toNumber(env).getLong() & 0xFFFFFFFF;
@ -615,7 +614,7 @@ public abstract class Value {
return NumberValue.of(_a >>> _b); 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 // In loose equality, null is equivalent to undefined
if (a instanceof VoidValue || b instanceof VoidValue) return a instanceof VoidValue && b instanceof VoidValue; if (a instanceof VoidValue || b instanceof VoidValue) return a instanceof VoidValue && b instanceof VoidValue;
@ -637,7 +636,7 @@ public abstract class Value {
return a.toString(env).equals(b.toString(env)); 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; prefix = prefix == null ? "Uncaught" : "Uncaught " + prefix;
if (err instanceof EngineException ee) { if (err instanceof EngineException ee) {
if (env == null) env = ee.env; if (env == null) env = ee.env;

View File

@ -26,7 +26,7 @@ public final class CodeFunction extends FunctionValue {
} }
@Override protected Value onApply(Environment env, Value self, Value... args) { @Override protected Value onApply(Environment env, Value self, Value... args) {
var frame = new Frame(env, false, null, self, args, this); var frame = new Frame(this.env, false, null, self, args, this);
var res = onCall(frame); var res = onCall(frame);
return res; return res;
} }
@ -37,7 +37,7 @@ public final class CodeFunction extends FunctionValue {
if (proto instanceof ObjectValue) self.setPrototype(env, (ObjectValue)proto); if (proto instanceof ObjectValue) self.setPrototype(env, (ObjectValue)proto);
else if (proto == Value.NULL) self.setPrototype(env, null); else if (proto == Value.NULL) self.setPrototype(env, null);
var frame = new Frame(env, true, target, self, args, this); var frame = new Frame(this.env, true, target, self, args, this);
var ret = onCall(frame); var ret = onCall(frame);

View File

@ -23,7 +23,7 @@ import me.topchetoeu.j2s.runtime.values.primitives.StringValue;
import me.topchetoeu.j2s.runtime.values.primitives.SymbolValue; import me.topchetoeu.j2s.runtime.values.primitives.SymbolValue;
import me.topchetoeu.j2s.runtime.values.primitives.numbers.NumberValue; import me.topchetoeu.j2s.runtime.values.primitives.numbers.NumberValue;
public class ObjectValue extends Value { public class ObjectValue implements Value {
public static interface PrototypeProvider { public static interface PrototypeProvider {
public ObjectValue get(Environment env); public ObjectValue get(Environment env);
} }

View File

@ -4,7 +4,7 @@ import me.topchetoeu.j2s.common.Environment;
import me.topchetoeu.j2s.runtime.values.objects.ObjectValue; import me.topchetoeu.j2s.runtime.values.objects.ObjectValue;
import me.topchetoeu.j2s.runtime.values.primitives.numbers.NumberValue; import me.topchetoeu.j2s.runtime.values.primitives.numbers.NumberValue;
public final class BoolValue extends PrimitiveValue { public final class BoolValue implements PrimitiveValue {
public static final BoolValue TRUE = new BoolValue(true); public static final BoolValue TRUE = new BoolValue(true);
public static final BoolValue FALSE = new BoolValue(false); public static final BoolValue FALSE = new BoolValue(false);

View File

@ -11,30 +11,29 @@ import me.topchetoeu.j2s.runtime.values.Value;
import me.topchetoeu.j2s.runtime.values.functions.FunctionValue; import me.topchetoeu.j2s.runtime.values.functions.FunctionValue;
import me.topchetoeu.j2s.runtime.values.objects.ObjectValue; import me.topchetoeu.j2s.runtime.values.objects.ObjectValue;
public abstract class PrimitiveValue extends Value { public interface PrimitiveValue extends Value {
@Override public boolean defineOwnField( public default boolean defineOwnField(
Environment env, KeyCache key, Value val, Environment env, KeyCache key, Value val,
Boolean writable, Boolean enumerable, Boolean configurable Boolean writable, Boolean enumerable, Boolean configurable
) { return false; } ) { return false; }
@Override public default boolean defineOwnProperty(
public boolean defineOwnProperty(
Environment env, KeyCache key, Optional<FunctionValue> get, Optional<FunctionValue> set, Environment env, KeyCache key, Optional<FunctionValue> get, Optional<FunctionValue> set,
Boolean enumerable, Boolean configurable Boolean enumerable, Boolean configurable
) { return false; } ) { return false; }
@Override public boolean deleteOwnMember(Environment env, KeyCache key) { return true; } public default boolean deleteOwnMember(Environment env, KeyCache key) { return true; }
@Override public final boolean isPrimitive() { return true; } public default boolean isPrimitive() { return true; }
@Override public final Value toPrimitive(Environment env) { return this; } public default Value toPrimitive(Environment env) { return this; }
@Override public final boolean setPrototype(Environment env, ObjectValue val) { return false; } public default boolean setPrototype(Environment env, ObjectValue val) { return false; }
@Override public Member getOwnMember(Environment env, KeyCache key) { return null; } public default Member getOwnMember(Environment env, KeyCache key) { return null; }
@Override public Set<String> getOwnMembers(Environment env, boolean onlyEnumerable) { return new HashSet<>(); } public default Set<String> getOwnMembers(Environment env, boolean onlyEnumerable) { return new HashSet<>(); }
@Override public Set<SymbolValue> getOwnSymbolMembers(Environment env, boolean onlyEnumerable) { return new HashSet<>(); } public default Set<SymbolValue> getOwnSymbolMembers(Environment env, boolean onlyEnumerable) { return new HashSet<>(); }
@Override public State getState() { return State.FROZEN; } public default State getState() { return State.FROZEN; }
@Override public void preventExtensions() {} public default void preventExtensions() {}
@Override public void seal() {} public default void seal() {}
@Override public void freeze() {} public default void freeze() {}
} }

View File

@ -15,7 +15,7 @@ import me.topchetoeu.j2s.runtime.values.Member.FieldMember;
import me.topchetoeu.j2s.runtime.values.objects.ObjectValue; import me.topchetoeu.j2s.runtime.values.objects.ObjectValue;
import me.topchetoeu.j2s.runtime.values.primitives.numbers.NumberValue; import me.topchetoeu.j2s.runtime.values.primitives.numbers.NumberValue;
public final class StringValue extends PrimitiveValue { public final class StringValue implements PrimitiveValue {
private static final WeakHashMap<String, StringValue> cache = new WeakHashMap<>(); private static final WeakHashMap<String, StringValue> cache = new WeakHashMap<>();
public final String value; public final String value;
@ -57,13 +57,13 @@ public final class StringValue extends PrimitiveValue {
} }
} }
return super.getOwnMember(env, key); return PrimitiveValue.super.getOwnMember(env, key);
} }
@Override public Set<String> getOwnMembers(Environment env, boolean onlyEnumerable) { @Override public Set<String> getOwnMembers(Environment env, boolean onlyEnumerable) {
var res = new LinkedHashSet<String>(); var res = new LinkedHashSet<String>();
res.addAll(super.getOwnMembers(env, onlyEnumerable)); res.addAll(StringValue.this.getOwnMembers(env, onlyEnumerable));
for (var i = 0; i < value.length(); i++) res.add(i + ""); for (var i = 0; i < value.length(); i++) res.add(i + "");

View File

@ -11,7 +11,7 @@ import me.topchetoeu.j2s.runtime.values.Value;
import me.topchetoeu.j2s.runtime.values.objects.ObjectValue; import me.topchetoeu.j2s.runtime.values.objects.ObjectValue;
import me.topchetoeu.j2s.runtime.values.primitives.numbers.NumberValue; import me.topchetoeu.j2s.runtime.values.primitives.numbers.NumberValue;
public final class SymbolValue extends PrimitiveValue { public final class SymbolValue implements PrimitiveValue {
private static final HashMap<String, SymbolValue> registry = new HashMap<>(); private static final HashMap<String, SymbolValue> registry = new HashMap<>();
public final String value; public final String value;

View File

@ -15,7 +15,7 @@ import me.topchetoeu.j2s.runtime.values.functions.FunctionValue;
import me.topchetoeu.j2s.runtime.values.objects.ObjectValue; import me.topchetoeu.j2s.runtime.values.objects.ObjectValue;
import me.topchetoeu.j2s.runtime.values.primitives.numbers.NumberValue; import me.topchetoeu.j2s.runtime.values.primitives.numbers.NumberValue;
public final class UserValue<T> extends Value { public final class UserValue<T> implements Value {
public final T value; public final T value;
public final ObjectValue prototype; public final ObjectValue prototype;

View File

@ -11,7 +11,7 @@ import me.topchetoeu.j2s.runtime.values.Member;
import me.topchetoeu.j2s.runtime.values.objects.ObjectValue; import me.topchetoeu.j2s.runtime.values.objects.ObjectValue;
import me.topchetoeu.j2s.runtime.values.primitives.numbers.NumberValue; import me.topchetoeu.j2s.runtime.values.primitives.numbers.NumberValue;
public final class VoidValue extends PrimitiveValue { public final class VoidValue implements PrimitiveValue {
public final String name; public final String name;
public final String type; public final String type;

View File

@ -2,7 +2,7 @@ package me.topchetoeu.j2s.runtime.values.primitives.numbers;
import me.topchetoeu.j2s.common.StringifyUtils; import me.topchetoeu.j2s.common.StringifyUtils;
public final class DoubleValue extends NumberValue { public final class DoubleValue implements NumberValue {
public final double value; public final double value;
@Override public boolean isInt() { @Override public boolean isInt() {

View File

@ -7,7 +7,7 @@ import java.util.List;
import me.topchetoeu.j2s.common.Environment; import me.topchetoeu.j2s.common.Environment;
import me.topchetoeu.j2s.runtime.values.objects.ObjectValue; import me.topchetoeu.j2s.runtime.values.objects.ObjectValue;
public final class IntValue extends NumberValue { public final class IntValue implements NumberValue {
public final long value; public final long value;
@Override public boolean isInt() { @Override public boolean isInt() {

View File

@ -5,10 +5,10 @@ import me.topchetoeu.j2s.runtime.values.objects.ObjectValue;
import me.topchetoeu.j2s.runtime.values.primitives.PrimitiveValue; import me.topchetoeu.j2s.runtime.values.primitives.PrimitiveValue;
import me.topchetoeu.j2s.runtime.values.primitives.StringValue; import me.topchetoeu.j2s.runtime.values.primitives.StringValue;
public abstract class NumberValue extends PrimitiveValue { public interface NumberValue extends PrimitiveValue {
public static final NumberValue NAN = new DoubleValue(Double.NaN); public static final NumberValue NAN = new DoubleValue(Double.NaN);
@Override public final StringValue type() { return StringValue.of("number"); } @Override public default StringValue type() { return StringValue.of("number"); }
public abstract double getDouble(); public abstract double getDouble();
public abstract int getInt(); public abstract int getInt();
@ -21,11 +21,11 @@ public abstract class NumberValue extends PrimitiveValue {
public abstract String toString(); public abstract String toString();
@Override public final boolean toBoolean() { return getDouble() != 0; } @Override public default boolean toBoolean() { return getDouble() != 0; }
@Override public final NumberValue toNumber(Environment ext) { return this; } @Override public default NumberValue toNumber(Environment ext) { return this; }
@Override public final String toString(Environment ext) { return toString(); } @Override public default String toString(Environment ext) { return toString(); }
@Override public final ObjectValue getPrototype(Environment env) { @Override public default ObjectValue getPrototype(Environment env) {
return env.get(NUMBER_PROTO); return env.get(NUMBER_PROTO);
} }