Skip to content

Nodes keep what they read and check every spelling of a property: JSON re-reads, one-element arrays, struct fields, bus elements, Complexity, Parameter shape - #152

Merged
ww-mw merged 10 commits into
mainfrom
nodes-keep-what-they-read-and-check-every-spelling
Oct 19, 2026
Merged

ww-mw merged 10 commits into
mainfrom
nodes-keep-what-they-read-and-check-every-spelling

Conversation

@ww-mw

@ww-mw ww-mw commented Oct 19, 2026 •

Copy link
Copy Markdown
Member

Seven node fixes, one commit each, plus two test-only commits from review (see "Review fix round 1" below). Every commit has dist rebuilt and committed, and npm run verify passed at each one.

B-09: a text dictionary's own value is kept as read when a host re-reads it

Ruling: F1-22, "kept as read, not refused". The open follow-up said: "a re-read from the JSON view still ... runs the _unwritableStream refusal". Theme 1 also applies: a save the writer cannot spell is still refused.
What was wrong: a text dictionary's own 2x2x2 cell holding a string opened fine. The { asRead: true } re-read then threw MatWriteError ... no MAT class for "string".
Root cause: SectionNode.parseEntry ran _unwritableStream() on every text entry and never checked options.asRead.
Fix: the refusal is skipped when asRead is set. The metadata stamp is left alone: it is an edit's stamp, the same one addEntry makes.
Tests (reparseAsRead.test.ts):

  • An asRead re-read of an N-D cell, an N-D string and a struct field holding the cell copies and saves as the open does. This failed before (the re-read threw).
  • Editing the entry after the re-read refuses the save, the same way it refuses for the opened entry. This failed before.
  • Without asRead the value is still refused (the A1 control). This passed both before and after.
    Changed existing test: none. The file's textDictionary helper now takes its entry list as a parameter, and the default is unchanged.
    Left out: the paste refusal of a text N-D string calls the value a "char". That is a wording issue, outside this row.

B-16: refuse a one-element object array whose dims count a different number of elements

Ruling: group F group 1, "refusing what the writers cannot spell", and theme 1, "a save the writer cannot spell is refused rather than written wrong". The open follow-up (#134) said: "a one-element array with dims that can be spelled but are not scalar ([1,0], [2,1]) is still written as a scalar."
What was wrong: a pasted {_array_class, _dimensions:[2,1], _elements:[one]} was written to a binary dictionary as a bare <Element Class=...>. Other dims did the same: [1,0], [1,2] and [0,0].
Root cause: DataNode._serializeSimulinkObjectXml only checked that the dims could be spelled. It never checked that the dims matched the number of elements.
Deviation from the row: the row says ObjectNode already refuses this. It does not when only the count is wrong. ObjectNode uses this same writer, so the fix covers it too, and the tests include a class with no node of its own.
Fix: when the array holds exactly one element and not every extent is 1, the save throws MatWriteError ("... declaring dimensions [2,1] and holding 1 element, which no Dimension spells. Nothing was written."). The check is "every extent is 1" rather than "prod(dims) is 1", because [2,0.5] multiplies to 1. A mutant that uses the product is killed by the test.
Tests (dimsWriters.test.ts, one new it after F1-21's): Parameter, Signal, AliasType and an unmodelled class, each pasted as an entry, as a cell element and as a struct field.

  • With [2,1], [1,0], [1,2], [0,0] or [2,0.5], the binary save is refused. This failed before: the save wrote a scalar <Element>.
  • In a text dictionary the save keeps the value.
  • The controls [1,1], [1,1,1], [1], [] and absent dims are written as before.
    Changed existing test: none.
    Left out (each needs its own call):
  • With zero elements, the array is saved as one 1x1 object.
  • When the element count is above 1 and disagrees with the dims, other writers (ObjectNode.serializeXml, _objectXml) write the mismatch.

B-17: in a binary dictionary, a struct field an element lacks is written as [], in the struct's field order

Ruling: theme 2, "follow MATLAB, and measure it first", plus F1-16 (an absent field is []). The open follow-up (#132) said: "_structXml ... is unmeasured in MATLAB".
What was wrong: pasted into a binary dictionary at a Parameter's Value or an object property, [{a:1},{}] was written <Element></Element>. StructNode's own path at an entry or a cell writes a = [].
Root cause: DataNode._structXml wrote only the fields each element held, in that element's own key order.
MATLAB truth (R2027a; probe_struct_absent_fields_binary.m, struct_absent_fields_binary.truth.json): 144 dictionaries, made of 9 struct shapes plus 3 controls, each at 4 places.

  • MATLAB reads the filled spelling exactly as it reads the text source, in every case.
  • It reads the old spelling as a fieldless struct for {} and null.
  • It reads the old spelling with its fields reordered when the keys are not in the struct's order.
  • MATLAB takes field order from the first element written. So the fix also writes every element in the struct's field order. This goes beyond the row, and the order was measured.
    Fix: each element is written with the struct's field list, in order. A missing field is written as []. Any other fields follow, each name written once. An element that already matches is written byte for byte as before.
    Tests (new structAbsentFieldBinary.test.ts, 14): these drive parseEntry, serializeSource and reopen against MATLAB's truth. 9 of the 14 failed before (wrong order, or a fieldless element). Mutants are killed: "[] after the held fields" by 8 tests, "no de-dup" by 1.
    Changed existing test: none.
    Left out (need a decision):
  • StructNode shows only the first element's keys, while MATLAB reads the union of the elements' fields.
  • A Parameter Value struct with an empty field can be pasted, and MATLAB then reads it as 0x0.

B-18: open a bus or an enumeration whose element list is not a list, with a warning

Ruling: theme 1 (group F group 2), "warn and show what was read; never refuse the file, never crash", plus C-190. The open follow-up (#132) said: "an _elements that is not an array still throws."
What was wrong: an _elements of 5, "x" or {a:1} threw _elements.forEach is not a function, and the whole open failed.
Root cause: BaseBusNode._parseElements and EnumTypeNode.parse only checked that _elements was truthy.
Deviation from the row: null is covered as well. It is the same hand edit. Before this change, null opened with no warning, and an untouched save rewrote it as [].
Fix: the loop now runs only when Array.isArray(_elements) is true. Any other present value gets one part-unreadable warning naming what was read (reportNotAList in notAnElement.ts), shows no rows, and is kept as read (_keepNullElement).
Tests (nullBusElement.test.ts, new describe): 80 tests: Bus, ConnectionBus, ServiceBus and EnumTypeDefinition, each with 5, "x", {a:1} and null. Each case checks:

  • the file opens with the other rows intact;
  • exactly one warning;
  • the untouched save is byte-equal;
  • an edit saves as if the elements had been deleted, and that save reopens clean.
    68 of the 80 failed before: the truthy values threw, and null gave no warning and rewrote the list.
    Changed existing test: none.
    Left out: an Elements_internal that is itself a scalar still opens silently with no rows. It does not crash.

B-22: Complexity and DimensionsMode are checked by either spelling of the property

Ruling: theme 2 and C-030 (Signal, BusElement and ValueType Complexity and DimensionsMode take only MATLAB's enumerals), "fix at the root".
What was wrong: setProperty('complexity','bogus') was refused, but setProperty('Complexity','bogus') was stored and saved. Setting the field spelling to the value already shown also marked the entry Modified.
Root cause: DataNode._propFor matched key or column but never nodeProperty. That skipped _rejectUnknownEnumeral, _isUnchangedCommit and SignalNode's ENUM_ATOMS.
Deviation from the row: DataModel.readPropertyValue, which captures the value an edit's undo needs, used the same lookup. With only the node fixed, undo of a field-spelling edit was refused, and the old value did not come back.
Fix: one rule, propNamed() in BaseNode.ts. It tries key or column first, then nodeProperty. Both _propFor and readPropertyValue use it, and SignalNode checks the prop the name resolves to.
Tests (new enumFieldSpelling.test.ts, 48): both formats; MATLAB-written sig1, MyBus.x and MyValueType; both properties. The field spelling is refused with the key spelling's message and the save is unchanged. A legal value saves the same bytes as the key spelling. The shown value is no edit. Session undo restores the old value. 36 of the 48 failed before. The 12 that passed are controls.
Changed existing test: none.
Left out: on a Parameter, setProperty('Complexity', x) returns true and marks the entry Modified, but nothing is shown or saved.

B-24: a one-element bus in a text dictionary is written back in the form MATLAB wrote it

Ruling: A4, "a no-op text save adds nothing that was not there ..., so a no-op save of the fixtures is byte-equal", plus D3, "Don't rewrite data nobody changed", plus theme 2 (MATLAB measured first).
What was wrong: a no-op save of the MATLAB-written params.sldd differed at byte 4731, where MyConnBus's one-element Elements_internal was rewritten in array form.
Root cause: BaseBusNode._getSerializedProperties always rebuilt Elements_internal as {_array_class, _dimensions, _elements}.
MATLAB truth (R2027a; probe_bus_one_element_text.m, bus_one_element_text.truth.json, fixtures in test/fixtures/bus_one_element/):

  • MATLAB writes one element as {_id, _object_class, _properties}.
  • It writes more than one as the array form, with no _object_class on the elements.
  • After an add then a remove it writes the element form again, differing from the original only in lastmod.
  • MATLAB reads each of core's 5 saves (noop, added, added-removed, added-removed-first, added-undone) as it reads its own file.
    Fix: a bag read in the element form that still holds one element is written in that form. An added element gets _object_class where MATLAB puts it. Binary output is unchanged. A one-element bus copied from a .mat now pastes into a text dictionary in the element form; MATLAB R2027a reads that form as the same Simulink.Bus (reviewer probe on generated /tmp files).

Deviation from the row: when such a bus grows, the array elements drop the redundant _object_class, as MATLAB's do (measured: MATLAB's own save of a grown bus), so the element read matches MATLAB's byte for byte.
Tests:

  • sessionSerializeSource.test.ts: the untouched params.sldd saves as its bytes.
  • New busOneElementText.test.ts (11): the real session on MATLAB's file, and the truth.
  • New cases in baseBusNode.test.ts: add, undo and redo, remove-first, ConnectionBus, the [1,1] array form kept, and remove-only.
  • 12 of 60 failed before (baseBusNode 5, busOneElementText 6, sessionSerializeSource 1). Mutants are killed: not stripping _object_class by 3 tests, not inserting it by 3.
    Changed existing test: in baseBusNode.test.ts, the pin "always emits the canonical _array_class form even when the source used _object_class" is replaced by "writes a one-element Elements_internal read as the element itself back in that form". A4 authorizes this, and the row says so. The pin's stated reason ("MATLAB would fail to load") applies only to _elements under _object_class, which is never written.
    Left out:
  • MATLAB renumbers element _ids on save. MATLAB reads core's ids back without complaint, so nothing was changed.
  • hexobj_text.sldd respells 10000000.0 in a Dimensions value. That is not a bus, so it is outside this row.

B-25: a Parameter's Dimensions and Complexity follow its Value after every edit

Ruling: A7, "a Parameter's Dimensions and Complexity rows are refreshed after an element removal collapses its Value. A plain bug." Theme 2 applies to the cases A7 did not measure (add, typed Value).
What was wrong: on complex_binary.sldd pRow, cutting 3 elements to 2, adding one, or typing [1 2 3 4] all still saved [1,3] complex.
Root cause: ParameterNode.childStructureChanged only handled the collapse and restore cases, and setProperty('Value') never updated the shape.
MATLAB truth (R2027a; probe_parameter_shape.m, parameter_shape.truth.json, 41 rows):

  • MATLAB refuses a hand-set numeric Dimensions or Complexity.
  • After every edit, Dimensions is the Value's size and Complexity its complexity. There are four exceptions: a symbolic '[1,N]' is kept, an empty Value keeps both, a struct Value keeps its Complexity, and text is [1 1] real.
  • Setting every element real gives 'real'.
  • MATLAB reopened core's save of 36 edits. 35 have the Dimensions and Complexity of MATLAB's own edit; 33 read back entirely as MATLAB's own edit (typed_logical and typed_logical_row read as double, under Left out). The 36th is listed under Left out.
    Fix: _followValueShape and _shapeFromValue (in ParameterNode, replacing A7's collapse and restore) run after every Add, removal, element edit, typed Value, and their undo and redo. They write only keys the file holds (locked down in review fix round 1, below). _restorerFor('Value') restores both keys on undo. MatlabVariableNode._setConstrainedValue notifies the parent after an element edit. Only ParameterNode overrides that hook.
    Deviation from the row: MATLAB's symbolic save is not committed. It names the saving user and respells every entry, and m9MergedSaves would reject a new binary fixture. The test builds that file from complex_binary.sldd using MATLAB's own tag, and asserts the tag against the truth.
    Tests (new parameterShape.test.ts, 87):
  • Every truth row is driven through the real session, checking rows, the save and the binary reopen.
  • Undo and redo give what was read.
  • A text-dictionary case.
  • MATLAB's reads of core's saves.
  • 37 of the 49 tests in the tests-first version failed before, each showing the shape as read. The final parameterShape.test.ts (at 34b5f46) against 34b5f46^ src fails 62 of 87, all on the shape as read; the 25 that pass are controls and the kept-shape exceptions (symbolic, empty, struct). (Run by the reviewer; the builder's copy for that run was refused by the permission system.)
    Changed existing test: none. complexCollapse.test.ts (A7) passes unchanged.
    Left out (for triage):
  • The binary writer spells a complex array whose elements are all real with no Dimension, and MATLAB reads it as 1x1. This is the same on main and is outside this row.
  • A typed logical Value is read back by MATLAB as a double.
  • MATLAB evaluates 1+0i to a real number, while core keeps it complex.
  • setProperty('Dimensions'|'Complexity') on a Parameter returns true and changes nothing.
  • Undo of a structural edit on a hand-edited file whose Dimensions disagree with its Value gives the Value's shape.

Review fix round 1 (test only; src and dist unchanged)

  • f420d68 (B-25): 14 tests in parameterShape.test.ts lock down that an edit writes no Dimensions or Complexity the file does not hold: complex_binary.sldd with pRow's Complexity, or its Dimensions, taken out, through five of the probe's edits; text typeLink.sldd Kp (no Dimensions) and typed_mcos.sldd pDoubleRow with its Complexity taken out. Dropping has('Dimensions') fails 7 of the 14, dropping has('Complexity') the other 7. parameterShape.test.ts now has 101 tests; with B-25's src reverted, 71 of them fail.
  • e930fa7 (B-17): 4 tests in structAbsentFieldBinary.test.ts: a pasted _fields of ['a', 5] or [null, 'a'] saves as a struct with field a and reopens with a = 1. A member of _fields that is not a string names no field and is written as none, as on main. Dropping the string check makes the save throw; String(field) writes <P Name="5" .../>; both fail all 4.
  • 0a4b469: comment only, in parameterShape.test.ts: R2027a's own edit writes both keys; it is MATLAB-written dictionaries that can lack Complexity.
  • B-25 and a numeric Dimensions of -1 (held by Parameters whose Value is an expression): measured on R2027a, a Parameter holding slexpr('a+1') has Dimensions -1, and after Value = [1 2 3] it has [1 3], in memory and after saveChanges in a dictionary. So replacing the -1 with the Value's size after a typed Value is MATLAB's own rule.
  • Reviewers' round 2: ruling MERGE, correctness MERGE. Base against head over the repo fixtures and the corpus (read-only): only B-24's inputs change. vscode main with this core packed in passes verify and integration.

Follow-ups seen, not built (for the maintainer's list)

  • A struct whose _fields holds a non-string member shows a row for it (5 = [ ]) with no warning, and the binary save drops that row, as on main (theme 1: the open should warn).
  • B-16 leaves a zero-element object array written as one 1x1 object.
  • A Parameter whose file holds no Complexity shows real whatever its Value; with no Dimensions, the Dimensions row stays blank after an edit (the new tests check the save, not the rows).

Gates at head 34b5f46 (base 38c20de; main is now 31b8ac3, #150, test only, and the PR merges cleanly)

  • rm -rf dist && npm run build: the tree stays clean.
  • npm run verify: exit 0. 299 test files passed and 1 skipped. 10430 tests passed and 27 skipped. typecheck, smoke, check:pack, check:leak and check:browser all passed.
  • DEX_READBACKS_CORPUS=... vitest run test/parity/readbacks.truth.test.ts test/parity/m9MergedSaves.truth.test.ts: 2 files, 45 tests passed.
  • DEX_MATLAB_CMD="mw -using Bmain matlab", run on the head: structural.fidelity "ConnectionBus structural" and element.fidelity "ConnectionElement fidelity" (these reach B-24's element-form bus): 20 tests. In the combined run 19 passed and 1 failed: "edits element Name and round-trips" hit its fixed 60 s timeout while the load average was about 25. Run again alone, the ConnectionElement fidelity block passed 16 of 16.
  • Each of the new MATLAB probes above is that item's live check. The reviewer's live run over 7 fidelity files (parameter, signal, element, structural, typedef, enumtype, valueTypeValueProps) passed 204; the 5 failures were each the fixed 60 s cold-start timeout under load and passed alone. parameter.fidelity's Value edits reach B-25.
  • At e930fa7 / 0a4b469: npm run verify exit 0, 299 files passed and 1 skipped, 10448 tests passed and 27 skipped; opt-in readbacks and m9MergedSaves 45 of 45; dist unchanged.

Implements the maintainer's rulings, as audited in group H (2026-10-19).

ww-mw added 10 commits October 18, 2026 22:18
…om its text

A text dictionary's own entry that has no MAT stream MatWriter can write
(a hand-typed bag of an N-D cell, struct or string holding a string) opens
as what was read, and a save writes it back as read until it is edited.
Re-read from the dictionary's text with { asRead: true }, as a JSON view
re-reads an entry the user edited there, the same entry was refused as a
paste is (ruling A1), with "no MAT class for string". parseEntry now skips
that refusal for the re-read alone (F1-22, kept as read, not refused); a
paste, a drop and an undo record are refused as before, and the re-read
entry's save is refused once it is edited, as the opened entry's is.
…elements

A one-element array of a class with a node of its own (ParameterNode, SignalNode,
AliasTypeNode and every other typed class), and of a class with none (ObjectNode),
writes its binary XML through DataNode._serializeSimulinkObjectXml. Since #134 that
writer refuses dims no Dimension can spell, but it still wrote the one element as a
scalar object whatever the dims counted: a Parameter pasted with _dimensions [2,1],
[1,0], [1,2] or [0,0] and one element was saved as a 1x1 Parameter, its dims gone.

The writer now refuses such an array, naming the entry, the path, the class, the
dims and the one element (MatWriteError: this writer has no spelling for it). Every
extent must be 1, as [2,0.5] multiplies to 1 too. [1,1], [1,1,1], [1], [] and no
_dimensions are written as before. An array of more than one element is
ObjectNode's to write, and one of none is left as it was. A text dictionary's save
keeps such a value as pasted.

The new dimsWriters case covers Parameter, Signal, AliasType and a class with no
node of its own, as an entry, a cell entry's element and a struct entry's field, at
the five dims, with the text-dictionary save and five scalar controls in each.
dist is rebuilt.
…onary, in the struct's order

A struct copied into a binary dictionary at a Simulink.Parameter's Value, at
an object's property, or in a cell or struct field under those, is written
from its bag (DataNode._structXml). Each element was written with only the
fields it held, in its own key order: <Element></Element> for an element
that is {} or null. MATLAB R2027a read a one-element struct back with no
fields. It read a struct whose first element lacked a field, or held its
fields in another order than _fields, with its fields in another order.
(MATLAB's own text dictionaries sort the keys and keep the order in
_fields.) Core's binary reader read those saves the same way.

Each element now gets every field the struct has, as StructNode reads them
(_fields, or element 1's keys), in that order. A field the element does not
hold is written as [] (Dimension="0*0"), then any other field it holds, each
name once. This is what StructNode writes for the same struct.

Measured first. probe_struct_absent_fields_binary.m reopens 144 dictionaries
that struct_absent_fields_binary.mjs saves through core: nine structs at
four places, each written as held, in the new spelling, with the [] after
the held fields, and as held in a text dictionary. In every case MATLAB
reads the new spelling as it reads the text source, and the other two
spellings differ where the commit says. At a Parameter's Value, a struct
with an empty field is not a Value MATLAB holds: it reads both the text
source and the binary save as a double 0x0. A struct whose elements already
hold every field in order is written as before.

B-17 (group H): theme 2, F1-16.
…th a warning

A text dictionary is JSON anyone can edit, so the list a bus's Elements_internal or an
enumeration's Enumerals holds its elements in can be a number, a string, an object or a null.
MATLAB never writes one. Read as a list, a number, a string or an object threw "forEach is not
a function" and the whole dictionary failed to open. A null opened as an entry with no
elements, with nothing said, and an untouched save rewrote the list as [].

Now the entry shows no element, one part-unreadable warning against it says what was read
where the list belongs, and an untouched save writes the entry as it was read
(_keepNullElement), as a list holding a value that is not an element already does (C-190). An
edit to the entry writes it from its rows, which are none: what the same edit writes once its
elements are all deleted, and its binary XML is the XML of that entry.

The null case goes beyond the item's row, which named the values that threw: it is the same
hand edit under the same ruling (warn and show what was read), and it was silent.
A Signal's, a BusElement's and a ValueType's Complexity and DimensionsMode take only MATLAB's
enumerals (C-030). An edit can name the property by its column (complexity) or by the field
the writers save (Complexity). The prop lookup every per-property gate asks matched only the
key and the column, so setProperty('Complexity', 'bogus') found no prop: the enumeral check,
the unchanged-commit check and the Signal's own enum test all let it through, and 'bogus' was
saved into a file MATLAB then refuses. The same call with 'complexity' was refused.

The lookup (BaseNode's propNamed, used by DataNode._propFor) now also matches the prop's node
field, after the key and the column, and SignalNode checks the prop the name resolves to. So
the field spelling is refused in MATLAB's words, a value set back to what it shows is no edit,
and an accepted value saves the file the column spelling saves.

The session's capture of the value an edit replaces, for its undo, had the same gap: it found
no prop for 'Complexity', so the undo set the field to nothing and was refused. It now finds
the prop by the same rule, and the undo puts the old value back. This goes beyond the item's
row, which named the node's gates: it is the same lookup, and without it the session edit by
the field's name could not be undone.
…ctionary

MATLAB R2027a writes a bus of one element, in a text dictionary, with its Elements_internal as
the element itself, {_id, _object_class, _properties}, and a bus of more as the array,
{_array_class, _dimensions, _elements}. BaseBusNode rebuilt every Elements_internal as the
array, so a save that changed nothing changed the file: a no-op save of the MATLAB-written
params.sldd differed at byte 4731, where MyConnBus's _object_class became _array_class (A4, D3).

An Elements_internal read in the element's form is now written back in it while the bus holds
one element. That element is the one read, as read, or one added here with its class put in
after its _id. With more elements the bus is written as MATLAB writes the array, without the
element's own _object_class. A bag read as the array stays the array. The binary writer's
output is unchanged: it already spelled both forms the same.

Measured first (probe_bus_one_element_text.m, R2027a): MATLAB made a Bus, a ConnectionBus and
a ServiceBus of one element each, then edited copies, adding an element, removing it again, or
removing the first. After an add and a remove it writes the element form again (only lastmod
differs). Core makes the same edits through the session API (bus_one_element_text.mjs). Each
save's value is MATLAB's, except the _id: MATLAB numbers the elements again on its save, and
core keeps the _id it gave the element. MATLAB reads back each of core's five saves as it reads
its own file after the same edits, with no warning.

The baseBusNode.test.ts case that pinned the array form ("always emits the canonical
_array_class form") flips, as A4 allows. Its reason, that MATLAB would fail to load the file,
holds for _elements written under _object_class. This change does not write that.
…ry edit of it

A Simulink.Parameter kept the Dimensions and Complexity it was read with
after an edit of its Value, shown and saved. Cut from three elements to
two, pRow (complex_binary.sldd) still had [1 3]. With an element added it
still had [1 3]. With [1 2 3 4] typed over it, it had [1 3] and 'complex'.
The ruling A7 (2026-10-15) had fixed only a removal that collapses the
Value to a scalar.

MATLAB R2027a was asked first (test/parity/matlab/probe_parameter_shape.m,
41 rows on copies of the fixture, truth parameter_shape.truth.json). It
refuses a numeric Dimensions other than the Value's size, and any
Complexity set. After every edit of the Value, both are the Value's size
and complexity, with these exceptions:
- a symbolic Dimensions ('[1,N]') is kept;
- an empty Value keeps both;
- a struct Value keeps its Complexity;
- text is a string scalar: [1 1], real.
MATLAB also turns Complexity 'real' once every element is set real.

ParameterNode._followValueShape now sets both from the Value after each
of these edits: an Add, a removal, a Value typed on the Parameter, on its
Value row, or on one of its elements, and the undo or redo of each. Each
is set only where the file holds it. MatlabVariableNode tells what holds
an array when one of its elements is set, as it already does for an Add
or a removal. The undo of a typed Value puts back what the two held.
The undo of an Add or a removal sets them again from the Value it puts
back. This replaces the bookkeeping that worked only for the collapse.

MATLAB reopened core's save of each of the 36 edits core makes. It reads
the Dimensions and Complexity its own edit leaves in all but one. That
one comes from an older defect in how the Value is written: a complex
array whose elements are all real now is written with no Dimension. It
is named in parameter_shape.steps.json and left for its own fix.

Tests: test/parameterShape.test.ts (new, 87 cases) makes each row
through the session on the same entry and file. It also covers undo and
redo, a text dictionary's Parameter, and MATLAB's reads of core's saves.
No existing test changed.
…its file does not hold

After an edit of its Value, a Simulink.Parameter's Dimensions and
Complexity are set from the Value only where the file holds them
(ParameterNode._followValueShape). No test held that: either guard
dropped passed the full suite, and such a save writes the Value's shape
into a Parameter that never carried it. MATLAB writes binary Parameters
with Dimensions and no Complexity, and core's own text dictionaries hold
Parameters with no Dimensions.

The new tests take complex_binary.sldd with pRow's Complexity, or its
Dimensions, taken out, make five of the probe's edits through the
session, and check that the save and its reopen hold no such key, and
the other key as MATLAB's own edit leaves it. Two text cases do the
same: typeLink.sldd Kp, which holds no Dimensions, and typed_mcos.sldd
pDoubleRow with its Complexity taken out. With either guard dropped, 7
of the 14 new tests fail, each on the key that guard writes.

Test only: src and dist are unchanged.
… no field in a binary dictionary

A text dictionary's `_fields` is JSON anyone can edit, so a member of it
can be a number or null. DataNode._structXml writes such a member as no
field, as main did, since main wrote only the fields an element holds.
No test held that: with the string check dropped, the full suite
passed, and such a save either fails (the name is not a string) or
writes a field `<P Name="5">` that no MATLAB struct can have.

The new test pastes a struct over {a: 1} whose `_fields` is ['a', 5] or
[null, 'a'] at each of the four places, into MATLAB's binary fixture.
It checks that the save is the spelling of the same struct with only
`a` (C1 held), which MATLAB reads as a struct with field `a`, and that
it reopens with `a = 1`. With the string check dropped, all four fail
because the save throws. With the name written as String(field), all
four fail on the extra `<P Name="5">`.

Test only: src and dist are unchanged.
The comment over the B-25 lock-down tests said MATLAB itself writes
binary Parameters with Dimensions and no Complexity. R2027a's own edit
writes both keys; it is MATLAB-written dictionaries that can lack
Complexity. Comment only.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant