Browse Source

Answer field existence questions without mapping the field value

ContentFieldProperty is a CustomJsValue descriptor whose CustomValue maps
the stored JsonValue to a JsValue on first read. Existence and
enumerability questions never need that value, but they used to pay for
it: `in`, hasOwnProperty, propertyIsEnumerable, Object.keys/values/entries,
Object.assign, object spread and JSON.stringify all reached the object
through GetOwnProperty, which materializes the descriptor whose value the
caller then reads or discards.

Jint lets a host answer those questions directly through ProbeOwnProperty.
The override deliberately mirrors GetOwnProperty line for line, minus the
descriptor: same initialization, same toJSON exclusion, same lookup, and
the enumerable flag read off the descriptor rather than off its value. The
engine trusts the probe without re-verifying it on the hot path, so a wrong
Missing would silently drop the key from every enumeration above - which is
why the two are kept adjacent in the file, pinned by tests covering `in`,
hasOwnProperty, propertyIsEnumerable, Object.keys, spread and
JSON.stringify plus a delete and an add, and checked on every test run by
the host-contract verification enabled earlier in this branch.

ContentDataObject deliberately does not get the same override: its
GetOwnProperty auto-creates a field for any name probed, so a probe that
agreed with it at the same instant would have to do the same, and that
quirk is pre-existing tenant-visible behaviour this change has no business
altering.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016uV6H9cTntzsoKiaJRBn4f
pull/1326/head
Marko Lahma 1 month ago
parent
commit
4a7ccbf75a
  1. 23
      backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ContentWrapper/ContentFieldObject.cs

23
backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ContentWrapper/ContentFieldObject.cs

@ -132,6 +132,29 @@ public sealed class ContentFieldObject : ObjectInstance
return valueProperties?.GetValueOrDefault(propertyName) ?? PropertyDescriptor.Undefined;
}
protected override OwnPropertyProbe ProbeOwnProperty(JsValue property)
{
// Deliberately mirrors GetOwnProperty above, minus the descriptor: the flags are on the descriptor
// itself, so an existence or enumerability question is answered without ever reading CustomValue,
// which is what maps the JSON value to a JsValue. The engine trusts the answer without verifying it,
// so the two must stay in step.
EnsurePropertiesInitialized();
var propertyName = property.AsString();
if (propertyName.Equals("toJSON", StringComparison.OrdinalIgnoreCase))
{
return OwnPropertyProbe.Missing;
}
if (!valueProperties.TryGetValue(propertyName, out var propertyDescriptor))
{
return OwnPropertyProbe.Missing;
}
return propertyDescriptor.Enumerable ? OwnPropertyProbe.Enumerable : OwnPropertyProbe.NonEnumerable;
}
public override IEnumerable<KeyValuePair<JsValue, PropertyDescriptor>> GetOwnProperties()
{
EnsurePropertiesInitialized();

Loading…
Cancel
Save