ui: regroup the CAP report into Package / Requires / Memory tables (v3.5.17)
The CAP analysis box was a flat list mixing memory, package and library
data, with run-on "table-like" lines of differing lengths. Rebuild it as
a labelled report (always visible except the bulky parts, which stay in
nested disclosures):
- Package: AID (plus the optional JC 2.2 package_name), version, header
flags (applet/exports/int), applet AIDs and the compiled-against hint
(javacard.framework -> Java Card SDK release + CAP format).
- Requires (n): one row per imported library with the minimum version
(">=", same major and minor >= the recorded export version per JC VM
4.5.2), the API family and the distinct constant-pool reference count.
The AID is not repeated when the name resolves (redundant); unresolved
AIDs are shown as the name.
- Memory: the NVRAM requirement stated once, with indented children (code
image + persistent data parts), RAM (volatile) and the suggested
C6/C7/C8 quotas - the old duplicated totals are gone.
- Components: collapsed table in load-file order with size and share,
ending in the load-file total row; Notes holds the GP caveat and the
estimate disclaimer.
- Numbers line up right-aligned in monospace columns; section labels use
uppercase letter-spacing inline, so no Tailwind rebuild is needed.
- i18n: 27 new EN/RU keys; help EN/RU and AGENTS updated.
Frontend tests reworked for the new markup (group labels, aligned values,
load-file order, total row, and the no-imports / unknown-AID /
older-response / missing-components cases).
592 frontend / 473 Python green; version 3.5.17; sw simple-v270.
This commit is contained in:
@@ -319,7 +319,7 @@
|
||||
<thead><tr class="border-b border-gray-300 dark:border-slate-700"><th class="text-left py-1 px-2">Операция</th><th class="text-left py-1 px-2">Описание</th></tr></thead>
|
||||
<tbody>
|
||||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">Обзор карты (все данные GP)</td><td class="py-1 px-2">Запрос GET STATUS для ISD, приложений, ELF и модулей ELF, а также GET DATA FF21 для информации о памяти. Результаты отображаются в обзоре с кнопками <strong>Удалить</strong> для каждого элемента.</td></tr>
|
||||
<tr><td class="py-1 px-2">Установка пакета (.cap файл)</td><td class="py-1 px-2">Отправка <code class="font-mono text-sm">.cap</code> файла на карту через сервер: INSTALL[for load] → LOAD ×N → INSTALL[for install (+make selectable)]. Load-файл делится на LOAD APDU размера <strong>размер блока LOAD</strong> (1–240 байт полезной нагрузки, по умолчанию 240); каждый защищённый пакет доставляется одним SMS или, если он больше, конкатенированной SMS-PP загрузкой до 5 сегментов, так что большой <code>.cap</code> просто занимает несколько SMS. Сразу после выбора файл проверяется и в форме показывается <strong>оценка требований к памяти</strong>: <strong>требование NVRAM</strong> (образ кода + постоянные данные, сумма в стиле C6+C8 из GP Card Spec) и оценка RAM. Там же перечислены <strong>библиотеки, с которыми слинкован CAP</strong> (его компонент Import, в виде <code class="font-mono text-sm">имя ≥ версия</code> — имена стандартных библиотек подставляются, а где известно, даётся подсказка о версии Java Card), идентификаторы пакета и апплетов и разбивка по компонентам. Повреждённый файл не проходит анализ, и <strong>Execute</strong> остаётся недоступной до успешного анализа.</td></tr>
|
||||
<tr><td class="py-1 px-2">Установка пакета (.cap файл)</td><td class="py-1 px-2">Отправка <code class="font-mono text-sm">.cap</code> файла на карту через сервер: INSTALL[for load] → LOAD ×N → INSTALL[for install (+make selectable)]. Load-файл делится на LOAD APDU размера <strong>размер блока LOAD</strong> (1–240 байт полезной нагрузки, по умолчанию 240); каждый защищённый пакет доставляется одним SMS или, если он больше, конкатенированной SMS-PP загрузкой до 5 сегментов, так что большой <code>.cap</code> просто занимает несколько SMS. Сразу после выбора файл проверяется и в форме показывается <strong>оценка требований к памяти</strong>: <strong>требование NVRAM</strong> (образ кода + постоянные данные, сумма в стиле C6+C8 из GP Card Spec) и оценка RAM. Там же показаны <strong>библиотеки, с которыми слинкован CAP</strong> (его компонент Import, в виде <code class="font-mono text-sm">имя ≥ версия</code> — имена стандартных библиотек подставляются, а где известно, даётся подсказка о версии Java Card), идентификаторы пакета и апплетов и разбивка по компонентам (размеры и доли в порядке load-файла) — всё сгруппировано по разделам <strong>Package</strong>, <strong>Requires</strong>, <strong>Memory</strong> и <strong>Components</strong>. Повреждённый файл не проходит анализ, и <strong>Execute</strong> остаётся недоступной до успешного анализа.</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -376,7 +376,7 @@
|
||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||
<li><strong>Empty</strong> — начать с пустого списка.</li>
|
||||
<li><strong>Explore ISD</strong> — эталонная административная последовательность (<code class="font-mono text-sm">GET DATA FF21</code>, листинги GET STATUS ISD/ELF/приложений, <code class="font-mono text-sm">GET DATA 0085</code>); длинные листинги автоматически продолжаются страницами <code class="font-mono text-sm">SW 6310/CAFE</code>.</li>
|
||||
<li><strong>Install from .cap</strong> — выберите <code class="font-mono text-sm">.cap</code>, при необходимости SD AID, параметры install/STK и <em>Make selectable</em>; кнопка <strong>Сгенерировать</strong> строит INSTALL [for load] → LOAD ×N → INSTALL [for install]. Файл используется только для генерации APDU — он не сохраняется, как и его имя. При выборе файл проверяется и показывается оценка требований к памяти (требование NVRAM как сумма образа кода и постоянных данных, плюс RAM); также перечисляются требуемые библиотеки, идентификаторы пакета и разбивка по компонентам. <strong>Сгенерировать</strong> недоступна до успешного анализа.</li>
|
||||
<li><strong>Install from .cap</strong> — выберите <code class="font-mono text-sm">.cap</code>, при необходимости SD AID, параметры install/STK и <em>Make selectable</em>; кнопка <strong>Сгенерировать</strong> строит INSTALL [for load] → LOAD ×N → INSTALL [for install]. Файл используется только для генерации APDU — он не сохраняется, как и его имя. При выборе файл проверяется и показывается оценка требований к памяти (требование NVRAM как сумма образа кода и постоянных данных, плюс RAM); также перечисляются требуемые библиотеки, идентификаторы пакета и разбивка по компонентам (сгруппировано по разделам Package / Requires / Memory / Components). <strong>Сгенерировать</strong> недоступна до успешного анализа.</li>
|
||||
<li><strong>Delete AID</strong> — список AID (по одному в строке) и P2 (<em>object only</em> / <em>object and related objects</em>) → APDU DELETE.</li>
|
||||
</ul>
|
||||
<p class="text-sm mb-3">Таблица показывает имя, тип, число APDU и время создания каждого скрипта, а также кнопки <strong>Редактировать</strong> и <strong>Удалить</strong>; в редакторе есть поле имени и текстовая область APDU. В ходе сессии сервер отдаёт по одному C-APDU на каждый POST карты и отслеживает выполнение: карта сообщает статус в следующем POST (<code class="font-mono text-sm">X-Admin-Script-Status</code>), оборвавшаяся сессия досылает только невыполненные APDU (<code class="font-mono text-sm">X-Admin-Resume</code> продолжает, новый диалог начинает заново), а завершённый скрипт закрывается ответом <code class="font-mono text-sm">204 No Content</code>. Конструктор RAM/GP вкладки Remote APDU может отправить цепочку команд прямо в прогон кнопкой <strong>«В очередь SCP81»</strong>.</p>
|
||||
|
||||
+2
-2
@@ -318,7 +318,7 @@
|
||||
<thead><tr class="border-b border-gray-300 dark:border-slate-700"><th class="text-left py-1 px-2">Operation</th><th class="text-left py-1 px-2">Description</th></tr></thead>
|
||||
<tbody>
|
||||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">Explore Card (all GP data)</td><td class="py-1 px-2">Queries GET STATUS for ISD, Applications, ELFs, and ELF Modules, plus GET DATA FF21 for memory info. Results appear in an explorer view with per-item <strong>Delete</strong> buttons.</td></tr>
|
||||
<tr><td class="py-1 px-2">Install Package (.cap file)</td><td class="py-1 px-2">Sends a <code class="font-mono text-sm">.cap</code> file to the card via the server: INSTALL[for load] → LOAD ×N → INSTALL[for install (+make selectable)]. The load file is split into LOAD APDUs of the <strong>LOAD block size</strong> (1–240 bytes of payload, 240 by default); each secured packet is delivered as a single SMS or, when larger, as a concatenated SMS-PP download of up to 5 segments, so a large <code class="font-mono text-sm">.cap</code> simply takes several SMS. As soon as the file is selected it is validated and its <strong>memory requirements are estimated</strong> in the form: the <strong>NVRAM requirement</strong> (the code image + persistent data, the GlobalPlatform Card Spec C6+C8 style total) and the RAM estimate. The same box lists the <strong>libraries the CAP is linked against</strong> (its Import component, shown as <code class="font-mono text-sm">name ≥ version</code> with standard library names resolved and a Java Card SDK hint where known) plus the package/applet identity and the component breakdown. A corrupt or wrong-format file fails the analysis, and <strong>Execute</strong> stays disabled until a successful analysis.</td></tr>
|
||||
<tr><td class="py-1 px-2">Install Package (.cap file)</td><td class="py-1 px-2">Sends a <code class="font-mono text-sm">.cap</code> file to the card via the server: INSTALL[for load] → LOAD ×N → INSTALL[for install (+make selectable)]. The load file is split into LOAD APDUs of the <strong>LOAD block size</strong> (1–240 bytes of payload, 240 by default); each secured packet is delivered as a single SMS or, when larger, as a concatenated SMS-PP download of up to 5 segments, so a large <code class="font-mono text-sm">.cap</code> simply takes several SMS. As soon as the file is selected it is validated and its <strong>memory requirements are estimated</strong> in the form: the <strong>NVRAM requirement</strong> (the code image + persistent data, the GlobalPlatform Card Spec C6+C8 style total) and the RAM estimate. The same box reports the <strong>libraries the CAP is linked against</strong> (its Import component, shown as <code class="font-mono text-sm">name ≥ version</code> with standard library names resolved and a Java Card SDK hint where known), the package/applet identity and the component breakdown (sizes and shares in load-file order) — grouped under the <strong>Package</strong>, <strong>Requires</strong>, <strong>Memory</strong> and <strong>Components</strong> headings. A corrupt or wrong-format file fails the analysis, and <strong>Execute</strong> stays disabled until a successful analysis.</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -375,7 +375,7 @@
|
||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||
<li><strong>Empty</strong> — start from scratch.</li>
|
||||
<li><strong>Explore ISD</strong> — the reference administration sequence (<code class="font-mono text-sm">GET DATA FF21</code>, GET STATUS ISD/ELF/application listings, <code class="font-mono text-sm">GET DATA 0085</code>); long listings auto-continue through the <code class="font-mono text-sm">SW 6310/CAFE</code> pages.</li>
|
||||
<li><strong>Install from .cap</strong> — pick a <code class="font-mono text-sm">.cap</code> plus optional SD AID, install/STK parameters and <em>make selectable</em>; <strong>Generate</strong> builds INSTALL [for load] → LOAD ×N → INSTALL [for install]. The file is only used to generate the APDUs — it is not stored, not even its name. On selection it is validated and its memory requirements are estimated (the NVRAM requirement as the code-image + persistent-data total, plus RAM); the required libraries, package identity and component breakdown are listed too. <strong>Generate</strong> stays disabled until the analysis succeeds.</li>
|
||||
<li><strong>Install from .cap</strong> — pick a <code class="font-mono text-sm">.cap</code> plus optional SD AID, install/STK parameters and <em>make selectable</em>; <strong>Generate</strong> builds INSTALL [for load] → LOAD ×N → INSTALL [for install]. The file is only used to generate the APDUs — it is not stored, not even its name. On selection it is validated and its memory requirements are estimated (the NVRAM requirement as the code-image + persistent-data total, plus RAM); the required libraries, package identity and component breakdown are listed too (grouped under Package / Requires / Memory / Components headings). <strong>Generate</strong> stays disabled until the analysis succeeds.</li>
|
||||
<li><strong>Delete AID</strong> — AID list (one per line) and P2 (<em>object only</em> / <em>object and related objects</em>) → DELETE APDUs.</li>
|
||||
</ul>
|
||||
<p class="text-sm mb-3">The table lists each script with its kind, APDU count and creation time, plus <strong>Edit</strong> and <strong>Delete</strong>; the editor has a name field and the APDU textarea. Over a session the server serves one C-APDU per card POST and tracks execution: the card reports status in its next POST (<code class="font-mono text-sm">X-Admin-Script-Status</code>), a session that dies resends only the unexecuted APDUs (<code class="font-mono text-sm">X-Admin-Resume</code> continues, a fresh dialog restarts), and a completed script is closed with <code class="font-mono text-sm">204 No Content</code>. The Remote APDU tab's RAM/GP builder can feed a command chain straight into the run with <strong>Queue in SCP81</strong>.</p>
|
||||
|
||||
+124
-64
@@ -1616,7 +1616,7 @@
|
||||
// ===== Version =====
|
||||
// Single source of truth for the PWA version: shown in the header and used
|
||||
// by the server version check in pysimConnect().
|
||||
const SIMPLE_VERSION = '3.5.16';
|
||||
const SIMPLE_VERSION = '3.5.17';
|
||||
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
|
||||
|
||||
// ===== Tab switching =====
|
||||
@@ -8032,83 +8032,116 @@ function capMemHtml(mem) {
|
||||
const requirement = Number(nv.requirement) || (loadFile + nvramData);
|
||||
const otherComponents = Math.max(0, loadFile - methodBytes);
|
||||
const imports = mem.imports || [];
|
||||
const applets = mem.applets || [];
|
||||
const comps = mem.components || [];
|
||||
const bytes = v => esc(capMemBytes(v));
|
||||
// the report is a set of one-heading tables: labels and column heads are
|
||||
// muted, numbers line up right-aligned in a monospace column
|
||||
const cell = (content, cls, span) => '<td' + (span ? ' colspan="' + span + '"' : '') +
|
||||
' class="py-0.5' + (cls ? ' ' + cls : '') + '">' + content + '</td>';
|
||||
const head = (content, cls) => '<td class="pb-0.5 border-b border-gray-200 dark:border-slate-700' + (cls ? ' ' + cls : '') + '">' + content + '</td>';
|
||||
const table = rows => '<table class="w-full border-collapse">' + rows + '</table>';
|
||||
const row = cells => '<tr>' + cells + '</tr>';
|
||||
const label = (content, cls) => cell(content, 'whitespace-nowrap' + (cls ? ' ' + cls : ''));
|
||||
const num = content => cell(content, 'pl-4 text-right whitespace-nowrap font-mono');
|
||||
const part = content => cell(content, 'pl-4 text-gray-400 dark:text-slate-500');
|
||||
const section = content => '<div class="mt-2 font-medium text-gray-500 dark:text-slate-400" style="text-transform:uppercase;letter-spacing:.06em">' + esc(content) + '</div>';
|
||||
const disclosure = content => '<details class="mt-1"><summary class="cursor-pointer mt-2 font-medium text-gray-500 dark:text-slate-400" style="text-transform:uppercase;letter-spacing:.06em">' + esc(content) + '</summary>';
|
||||
let html = '<div class="font-medium text-gray-700 dark:text-slate-300">' + esc(t('CAP requirements (estimate)')) + '</div>';
|
||||
// the libraries the CAP is linked against (JC VM spec 6.6), shown as
|
||||
// ">=" requirements: same major, minor >= the export version (4.5.2)
|
||||
if (imports.length) {
|
||||
const req = imports.map(im => {
|
||||
const name = jcAidName(im.aid) || im.aid;
|
||||
return name + ' \u2265 ' + (Number(im.major) || 0) + '.' + (Number(im.minor) || 0);
|
||||
}).join(' \u00b7 ');
|
||||
html += '<div>' + esc(t('Requires:')) + ' ' + esc(req) + '</div>';
|
||||
|
||||
// ── package identity: AIDs, header flags (Table 6-4), applets and the
|
||||
// compiled-against hint (javacard.framework version + CAP format) ──
|
||||
let pkgRows = '';
|
||||
if (mem.package_aid) {
|
||||
let val = esc(mem.package_aid);
|
||||
if (mem.package_name) val += '<div class="text-gray-400 break-all">' + esc(mem.package_name) + '</div>';
|
||||
pkgRows += row(label(esc(t('Package'))) + cell(val, 'pl-4 font-mono break-all'));
|
||||
}
|
||||
// the NVRAM requirement is a C6+C8-style total (GP Card Spec v2.3.1
|
||||
// Table 11-48): the package image (load file) plus the persistent data
|
||||
html += '<div>' + esc(t('NVRAM requirement ≈')) + ' ' + esc(capMemBytes(requirement)) +
|
||||
' (' + esc(t('code image')) + ' ' + esc(capMemBytes(loadFile)) +
|
||||
' + ' + esc(t('data')) + ' ' + esc(capMemBytes(nvramData)) + ')' +
|
||||
' · ' + esc(t('RAM (volatile):')) + ' ' + esc(capMemBytes(ram.total)) + '</div>';
|
||||
html += '<details class="mt-1"><summary class="cursor-pointer text-gray-400">' + esc(t('Details')) + '</summary>';
|
||||
// compiled-against hint from the javacard.framework version + CAP format
|
||||
const fw = imports.find(im => (im.aid || '').toUpperCase() === 'A0000000620101');
|
||||
const sdk = fw ? JC_FRAMEWORK_SDK[(Number(fw.major) || 0) + '.' + (Number(fw.minor) || 0)] : '';
|
||||
let against = sdk ? 'Java Card ' + sdk : '';
|
||||
if (mem.cap_version) against += (against ? ' · ' : '') + t('CAP format') + ' ' + mem.cap_version;
|
||||
if (against) html += '<div>' + esc(t('Compiled against:')) + ' ' + esc(against) + '</div>';
|
||||
if (imports.length) {
|
||||
imports.forEach(im => {
|
||||
const name = jcAidName(im.aid);
|
||||
const fam = jcAidFamily(im.aid);
|
||||
let line = (name || im.aid) + ' ' + (Number(im.major) || 0) + '.' + (Number(im.minor) || 0);
|
||||
if (fam) line += ' — ' + fam;
|
||||
line += ' — ' + (Number(im.refs) || 0) + ' ' + t('refs');
|
||||
if (name) line += ' — ' + im.aid;
|
||||
html += '<div>' + esc(line) + '</div>';
|
||||
});
|
||||
if (mem.package_version) {
|
||||
pkgRows += row(label(esc(t('Version'))) + cell(esc('v' + mem.package_version), 'pl-4 font-mono'));
|
||||
}
|
||||
// package identity + header flags (Table 6-4) + applets
|
||||
let pkg = esc(mem.package_aid || '?') + (mem.package_version ? ' v' + esc(mem.package_version) : '');
|
||||
const fl = mem.flags || {};
|
||||
const fls = [];
|
||||
if (fl.applet) fls.push(t('applet package'));
|
||||
if (fl.export) fls.push(t('exports an API'));
|
||||
if (fl.int) fls.push(t('uses int'));
|
||||
if (fls.length) pkg += ' (' + fls.join(', ') + ')';
|
||||
if (mem.package_name) pkg += ' — ' + esc(mem.package_name);
|
||||
html += '<div>' + esc(t('Package:')) + ' ' + pkg + '</div>';
|
||||
if ((mem.applets || []).length) {
|
||||
html += '<div>' + esc(t('Applets:')) + ' ' + esc((mem.applets || []).map(a => {
|
||||
if (fls.length) pkgRows += row(label(esc(t('Type'))) + cell(esc(fls.join(' \u00b7 ')), 'pl-4'));
|
||||
if (applets.length) {
|
||||
pkgRows += row(label(esc(t('Applets'))) + cell(applets.map(a => {
|
||||
const n = jcAidName(a);
|
||||
return n ? a + ' (' + n + ')' : a;
|
||||
}).join(', ')) + '</div>';
|
||||
return esc(a) + (n ? ' <span class="text-gray-400">(' + esc(n) + ')</span>' : '');
|
||||
}).join('<br>'), 'pl-4 font-mono break-all'));
|
||||
}
|
||||
const comps = mem.components || [];
|
||||
const fw = imports.find(im => (im.aid || '').toUpperCase() === 'A0000000620101');
|
||||
const sdk = fw ? JC_FRAMEWORK_SDK[(Number(fw.major) || 0) + '.' + (Number(fw.minor) || 0)] : '';
|
||||
let against = sdk ? 'Java Card ' + sdk : '';
|
||||
if (mem.cap_version) against += (against ? ' \u00b7 ' : '') + t('CAP format') + ' ' + mem.cap_version;
|
||||
if (against) pkgRows += row(label(esc(t('Compiled'))) + cell(esc(against), 'pl-4'));
|
||||
if (pkgRows) html += section(t('Package')) + table(pkgRows);
|
||||
|
||||
// ── the libraries the CAP is linked against (JC VM spec 6.6), shown as
|
||||
// ">=" requirements: same major, minor >= the export version (4.5.2);
|
||||
// refs = distinct constant-pool references to the package (6.7) ──
|
||||
if (imports.length) {
|
||||
let reqRows = row(head('', 'text-left') + head(esc(t('Version')), 'pl-4 text-right whitespace-nowrap') +
|
||||
head(esc(t('Family')), 'pl-4 text-left') + head(esc(t('Refs')), 'pl-4 text-right whitespace-nowrap'));
|
||||
imports.forEach(im => {
|
||||
const name = jcAidName(im.aid);
|
||||
const fam = jcAidFamily(im.aid);
|
||||
reqRows += row(cell(name ? esc(name) : '<span class="font-mono break-all">' + esc(im.aid) + '</span>', 'whitespace-nowrap') +
|
||||
num('\u2265 ' + (Number(im.major) || 0) + '.' + (Number(im.minor) || 0)) +
|
||||
part(esc(fam)) + num(String(Number(im.refs) || 0)));
|
||||
});
|
||||
html += section(t('Requires') + ' (' + imports.length + ')') + table(reqRows);
|
||||
}
|
||||
|
||||
// ── memory: the NVRAM requirement is the C6+C8-style total (GP Card
|
||||
// Spec v2.3.1 Table 11-48) of the code image (load file) and the
|
||||
// persistent data; children are indented under their total ──
|
||||
const mrows = [];
|
||||
mrows.push(row(label(esc(t('NVRAM requirement'))) + num(bytes(requirement)) +
|
||||
part(esc(t('code image') + ' + ' + t('data')))));
|
||||
mrows.push(row(label(esc(t('code image (load file)')), 'pl-4') + num(bytes(loadFile)) +
|
||||
part(esc('Method.cap ' + capMemBytes(methodBytes) + ' \u00b7 ' + t('other components') + ' ' + capMemBytes(otherComponents)))));
|
||||
mrows.push(row(label(esc(t('persistent data')), 'pl-4') + num(bytes(nvramData)) + part(esc([
|
||||
t('static image') + ' ' + capMemBytes(nv.static_image),
|
||||
t('static array init') + ' ' + capMemBytes(nv.array_init),
|
||||
t('install-time objects') + ' ' + capMemBytes(nv.install_objects),
|
||||
t('object headers') + ' ' + capMemBytes(nv.header_overhead),
|
||||
t('reference storage') + ' ' + capMemBytes(nv.ref_storage),
|
||||
].join(' \u00b7 ')))));
|
||||
mrows.push(row(label(esc(t('RAM (volatile)'))) + num(bytes(ram.total)) + part(esc([
|
||||
t('transient arrays') + ' ' + capMemBytes(ram.transient_arrays),
|
||||
t('runtime transient') + ' ' + capMemBytes(ram.runtime_transient),
|
||||
t('peak method frame') + ' ' + capMemBytes(ram.peak_frame),
|
||||
].join(' \u00b7 ')))));
|
||||
mrows.push(row(label(esc(t('Suggested quotas (informational)'))) + cell(
|
||||
'C6=' + esc(String(Number(sug.c6) || 0)) + ' C7=0x' + esc(hex2(sug.c7)) + ' C8=0x' + esc(hex2(sug.c8)),
|
||||
'pl-4 font-mono', 2)));
|
||||
html += section(t('Memory')) + table(mrows.join(''));
|
||||
|
||||
// ── component sizes in load-file order (incl. the Directory/Export
|
||||
// entries capmem does not parse) with each entry's share of the load file
|
||||
if (comps.length) {
|
||||
const ctotal = loadFile || comps.reduce((sum, c) => sum + (Number(c.size) || 0), 0);
|
||||
html += '<div>' + esc(t('Components:')) + ' ' + comps.map(c => {
|
||||
const tcls = 'border-t border-gray-200 dark:border-slate-700';
|
||||
let crows = row(head(esc(t('Component')), 'text-left') + head(esc(t('Size')), 'pl-4 text-right whitespace-nowrap') +
|
||||
head(esc(t('Share')), 'pl-4 text-right whitespace-nowrap'));
|
||||
comps.forEach(c => {
|
||||
const sz = Number(c.size) || 0;
|
||||
const pct = ctotal ? ' (' + Math.round(sz * 100 / ctotal) + '%)' : '';
|
||||
return esc(c.name + ' ' + capMemBytes(sz) + pct);
|
||||
}).join(' · ') + '</div>';
|
||||
crows += row(label(esc(c.name)) + num(bytes(sz)) +
|
||||
num(ctotal ? Math.round(sz * 100 / ctotal) + '%' : '\u2014'));
|
||||
});
|
||||
crows += row(cell(esc(t('Load file (total)')), tcls) +
|
||||
cell(bytes(loadFile), 'pl-4 text-right whitespace-nowrap font-mono ' + tcls) +
|
||||
cell('100%', 'pl-4 text-right whitespace-nowrap font-mono ' + tcls));
|
||||
html += disclosure(t('Components') + ' (' + comps.length + ')') + table(crows) + '</details>';
|
||||
}
|
||||
html += '<div>' + esc(t('Code image (load file):')) + ' ' + esc(capMemBytes(loadFile)) +
|
||||
' · ' + esc(t('bytecode (Method.cap):')) + ' ' + esc(capMemBytes(methodBytes)) +
|
||||
' · ' + esc(t('other components:')) + ' ' + esc(capMemBytes(otherComponents)) + '</div>';
|
||||
html += '<div>' + esc(t('Persistent data (NVRAM):')) + ' ' + esc(capMemBytes(nvramData)) +
|
||||
' · ' + esc(t('Static image:')) + ' ' + esc(capMemBytes(nv.static_image)) +
|
||||
' · ' + esc(t('Static array init:')) + ' ' + esc(capMemBytes(nv.array_init)) +
|
||||
' · ' + esc(t('Install-time objects:')) + ' ' + esc(capMemBytes(nv.install_objects)) +
|
||||
' · ' + esc(t('Object headers:')) + ' ' + esc(capMemBytes(nv.header_overhead)) +
|
||||
' · ' + esc(t('Reference storage:')) + ' ' + esc(capMemBytes(nv.ref_storage)) + '</div>';
|
||||
html += '<div>' + esc(t('RAM (volatile):')) + ' ' + esc(capMemBytes(ram.total)) +
|
||||
' · ' + esc(t('Transient arrays:')) + ' ' + esc(capMemBytes(ram.transient_arrays)) +
|
||||
' · ' + esc(t('Runtime transient:')) + ' ' + esc(capMemBytes(ram.runtime_transient)) +
|
||||
' · ' + esc(t('Peak method frame:')) + ' ' + esc(capMemBytes(ram.peak_frame)) + '</div>';
|
||||
html += '<div>' + esc(t('Suggested install quotas (informational):')) +
|
||||
' C6=' + esc(String(Number(sug.c6) || 0)) + ' C7=0x' + esc(hex2(sug.c7)) + ' C8=0x' + esc(hex2(sug.c8)) + '</div>';
|
||||
html += '<div class="text-gray-400">' + esc(t('NVRAM = code image + persistent data (GP Card Spec v2.3.1 Table 11-48, C6+C8 style); card memory management, rounding and the registry entry are not included, and the static field image appears in both parts.')) + '</div>';
|
||||
html += '<div class="text-gray-400">' + esc(t('Estimate only — memory values are not used for installation')) + '</div>';
|
||||
html += '</details>';
|
||||
|
||||
html += disclosure(t('Notes')) +
|
||||
'<div class="text-gray-400">' + esc(t('NVRAM = code image + persistent data (GP Card Spec v2.3.1 Table 11-48, C6+C8 style); card memory management, rounding and the registry entry are not included, and the static field image appears in both parts.')) + '</div>' +
|
||||
'<div class="text-gray-400">' + esc(t('Estimate only — memory values are not used for installation')) + '</div>' +
|
||||
'</details>';
|
||||
(mem.warnings || []).forEach(w => {
|
||||
html += '<div class="text-amber-600 dark:text-amber-400">\u26a0 ' + esc(w) + '</div>';
|
||||
});
|
||||
@@ -16140,6 +16173,33 @@ const LANG_RU = {
|
||||
'Peak method frame:': 'Пиковый кадр метода:',
|
||||
'Suggested install quotas (informational):': 'Рекомендуемые квоты установки (справочно):',
|
||||
'Estimate only — memory values are not used for installation': 'Только оценка — значения памяти не используются при установке',
|
||||
'Package': 'Пакет',
|
||||
'Version': 'Версия',
|
||||
'Type': 'Тип',
|
||||
'Applets': 'Апплеты',
|
||||
'Compiled': 'Собрано',
|
||||
'Requires': 'Требуются',
|
||||
'Family': 'Семейство',
|
||||
'Refs': 'Ссылки',
|
||||
'NVRAM requirement': 'Требование NVRAM',
|
||||
'code image (load file)': 'образ кода (load-файл)',
|
||||
'persistent data': 'постоянные данные',
|
||||
'RAM (volatile)': 'ОЗУ (volatile)',
|
||||
'other components': 'прочие компоненты',
|
||||
'static image': 'статический образ',
|
||||
'static array init': 'инициализация массивов',
|
||||
'install-time objects': 'объекты установки',
|
||||
'object headers': 'заголовки объектов',
|
||||
'reference storage': 'хранение ссылок',
|
||||
'transient arrays': 'transient-массивы',
|
||||
'runtime transient': 'transient во время работы',
|
||||
'peak method frame': 'пиковый кадр',
|
||||
'Suggested quotas (informational)': 'Рекомендуемые квоты (справочно)',
|
||||
'Components': 'Компоненты',
|
||||
'Component': 'Компонент',
|
||||
'Share': 'Доля',
|
||||
'Load file (total)': 'Load-файл (итого)',
|
||||
'Notes': 'Примечания',
|
||||
'Step': 'Шаг',
|
||||
'Install OK': 'Установка OK',
|
||||
'Install FAILED at step:': 'Установка не удалась на шаге:',
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
const CACHE = 'simple-v269';
|
||||
const CACHE = 'simple-v270';
|
||||
const URLS = [
|
||||
'index.html',
|
||||
'help.html',
|
||||
|
||||
@@ -79,33 +79,54 @@ test('capMemBytes formats bytes and kilobytes', () => {
|
||||
assert.strictEqual(capMemBytes(null), '0 B');
|
||||
});
|
||||
|
||||
test('capMemHtml renders the NVRAM requirement, breakdown and warnings', () => {
|
||||
test('capMemHtml renders the grouped report: package, requires, memory, components, warnings', () => {
|
||||
const mem = memFixture();
|
||||
mem.warnings = ['method class[0].token[1]: unknown opcode 0xAA, scan stopped'];
|
||||
const out = capMemHtml(mem);
|
||||
assert.ok(out.includes('CAP requirements (estimate)'), out);
|
||||
// C6+C8-style total: load file + persistent data
|
||||
assert.ok(out.includes('NVRAM requirement ≈ 3.1 kB (code image 2.9 kB + data 136 B)'), out);
|
||||
assert.ok(out.includes('RAM (volatile): 24 B'), out);
|
||||
assert.ok(out.includes('Code image (load file): 2.9 kB'), out);
|
||||
assert.ok(out.includes('bytecode (Method.cap): 2.0 kB'), out);
|
||||
assert.ok(out.includes('other components: 952 B'), out);
|
||||
assert.ok(out.includes('Persistent data (NVRAM): 136 B'), out);
|
||||
assert.ok(out.includes('Static image: 12 B'), out);
|
||||
assert.ok(out.includes('Reference storage: 8 B'), out);
|
||||
assert.ok(out.includes('Peak method frame: 8 B'), out);
|
||||
assert.ok(out.includes('C6=3000 C7=0x0110 C8=0x0088'), out);
|
||||
// grouped, labelled sections (always visible except components/notes)
|
||||
assert.ok(out.includes('>Package</div>'), out);
|
||||
assert.ok(out.includes('>Requires (1)</div>'), out);
|
||||
assert.ok(out.includes('>Memory</div>'), out);
|
||||
assert.ok(out.includes('>Components (3)'), out);
|
||||
assert.ok(out.includes('>Notes'), out);
|
||||
assert.ok(out.includes('Table 11-48'), out);
|
||||
assert.ok(out.includes('Estimate only'), out);
|
||||
assert.ok(out.includes('unknown opcode 0xAA'), out);
|
||||
// the required libraries with the family, reference count and the
|
||||
// compiled-against hint derived from the framework version
|
||||
assert.ok(out.includes('Requires: javacard.framework \u2265 1.0'), out);
|
||||
assert.ok(out.includes('Compiled against: Java Card 2.1.1/2.1.2 \u00b7 CAP format 2.1'), out);
|
||||
assert.ok(out.includes('javacard.framework 1.0 — Oracle JavaCard API — 6 refs — A0000000620101'), out);
|
||||
assert.ok(out.includes('Package: A0000000620101 v1.0 (applet package)'), out);
|
||||
assert.ok(out.includes('Applets: A0000000620101 (javacard.framework)'), out);
|
||||
assert.ok(out.includes('Components: Header 20 B (1%) \u00b7 Method 2.7 kB (93%) \u00b7 ConstantPool 180 B (6%)'), out);
|
||||
// memory: the C6+C8-style total once, with indented children
|
||||
assert.ok(out.includes('NVRAM requirement'), out);
|
||||
assert.ok(out.includes('>3.1 kB</td>'), out);
|
||||
assert.ok(out.includes('code image + data'), out);
|
||||
assert.ok(out.includes('>2.9 kB</td>'), out);
|
||||
assert.ok(out.includes('Method.cap 2.0 kB'), out);
|
||||
assert.ok(out.includes('other components 952 B'), out);
|
||||
assert.ok(out.includes('>136 B</td>'), out);
|
||||
assert.ok(out.includes('static image 12 B'), out);
|
||||
assert.ok(out.includes('reference storage 8 B'), out);
|
||||
assert.ok(out.includes('>24 B</td>'), out);
|
||||
assert.ok(out.includes('peak method frame 8 B'), out);
|
||||
assert.ok(out.includes('C6=3000 C7=0x0110 C8=0x0088'), out);
|
||||
// requires: library name, version, family and refs; the AID is not
|
||||
// repeated when the name resolves (it adds nothing), and the section
|
||||
// carries no package AIDs
|
||||
assert.ok(out.includes('>javacard.framework</td>'), out);
|
||||
assert.ok(out.includes('>\u2265 1.0</td>'), out);
|
||||
assert.ok(out.includes('>Oracle JavaCard API</td>'), out);
|
||||
assert.ok(out.includes('>6</td>'), out);
|
||||
const req = out.slice(out.indexOf('Requires (1)'), out.indexOf('Memory</div>'));
|
||||
assert.ok(!req.includes('A0000000620101'), req);
|
||||
// package identity + compiled-against hint derived from the framework version
|
||||
assert.ok(out.includes('A0000000620101') && out.includes('>v1.0</td>'), out);
|
||||
assert.ok(out.includes('applet package'), out);
|
||||
assert.ok(out.includes('(javacard.framework)</span>'), out);
|
||||
assert.ok(out.includes('Java Card 2.1.1/2.1.2 \u00b7 CAP format 2.1'), out);
|
||||
// components in load-file order with sizes, shares and the total row
|
||||
assert.ok(out.includes('>Header</td>') && out.includes('>20 B</td>') && out.includes('>1%</td>'), out);
|
||||
assert.ok(out.includes('>Method</td>') && out.includes('>2.7 kB</td>') && out.includes('>93%</td>'), out);
|
||||
assert.ok(out.includes('>ConstantPool</td>') && out.includes('>180 B</td>') && out.includes('>6%</td>'), out);
|
||||
assert.ok(out.includes('Load file (total)') && out.includes('>100%</td>'), out);
|
||||
const c1 = out.indexOf('Header</td>'), c2 = out.indexOf('Method</td>'), c3 = out.indexOf('ConstantPool</td>');
|
||||
assert.ok(c1 >= 0 && c1 < c2 && c2 < c3, 'components keep the load-file order');
|
||||
assert.strictEqual(capMemHtml(null), '');
|
||||
});
|
||||
|
||||
@@ -115,17 +136,23 @@ test('capMemHtml skips the import section on a response without imports', () =>
|
||||
mem.code = { method_component: 2048 };
|
||||
delete mem.nvram.requirement;
|
||||
const out = capMemHtml(mem);
|
||||
assert.ok(!out.includes('Requires:'), out);
|
||||
assert.ok(out.includes('NVRAM requirement \u2248 2.1 kB'), out);
|
||||
assert.ok(!out.includes('>Requires'), out);
|
||||
assert.ok(!out.includes('Java Card'), out); // no framework import -> no SDK hint
|
||||
assert.ok(out.includes('CAP format 2.1'), out);
|
||||
assert.ok(out.includes('NVRAM requirement'), out);
|
||||
assert.ok(out.includes('>2.1 kB</td>'), out);
|
||||
});
|
||||
|
||||
test('capMemHtml keeps unknown import AIDs bare and labels the family only when known', () => {
|
||||
const mem = memFixture();
|
||||
mem.imports = [{ aid: 'A1130001180001', minor: 0, major: 1, refs: 2 }];
|
||||
const out = capMemHtml(mem);
|
||||
assert.ok(out.includes('Requires: A1130001180001 \u2265 1.0'), out);
|
||||
assert.ok(out.includes('A1130001180001 1.0 — 2 refs'), out);
|
||||
assert.ok(!out.includes('Compiled against: Java Card'), out); // no framework import
|
||||
const req = out.slice(out.indexOf('Requires (1)'), out.indexOf('Memory</div>'));
|
||||
assert.ok(req.includes('A1130001180001'), req); // unknown AID: shown as the name
|
||||
assert.ok(req.includes('>\u2265 1.0</td>'), req);
|
||||
assert.ok(req.includes('>2</td>'), req); // refs
|
||||
assert.ok(!out.includes('Java Card'), out); // no framework import
|
||||
assert.ok(out.includes('CAP format 2.1'), out);
|
||||
});
|
||||
|
||||
test('capMemHtml falls back to the bytecode size on older server responses', () => {
|
||||
@@ -133,7 +160,16 @@ test('capMemHtml falls back to the bytecode size on older server responses', ()
|
||||
delete mem.code.load_file;
|
||||
delete mem.nvram.requirement;
|
||||
const out = capMemHtml(mem);
|
||||
assert.ok(out.includes('NVRAM requirement ≈ 2.1 kB (code image 2.0 kB + data 136 B)'), out);
|
||||
assert.ok(out.includes('NVRAM requirement'), out);
|
||||
assert.ok(out.includes('>2.1 kB</td>'), out);
|
||||
});
|
||||
|
||||
test('capMemHtml drops the components section without a component list', () => {
|
||||
const mem = memFixture();
|
||||
delete mem.components;
|
||||
const out = capMemHtml(mem);
|
||||
assert.ok(!out.includes('>Components'), out);
|
||||
assert.ok(out.includes('>Notes'), out);
|
||||
});
|
||||
|
||||
test('capGateOk gates the RAM install op and the scripts form', () => {
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "pysim-simple-server"
|
||||
version = "3.5.16"
|
||||
version = "3.5.17"
|
||||
description = "HTTP REST server wrapping pysim for the SIMple PWA"
|
||||
requires-python = ">=3.8"
|
||||
# pysim is a git-only dependency installed explicitly by setup.bat/setup.sh.
|
||||
|
||||
@@ -31,7 +31,7 @@ from osmocom.tlv import BER_TLV_IE
|
||||
from osmocom.utils import rpad
|
||||
|
||||
|
||||
VERSION = '3.5.16'
|
||||
VERSION = '3.5.17'
|
||||
|
||||
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE
|
||||
|
||||
|
||||
Reference in New Issue
Block a user