Compare commits

...

5 Commits

Author SHA1 Message Date
45292990b1
some thesis fixes 2025-01-29 14:12:57 +02:00
2dcfff689a
add page numbers to thesis 2025-01-28 14:33:34 +02:00
2619e50e9b
fix typos in thesis 2025-01-28 13:25:09 +02:00
4bfda6b0a1
bump
All checks were successful
tagged-release / Tagged Release (push) Successful in 5m38s
2025-01-28 13:11:29 +02:00
58d6110e1d
fix: stack overflow!! 2025-01-28 13:10:58 +02:00
7 changed files with 111 additions and 40 deletions

View File

@ -20,6 +20,10 @@ emit {
target = "res.html", target = "res.html",
template { template {
template = "template.html", template = "template.html",
year = os.date "%Y",
prev_year = os.date "%Y" - 1,
config,
profession(config.profession),
build { build {
"requirements.md", "requirements.md",
@ -34,12 +38,5 @@ emit {
chapter_format_plain = "Глава %s: ", chapter_format_plain = "Глава %s: ",
}, },
}, },
profession(config.profession),
year = os.date "%Y",
prev_year = os.date "%Y" - 1,
config,
}, },
} }

View File

@ -3,13 +3,21 @@ title: Дипломна работа
--- ---
# Увод {.nonum} # Увод {.nonum}
Един от най-важните принципи на програмирането е този на разширимостта. Този принцип повелява, че софтуерът трябва да изграждан по начин, по който може неговата функционалност да бъде разширена без вече написаният софтуер да бъде променян. Това е добре известен проблем в сферата на софтуерното инженерство, като едно от най-ефективните решения е интегрирането на втори помощен език, чрез който потребителя да може да разширява функционалността на оригиналният продукт. Един от най-важните принципи на програмирането е този на разширимостта. Този принцип повелява, че софтуерът трябва да бъде изграждан по начин, по който може неговата функционалност да бъде разширена, без вече написаният софтуер да бъде променян. Това е добре известен проблем в сферата на софтуерното инженерство.
Но защо разширимостта е толкова важна? Това се дължи на факта, че често потребителите на софтуерните продукти искат да разширят функционалността на продукта. Традиционно, разширяването може да стане само като потребителя се свърже с разработчика на продукта и да помоли за добавянето на функционалността. Това обаче е не само непрактично, но и води до прекомерно нарастване на софтуера, което го прави сложен за поддръжка и използване. Но защо разширимостта е толкова важна? Често потребителите на софтуерните продукти искат да разширят и персонализират функционалността на продукта. Традиционно, разширяването става само чрез модификация на кода на софтуера. Това обаче е не само непрактично, но и води до прекомерно нарастване на обема на софтуера, което го прави сложен за поддръжка. Едно добро решение на този проблем е внедряването на втори помощен език, който да служи за допълването на функционалността на продукта, без неговата промяна.
Въвеждането на скриптови езици като помощни механизми за разширяване на функционалността предлага значително по-ефективно решение. Езиците като Python, Lua и JavaScript могат да бъдат използвани за добавяне на нова функционалност, без да се променя основният код на приложението, като по този начин се осигурява гъвкавост на софтуерния продукт. JavaScript, в частност, е един от най-широко използваните езици в съвременната разработка, поради своята популярност, която се дължи на употребата на езика в уеб технологиите. Езици като Python, Lua и JavaScript са едни от най-често вгражданите езици, като има безброй такива примери:
Целта на тази дипломна работа е да създаде лесно вградим, малък на размер интерпретатор на „EcmaScript 5.1“ езика (по-добре познат като JavaScript, както ще се и нарича оттук нататък) за продукти, написани на езикът „Java“. - Roblox, платформа за създаване на игри, внедрява модифицирана версия на Lua
- Nginx, софтуер за интернет сървъри, внедрява JavaScript
- WirePlumber, софтуер за контролиране на аудиото на линукс машини, използва Lua като основен език за конфигурация
- Vim, конзолен текстов редактор, внедрява Lua като език за разширения
- Blender, софтуер за създаване на 3D модели, внедрява Python като скриптов език, както и език за разширения
JavaScript, в частност, е език, който първоначално е бил използван само за уеб приложения. Понеже обаче езикът е станал изключително популярен, е започнал да се ползва и извън уеб приложенията. Езикът има изключително богата екосистема от инструменти за разработка и библиотеки, заради което е добър избор за език, който да бъде внедрен в даден софтуерен продукт.
Заради това, тази дипломна работа цели да създаде лесно вградим и малък на размер интерпретатор на „EcmaScript 5.1“ езика (по-добре познат като JavaScript) за продукти, написани на езикът „Java“. Интерпретатора трябва да изпълнява бързо и правилно JavaScript код от голям размер и да бъде удобен за внедряване във вече съществуващи продукти.
# Проучвателна част # Проучвателна част
@ -325,6 +333,18 @@ tasks {
Методи, съответстващи на събития в интерпретатора Методи, съответстващи на събития в интерпретатора
::: :::
### Събитиен цикъл
Това е основен механизъм за поддръжката на привидно многонижково програмиране само чрез една нижка. В основата на събитийния цикъл стои опашка от „съобщения“, като всяко съобщение представлява команда, която събитийният цикъл трябва да изпълни. Командите се подреждат на тази опашка, като първите, които са постъпили в опашката биват изпълнени.
Събитийния цикъл гарантира, че съобщенията, който постъпват във събитийния цикъл, ще бъдат изпълнявани последователно. Това освобождава програмиста от нуждата да създава код, който се грижи за синхронизацията между две ядра.
Комуникацията между два събитийни цикъла може да бъде осъществена като първият цикъл изпраща съобщение на втория събитиен цикъл, който евентуално обработва това съобщение. Това съобщение пък може да направи дадени изчисление и да върне резултата чрез второ съобщение, което се праща на първия събитиен цикъл. На практика, приложението имплементира два събитийни цикъла - този, който изпълнява интерпретирания код и този, който приема резултатите от събитийния цикъл.
Друг модел на комуникация е да се изпрати съобщение от текущата нижка на друга нижка, която изпълнява съобщенията, като текущата нижка трябва да изчака изпълнението на съобщението. При този метод употребата на събитиен цикъл се обезмисля до известна степен, понеже се елиминира всякакво паралелно изпълнение на интерпретирания код и основното приложение.
В кода, интерфейса, който е основен за всички събитиини цикли е `EventLoop`, а неговата основна имплементация е `Engine`. `EventLoop` предоставя само един метод - `pushMsg`, на който се подава функционалния интерфейс `Runnable`, който се очаква да бъде изпълнен евентуално. `Engine` е имплементацията, която използва „блокираща“ опашка (опашка, при която присъства операцията за изчакване на добавяне на елемент).
---- ----
## Имплементация на JavaScript стойностите ## Имплементация на JavaScript стойностите
@ -350,8 +370,6 @@ tasks {
В кода и двете стойности са имплементирани с класа `VoidValue`, който може да бъде използван за да бъдат създадени и други празни стойности. В кода и двете стойности са имплементирани с класа `VoidValue`, който може да бъде използван за да бъдат създадени и други празни стойности.
----
### Елементарни стойности ### Елементарни стойности
В JavaScript елементарните стойности са всички стойности, които не са обекти. Такива стойности не могат да съдържат референция към друга JavaScript стойност. Такива стойности биват: В JavaScript елементарните стойности са всички стойности, които не са обекти. Такива стойности не могат да съдържат референция към друга JavaScript стойност. Такива стойности биват:
@ -380,6 +398,10 @@ tasks {
В кода, целите числа са имплементирани в класа `IntValue`, а числата с плаваща запетая - класа `DoubleValue`. Тези класове обаче не могат да бъдат инстанцирани ръчно - трябва да бъде използван метода `NumberValue.of`, който създава една от двете инстанции, в зависимост от това дали подаденото число може да бъде представено с 32-битово число. В кода, целите числа са имплементирани в класа `IntValue`, а числата с плаваща запетая - класа `DoubleValue`. Тези класове обаче не могат да бъдат инстанцирани ръчно - трябва да бъде използван метода `NumberValue.of`, който създава една от двете инстанции, в зависимост от това дали подаденото число може да бъде представено с 32-битово число.
### Потребителски стойности
Това са служебен тип стойности, които съдържат само една Java стойност. Те се използват за подаване на Java стойности на JavaScript кода, за да бъдат използвани по-късно от Java кода. Потребителските стойности се имплементират от класа „UserValue“, като той съдържа само две полета: съдържаната Java стойност, както и прототипа, който стойността да използва.
### Обекти ### Обекти
Обектите са основополагаща концепция в JavaScript. В основата на обектите стои списък от уникални низове (т. нар. ключове) и съответстващият им член [@членове] (съществува и паралелен списък със списък от ключове-символи към членове). Обектите се използват най-вече за съхранение на няколко подстойности под формата на структурирани данни (подобно на класовете на Java), но могат да бъдат използвани и като хеш-таблици. Обектите са основополагаща концепция в JavaScript. В основата на обектите стои списък от уникални низове (т. нар. ключове) и съответстващият им член [@членове] (съществува и паралелен списък със списък от ключове-символи към членове). Обектите се използват най-вече за съхранение на няколко подстойности под формата на структурирани данни (подобно на класовете на Java), но могат да бъдат използвани и като хеш-таблици.
@ -391,7 +413,7 @@ tasks {
- Режим без пренастройвани (запечатване) - освен ограниченията от предишният режим, полетата не могат да бъдат трити и пренастройвани - Режим без пренастройвани (запечатване) - освен ограниченията от предишният режим, полетата не могат да бъдат трити и пренастройвани
- Режим на заключване (замразяване) - обекта става на практика константа, нито едно негово свойство не може да бъде променено - Режим на заключване (замразяване) - обекта става на практика константа, нито едно негово свойство не може да бъде променено
JavaScript кода може да променя режима от по-свободен към по-ограничаващ, но не и обратно. Това разрешава на разработчицитие да осигурят дадено ниво на константност на данните на обектите си. JavaScript кода може да променя режима от по-свободен към по-ограничаващ, но не и обратно. Това разрешава на разработчиците да осигурят дадено ниво на константност на данните на обектите си.
Накрая, обектите имат прототипи. Прототипа може да е `null` или обект. При търсенето на член, може обект да няма дадения ключ. Тогава търсенето продължава в прототипа на обекта. Понеже обаче прототипа е обект на практика съществува свързан списък от обекти, или т. нар. прототипна верига. Прототипите са в основата на обектно-ориентираното програмиране в JavaScript, понеже може чрез прототипите да бъде дефиниран един основен обект, който да съдържа всички функции за даден „тип“ обекти, а всички обекти от този „тип“ просто имат този основен обект като прототип. Освен това обаче прототипите могат да бъдат използвани и за осъществяване на наследство на обекти - обекта „AnimalPrototype“, който съдържа члена „speak“ може да бъде прототип на обекта „DogPrototype“, който съдържа члена „bark“. При тази конфигурация, „DogPrototype“ де факто има и двете полета - „speak“ и „bark“. Накрая, обектите имат прототипи. Прототипа може да е `null` или обект. При търсенето на член, може обект да няма дадения ключ. Тогава търсенето продължава в прототипа на обекта. Понеже обаче прототипа е обект на практика съществува свързан списък от обекти, или т. нар. прототипна верига. Прототипите са в основата на обектно-ориентираното програмиране в JavaScript, понеже може чрез прототипите да бъде дефиниран един основен обект, който да съдържа всички функции за даден „тип“ обекти, а всички обекти от този „тип“ просто имат този основен обект като прототип. Освен това обаче прототипите могат да бъдат използвани и за осъществяване на наследство на обекти - обекта „AnimalPrototype“, който съдържа члена „speak“ може да бъде прототип на обекта „DogPrototype“, който съдържа члена „bark“. При тази конфигурация, „DogPrototype“ де факто има и двете полета - „speak“ и „bark“.
@ -445,7 +467,7 @@ console.log(arr); // [5, 6, <empty x 2>, 8, <empty x 7> 3]
- Промяната на една от тези настройки, освен ако презаписваемо, но ненастройваемо поле се направи непрезаписваемо - Промяната на една от тези настройки, освен ако презаписваемо, но ненастройваемо поле се направи непрезаписваемо
- Изтриване на полето от обекта - Изтриване на полето от обекта
Членовете са дефинирани чрез интерфейса „Member“, полетата чрез класа „FieldMember“, а свойствата чрез „PropertyMember“. Понеже полетата могат да бъдат виртуални и контролирани от Java код, е осигурена базова имплементация за съвсем прости полета, които са само контейнер за JavaScript стойност, в класа „SimpleFieldMember“, докато по-сложна логика може да създаде собствена имплементация на „FieldMember“ класа (на този механизъм са базирани масивите). Членовете са дефинирани чрез интерфейса „Member“, полетата чрез класа „FieldMember“, а свойствата чрез „PropertyMember“. Понеже полетата могат да бъдат виртуални (като тези на масивите), е „FieldMember“ е абстрактен, а „SimpleFieldMember“ е имплементацията на нормалните полета.
---- ----
@ -688,7 +710,7 @@ public class Frame {
```txt ```txt
; Добавя 10 към стека ; Добавя 10 към стека
LOAD_NUMBER 10 LOAD_NUMBER 10
; Добадя 1 към стека ; Добавя 1 към стека
LOAD_NUMBER 1 LOAD_NUMBER 1
; Добавя първият елемент на ; Добавя първият елемент на
; стека с новодобавената единица ; стека с новодобавената единица
@ -792,7 +814,7 @@ STORE_MEMBER_STR | key, keep | value, object | Еквивалент на ST
STORE_MEMBER_INT | key, keep | value, object | Еквивалент на STORE_MEMBER, но с константен числов ключ `key` STORE_MEMBER_INT | key, keep | value, object | Еквивалент на STORE_MEMBER, но с константен числов ключ `key`
GLOB_GET | name, force | | Зарежда променливата `name` от глобалния обект. Ако `force` е `true` не се хвърля грешка ако променливата не съществува. Стойността на променливата се добавя към стека GLOB_GET | name, force | | Зарежда променливата `name` от глобалния обект. Ако `force` е `true` не се хвърля грешка ако променливата не съществува. Стойността на променливата се добавя към стека
GLOB_SET | name, keep, force | value | Съхранява променливата `name` в глобалния обект. Ако `force` е `true` не се хвърля грешка ако променливата не съществува. Стойността на променливата се добавя към стека, ако `keep` е `true` GLOB_SET | name, keep, force | value | Съхранява променливата `name` в глобалния обект. Ако `force` е `true` не се хвърля грешка ако променливата не съществува. Стойността на променливата се добавя към стека, ако `keep` е `true`
GLOB_DEF | name | value | Създава променливата `name` със стойност `undefined` в глобавният обект, ако не съществува GLOB_DEF | name | value | Създава променливата `name` със стойност `undefined` в глобалния обект, ако не съществува
OPERATION | type | values... | Изпълнява дадената операция (фиг. [@operations-table]) с даденият брой операнда, които операцията изисква. Добавя стойността при изчислението на операцията към стека OPERATION | type | values... | Изпълнява дадената операция (фиг. [@operations-table]) с даденият брой операнда, които операцията изисква. Добавя стойността при изчислението на операцията към стека
Набор от инструкции на междинния език (бележка: в колоната с аргументи от стека са показани аргументите в реда, в който се взимат от стека, освен ако друг ред не е уточнен) Набор от инструкции на междинния език (бележка: в колоната с аргументи от стека са показани аргументите в реда, в който се взимат от стека, освен ако друг ред не е уточнен)
@ -906,7 +928,7 @@ class Frame {
## Компилиране на проекта ## Компилиране на проекта
Можете да изтеглите кода на проекта, като и всичко нужно за компилирането му от приложената флашка (в директорията „j2s“) или от Git репозиторията <https://git.topcheto.eu/topchetoeu/j2s.git>. За компилацията на кода е нужен следния софтуер: Можете да изтеглите кода на проекта, като и всичко нужно за компилирането му от приложената флашка (в директорията „j2s“) или от Git хранилището <https://git.topcheto.eu/topchetoeu/j2s.git>. За компилацията на кода е нужен следния софтуер:
- OpenJDK 17 SDK - OpenJDK 17 SDK
- Gradle 8.10 - Gradle 8.10
@ -924,7 +946,7 @@ class Frame {
## Използване на проекта като зависимост в друг проект ## Използване на проекта като зависимост в друг проект
За проекта е налично и Maven хранилище, достъпно чрез URL адреса [https://git.topcheto.eu/api/packages/topchetoeu/maven], в което присъстват всички компонети на проекта. За проекта е налично и Maven хранилище, достъпно чрез URL адреса [https://git.topcheto.eu/api/packages/topchetoeu/maven], в което присъстват всички компоненти на проекта.
---- ----
@ -1026,7 +1048,7 @@ $ for (const el of myGen(0, 30, 10)) print(el);
20i 20i
undefined undefined
``` ```
Употреба на по-нов синтаксиси, с помощта на Babel Употреба на по-нови синтаксиси, с помощта на Babel
::: :::
---- ----
@ -1055,6 +1077,8 @@ StdLib.addGlobals(env);
env.add(Compiler.KEY, Compilers.jsCompiler()); env.add(Compiler.KEY, Compilers.jsCompiler());
``` ```
Важно е да се отбележе, че събитийния цикъл работи в отделна нижка по подразбиране. Не е нужно обаче да бъде стартиран събитийния цикъл, а може да бъде повиквана само неговия метод `Engine.run(true)`, като `true` в това повикване означава „изпълнение на събитийния цикъл докато опашката от съобщения приключи“.
Ако искате да регистрирате транспилатори, може да използвате следния код (имайте в предвид, че тези функции създават нова среда и нова стандартна библиотека за всеки транспилатор, за да бъдат изолирани от останалия код.): Ако искате да регистрирате транспилатори, може да използвате следния код (имайте в предвид, че тези функции създават нова среда и нова стандартна библиотека за всеки транспилатор, за да бъдат изолирани от останалия код.):
```java ```java
@ -1081,7 +1105,7 @@ engine.pushMsg(false, env, new Filename("test", "test"), "console.log(10 + 5, \"
Работата с JavaScript обекти е лесна, трябва да бъдат използвани само описаните в [@имплементация-на-javascript-стойностите] методи: Работата с JavaScript обекти е лесна, трябва да бъдат използвани само описаните в [@имплементация-на-javascript-стойностите] методи:
```java ```java
ArrayValue doInterestingStuff(Environment env, Value input) { ArrayValue doStuff(Environment env, Value input) {
var obj = new ObjectValue(); var obj = new ObjectValue();
obj.defineOwnField(env, "test", NumberValue.of(10)); obj.defineOwnField(env, "test", NumberValue.of(10));
obj.defineOwnField(env, "a", StringValue.of("fsadf")); obj.defineOwnField(env, "a", StringValue.of("fsadf"));
@ -1095,7 +1119,7 @@ ArrayValue doInterestingStuff(Environment env, Value input) {
} }
``` ```
Често ще Ви се налага да създавате и Java функцкии (трябва да правите такива функции с повишено внимание, понеже те имат неограничен достъп до Java средата на изпълнение, което може да означава че зле написана Java функция може да компрометира сигурността). Следния код е пример как се създава Java функция: Често ще Ви се налага да създавате и Java функции, за да давате достъп на JavaScript до Вашият софтуер. Подобни функции обаче трябва да бъдат създавани с повишено внимание, понеже могат да бъдат използвани и злонамерено. Подобни функции се създават по следния начин:
```java ```java
var func = new NativeFunction("myFunc", args -> { var func = new NativeFunction("myFunc", args -> {
@ -1106,6 +1130,18 @@ var func = new NativeFunction("myFunc", args -> {
}); });
``` ```
`args` аргумента е от типа `Argument`. Това е служебен клас, който служи за улеснен достъп до аргументите, подадени на функцията. Той има следните основни методи и полета:
- self - „this“ аргумента
- args - масив от подадените аргументи
- env - средата, подадена при извикването
- isNew - „true“, ако е извикана в режим на конструкция (в който случай „self“ е „target“ функцията)
- setTargetProto(obj) - в контекста на конструкция, прилака прототипа на „target“ функцията на подадения обект
- n() - връща броя на аргументите
- has(i) - проверка дали този аргумент е бил подаден
- self(clazz) - ако „this“ аргумента е „UserValue“, превръща съдържаната стойност в дадения клас, иначе връща „null“
- get(i) - връща съответния на i аргумент, или „null“, ако индекса е извън обхвата на масива
# Заключение {.nonum} # Заключение {.nonum}
В настоящата дипломна работа е демонстриран интерпретатора на JavaScript, написан на Java. За да бъде създаден, беше направено проучване на съществуващите технологии, както и методите на интерпретиране. Беше отделено и голямо внимание на добрата и проста архитектура на проекта. Архитектурата се развиваше заедно с кода, докато достигне до днешната си форма, като не стана заплетена и ненужно сложна. Интерпретатора беше тестван най-вече ръчно чрез сравнение с държанието на V8 при същия код, както и чрез трите транспилатора „Babel“, „TypeScript“ и „CoffeeScript“. В настоящата дипломна работа е демонстриран интерпретатора на JavaScript, написан на Java. За да бъде създаден, беше направено проучване на съществуващите технологии, както и методите на интерпретиране. Беше отделено и голямо внимание на добрата и проста архитектура на проекта. Архитектурата се развиваше заедно с кода, докато достигне до днешната си форма, като не стана заплетена и ненужно сложна. Интерпретатора беше тестван най-вече ръчно чрез сравнение с държанието на V8 при същия код, както и чрез трите транспилатора „Babel“, „TypeScript“ и „CoffeeScript“.
@ -1171,7 +1207,7 @@ else return end.chainError(src.loc(i + n), "Expected end of statement");
Всеки клас за елемент от синтактичното дърво съответно има следните три основни функции: Всеки клас за елемент от синтактичното дърво съответно има следните три основни функции:
- `compile` - Добавя съответставщите на елемента инструкции в подадения `CompileResult` - `compile` - Добавя съответставащите на елемента инструкции в подадения `CompileResult`
- `compileFunctions` - Използва се като служебна функция, която да позволява компилирането първо на по-дълбоките функции - `compileFunctions` - Използва се като служебна функция, която да позволява компилирането първо на по-дълбоките функции
- `resolve` - Дефинира в подадения `CompileResult` променливите, които елемента декларира - `resolve` - Дефинира в подадения `CompileResult` променливите, които елемента декларира
@ -1193,7 +1229,7 @@ else return end.chainError(src.loc(i + n), "Expected end of statement");
Създадената система за добавяне на интерпретатори от своя страна е създадена такава, че може няколко интерпретатора да бъдат „наредени“ един след друг. Това позволява изключителна гъвкавост, както и допълнителна изолация на средата - може код, който използва компилатор да дефинира собствен компилатор без да знае за компилатора, на който е базиран. Създадената система за добавяне на интерпретатори от своя страна е създадена такава, че може няколко интерпретатора да бъдат „наредени“ един след друг. Това позволява изключителна гъвкавост, както и допълнителна изолация на средата - може код, който използва компилатор да дефинира собствен компилатор без да знае за компилатора, на който е базиран.
Освен кода, за изпълнението на TypeScript кода са включени и типизации на стандартните билиотеки, описващи специфичната стандартна библиотека, дефинирана тук. Освен кода, за изпълнението на TypeScript кода са включени и типизации на стандартните библиотеки, описващи специфичната стандартна библиотека, дефинирана тук.
За да има достъп кода на стандартната библиотека до някои вътрешни функции на интерпретатора, беше създаден и класа „Primordials“, който създава набор от функции, които могат да бъдат използвани за ***незащитен*** достъп до иначе недостъпните чрез синтаксис функционалности. За да има достъп кода на стандартната библиотека до някои вътрешни функции на интерпретатора, беше създаден и класа „Primordials“, който създава набор от функции, които могат да бъдат използвани за ***незащитен*** достъп до иначе недостъпните чрез синтаксис функционалности.
@ -1203,6 +1239,22 @@ else return end.chainError(src.loc(i + n), "Expected end of statement");
Това е втората част от „lib“ компонента. Той използва `DebugHandler` механизма, за да прихване различните събития на изпълнението на кода. Специалното на дебъгера е, че имплементира V8 протокола за дебъгване [@refs-v8-debug-protocol], което означава, че дебъгването на програма, изпълнявана в този интерпретатор, е възможно в инструментите за разработчици на Chrome, както и във Visual Studio Code. Това е втората част от „lib“ компонента. Той използва `DebugHandler` механизма, за да прихване различните събития на изпълнението на кода. Специалното на дебъгера е, че имплементира V8 протокола за дебъгване [@refs-v8-debug-protocol], което означава, че дебъгването на програма, изпълнявана в този интерпретатор, е възможно в инструментите за разработчици на Chrome, както и във Visual Studio Code.
За да бъде използван дебъгера, трябва бъде създаден „DebugServer“, който имплементира прост HTTP и WebSocket сървър, който да комуникира с V8 дебъг клиента. След това, трябва да бъде създаден регистратор на дебъгера при осъществяването на връзка със сървъра. За целта може да бъде използван вградения „SimpleDebugger“. Кода, който може да използвате за целта е следния:
```java
var handler = new SimpleDebugHandler();
env.add(DebugHandler.KEY, handler);
var server = new DebugServer();
var debugTask = server.start(new InetSocketAddress("127.0.0.1", 9229), true);
server.targets.put("default", (socket, req) -> {
var debugger = new SimpleDebugger(socket);
debugger.attach(handler);
});
```
След това, за да бъде използван дебъгера може да се използва или Chrome (или всеки браузър, базиран на Chromium), или VSCode, като Chrome автоматично ще засече присъствието на Debug сървър и ще предложи стартирането на дебъг клиент чрез зелена икона на NodeJS, която ще се покаже в горния ляв ъгъл на опциите на разработчиците (достъпно чрез F12).
![Пример за дебъгване на програма от интерпретатора](./img/debugging-example.png "debug") ![Пример за дебъгване на програма от интерпретатора](./img/debugging-example.png "debug")
# Използвани термини и чуждици {.nonum} # Използвани термини и чуждици {.nonum}

View File

@ -315,7 +315,7 @@ function converters.Image(data, ctx)
return text, title and { title } or alt_plain or url or "[picture]"; return text, title and { title } or alt_plain or url or "[picture]";
end end
function converters.Figure(data, ctx) function converters.Figure(data, ctx)
local chapter_i = #(ctx.headers or {}); local chapter_i = count_headers(ctx.headers or {});
ctx.figures_indices = ctx.figures_indices or {}; ctx.figures_indices = ctx.figures_indices or {};
ctx.figures_indices[chapter_i] = (ctx.figures_indices[chapter_i] or 0) + 1; ctx.figures_indices[chapter_i] = (ctx.figures_indices[chapter_i] or 0) + 1;
local figure_i = ctx.figures_indices[chapter_i]; local figure_i = ctx.figures_indices[chapter_i];

View File

@ -8,6 +8,12 @@
@page { @page {
size: A4; size: A4;
} }
@page {
@bottom-right-corner {
text-align: center;
content: counter(page);
}
}
h1 { h1 {
break-before: page; break-before: page;
} }
@ -34,6 +40,7 @@
} }
.page { .page {
height: 100%;
line-height: 1.25; line-height: 1.25;
h1, h2, h3, h4, h5, h6 { h1, h2, h3, h4, h5, h6 {
margin: 0; margin: 0;
@ -167,6 +174,7 @@
.page { .page {
height: 100%; height: 100%;
max-height: 100%;
font-size: 12pt; font-size: 12pt;
} }
@ -175,9 +183,12 @@
flex-direction: row; flex-direction: row;
width: 100%; width: 100%;
gap: 2em; gap: 2em;
font-size: 1.25em;
text-align: right;
margin-bottom: 1em;
} }
.school-img { .school-img {
height: 5em; height: 6em;
width: unset; width: unset;
} }
@ -213,6 +224,9 @@
text-align: right; */ text-align: right; */
} }
.asm-title { .asm-title {
display: flex;
flex-direction: column;
gap: 1em;
text-align: center; text-align: center;
} }
.asm-content { .asm-content {
@ -226,8 +240,6 @@
height: 100%; height: 100%;
} }
.asm-page { .asm-page {
height: 100%;
max-height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
@ -471,6 +483,11 @@
// console.log(target, name, i); // 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> </script>
</head> </head>
@ -484,10 +501,10 @@
<div class="title-content"> <div class="title-content">
<h2>ДИПЛОМНА РАБОТА</h2> <h2>ДИПЛОМНА РАБОТА</h2>
<h4>по професия код {{profession}}</h4> <div>по професия код {{profession}}</div>
<h4>специалност код {{specialty}}</h4> <div>специалност код {{specialty}}</div>
<div>Тема: {{topic}}</div>
</div> </div>
<div class="title-content">Тема: {{topic}}</div>
<div class="title-authors"> <div class="title-authors">
<div class="title-author"> <div class="title-author">
<div class="author-type">Дипломант:</div> <div class="author-type">Дипломант:</div>
@ -518,11 +535,12 @@
</div> </div>
<div class="asm-content"> <div class="asm-content">
<div class="asm-title"> <div class="asm-title">
<h2>ЗАДАНИЕ</h2> <h2>ЗАДАНИЕ<br/>за дипломна работа</h2>
<h2>за дипломна работа</h2>
<h4>ДЪРЖАВЕН ИЗПИТ ЗА ПРИДОБИВАНЕ НА ТРЕТА СТЕПЕН НА ПРОФЕСИОНАЛНА КВАЛИФИКАЦИЯ</h4> <h4>ДЪРЖАВЕН ИЗПИТ ЗА ПРИДОБИВАНЕ НА ТРЕТА СТЕПЕН НА ПРОФЕСИОНАЛНА КВАЛИФИКАЦИЯ</h4>
<h4>по професия код {{profession}}</h4> <div>
<h4>специалност код {{specialty}}</h4> <div>по професия код {{profession}}</div>
<div>специалност код {{specialty}}</div>
</div>
</div> </div>
<div class="asm-requirements"> <div class="asm-requirements">
на ученика {{author}} от {{class}} клас<br/> на ученика {{author}} от {{class}} клас<br/>
@ -545,6 +563,10 @@
</div> </div>
</div> </div>
<div class="page">
<div>prazna str</div>
</div>
{{content}} {{content}}
<nav> <nav>

View File

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

View File

@ -323,7 +323,7 @@ public final class Frame {
/** /**
* Induces a value on the stack (as if it were returned by the last function call) * Induces a value on the stack (as if it were returned by the last function call)
* and executes the next instruction in the frame. * and executes the next instruction in the frame.
* *
* @param value The value to induce * @param value The value to induce
*/ */
public final Value next(Value value) { public final Value next(Value value) {
@ -334,7 +334,7 @@ public final class Frame {
* Note that this is different than just throwing the error outside the * Note that this is different than just throwing the error outside the
* function, as the function executed could have a try-catch which * function, as the function executed could have a try-catch which
* would otherwise handle the error * would otherwise handle the error
* *
* @param error The error to induce * @param error The error to induce
*/ */
public final Value induceError(EngineException error) { public final Value induceError(EngineException error) {
@ -346,7 +346,7 @@ public final class Frame {
* Note that this is different than just returning the value outside the * Note that this is different than just returning the value outside the
* function, as the function executed could have a try-catch which * function, as the function executed could have a try-catch which
* would otherwise handle the error * would otherwise handle the error
* *
* @param value The retunr value to induce * @param value The retunr value to induce
*/ */
public final Value induceReturn(Value value) { public final Value induceReturn(Value value) {

View File

@ -57,7 +57,7 @@ public final class StringValue implements PrimitiveValue {
} }
} }
return StringValue.this.getOwnMember(env, key); return PrimitiveValue.super.getOwnMember(env, key);
} }
@Override public Set<String> getOwnMembers(Environment env, boolean onlyEnumerable) { @Override public Set<String> getOwnMembers(Environment env, boolean onlyEnumerable) {