From 75e861da6ed0ef4d8f9a9376d781fae8f3a97880 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sat, 1 Aug 2026 19:25:24 +0300 Subject: [PATCH] =?UTF-8?q?fix(meta-decompile,meta-compile):=20=D0=BF?= =?UTF-8?q?=D1=83=D1=81=D1=82=D0=B0=D1=8F=20=D1=81=D1=81=D1=8B=D0=BB=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=B2=20ChoiceParameters=20=D1=82=D0=B5=D1=80=D1=8F?= =?UTF-8?q?=D0=BB=D0=B0=20=D1=82=D0=B8=D0=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Раундтрип по справочникам УТ 11.5.27 вскрыл: с пустым содержимым декомпилировался в "value": "", и компилятор законно эмитил свой дефолт xs:string — тип ссылки терялся. Конвенция для этого случая уже была (маркер {emptyRef: true} у fillValue), пробел был только в ChoiceParameters. Декомпилятор теперь ставит маркер, компилятор его понимает — и в скалярном значении, и внутри FixedArray. Форма редкая, поэтому прежние кампании её не поймали: в acc она встречается 1 раз, в erp — ни разу, в УТ — 7 раз. Is-EmptyRef в PS явно отсекает коллекции: у массива $v.emptyRef разворачивается в свойства элементов (member enumeration), и массив с одним таким элементом схлопывал FixedArray в скаляр. В py-порте isinstance(v, dict) такого не допускает — расхождение поймано снэпшотом. Проверка: справочники УТ 519/519 без расхождений (было 519 diff), сюита meta-compile 76/76 на обоих портах, 1С-сертификация кейса пройдена. Co-Authored-By: Claude Opus 5 (1M context) --- .../meta-compile/scripts/meta-compile.ps1 | 15 ++- .../meta-compile/scripts/meta-compile.py | 13 ++- .../meta-decompile/scripts/meta-decompile.ps1 | 5 +- .../meta-decompile/scripts/meta-decompile.py | 6 +- .../catalog-attr-choice-params.json | 6 +- .../Catalogs/ТестВыбора.xml | 99 +++++++++++++++++++ 6 files changed, 139 insertions(+), 5 deletions(-) diff --git a/.claude/skills/meta-compile/scripts/meta-compile.ps1 b/.claude/skills/meta-compile/scripts/meta-compile.ps1 index 14ed75dd..5452c3cf 100644 --- a/.claude/skills/meta-compile/scripts/meta-compile.ps1 +++ b/.claude/skills/meta-compile/scripts/meta-compile.ps1 @@ -1,4 +1,4 @@ -# meta-compile v1.69 — Compile 1C metadata object from JSON +# meta-compile v1.70 — Compile 1C metadata object from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -1612,6 +1612,15 @@ function ConvertFrom-ChLinkShorthand { # — [{name, value?}]. Значение ПРЯМО на app:value (xsi:type=тип); массив → v8:FixedArray # с детьми v8:Value; без value → app:value nil. +# Маркер пустой ссылки от декомпилятора: {emptyRef: true}. Работает и на Hashtable, и на PSCustomObject. +# Массивы отсекаем ЯВНО: у коллекции `$v.emptyRef` разворачивается в свойства элементов (member +# enumeration), и массив с одним таким элементом дал бы -eq $true → FixedArray схлопнулся бы в скаляр. +function Is-EmptyRef($v) { + if ($null -eq $v -or $v -is [string]) { return $false } + if (($v -is [System.Array]) -or ($v -is [System.Collections.IList])) { return $false } + return ($v.emptyRef -eq $true) +} + function Emit-ChoiceParameters { param([string]$indent, $cp, [string]$tag = 'ChoiceParameters') if (-not $cp -or @($cp).Count -eq 0) { X "$indent<$tag/>"; return } @@ -1632,9 +1641,13 @@ function Emit-ChoiceParameters { X "$indent`t" if (-not $hasVal) { X "$indent`t`t" + } elseif ((Is-EmptyRef $val)) { + # Пустая ссылка (маркер декомпилятора emptyRef) — тип обязан остаться DesignTimeRef. + X "$indent`t`t" } elseif ($valIsArray) { X "$indent`t`t" foreach ($v in $val) { + if (Is-EmptyRef $v) { X "$indent`t`t`t"; continue } $norm = Normalize-ChoiceValueT $v $ptype if ([string]::IsNullOrEmpty($norm.Text)) { X "$indent`t`t`t" } else { X "$indent`t`t`t$(Esc-Xml $norm.Text)" } diff --git a/.claude/skills/meta-compile/scripts/meta-compile.py b/.claude/skills/meta-compile/scripts/meta-compile.py index 076f5c3f..0d4f1cb6 100644 --- a/.claude/skills/meta-compile/scripts/meta-compile.py +++ b/.claude/skills/meta-compile/scripts/meta-compile.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# meta-compile v1.69 — Compile 1C metadata object from JSON +# meta-compile v1.70 — Compile 1C metadata object from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -1706,6 +1706,11 @@ def convert_from_ch_link_shorthand(s): o['dataPath'] = rest return o +def is_empty_ref(v): + """Маркер пустой ссылки от декомпилятора: {emptyRef: true}.""" + return isinstance(v, dict) and v.get('emptyRef') is True + + def emit_choice_parameters(indent, cp, tag='ChoiceParameters'): if not cp: X(f'{indent}<{tag}/>') @@ -1724,9 +1729,15 @@ def emit_choice_parameters(indent, cp, tag='ChoiceParameters'): X(f'{indent}\t') if not has_val: X(f'{indent}\t\t') + elif is_empty_ref(val): + # Пустая ссылка (маркер декомпилятора emptyRef) — тип обязан остаться DesignTimeRef. + X(f'{indent}\t\t') elif val_is_array: X(f'{indent}\t\t') for v in val: + if is_empty_ref(v): + X(f'{indent}\t\t\t') + continue xt, tx = normalize_choice_value_t(v, ptype) if tx == '' or tx is None: X(f'{indent}\t\t\t') diff --git a/.claude/skills/meta-decompile/scripts/meta-decompile.ps1 b/.claude/skills/meta-decompile/scripts/meta-decompile.ps1 index f6ed05fb..8bb6d15b 100644 --- a/.claude/skills/meta-decompile/scripts/meta-decompile.ps1 +++ b/.claude/skills/meta-decompile/scripts/meta-decompile.ps1 @@ -1,4 +1,4 @@ -# meta-decompile v0.55 — XML объекта метаданных 1С → JSON-черновик формата meta-compile +# meta-decompile v0.56 — XML объекта метаданных 1С → JSON-черновик формата meta-compile # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # # Поддержаны: Catalog, ExchangePlan, ChartOfCharacteristicTypes, ChartOfAccounts, ChartOfCalculationTypes, Document, @@ -226,6 +226,9 @@ function Convert-ChScalarNode { if ($txt -match '^-?\d+$') { return [int]$txt } return [double]::Parse($txt, [System.Globalization.CultureInfo]::InvariantCulture) } + # Пустой DesignTimeRef ≠ пустая строка: без маркера тип терялся, и компилятор эмитил xs:string. + # Та же конвенция, что у fillValue (см. ниже) — маркер emptyRef. + if ($xt -match 'DesignTimeRef$' -and $txt -eq '') { return [ordered]@{ emptyRef = $true } } return $txt } # app:value (тип прямо на узле) → значение ЛИБО массив (v8:FixedArray с детьми v8:Value). diff --git a/.claude/skills/meta-decompile/scripts/meta-decompile.py b/.claude/skills/meta-decompile/scripts/meta-decompile.py index f6a17262..d09d0b8b 100644 --- a/.claude/skills/meta-decompile/scripts/meta-decompile.py +++ b/.claude/skills/meta-decompile/scripts/meta-decompile.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# meta-decompile v0.55 — XML объекта метаданных 1С → JSON-черновик формата meta-compile +# meta-decompile v0.56 — XML объекта метаданных 1С → JSON-черновик формата meta-compile # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # # Зеркало meta-decompile.ps1 (КАНОН). Структура 1:1 — те же имена функций, порядок, комментарии. @@ -299,6 +299,10 @@ def convert_ch_scalar_node(vN): if re.match(r'^-?\d+$', txt): return int(txt) return float(txt) + # Пустой DesignTimeRef != пустая строка: без маркера тип терялся, и компилятор эмитил xs:string. + # Та же конвенция, что у fillValue — маркер emptyRef. + if re.search(r'DesignTimeRef$', xt, re.I) and txt == '': + return {'emptyRef': True} return txt diff --git a/tests/skills/cases/meta-compile/catalog-attr-choice-params.json b/tests/skills/cases/meta-compile/catalog-attr-choice-params.json index b712a9e2..d8b95794 100644 --- a/tests/skills/cases/meta-compile/catalog-attr-choice-params.json +++ b/tests/skills/cases/meta-compile/catalog-attr-choice-params.json @@ -11,7 +11,11 @@ { "name": "ТипТТН", "type": "CatalogRef.ВидыТранспорта", "choiceParameters": [ { "name": "Отбор.Тип", "type": "EnumRef.ТипыВЕТИС", "value": ["EmptyRef", "ТТН"] } ] }, { "name": "Валюта", "type": "CatalogRef.Валюты", - "choiceParameterLinks": "Отбор.Владелец=Ссылка" } + "choiceParameterLinks": "Отбор.Владелец=Ссылка" }, + { "name": "ПустаяСсылка", "type": "String(10)", + "choiceParameters": [ { "name": "Отбор.ТипПоказателя", "value": { "emptyRef": true } } ] }, + { "name": "ПустаяСсылкаВМассиве", "type": "String(10)", + "choiceParameters": [ { "name": "Отбор.Набор", "value": [ { "emptyRef": true }, "ТТН" ] } ] } ] }, "validatePath": "Catalogs/ТестВыбора", diff --git a/tests/skills/cases/meta-compile/snapshots/catalog-attr-choice-params/Catalogs/ТестВыбора.xml b/tests/skills/cases/meta-compile/snapshots/catalog-attr-choice-params/Catalogs/ТестВыбора.xml index 88d6dbf2..2b64485c 100644 --- a/tests/skills/cases/meta-compile/snapshots/catalog-attr-choice-params/Catalogs/ТестВыбора.xml +++ b/tests/skills/cases/meta-compile/snapshots/catalog-attr-choice-params/Catalogs/ТестВыбора.xml @@ -270,6 +270,105 @@ Use + + + ПустаяСсылка + + + ru + Пустая ссылка + + + + + xs:string + + 10 + Variable + + + false + + + + false + + false + false + + + false + + DontCheck + Items + + + + + + + Auto + Auto + + + Auto + ForItem + DontIndex + Use + Use + + + + + ПустаяСсылкаВМассиве + + + ru + Пустая ссылка вмассиве + + + + + xs:string + + 10 + Variable + + + false + + + + false + + false + false + + + false + + DontCheck + Items + + + + + + ТТН + + + + Auto + Auto + + + Auto + ForItem + DontIndex + Use + Use + +