Compare commits

..

No commits in common. "master" and "v0.10.8-beta" have entirely different histories.

78 changed files with 1898 additions and 5393 deletions

View File

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

View File

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

View File

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

9
doc/text/.gitignore vendored
View File

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

View File

@ -1 +0,0 @@
This is the main body of my thesis. **BE WARNED!** It is quite lengthy and written in Bulgarian.

View File

@ -1,42 +0,0 @@
#!/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: ",
},
},
},
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

View File

@ -1,14 +0,0 @@
---
title: Requirements
---
1. Тема: Среда за изпълнение на „EcmaScript 5“ код за виртуалната машина на „Java“
2. Изисквания
- Покритие на „ECMA-262 5.1 Edition“ стандарта
- Задоволително бързодействие
- Възможност за лесно вграждане в други софтуерни продукти
- Добри тестове, подробна документация и удобен и интуитивен публичен интерфейс за разработчици
3. Съдържание
1. Теоретична част
2. Практическа част
3. Приложение

View File

@ -1,728 +0,0 @@
---@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;

View File

@ -1,244 +0,0 @@
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

View File

@ -1,30 +0,0 @@
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,
}

View File

@ -1,429 +0,0 @@
-- 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

View File

@ -1,577 +0,0 @@
<!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_name = j2s
project_version = 0.10.11-beta
project_version = 0.10.8-beta
main_class = me.topchetoeu.j2s.repl.SimpleRepl

View File

@ -17,7 +17,6 @@
"typescript": "^5.7.2"
},
"devDependencies": {
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/plugin-transform-class-properties": "^7.25.9",
"@babel/plugin-transform-runtime": "^7.25.9",
"@babel/plugin-transform-typescript": "^7.25.9",

View File

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

View File

@ -22,16 +22,3 @@ declare interface PromiseConstructor {
resolve<T>(val: T): Promise<Awaited<T>>;
reject<T>(err: unknown): Promise<T>;
}
declare interface AsyncIterator<T, Return = unknown, Next = unknown> {
next(): Promise<IterationData<T, Return>>;
next(val: Next): Promise<IterationData<T, Return>>;
error?(err: unknown): Promise<IterationData<T, Return>>;
return?(val: Return): Promise<IterationData<T, Return>>;
}
declare interface AsyncIterableIterator<T, Return = unknown, Next = unknown> extends AsyncIterator<T, Return, Next> {
[Symbol.iterator](): this;
}
declare interface AsyncIterable<T> {
[Symbol.iterator](): AsyncIterator<T>;
}

View File

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

View File

@ -59,8 +59,6 @@ public class Compilers {
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];
@ -85,19 +83,9 @@ public class Compilers {
}));
var compiled = JavaScript.compile(compilerName, compilerSrc, false);
new CodeFunction(env, "intializer", compiled.body(), new Value[0][]).apply(env, Value.UNDEFINED);
// 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;
}
return wrap(prev, env, target, compilerFactory[0]);
}
public static Compiler babelCompiler(Compiler prev, Environment target) {
@ -123,7 +111,7 @@ public class Compilers {
Compiler create(Compiler prev, Environment target);
}
public static Compiler chainTranspilers(Compiler base, Environment target, TranspilerFactory ...factories) {
public static Compiler chainTranspilers(Environment target, Compiler base, TranspilerFactory ...factories) {
var res = base;
for (var el : factories) {

View File

@ -690,4 +690,5 @@ public class Primordials {
return res;
}
}

View File

@ -6,36 +6,24 @@ 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
);
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();
public static Environment apply(Environment env) {
if (env == null) {
env = new Environment();
}
var stubEnv = new Environment();
Value.global(stubEnv).defineOwnField(stubEnv, "target", Value.global(env));
Value.global(stubEnv).defineOwnField(stubEnv, "primordials", Primordials.create(env));
var func = new CodeFunction(stubEnv, "intializer", body.body(), new Value[0][]);
try {
func.apply(stubEnv, Value.UNDEFINED);
}
catch (EngineException e) {
System.out.println(Value.errorToReadable(env, e, "in environment initializer"));
}
var func = new CodeFunction(stubEnv, "intializer", RUNNER.body(), new Value[0][]);
func.apply(stubEnv, Value.UNDEFINED);
return env;
}
public static Environment apply(Environment env) {
if (env == null) env = new Environment();
return apply(env, RUNNER);
}
}

View File

@ -1,37 +0,0 @@
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

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

View File

@ -1,100 +0,0 @@
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,19 +0,0 @@
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

@ -1,22 +0,0 @@
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

@ -1,50 +0,0 @@
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

@ -1,22 +0,0 @@
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

@ -1,186 +0,0 @@
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

@ -1,29 +0,0 @@
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

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

View File

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

View File

@ -1,28 +1,25 @@
import { buffer, type InternalBuffer, map, symbol } from "../primordials.ts";
export const abs = new map(true);
export const abKey: unique symbol = symbol.getSymbol("ArrayBuffer.impl") as any;
export class ArrayBuffer {
#internal!: InternalBuffer;
public [abKey]!: InternalBuffer;
public get byteLength() {
return this.#internal.length;
return this[abKey].length;
}
public get byteOffset() {
return 0;
}
public constructor(val: unknown) {
if (buffer.isBuff(val)) this.#internal = val;
else this.#internal = buffer.buff(Number(val));
}
public static unwrap(instance: ArrayBuffer) {
return instance.#internal;
if (buffer.isBuff(val)) this[abKey] = val;
else this[abKey] = buffer.buff(Number(val));
}
}
function wrapAB(buff: InternalBuffer): ArrayBuffer {
export function getAB(buff: InternalBuffer): ArrayBuffer {
let res = abs.get(buff);
if (res == null) {
res = new ArrayBuffer(buff);
@ -31,11 +28,3 @@ function wrapAB(buff: InternalBuffer): ArrayBuffer {
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 { token, TypedArray, typedArrayFuncs } from "./TypedArray.ts";
import { abstractIgnore, TypedArray, typedArrayFuncs } from "./TypedArray.ts";
const factory = buffer.int32;
const funcs = typedArrayFuncs(4, factory);
@ -19,7 +19,7 @@ export class Int32Array extends TypedArray {
}
public constructor(obj: any, start?: number, end?: number) {
super(token);
super(abstractIgnore);
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 { symbols, wrapI } from "../utils.ts";
import { Error, TypeError } from "../values/errors.ts";
import { ArrayBuffer, unwrapAB, wrapAB } from "./ArrayBuffer.ts";
import { abKey, ArrayBuffer, getAB } from "./ArrayBuffer.ts";
export const token = symbol.getSymbol("TypedArray.abstractIgnore");
export const abstractIgnore = symbol.getSymbol("TypedArray.abstractIgnore");
export function typedArrayFuncs(perEl: number, constructor: (buff: InternalBuffer, start: number, end: number) => number[]) {
return {
@ -56,7 +56,7 @@ export function typedArrayFuncs(perEl: number, constructor: (buff: InternalBuffe
return constructor(buffer.buff(self * perEl), 0, self);
}
if (self instanceof ArrayBuffer) {
const internal = unwrapAB(self);
const internal = self[abKey];
if (start === undefined) start = 0;
if (end === undefined) end = (internal.length / perEl) | 0;
return constructor(internal, start, end);
@ -90,7 +90,7 @@ export function typedArrayFuncs(perEl: number, constructor: (buff: InternalBuffe
export class TypedArray {
public get buffer() {
return wrapAB(buffer.backer(this as any));
return getAB(buffer.backer(this as any));
}
public get byteOffset(): number {
throw new Error("abstract");
@ -212,8 +212,8 @@ export class TypedArray {
return this;
}
public constructor(_token?: typeof token) {
if (_token !== token) {
public constructor(token?: typeof abstractIgnore) {
if (token !== abstractIgnore) {
throw new TypeError("TypedArray constructor can't be called");
}
}

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
import { next } from "../primordials.ts";
import { func, next, object, symbol } from "../primordials.ts";
enum PromiseState {
Pending = "pend",
@ -6,106 +6,109 @@ enum PromiseState {
Rejected = "rej",
}
const pState: unique symbol = symbol.makeSymbol("Promise.state") as any;
const pValue: unique symbol = symbol.makeSymbol("Promise.value") as any;
const pFulHandles: unique symbol = symbol.makeSymbol("Promise.fulfillHandles") as any;
const pRejHandles: unique symbol = symbol.makeSymbol("Promise.rejectHandles") as any;
function makePromise<T>(): Promise<T> {
return object.setPrototype({
[pState]: PromiseState.Pending,
[pFulHandles]: [],
[pRejHandles]: [],
}, Promise.prototype) as Promise<T>;
}
function fulfill(self: Promise<any>, val: any) {
if (self[pState] !== PromiseState.Pending) return;
if (self === val) throw new Error("A promise may not be fulfilled with itself");
if (val != null && typeof val.then === "function") {
val.then(
(val: any) => fulfill(self, val),
(err: any) => reject(self, err),
);
}
else {
self[pValue] = val;
self[pState] = PromiseState.Fulfilled;
const handles = self[pFulHandles]!;
for (let i = 0; i < handles.length; i++) {
handles[i](val);
}
self[pFulHandles] = undefined;
self[pRejHandles] = undefined;
}
}
function reject(self: Promise<any>, val: any) {
if (self[pState] !== PromiseState.Pending) return;
if (self === val) throw new Error("A promise may not be rejected with itself");
if (val != null && typeof val.then === "function") {
val.then(
(val: any) => reject(self, val),
(err: any) => reject(self, err),
);
}
else {
self[pValue] = val;
self[pState] = PromiseState.Rejected;
const handles = self[pRejHandles]!;
for (let i = 0; i < handles.length; i++) {
handles[i](val);
}
self[pFulHandles] = undefined;
self[pRejHandles] = undefined;
}
}
function handle<T>(self: Promise<T>, ful?: (val: T) => void, rej?: (err: any) => void) {
if (self[pState] === PromiseState.Pending) {
if (ful != null) {
self[pFulHandles]![self[pFulHandles]!.length] = ful;
}
if (rej != null) {
self[pRejHandles]![self[pRejHandles]!.length] = rej;
}
}
else if (self[pState] === PromiseState.Fulfilled) {
if (ful != null) ful(self[pValue] as T);
}
else if (self[pState] === PromiseState.Rejected) {
if (rej != null) rej(self[pValue]);
}
}
export class Promise<T> {
static #InternalPromise = function <T>(this: Promise<T>) {
this.#state = PromiseState.Pending;
this.#fulHandles = [];
this.#rejHandles = [];
} as any as new <T>() => Promise<T>;
static {
this.#InternalPromise.prototype = this.prototype;
}
#state: PromiseState;
#value?: T | unknown;
#fulHandles?: ((val: T) => void)[] = [];
#rejHandles?: ((val: T) => void)[] = [];
#fulfill(val: any) {
if (this.#state !== PromiseState.Pending) return;
if (this === val) throw new Error("A promise may not be fulfilled with itself");
if (val != null && typeof val.then === "function") {
val.then(
(val: any) => this.#fulfill(val),
(err: any) => this.#reject(err),
);
}
else {
this.#value = val;
this.#state = PromiseState.Fulfilled;
const handles = this.#fulHandles!;
for (let i = 0; i < handles.length; i++) {
handles[i](val);
}
this.#fulHandles = undefined;
this.#rejHandles = undefined;
}
}
#reject(val: any) {
if (this.#state !== PromiseState.Pending) return;
if (this === val) throw new Error("A promise may not be rejected with itself");
if (val != null && typeof val.then === "function") {
val.then(
(val: any) => this.#reject(val),
(err: any) => this.#reject(err),
);
}
else {
this.#value = val;
this.#state = PromiseState.Rejected;
const handles = this.#rejHandles!;
for (let i = 0; i < handles.length; i++) {
handles[i](val);
}
this.#fulHandles = undefined;
this.#rejHandles = undefined;
}
}
#handle(ful?: (val: T) => void, rej?: (err: any) => void) {
if (this.#state === PromiseState.Pending) {
if (ful != null) {
this.#fulHandles![this.#fulHandles!.length] = ful;
}
if (rej != null) {
this.#rejHandles![this.#rejHandles!.length] = rej;
}
}
else if (this.#state === PromiseState.Fulfilled) {
if (ful != null) ful(this.#value as T);
}
else if (this.#state === PromiseState.Rejected) {
if (rej != null) rej(this.#value);
}
}
public [pState]: PromiseState;
public [pValue]?: T | unknown;
public [pFulHandles]?: ((val: T) => void)[] = [];
public [pRejHandles]?: ((val: T) => void)[] = [];
public then<Res>(ful?: (val: T) => Res, rej?: (err: any) => Res) {
if (typeof ful !== "function") ful = undefined;
if (typeof rej !== "function") rej = undefined;
const promise = new Promise.#InternalPromise<Res>();
const promise = makePromise<Res>();
this.#handle(
handle(this,
val => next(() => {
if (ful == null) promise.#fulfill(val);
if (ful == null) fulfill(promise, val);
else {
try { promise.#fulfill(ful(val)); }
catch (e) { promise.#reject(e); }
try { fulfill(promise, ful(val)); }
catch (e) { reject(promise, e); }
}
}),
err => next(() => {
if (rej == null) promise.#reject(err);
if (rej == null) reject(promise, err);
else {
try { promise.#fulfill(rej(err)); }
catch (e) { promise.#reject(e); }
try { fulfill(promise, rej(err)); }
catch (e) { reject(promise, e); }
}
}),
);
@ -131,19 +134,21 @@ export class Promise<T> {
}
public constructor(fn: (fulfil: (val: T) => void, reject: (err: unknown) => void) => void) {
this.#state = PromiseState.Pending;
this[pState] = PromiseState.Pending;
fn(val => this.#fulfill(val), err => this.#reject(err));
fn(val => fulfill(this, val), err => reject(this, err));
}
public static resolve(val: any) {
const res = new this.#InternalPromise();
res.#fulfill(val);
const res = makePromise();
fulfill(res, val);
return res;
}
public static reject(val: any) {
const res = new this.#InternalPromise();
res.#reject(val);
const res = makePromise();
reject(res, val);
return res;
}
}
func.setCallable(Promise, false);

View File

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

View File

@ -4,14 +4,6 @@ export interface InternalBuffer {
length: number;
[buffSymbol]: "buffer";
}
export interface InternalSocket {
read(onRes: (data: Uint8Array) => void, onErr: (err: unknown) => void, onDone: () => void): void;
write(data: Uint8Array, onRes: () => void, onErr: (err: unknown) => void): void;
}
export interface InternalServer {
bind(address: string, onRes: () => void, onErr: (err: unknown) => void): void;
next(onRes: (socket: InternalSocket) => void, onErr: (err: unknown) => void, onDone: () => void): void;
}
export interface SymbolPrimordials {
makeSymbol(name: string): symbol;
@ -90,9 +82,6 @@ export interface JSONPrimordials {
parse(data: string): any;
stringify(data: any): string;
}
export interface NetPrimordials {
server(): InternalServer;
}
export interface Primordials {
symbol: SymbolPrimordials;
@ -102,7 +91,6 @@ export interface Primordials {
function: FunctionPrimordials;
json: JSONPrimordials;
buffer: BufferPrimordials;
net: NetPrimordials;
map: new (weak?: boolean) => {
get(key: any): any;
has(key: any): boolean;
@ -139,7 +127,6 @@ export const {
buffer,
function: func,
json,
net: socket,
map,
regex,
setGlobalPrototypes,
@ -152,4 +139,3 @@ export const {
} = primordials;
export type regex = InstanceType<typeof regex>;
export const self = (globalThis as any);

View File

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

View File

@ -2,7 +2,6 @@ package me.topchetoeu.j2s.repl;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@ -18,10 +17,10 @@ import me.topchetoeu.j2s.common.Reading;
import me.topchetoeu.j2s.common.SyntaxException;
import me.topchetoeu.j2s.lib.Compilers;
import me.topchetoeu.j2s.lib.StdLib;
import me.topchetoeu.j2s.lib.debug.DebugServer;
import me.topchetoeu.j2s.lib.debug.Debugger;
import me.topchetoeu.j2s.lib.debug.SimpleDebugHandler;
import me.topchetoeu.j2s.lib.debug.SimpleDebugger;
import me.topchetoeu.j2s.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.runtime.Compiler;
import me.topchetoeu.j2s.runtime.Engine;
import me.topchetoeu.j2s.runtime.EventLoop;
@ -105,10 +104,11 @@ public class SimpleRepl {
}
private static Environment createESEnv() {
var env = StdLib.apply(null);
var env = new Environment();
env.add(EventLoop.KEY, engine);
env.add(DebugHandler.KEY, new SimpleDebugHandler());
env.add(Compiler.KEY, Compilers.chainTranspilers(Compilers.jsCompiler(), env, Compilers::babelCompiler));
env.add(Compiler.KEY, Compilers.chainTranspilers(environment, Compilers.jsCompiler(), Compilers::babelCompiler, Compilers::coffeescriptCompiler));
StdLib.apply(env);
var glob = Value.global(env);
@ -116,18 +116,6 @@ public class SimpleRepl {
Thread.currentThread().interrupt();
throw new CancellationException();
}));
glob.defineOwnField(null, "dofile", new NativeFunction("dofile", args -> {
var file = args.get(0).toString(args.env);
var filename = new Filename("file", new File(file).getAbsolutePath());
try {
var src = Reading.streamToString(new FileInputStream(file));
return Compiler.get(args.env).compile(args.env, filename, src, v -> v).apply(args.env, Value.UNDEFINED);
}
catch (FileNotFoundException e) {
throw EngineException.ofError("IOException", e.getMessage());
}
}));
return env;
}

View File

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

View File

@ -0,0 +1,37 @@
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

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
package me.topchetoeu.j2s.lib.debug;
package me.topchetoeu.j2s.repl.debug;
import java.io.IOException;
import java.util.ArrayList;

View File

@ -1,4 +1,4 @@
package me.topchetoeu.j2s.lib.debug;
package me.topchetoeu.j2s.repl.debug;
import me.topchetoeu.j2s.common.Environment;
import me.topchetoeu.j2s.runtime.Frame;

View File

@ -0,0 +1,19 @@
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

@ -0,0 +1,22 @@
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

@ -0,0 +1,50 @@
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

@ -0,0 +1,22 @@
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

@ -0,0 +1,186 @@
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

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

File diff suppressed because it is too large Load Diff

View File

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

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.numbers.NumberValue;
public interface Value {
public abstract class Value {
public static enum State {
NORMAL(true, true, true),
NON_EXTENDABLE(false, true, true),
@ -76,43 +76,28 @@ public interface Value {
public abstract StringValue type();
public abstract boolean isPrimitive();
public default boolean isNaN() {
public final boolean isNaN() {
return this == NumberValue.NAN || this instanceof NumberValue num && Double.isNaN(num.getDouble());
}
public default Value apply(Environment env, Value self, Value ...args) {
public Value apply(Environment env, Value self, Value ...args) {
throw EngineException.ofType("Value is not a function");
}
public default Value construct(Environment env, Value target, Value ...args) {
public Value construct(Environment env, Value target, Value ...args) {
throw EngineException.ofType("Value is not a constructor");
}
public default Value constructNoSelf(Environment env, Value ...args) {
public final Value constructNoSelf(Environment env, Value ...args) {
return this.construct(env, this, args);
}
public Value toPrimitive(Environment env);
public NumberValue toNumber(Environment env);
public String toString(Environment env);
public boolean toBoolean();
public Member getOwnMember(Environment env, KeyCache key);
public Set<String> getOwnMembers(Environment env, boolean onlyEnumerable);
public Set<SymbolValue> getOwnSymbolMembers(Environment env, boolean onlyEnumerable);
public boolean defineOwnField(Environment env, KeyCache key, Value val, Boolean writable, Boolean enumerable, Boolean configurable);
public boolean defineOwnProperty(Environment env, KeyCache key, Optional<FunctionValue> get, Optional<FunctionValue> set, Boolean enumerable, Boolean configurable);
public boolean deleteOwnMember(Environment env, KeyCache key);
public abstract Value toPrimitive(Environment env);
public abstract NumberValue toNumber(Environment env);
public abstract String toString(Environment env);
public abstract boolean toBoolean();
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) {
public final boolean isInstanceOf(Environment env, Value proto) {
for (var val = getPrototype(env); val != null; val = val.getPrototype(env)) {
if (val.equals(proto)) return true;
}
@ -120,75 +105,91 @@ public interface Value {
return false;
}
public default Member getOwnMember(Environment env, Value key) {
public abstract Member getOwnMember(Environment env, KeyCache key);
public abstract Set<String> getOwnMembers(Environment env, boolean onlyEnumerable);
public abstract Set<SymbolValue> getOwnSymbolMembers(Environment env, boolean onlyEnumerable);
public abstract boolean defineOwnField(Environment env, KeyCache key, Value val, Boolean writable, Boolean enumerable, Boolean configurable);
public abstract boolean defineOwnProperty(Environment env, KeyCache key, Optional<FunctionValue> get, Optional<FunctionValue> set, Boolean enumerable, Boolean configurable);
public abstract boolean deleteOwnMember(Environment env, KeyCache key);
public abstract ObjectValue getPrototype(Environment env);
public abstract boolean setPrototype(Environment env, ObjectValue val);
public abstract State getState();
public abstract void preventExtensions();
public abstract void seal();
public abstract void freeze();
public final Member getOwnMember(Environment env, Value key) {
return getOwnMember(env, new KeyCache(key));
}
public default Member getOwnMember(Environment env, String key) {
public final Member getOwnMember(Environment env, String key) {
return getOwnMember(env, new KeyCache(key));
}
public default Member getOwnMember(Environment env, int key) {
public final Member getOwnMember(Environment env, int key) {
return getOwnMember(env, new KeyCache(key));
}
public default Member getOwnMember(Environment env, double key) {
public final Member getOwnMember(Environment env, double key) {
return getOwnMember(env, new KeyCache(key));
}
public default boolean defineOwnProperty(Environment env, Value key, Optional<FunctionValue> get, Optional<FunctionValue> set, Boolean enumerable, Boolean configurable) {
public final boolean defineOwnProperty(Environment env, Value key, Optional<FunctionValue> get, Optional<FunctionValue> set, Boolean enumerable, Boolean configurable) {
return defineOwnProperty(env, new KeyCache(key), get, set, enumerable, configurable);
}
public default boolean defineOwnProperty(Environment env, String key, Optional<FunctionValue> get, Optional<FunctionValue> set, Boolean enumerable, Boolean configurable) {
public final boolean defineOwnProperty(Environment env, String key, Optional<FunctionValue> get, Optional<FunctionValue> set, Boolean enumerable, Boolean configurable) {
return defineOwnProperty(env, new KeyCache(key), get, set, enumerable, configurable);
}
public default boolean defineOwnProperty(Environment env, int key, Optional<FunctionValue> get, Optional<FunctionValue> set, Boolean enumerable, Boolean configurable) {
public final boolean defineOwnProperty(Environment env, int key, Optional<FunctionValue> get, Optional<FunctionValue> set, Boolean enumerable, Boolean configurable) {
return defineOwnProperty(env, new KeyCache(key), get, set, enumerable, configurable);
}
public default boolean defineOwnProperty(Environment env, double key, Optional<FunctionValue> get, Optional<FunctionValue> set, Boolean enumerable, Boolean configurable) {
public final boolean defineOwnProperty(Environment env, double key, Optional<FunctionValue> get, Optional<FunctionValue> set, Boolean enumerable, Boolean configurable) {
return defineOwnProperty(env, new KeyCache(key), get, set, enumerable, configurable);
}
public default boolean defineOwnField(Environment env, Value key, Value val, Boolean writable, Boolean enumerable, Boolean configurable) {
public final boolean defineOwnField(Environment env, Value key, Value val, Boolean writable, Boolean enumerable, Boolean configurable) {
return defineOwnField(env, new KeyCache(key), val, writable, enumerable, configurable);
}
public default boolean defineOwnField(Environment env, String key, Value val, Boolean writable, Boolean enumerable, Boolean configurable) {
public final boolean defineOwnField(Environment env, String key, Value val, Boolean writable, Boolean enumerable, Boolean configurable) {
return defineOwnField(env, new KeyCache(key), val, writable, enumerable, configurable);
}
public default boolean defineOwnField(Environment env, int key, Value val, Boolean writable, Boolean enumerable, Boolean configurable) {
public final boolean defineOwnField(Environment env, int key, Value val, Boolean writable, Boolean enumerable, Boolean configurable) {
return defineOwnField(env, new KeyCache(key), val, writable, enumerable, configurable);
}
public default boolean defineOwnField(Environment env, double key, Value val, Boolean writable, Boolean enumerable, Boolean configurable) {
public final boolean defineOwnField(Environment env, double key, Value val, Boolean writable, Boolean enumerable, Boolean configurable) {
return defineOwnField(env, new KeyCache(key), val, writable, enumerable, configurable);
}
public default boolean defineOwnField(Environment env, KeyCache key, Value val) {
public final boolean defineOwnField(Environment env, KeyCache key, Value val) {
return defineOwnField(env, key, val, true, true, true);
}
public default boolean defineOwnField(Environment env, Value key, Value val) {
public final boolean defineOwnField(Environment env, Value key, Value val) {
return defineOwnField(env, new KeyCache(key), val);
}
public default boolean defineOwnField(Environment env, String key, Value val) {
public final boolean defineOwnField(Environment env, String key, Value val) {
return defineOwnField(env, new KeyCache(key), val);
}
public default boolean defineOwnField(Environment env, int key, Value val) {
public final boolean defineOwnField(Environment env, int key, Value val) {
return defineOwnField(env, new KeyCache(key), val);
}
public default boolean defineOwnField(Environment env, double key, Value val) {
public final boolean defineOwnField(Environment env, double key, Value val) {
return defineOwnField(env, new KeyCache(key), val);
}
public default boolean deleteOwnMember(Environment env, Value key) {
public final boolean deleteOwnMember(Environment env, Value key) {
return deleteOwnMember(env, new KeyCache(key));
}
public default boolean deleteOwnMember(Environment env, String key) {
public final boolean deleteOwnMember(Environment env, String key) {
return deleteOwnMember(env, new KeyCache(key));
}
public default boolean deleteOwnMember(Environment env, int key) {
public final boolean deleteOwnMember(Environment env, int key) {
return deleteOwnMember(env, new KeyCache(key));
}
public default boolean deleteOwnMember(Environment env, double key) {
public final boolean deleteOwnMember(Environment env, double key) {
return deleteOwnMember(env, new KeyCache(key));
}
public default Value getMemberOrNull(Environment env, KeyCache key) {
public final Value getMemberOrNull(Environment env, KeyCache key) {
for (Value obj = this; obj != null; obj = obj.getPrototype(env)) {
var member = obj.getOwnMember(env, key);
if (member != null) return member.get(env, this);
@ -196,38 +197,38 @@ public interface Value {
return null;
}
public default Value getMemberOrNull(Environment env, Value key) {
public final Value getMemberOrNull(Environment env, Value key) {
return getMemberOrNull(env, new KeyCache(key));
}
public default Value getMemberOrNull(Environment env, String key) {
public final Value getMemberOrNull(Environment env, String key) {
return getMemberOrNull(env, new KeyCache(key));
}
public default Value getMemberOrNull(Environment env, int key) {
public final Value getMemberOrNull(Environment env, int key) {
return getMemberOrNull(env, new KeyCache(key));
}
public default Value getMemberOrNull(Environment env, double key) {
public final Value getMemberOrNull(Environment env, double key) {
return getMemberOrNull(env, new KeyCache(key));
}
public default Value getMember(Environment env, KeyCache key) {
public final Value getMember(Environment env, KeyCache key) {
var res = getMemberOrNull(env, key);
if (res != null) return res;
else return Value.UNDEFINED;
}
public default Value getMember(Environment env, Value key) {
public final Value getMember(Environment env, Value key) {
return getMember(env, new KeyCache(key));
}
public default Value getMember(Environment env, String key) {
public final Value getMember(Environment env, String key) {
return getMember(env, new KeyCache(key));
}
public default Value getMember(Environment env, int key) {
public final Value getMember(Environment env, int key) {
return getMember(env, new KeyCache(key));
}
public default Value getMember(Environment env, double key) {
public final Value getMember(Environment env, double key) {
return getMember(env, new KeyCache(key));
}
public default boolean setMember(Environment env, KeyCache key, Value val) {
public final boolean setMember(Environment env, KeyCache key, Value val) {
for (Value obj = this; obj != null; obj = obj.getPrototype(env)) {
var member = obj.getOwnMember(env, key);
if (member != null && (member instanceof PropertyMember || obj == this)) {
@ -248,20 +249,20 @@ public interface Value {
}
else return false;
}
public default boolean setMember(Environment env, Value key, Value val) {
public final boolean setMember(Environment env, Value key, Value val) {
return setMember(env, new KeyCache(key), val);
}
public default boolean setMember(Environment env, String key, Value val) {
public final boolean setMember(Environment env, String key, Value val) {
return setMember(env, new KeyCache(key), val);
}
public default boolean setMember(Environment env, int key, Value val) {
public final boolean setMember(Environment env, int key, Value val) {
return setMember(env, new KeyCache(key), val);
}
public default boolean setMember(Environment env, double key, Value val) {
public final boolean setMember(Environment env, double key, Value val) {
return setMember(env, new KeyCache(key), val);
}
public default boolean setMemberIfExists(Environment env, KeyCache key, Value val) {
public final boolean setMemberIfExists(Environment env, KeyCache key, Value val) {
for (Value obj = this; obj != null; obj = obj.getPrototype(env)) {
var member = obj.getOwnMember(env, key);
if (member != null) {
@ -275,20 +276,20 @@ public interface Value {
return false;
}
public default boolean setMemberIfExists(Environment env, Value key, Value val) {
public final boolean setMemberIfExists(Environment env, Value key, Value val) {
return setMemberIfExists(env, new KeyCache(key), val);
}
public default boolean setMemberIfExists(Environment env, String key, Value val) {
public final boolean setMemberIfExists(Environment env, String key, Value val) {
return setMemberIfExists(env, new KeyCache(key), val);
}
public default boolean setMemberIfExists(Environment env, int key, Value val) {
public final boolean setMemberIfExists(Environment env, int key, Value val) {
return setMemberIfExists(env, new KeyCache(key), val);
}
public default boolean setMemberIfExists(Environment env, double key, Value val) {
public final boolean setMemberIfExists(Environment env, double key, Value val) {
return setMemberIfExists(env, new KeyCache(key), val);
}
public default boolean hasMember(Environment env, KeyCache key, boolean own) {
public final boolean hasMember(Environment env, KeyCache key, boolean own) {
for (Value obj = this; obj != null; obj = obj.getPrototype(env)) {
if (obj.getOwnMember(env, key) != null) return true;
if (own) return false;
@ -296,37 +297,37 @@ public interface Value {
return false;
}
public default boolean hasMember(Environment env, Value key, boolean own) {
public final boolean hasMember(Environment env, Value key, boolean own) {
return hasMember(env, new KeyCache(key), own);
}
public default boolean hasMember(Environment env, String key, boolean own) {
public final boolean hasMember(Environment env, String key, boolean own) {
return hasMember(env, new KeyCache(key), own);
}
public default boolean hasMember(Environment env, int key, boolean own) {
public final boolean hasMember(Environment env, int key, boolean own) {
return hasMember(env, new KeyCache(key), own);
}
public default boolean hasMember(Environment env, double key, boolean own) {
public final boolean hasMember(Environment env, double key, boolean own) {
return hasMember(env, new KeyCache(key), own);
}
public default boolean deleteMember(Environment env, KeyCache key) {
public final boolean deleteMember(Environment env, KeyCache key) {
if (!hasMember(env, key, true)) return true;
return deleteOwnMember(env, key);
}
public default boolean deleteMember(Environment env, Value key) {
public final boolean deleteMember(Environment env, Value key) {
return deleteMember(env, new KeyCache(key));
}
public default boolean deleteMember(Environment env, String key) {
public final boolean deleteMember(Environment env, String key) {
return deleteMember(env, new KeyCache(key));
}
public default boolean deleteMember(Environment env, int key) {
public final boolean deleteMember(Environment env, int key) {
return deleteMember(env, new KeyCache(key));
}
public default boolean deleteMember(Environment env, double key) {
public final boolean deleteMember(Environment env, double key) {
return deleteMember(env, new KeyCache(key));
}
public default Set<String> getMembers(Environment env, boolean own, boolean onlyEnumerable) {
public final Set<String> getMembers(Environment env, boolean own, boolean onlyEnumerable) {
var res = new LinkedHashSet<String>();
var protos = new ArrayList<Value>();
@ -343,7 +344,7 @@ public interface Value {
return res;
}
public default Set<SymbolValue> getSymbolMembers(Environment env, boolean own, boolean onlyEnumerable) {
public final Set<SymbolValue> getSymbolMembers(Environment env, boolean own, boolean onlyEnumerable) {
var res = new LinkedHashSet<SymbolValue>();
var protos = new ArrayList<Value>();
@ -361,24 +362,24 @@ public interface Value {
return res;
}
public default Value getMemberPath(Environment env, String ...path) {
public final Value getMemberPath(Environment env, String ...path) {
var res = this;
for (var key : path) res = res.getMember(env, key);
return res;
}
public default Value getMemberPath(Environment env, Value ...path) {
public final Value getMemberPath(Environment env, Value ...path) {
var res = this;
for (var key : path) res = res.getMember(env, key);
return res;
}
public default ObjectValue getMemberDescriptor(Environment env, Value key) {
public final ObjectValue getMemberDescriptor(Environment env, Value key) {
var member = getOwnMember(env, new KeyCache(key));
if (member != null) return member.descriptor(env, this);
else return null;
}
public default Iterable<Object> toIterable(Environment env) {
public Iterable<Object> toIterable(Environment env) {
return () -> {
if (!(this instanceof FunctionValue)) return Collections.emptyIterator();
var func = (FunctionValue)this;
@ -417,29 +418,29 @@ public interface Value {
};
}
public default void callWith(Environment env, Iterable<? extends Value> it) {
public void callWith(Environment env, Iterable<? extends Value> it) {
for (var el : it) {
this.apply(env, Value.UNDEFINED, el);
}
}
public default void callWithAsync(Environment env, Iterable<? extends Value> it, boolean async) {
public void callWithAsync(Environment env, Iterable<? extends Value> it, boolean async) {
for (var el : it) {
env.get(EventLoop.KEY).pushMsg(() -> this.apply(env, Value.UNDEFINED, el), true);
}
}
public default List<String> toReadableLines(Environment env, HashSet<ObjectValue> passed) {
public List<String> toReadableLines(Environment env, HashSet<ObjectValue> passed) {
return Arrays.asList(toString(env));
}
public default String toReadable(Environment ext) {
public final String toReadable(Environment ext) {
return String.join("\n", toReadableLines(ext, new HashSet<>()));
}
public static Value global(Environment env) {
public static final Value global(Environment env) {
return env.initFrom(GLOBAL, () -> new ObjectValue());
}
public static Map<String, Value> intrinsics(Environment env) {
public static final Map<String, Value> intrinsics(Environment env) {
return env.initFrom(INTRINSICS, () -> new HashMap<>());
}
@ -456,7 +457,7 @@ public interface Value {
});
}
public static boolean lessOrEqual(Environment env, Value a, Value b) {
public static final boolean lessOrEqual(Environment env, Value a, Value b) {
a = a.toPrimitive(env);
b = b.toPrimitive(env);
@ -471,7 +472,7 @@ public interface Value {
else return na.getDouble() <= nb.getDouble();
}
}
public static boolean greaterOrEqual(Environment env, Value a, Value b) {
public static final boolean greaterOrEqual(Environment env, Value a, Value b) {
a = a.toPrimitive(env);
b = b.toPrimitive(env);
@ -486,7 +487,7 @@ public interface Value {
else return na.getDouble() >= nb.getDouble();
}
}
public static boolean less(Environment env, Value a, Value b) {
public static final boolean less(Environment env, Value a, Value b) {
a = a.toPrimitive(env);
b = b.toPrimitive(env);
@ -501,7 +502,7 @@ public interface Value {
else return na.getDouble() < nb.getDouble();
}
}
public static boolean greater(Environment env, Value a, Value b) {
public static final boolean greater(Environment env, Value a, Value b) {
a = a.toPrimitive(env);
b = b.toPrimitive(env);
@ -517,7 +518,7 @@ public interface Value {
}
}
public static Value add(Environment env, Value a, Value b) {
public static final Value add(Environment env, Value a, Value b) {
a = a.toPrimitive(env);
b = b.toPrimitive(env);
@ -533,21 +534,21 @@ public interface Value {
}
}
public static NumberValue subtract(Environment env, Value a, Value b) {
public static final NumberValue subtract(Environment env, Value a, Value b) {
var na = a.toNumber(env);
var nb = b.toNumber(env);
if (na.isInt() && nb.isInt()) return NumberValue.of(na.getInt() - nb.getInt());
else return NumberValue.of(na.getDouble() - nb.getDouble());
}
public static NumberValue multiply(Environment env, Value a, Value b) {
public static final NumberValue multiply(Environment env, Value a, Value b) {
var na = a.toNumber(env);
var nb = b.toNumber(env);
if (na.isInt() && nb.isInt()) return NumberValue.of(na.getInt() * nb.getInt());
else return NumberValue.of(na.getDouble() * nb.getDouble());
}
public static NumberValue divide(Environment env, Value a, Value b) {
public static final NumberValue divide(Environment env, Value a, Value b) {
var na = a.toNumber(env);
var nb = b.toNumber(env);
@ -565,7 +566,7 @@ public interface Value {
}
else return NumberValue.of(na.getDouble() / nb.getDouble());
}
public static NumberValue modulo(Environment env, Value a, Value b) {
public static final NumberValue modulo(Environment env, Value a, Value b) {
var na = a.toNumber(env);
var nb = b.toNumber(env);
@ -578,33 +579,33 @@ public interface Value {
}
else return NumberValue.of(na.getDouble() % nb.getDouble());
}
public static NumberValue negative(Environment env, Value a) {
public static final NumberValue negative(Environment env, Value a) {
var na = a.toNumber(env);
if (na.isInt()) return NumberValue.of(-na.getInt());
else return NumberValue.of(-na.getDouble());
}
public static NumberValue and(Environment env, Value a, Value b) {
public static final NumberValue and(Environment env, Value a, Value b) {
return NumberValue.of(a.toNumber(env).getInt() & b.toNumber(env).getInt());
}
public static NumberValue or(Environment env, Value a, Value b) {
public static final NumberValue or(Environment env, Value a, Value b) {
return NumberValue.of(a.toNumber(env).getInt() | b.toNumber(env).getInt());
}
public static NumberValue xor(Environment env, Value a, Value b) {
public static final NumberValue xor(Environment env, Value a, Value b) {
return NumberValue.of(a.toNumber(env).getInt() ^ b.toNumber(env).getInt());
}
public static NumberValue bitwiseNot(Environment env, Value a) {
public static final NumberValue bitwiseNot(Environment env, Value a) {
return NumberValue.of(~a.toNumber(env).getInt());
}
public static NumberValue shiftLeft(Environment env, Value a, Value b) {
public static final NumberValue shiftLeft(Environment env, Value a, Value b) {
return NumberValue.of(a.toNumber(env).getInt() << b.toNumber(env).getInt());
}
public static NumberValue shiftRight(Environment env, Value a, Value b) {
public static final NumberValue shiftRight(Environment env, Value a, Value b) {
return NumberValue.of(a.toNumber(env).getInt() >> b.toNumber(env).getInt());
}
public static NumberValue unsignedShiftRight(Environment env, Value a, Value b) {
public static final NumberValue unsignedShiftRight(Environment env, Value a, Value b) {
long _a = a.toNumber(env).getLong() & 0xFFFFFFFF;
long _b = b.toNumber(env).getLong() & 0xFFFFFFFF;
@ -614,7 +615,7 @@ public interface Value {
return NumberValue.of(_a >>> _b);
}
public static boolean looseEqual(Environment env, Value a, Value b) {
public static final boolean looseEqual(Environment env, Value a, Value b) {
// In loose equality, null is equivalent to undefined
if (a instanceof VoidValue || b instanceof VoidValue) return a instanceof VoidValue && b instanceof VoidValue;
@ -636,7 +637,7 @@ public interface Value {
return a.toString(env).equals(b.toString(env));
}
public static String errorToReadable(Environment env, RuntimeException err, String prefix) {
public static final String errorToReadable(Environment env, RuntimeException err, String prefix) {
prefix = prefix == null ? "Uncaught" : "Uncaught " + prefix;
if (err instanceof EngineException ee) {
if (env == null) env = ee.env;

View File

@ -26,7 +26,7 @@ public final class CodeFunction extends FunctionValue {
}
@Override protected Value onApply(Environment env, Value self, Value... args) {
var frame = new Frame(this.env, false, null, self, args, this);
var frame = new Frame(env, false, null, self, args, this);
var res = onCall(frame);
return res;
}
@ -37,7 +37,7 @@ public final class CodeFunction extends FunctionValue {
if (proto instanceof ObjectValue) self.setPrototype(env, (ObjectValue)proto);
else if (proto == Value.NULL) self.setPrototype(env, null);
var frame = new Frame(this.env, true, target, self, args, this);
var frame = new Frame(env, true, target, self, args, this);
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.numbers.NumberValue;
public class ObjectValue implements Value {
public class ObjectValue extends Value {
public static interface PrototypeProvider {
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.primitives.numbers.NumberValue;
public final class BoolValue implements PrimitiveValue {
public final class BoolValue extends PrimitiveValue {
public static final BoolValue TRUE = new BoolValue(true);
public static final BoolValue FALSE = new BoolValue(false);

View File

@ -11,29 +11,30 @@ import me.topchetoeu.j2s.runtime.values.Value;
import me.topchetoeu.j2s.runtime.values.functions.FunctionValue;
import me.topchetoeu.j2s.runtime.values.objects.ObjectValue;
public interface PrimitiveValue extends Value {
public default boolean defineOwnField(
public abstract class PrimitiveValue extends Value {
@Override public boolean defineOwnField(
Environment env, KeyCache key, Value val,
Boolean writable, Boolean enumerable, Boolean configurable
) { return false; }
public default boolean defineOwnProperty(
@Override
public boolean defineOwnProperty(
Environment env, KeyCache key, Optional<FunctionValue> get, Optional<FunctionValue> set,
Boolean enumerable, Boolean configurable
) { return false; }
public default boolean deleteOwnMember(Environment env, KeyCache key) { return true; }
@Override public boolean deleteOwnMember(Environment env, KeyCache key) { return true; }
public default boolean isPrimitive() { return true; }
public default Value toPrimitive(Environment env) { return this; }
@Override public final boolean isPrimitive() { return true; }
@Override public final Value toPrimitive(Environment env) { return this; }
public default boolean setPrototype(Environment env, ObjectValue val) { return false; }
@Override public final boolean setPrototype(Environment env, ObjectValue val) { return false; }
public default Member getOwnMember(Environment env, KeyCache key) { return null; }
public default Set<String> getOwnMembers(Environment env, boolean onlyEnumerable) { return new HashSet<>(); }
public default Set<SymbolValue> getOwnSymbolMembers(Environment env, boolean onlyEnumerable) { return new HashSet<>(); }
@Override public Member getOwnMember(Environment env, KeyCache key) { return null; }
@Override public Set<String> getOwnMembers(Environment env, boolean onlyEnumerable) { return new HashSet<>(); }
@Override public Set<SymbolValue> getOwnSymbolMembers(Environment env, boolean onlyEnumerable) { return new HashSet<>(); }
public default State getState() { return State.FROZEN; }
@Override public State getState() { return State.FROZEN; }
public default void preventExtensions() {}
public default void seal() {}
public default void freeze() {}
@Override public void preventExtensions() {}
@Override public void seal() {}
@Override public 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.primitives.numbers.NumberValue;
public final class StringValue implements PrimitiveValue {
public final class StringValue extends PrimitiveValue {
private static final WeakHashMap<String, StringValue> cache = new WeakHashMap<>();
public final String value;
@ -57,13 +57,13 @@ public final class StringValue implements PrimitiveValue {
}
}
return PrimitiveValue.super.getOwnMember(env, key);
return super.getOwnMember(env, key);
}
@Override public Set<String> getOwnMembers(Environment env, boolean onlyEnumerable) {
var res = new LinkedHashSet<String>();
res.addAll(StringValue.this.getOwnMembers(env, onlyEnumerable));
res.addAll(super.getOwnMembers(env, onlyEnumerable));
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.primitives.numbers.NumberValue;
public final class SymbolValue implements PrimitiveValue {
public final class SymbolValue extends PrimitiveValue {
private static final HashMap<String, SymbolValue> registry = new HashMap<>();
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.primitives.numbers.NumberValue;
public final class UserValue<T> implements Value {
public final class UserValue<T> extends Value {
public final T value;
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.primitives.numbers.NumberValue;
public final class VoidValue implements PrimitiveValue {
public final class VoidValue extends PrimitiveValue {
public final String name;
public final String type;

View File

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

View File

@ -7,7 +7,7 @@ import java.util.List;
import me.topchetoeu.j2s.common.Environment;
import me.topchetoeu.j2s.runtime.values.objects.ObjectValue;
public final class IntValue implements NumberValue {
public final class IntValue extends NumberValue {
public final long value;
@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.StringValue;
public interface NumberValue extends PrimitiveValue {
public abstract class NumberValue extends PrimitiveValue {
public static final NumberValue NAN = new DoubleValue(Double.NaN);
@Override public default StringValue type() { return StringValue.of("number"); }
@Override public final StringValue type() { return StringValue.of("number"); }
public abstract double getDouble();
public abstract int getInt();
@ -21,11 +21,11 @@ public interface NumberValue extends PrimitiveValue {
public abstract String toString();
@Override public default boolean toBoolean() { return getDouble() != 0; }
@Override public default NumberValue toNumber(Environment ext) { return this; }
@Override public default String toString(Environment ext) { return toString(); }
@Override public final boolean toBoolean() { return getDouble() != 0; }
@Override public final NumberValue toNumber(Environment ext) { return this; }
@Override public final String toString(Environment ext) { return toString(); }
@Override public default ObjectValue getPrototype(Environment env) {
@Override public final ObjectValue getPrototype(Environment env) {
return env.get(NUMBER_PROTO);
}