static void AppendStackFrame(StringBuilder sb, ref ReadStackFrame frame) { // Append the property name. string?propertyName = GetPropertyName(ref frame); AppendPropertyName(sb, propertyName); if (frame.JsonTypeInfo != null && frame.IsProcessingEnumerable()) { if (frame.ReturnValue is not IEnumerable enumerable) { return; } // For continuation scenarios only, before or after all elements are read, the exception is not within the array. if (frame.ObjectState == StackFrameObjectState.None || frame.ObjectState == StackFrameObjectState.CreatedObject || frame.ObjectState == StackFrameObjectState.ReadElements) { sb.Append('['); sb.Append(GetCount(enumerable)); sb.Append(']'); } } }
public void UpdateSortedPropertyCache(ref ReadStackFrame frame) { Debug.Assert(frame.PropertyRefCache != null); // frame.PropertyRefCache is only read\written by a single thread -- the thread performing // the deserialization for a given object instance. List <PropertyRef> listToAppend = frame.PropertyRefCache; // _propertyRefsSorted can be accessed by multiple threads, so replace the reference when // appending to it. No lock() is necessary. if (_propertyRefsSorted != null) { List <PropertyRef> replacementList = new List <PropertyRef>(_propertyRefsSorted); Debug.Assert(replacementList.Count <= PropertyNameCountCacheThreshold); // Verify replacementList will not become too large. while (replacementList.Count + listToAppend.Count > PropertyNameCountCacheThreshold) { // This code path is rare; keep it simple by using RemoveAt() instead of RemoveRange() which requires calculating index\count. listToAppend.RemoveAt(listToAppend.Count - 1); } // Add the new items; duplicates are possible but that is tolerated during property lookup. replacementList.AddRange(listToAppend); _propertyRefsSorted = replacementList.ToArray(); } else { _propertyRefsSorted = listToAppend.ToArray(); } frame.PropertyRefCache = null; }
static string?GetPropertyName(ref ReadStackFrame frame) { string?propertyName = null; // Attempt to get the JSON property name from the frame. byte[]? utf8PropertyName = frame.JsonPropertyName; if (utf8PropertyName == null) { if (frame.JsonPropertyNameAsString != null) { // Attempt to get the JSON property name set manually for dictionary // keys and KeyValuePair property names. propertyName = frame.JsonPropertyNameAsString; } else { // Attempt to get the JSON property name from the JsonPropertyInfo or JsonParameterInfo. utf8PropertyName = frame.JsonPropertyInfo?.NameAsUtf8Bytes ?? frame.CtorArgumentState?.JsonParameterInfo?.NameAsUtf8Bytes; } } if (utf8PropertyName != null) { propertyName = JsonHelpers.Utf8GetString(utf8PropertyName); } return(propertyName); }
private static void HandleStartObject(JsonSerializerOptions options, ref ReadStack state) { Debug.Assert(!state.Current.IsProcessingDictionary()); // Note: unless we are a root object, we are going to push a property onto the ReadStack // in the if/else if check below. if (state.Current.IsProcessingEnumerable()) { // A nested object within an enumerable (non-dictionary). HandleStartObjectInEnumerable(ref state, options, state.Current.JsonPropertyInfo !.DeclaredPropertyType); } else if (state.Current.JsonPropertyInfo != null) { // Nested object within an object. Debug.Assert(state.Current.IsProcessingObject(ClassType.Object)); Type objType = state.Current.JsonPropertyInfo.RuntimePropertyType; state.Push(); state.Current.Initialize(objType, options); } JsonClassInfo classInfo = state.Current.JsonClassInfo !; Debug.Assert(state.Current.IsProcessingObject(ClassType.Dictionary) || state.Current.IsProcessingObject(ClassType.Object) || state.Current.IsProcessingObject(ClassType.Enumerable)); if (state.Current.IsProcessingObject(ClassType.Dictionary)) { object?value = ReadStackFrame.CreateDictionaryValue(ref state); // If value is not null, then we don't have a converter so apply the value. if (value != null) { state.Current.ReturnValue = value; state.Current.DetermineIfDictionaryCanBePopulated(state.Current.ReturnValue); } state.Current.CollectionPropertyInitialized = true; } else if (state.Current.IsProcessingObject(ClassType.Object)) { if (classInfo.CreateObject == null) { ThrowHelper.ThrowNotSupportedException_DeserializeCreateObjectDelegateIsNull(classInfo.Type); } state.Current.ReturnValue = classInfo.CreateObject(); } else if (state.Current.IsProcessingObject(ClassType.Enumerable)) { // Nested array with metadata within another array with metadata. HandleStartObjectInEnumerable(ref state, options, classInfo.Type); Debug.Assert(options.ReferenceHandling.ShouldReadPreservedReferences()); Debug.Assert(state.Current.JsonClassInfo !.Type.GetGenericTypeDefinition() == typeof(JsonPreservableArrayReference <>)); state.Current.ReturnValue = state.Current.JsonClassInfo.CreateObject !(); state.Current.IsNestedPreservedArray = true; } }
private static bool HandleEndArray( JsonSerializerOptions options, ref ReadStack state) { bool lastFrame = state.IsLastFrame; if (state.Current.Drain) { // The array is not being applied to the object. state.Pop(); return(lastFrame); } IEnumerable value = ReadStackFrame.GetEnumerableValue(ref state.Current); if (state.Current.TempEnumerableValues != null) { // We have a converter; possibilities: // - Add value to current frame's current property or TempEnumerableValues. // - Add value to previous frame's current property or TempEnumerableValues. // - Set current property on current frame to value. // - Set current property on previous frame to value. // - Set ReturnValue if root frame and value is the actual return value. JsonEnumerableConverter converter = state.Current.JsonPropertyInfo.EnumerableConverter; Debug.Assert(converter != null); value = converter.CreateFromList(ref state, (IList)value, options); state.Current.TempEnumerableValues = null; } else if (state.Current.IsProcessingProperty(ClassType.Enumerable)) { // We added the items to the list already. state.Current.EndProperty(); return(false); } if (lastFrame) { if (state.Current.ReturnValue == null) { // Returning a converted list or object. state.Current.Reset(); state.Current.ReturnValue = value; return(true); } else if (state.Current.IsProcessingCollectionObject()) { // Returning a non-converted list. return(true); } // else there must be an outer object, so we'll return false here. } else if (state.Current.IsProcessingObject(ClassType.Enumerable)) { state.Pop(); } ApplyObjectToEnumerable(value, ref state); return(false); }
internal void UpdateSortedPropertyCache(ref ReadStackFrame frame) { // Todo: on classes with many properties (about 50) we need to switch to a hashtable for performance. // Todo: when using PropertyNameCaseInsensitive we also need to use the hashtable with case-insensitive // comparison to handle Turkish etc. cultures properly. Debug.Assert(_propertyRefs != null); // Set the sorted property cache. Overwrite any existing cache which can occur in multi-threaded cases. if (frame.PropertyRefCache != null) { HashSet <PropertyRef> cache = frame.PropertyRefCache; // Add any missing properties. This creates a consistent cache count which is important for // the loop in GetProperty() when there are multiple threads in a race conditions each generating // a cache for a given POCO type but with different property counts in the json. while (cache.Count < _propertyRefs.Count) { for (int iProperty = 0; iProperty < _propertyRefs.Count; iProperty++) { PropertyRef propertyRef = _propertyRefs[iProperty]; // Cache the missing property or override the existing property. cache.Add(propertyRef); } } Debug.Assert(cache.Count == _propertyRefs.Count); if (_propertyRefsSorted == null || _propertyRefsSorted.Length < cache.Count) { _propertyRefsSorted = new PropertyRef[cache.Count]; } cache.CopyTo(_propertyRefsSorted); frame.PropertyRefCache = null; } }
public static IEnumerable?GetEnumerableValue(ref ReadStackFrame current) { if (current.IsProcessingObject(ClassType.Enumerable)) { if (current.ReturnValue != null) { return((IEnumerable)current.ReturnValue); } } // IEnumerable properties are finished (values added inline) unless they are using tempEnumerableValues. return(current.TempEnumerableValues); }
public void Push() { if (_continuationCount == 0) { if (_count == 0) { // Performance optimization: reuse the first stackframe on the first push operation. // NB need to be careful when making writes to Current _before_ the first `Push` // operation is performed. _count = 1; } else { JsonTypeInfo jsonTypeInfo = Current.JsonPropertyInfo?.JsonTypeInfo ?? Current.CtorArgumentState !.JsonParameterInfo !.JsonTypeInfo; JsonNumberHandling?numberHandling = Current.NumberHandling; EnsurePushCapacity(); _stack[_count - 1] = Current; Current = default; _count++; Current.JsonTypeInfo = jsonTypeInfo; Current.JsonPropertyInfo = jsonTypeInfo.PropertyInfoForTypeInfo; // Allow number handling on property to win over handling on type. Current.NumberHandling = numberHandling ?? Current.JsonPropertyInfo.EffectiveNumberHandling; Current.CanContainMetadata = PreserveReferences || jsonTypeInfo.PolymorphicTypeResolver?.UsesTypeDiscriminators == true; } } else { // We are re-entering a continuation, adjust indices accordingly. if (_count++ > 0) { _stack[_count - 2] = Current; Current = _stack[_count - 1]; } // check if we are done if (_continuationCount == _count) { _continuationCount = 0; } } SetConstructorArgumentState(); #if DEBUG // Ensure the method is always exercised in debug builds. _ = JsonPath(); #endif }
public void Push() { if (_continuationCount == 0) { if (_count == 0) { // The first stack frame is held in Current. _count = 1; } else { JsonClassInfo jsonClassInfo; if ((Current.JsonClassInfo.ClassType & (ClassType.Object | ClassType.Value | ClassType.NewValue)) != 0) { // Although ClassType.Value doesn't push, a custom custom converter may re-enter serialization. jsonClassInfo = Current.JsonPropertyInfo !.RuntimeClassInfo; } else { jsonClassInfo = Current.JsonClassInfo.ElementClassInfo !; } AddCurrent(); Current.Reset(); Current.JsonClassInfo = jsonClassInfo; Current.JsonPropertyInfo = jsonClassInfo.PropertyInfoForClassInfo; } } else if (_continuationCount == 1) { // No need for a push since there is only one stack frame. Debug.Assert(_count == 1); _continuationCount = 0; } else { // A continuation; adjust the index. Current = _previous[_count - 1]; // Check if we are done. if (_count == _continuationCount) { _continuationCount = 0; } else { _count++; } } }
private static void HandleStartObject(JsonSerializerOptions options, ref ReadStack state) { Debug.Assert(!state.Current.IsProcessingDictionary()); if (state.Current.IsProcessingEnumerable()) { // A nested object within an enumerable. Type objType = state.Current.GetElementType(); state.Push(); state.Current.Initialize(objType, options); } else if (state.Current.JsonPropertyInfo != null) { // Nested object. Type objType = state.Current.JsonPropertyInfo.RuntimePropertyType; state.Push(); state.Current.Initialize(objType, options); } JsonClassInfo classInfo = state.Current.JsonClassInfo; if (classInfo.CreateObject is null && classInfo.ClassType == ClassType.Object) { if (classInfo.Type.IsInterface) { ThrowHelper.ThrowInvalidOperationException_DeserializePolymorphicInterface(classInfo.Type); } else { ThrowHelper.ThrowInvalidOperationException_DeserializeMissingParameterlessConstructor(classInfo.Type); } } if (state.Current.IsProcessingObject(ClassType.Dictionary)) { object value = ReadStackFrame.CreateDictionaryValue(ref state); // If value is not null, then we don't have a converter so apply the value. if (value != null) { state.Current.ReturnValue = value; state.Current.DetermineIfDictionaryCanBePopulated(state.Current.ReturnValue); } state.Current.CollectionPropertyInitialized = true; } else { state.Current.ReturnValue = classInfo.CreateObject(); } }
public void Pop(bool success) { Debug.Assert(_count > 0); if (!success) { // Check if we need to initialize the continuation. if (_continuationCount == 0) { if (_count == 1) { // No need for a continuation since there is only one stack frame. _continuationCount = 1; } else { AddCurrent(); _count--; _continuationCount = _count; _count--; Current = _previous[_count - 1]; } return; } if (_continuationCount == 1) { // No need for a pop since there is only one stack frame. Debug.Assert(_count == 1); return; } // Update the list entry to the current value. _previous[_count - 1] = Current; Debug.Assert(_count > 0); } else { Debug.Assert(_continuationCount == 0); } if (_count > 1) { Current = _previous[--_count - 1]; } SetConstrutorArgumentState(); }
internal void UpdateSortedPropertyCache(ref ReadStackFrame frame) { // Todo: on classes with many properties (about 50) we need to switch to a hashtable for performance. // Todo: when using PropertyNameCaseInsensitive we also need to use the hashtable with case-insensitive // comparison to handle Turkish etc. cultures properly. Debug.Assert(_propertyRefs != null); // Set the sorted property cache. Overwrite any existing cache which can occur in multi-threaded cases. if (frame.PropertyRefCache != null) { List <PropertyRef> cache = frame.PropertyRefCache; // Add any missing properties. This creates a consistent cache count which is important for // the loop in GetProperty() when there are multiple threads in a race conditions each generating // a cache for a given POCO type but with different property counts in the json. while (cache.Count < _propertyRefs.Count) { for (int iProperty = 0; iProperty < _propertyRefs.Count; iProperty++) { PropertyRef propertyRef = _propertyRefs[iProperty]; bool found = false; int iCacheProperty = 0; for (; iCacheProperty < cache.Count; iCacheProperty++) { if (IsPropertyRefEqual(ref propertyRef, cache[iCacheProperty])) { // The property is already cached, skip to the next property. found = true; break; } } if (found == false) { cache.Add(propertyRef); break; } } } Debug.Assert(cache.Count == _propertyRefs.Count); _propertyRefsSorted = cache.ToArray(); frame.PropertyRefCache = null; } }
public void Pop(bool success) { Debug.Assert(_count > 0); Debug.Assert(JsonPath() is not null); if (!success) { // Check if we need to initialize the continuation. if (_continuationCount == 0) { if (_count == 1) { // No need to copy any frames here. _continuationCount = 1; _count = 0; return; } // Need to push the Current frame to the stack, // ensure that we have sufficient capacity. EnsurePushCapacity(); _continuationCount = _count--; } else if (--_count == 0) { // reached the root, no need to copy frames. return; } _stack[_count] = Current; Current = _stack[_count - 1]; } else { Debug.Assert(_continuationCount == 0); if (--_count > 0) { Current = _stack[_count - 1]; } } SetConstructorArgumentState(); }
internal void UpdateSortedPropertyCache(ref ReadStackFrame frame) { // Check if we are trying to build the sorted cache. if (frame.PropertyRefCache == null) { return; } List <PropertyRef> newList; if (_propertyRefsSorted != null) { newList = new List <PropertyRef>(_propertyRefsSorted); newList.AddRange(frame.PropertyRefCache); _propertyRefsSorted = newList.ToArray(); } else { _propertyRefsSorted = frame.PropertyRefCache.ToArray(); } frame.PropertyRefCache = null; }
private static bool HandleEndArray( JsonSerializerOptions options, ref ReadStack state) { bool lastFrame = state.IsLastFrame; if (state.Current.Drain) { // The array is not being applied to the object. state.Pop(); return(lastFrame); } IEnumerable?value = ReadStackFrame.GetEnumerableValue(ref state.Current); bool setPropertyDirectly = false; if (state.Current.TempEnumerableValues != null) { Debug.Assert(state.Current.JsonPropertyInfo != null); // We have a converter; possibilities: // - Add value to current frame's current property or TempEnumerableValues. // - Add value to previous frame's current property or TempEnumerableValues. // - Set current property on current frame to value. // - Set current property on previous frame to value. // - Set ReturnValue if root frame and value is the actual return value. JsonEnumerableConverter?converter = state.Current.JsonPropertyInfo.EnumerableConverter; Debug.Assert(converter != null); value = converter.CreateFromList(ref state, (IList)value !, options); state.Current.TempEnumerableValues = null; // Since we used a converter, we just processed an array or an immutable collection. This means we created a new enumerable object. // If we are processing an enumerable property, replace the current value of the property with the new instance. if (state.Current.IsProcessingProperty(ClassType.Enumerable)) { setPropertyDirectly = true; } } else if (state.Current.IsProcessingProperty(ClassType.Enumerable)) { // We added the items to the list already. state.Current.EndProperty(); return(false); } if (lastFrame) { if (state.Current.ReturnValue == null) { // Returning a converted list or object. state.Current.Reset(); state.Current.ReturnValue = value; return(true); } else if (state.Current.IsProcessingCollectionObject()) { // Returning a non-converted list. return(true); } // else there must be an outer object, so we'll return false here. } else if (state.Current.IsProcessingObject(ClassType.Enumerable)) { state.Pop(); } ApplyObjectToEnumerable(value, ref state, setPropertyDirectly); return(false); }
private static void HandleStartArray( JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo; if (state.Current.SkipProperty) { // The array is not being applied to the object. state.Push(); state.Current.Drain = true; return; } if (jsonPropertyInfo == null) { jsonPropertyInfo = state.Current.JsonClassInfo.CreateRootObject(options); } else if (state.Current.JsonClassInfo.ClassType == ClassType.Unknown) { jsonPropertyInfo = state.Current.JsonClassInfo.CreatePolymorphicProperty(jsonPropertyInfo, typeof(object), options); } // Verify that we don't have a multidimensional array. Type arrayType = jsonPropertyInfo.RuntimePropertyType; if (!typeof(IEnumerable).IsAssignableFrom(arrayType) || (arrayType.IsArray && arrayType.GetArrayRank() > 1)) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(arrayType, reader, state.JsonPath); } Debug.Assert(state.Current.IsProcessingEnumerableOrDictionary); if (state.Current.CollectionPropertyInitialized) { // A nested json array so push a new stack frame. Type elementType = jsonPropertyInfo.ElementClassInfo.Type; state.Push(); state.Current.Initialize(elementType, options); } state.Current.CollectionPropertyInitialized = true; jsonPropertyInfo = state.Current.JsonPropertyInfo; if (state.Current.JsonClassInfo.ClassType == ClassType.Value) { state.Current.JsonPropertyInfo.Read(JsonTokenType.StartObject, ref state, ref reader); } else { // If current property is already set (from a constructor, for example) leave as-is. if (jsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue) == null) { // Create the enumerable. object value = ReadStackFrame.CreateEnumerableValue(ref reader, ref state, options); // If value is not null, then we don't have a converter so apply the value. if (value != null) { if (state.Current.ReturnValue != null) { state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value); } else { // Primitive arrays being returned without object state.Current.SetReturnValue(value); } } } } }
private static void HandleStartArray(JsonSerializerOptions options, ref ReadStack state) { if (state.Current.SkipProperty) { // The array is not being applied to the object. state.Push(); state.Current.Drain = true; return; } Debug.Assert(state.Current.JsonClassInfo != null); JsonPropertyInfo?jsonPropertyInfo = state.Current.JsonPropertyInfo; if (jsonPropertyInfo == null) { jsonPropertyInfo = state.Current.JsonClassInfo.CreateRootProperty(options); } // Verify that we are processing a valid enumerable or dictionary. if (((ClassType.Enumerable | ClassType.Dictionary) & jsonPropertyInfo.ClassType) == 0) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(jsonPropertyInfo.RuntimePropertyType); } if (state.Current.CollectionPropertyInitialized) { // An array nested in a dictionary or array, so push a new stack frame. Type elementType = jsonPropertyInfo.ElementClassInfo !.Type; state.Push(); state.Current.Initialize(elementType, options); } state.Current.CollectionPropertyInitialized = true; // The current JsonPropertyInfo will be null if the current type is not one of // ClassType.Value | ClassType.Enumerable | ClassType.Dictionary. // We should not see ClassType.Value here because we handle it on a different code // path invoked in the main read loop. // Only ClassType.Enumerable is valid here since we just saw a StartArray token. if (state.Current.JsonPropertyInfo == null || state.Current.JsonPropertyInfo.ClassType != ClassType.Enumerable) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(state.Current.JsonClassInfo.Type); } // Set or replace the existing enumerable value. object?value = ReadStackFrame.CreateEnumerableValue(ref state); // If value is not null, then we don't have a converter so apply the value. if (value != null) { state.Current.DetermineEnumerablePopulationStrategy(value); if (state.Current.ReturnValue != null) { state.Current.JsonPropertyInfo !.SetValueAsObject(state.Current.ReturnValue, value); } else { state.Current.ReturnValue = value; } } }
private static void HandleStartDictionary(JsonSerializerOptions options, ref ReadStack state) { Debug.Assert(!state.Current.IsProcessingEnumerable()); JsonPropertyInfo?jsonPropertyInfo = state.Current.JsonPropertyInfo; if (jsonPropertyInfo == null) { jsonPropertyInfo = state.Current.JsonClassInfo !.CreateRootProperty(options); } Debug.Assert(jsonPropertyInfo != null); // A nested object or dictionary, so push new frame. if (state.Current.CollectionPropertyInitialized) { state.Push(); state.Current.JsonClassInfo = jsonPropertyInfo.ElementClassInfo !; state.Current.InitializeJsonPropertyInfo(); JsonClassInfo classInfo = state.Current.JsonClassInfo; Debug.Assert(state.Current.IsProcessingDictionary() || state.Current.IsProcessingObject(ClassType.Object) || state.Current.IsProcessingObject(ClassType.Enumerable)); if (state.Current.IsProcessingDictionary()) { object?dictValue = ReadStackFrame.CreateDictionaryValue(ref state); // If value is not null, then we don't have a converter so apply the value. if (dictValue != null) { state.Current.ReturnValue = dictValue; state.Current.DetermineIfDictionaryCanBePopulated(state.Current.ReturnValue); } state.Current.CollectionPropertyInitialized = true; } else if (state.Current.IsProcessingObject(ClassType.Object)) { if (classInfo.CreateObject == null) { ThrowHelper.ThrowNotSupportedException_DeserializeCreateObjectDelegateIsNull(classInfo.Type); } state.Current.ReturnValue = classInfo.CreateObject(); } else if (state.Current.IsProcessingObject(ClassType.Enumerable)) { // Array with metadata within the dictionary. HandleStartObjectInEnumerable(ref state, options, classInfo.Type); Debug.Assert(options.ReferenceHandling.ShouldReadPreservedReferences()); Debug.Assert(state.Current.JsonClassInfo !.Type.GetGenericTypeDefinition() == typeof(JsonPreservableArrayReference <>)); state.Current.ReturnValue = state.Current.JsonClassInfo.CreateObject !(); } return; } state.Current.CollectionPropertyInitialized = true; object?value = ReadStackFrame.CreateDictionaryValue(ref state); if (value != null) { state.Current.DetermineIfDictionaryCanBePopulated(value); if (state.Current.ReturnValue != null) { Debug.Assert(state.Current.JsonPropertyInfo != null); state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value); } else { // A dictionary is being returned directly, or a nested dictionary. state.Current.ReturnValue = value; } } }
internal JsonPropertyInfo GetProperty(JsonSerializerOptions options, ReadOnlySpan <byte> propertyName, ref ReadStackFrame frame) { // If we should compare with case-insensitive, normalize to an uppercase format since that is what is cached on the propertyInfo. if (options.PropertyNameCaseInsensitive) { string utf16PropertyName = JsonHelpers.Utf8GetString(propertyName); string upper = utf16PropertyName.ToUpperInvariant(); propertyName = Encoding.UTF8.GetBytes(upper); } ulong key = GetKey(propertyName); JsonPropertyInfo info = null; // First try sorted lookup. int propertyIndex = frame.PropertyIndex; // If we're not trying to build the cache locally, and there is an existing cache, then use it. bool hasPropertyCache = frame.PropertyRefCache == null && _propertyRefsSorted != null; if (hasPropertyCache) { // This .Length is consistent no matter what json data intialized _propertyRefsSorted. int count = _propertyRefsSorted.Length; if (count != 0) { int iForward = propertyIndex; int iBackward = propertyIndex - 1; while (iForward < count || iBackward >= 0) { if (iForward < count) { if (TryIsPropertyRefEqual(ref _propertyRefsSorted[iForward], propertyName, key, ref info)) { return(info); } ++iForward; } if (iBackward >= 0) { if (TryIsPropertyRefEqual(ref _propertyRefsSorted[iBackward], propertyName, key, ref info)) { return(info); } --iBackward; } } } } // Try the main list which has all of the properties in a consistent order. // We could get here even when hasPropertyCache==true if there is a race condition with different json // property ordering and _propertyRefsSorted is re-assigned while in the loop above. for (int i = 0; i < _propertyRefs.Count; i++) { PropertyRef propertyRef = _propertyRefs[i]; if (TryIsPropertyRefEqual(ref propertyRef, propertyName, key, ref info)) { break; } } if (!hasPropertyCache) { if (propertyIndex == 0 && frame.PropertyRefCache == null) { // Create the temporary list on first property access to prevent a partially filled List. frame.PropertyRefCache = new List <PropertyRef>(); } if (info != null) { Debug.Assert(frame.PropertyRefCache != null); frame.PropertyRefCache.Add(new PropertyRef(key, info)); } } return(info); }
public JsonPropertyInfo GetProperty(ReadOnlySpan <byte> propertyName, ref ReadStackFrame frame) { JsonPropertyInfo info = null; // Keep a local copy of the cache in case it changes by another thread. PropertyRef[] localPropertyRefsSorted = _propertyRefsSorted; // If there is an existing cache, then use it. if (localPropertyRefsSorted != null) { ulong key = GetKey(propertyName); // Start with the current property index, and then go forwards\backwards. int propertyIndex = frame.PropertyIndex; int count = localPropertyRefsSorted.Length; int iForward = Math.Min(propertyIndex, count); int iBackward = iForward - 1; while (iForward < count || iBackward >= 0) { if (iForward < count) { if (TryIsPropertyRefEqual(localPropertyRefsSorted[iForward], propertyName, key, ref info)) { return(info); } ++iForward; } if (iBackward >= 0) { if (TryIsPropertyRefEqual(localPropertyRefsSorted[iBackward], propertyName, key, ref info)) { return(info); } --iBackward; } } } // No cached item was found. Try the main list which has all of the properties. string stringPropertyName = JsonHelpers.Utf8GetString(propertyName); if (PropertyCache.TryGetValue(stringPropertyName, out info)) { // Check if we should add this to the cache. // Only cache up to a threshold length and then just use the dictionary when an item is not found in the cache. int count; if (localPropertyRefsSorted != null) { count = localPropertyRefsSorted.Length; } else { count = 0; } // Do a quick check for the stable (after warm-up) case. if (count < PropertyNameCountCacheThreshold) { // Do a slower check for the warm-up case. if (frame.PropertyRefCache != null) { count += frame.PropertyRefCache.Count; } // Check again to append the cache up to the threshold. if (count < PropertyNameCountCacheThreshold) { if (frame.PropertyRefCache == null) { frame.PropertyRefCache = new List <PropertyRef>(); } ulong key = info.PropertyNameKey; PropertyRef propertyRef = new PropertyRef(key, info); frame.PropertyRefCache.Add(propertyRef); } } } return(info); }
public void Push() { if (_continuationCount == 0) { if (_count == 0) { // The first stack frame is held in Current. _count = 1; } else { JsonTypeInfo jsonTypeInfo; JsonNumberHandling?numberHandling = Current.NumberHandling; ConverterStrategy converterStrategy = Current.JsonTypeInfo.PropertyInfoForTypeInfo.ConverterStrategy; if (converterStrategy == ConverterStrategy.Object) { if (Current.JsonPropertyInfo != null) { jsonTypeInfo = Current.JsonPropertyInfo.RuntimeTypeInfo; } else { jsonTypeInfo = Current.CtorArgumentState !.JsonParameterInfo !.RuntimeTypeInfo; } } else if (converterStrategy == ConverterStrategy.Value) { // Although ConverterStrategy.Value doesn't push, a custom custom converter may re-enter serialization. jsonTypeInfo = Current.JsonPropertyInfo !.RuntimeTypeInfo; } else { Debug.Assert(((ConverterStrategy.Enumerable | ConverterStrategy.Dictionary) & converterStrategy) != 0); jsonTypeInfo = Current.JsonTypeInfo.ElementTypeInfo !; } EnsurePushCapacity(); _stack[_count - 1] = Current; Current = default; _count++; Current.JsonTypeInfo = jsonTypeInfo; Current.JsonPropertyInfo = jsonTypeInfo.PropertyInfoForTypeInfo; // Allow number handling on property to win over handling on type. Current.NumberHandling = numberHandling ?? Current.JsonPropertyInfo.NumberHandling; } } else { // We are re-entering a continuation, adjust indices accordingly if (_count++ > 0) { Current = _stack[_count - 1]; } // check if we are done if (_continuationCount == _count) { _continuationCount = 0; } } SetConstructorArgumentState(); #if DEBUG // Ensure the method is always exercised in debug builds. _ = JsonPath(); #endif }
private static void HandleStartObject(JsonSerializerOptions options, ref ReadStack state) { Debug.Assert(!state.Current.IsProcessingDictionary()); // Note: unless we are a root object, we are going to push a property onto the ReadStack // in the if/else if check below. if (state.Current.IsProcessingEnumerable()) { // A nested object within an enumerable (non-dictionary). if (!state.Current.CollectionPropertyInitialized) { // We have bad JSON: enumerable element appeared without preceding StartArray token. ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(state.Current.JsonPropertyInfo.DeclaredPropertyType); } Type objType = state.Current.GetElementType(); state.Push(); state.Current.Initialize(objType, options); } else if (state.Current.JsonPropertyInfo != null) { // Nested object within an object. Debug.Assert(state.Current.IsProcessingObject(ClassType.Object)); Type objType = state.Current.JsonPropertyInfo.RuntimePropertyType; state.Push(); state.Current.Initialize(objType, options); } JsonClassInfo classInfo = state.Current.JsonClassInfo; if (state.Current.IsProcessingObject(ClassType.Dictionary)) { object value = ReadStackFrame.CreateDictionaryValue(ref state); // If value is not null, then we don't have a converter so apply the value. if (value != null) { state.Current.ReturnValue = value; state.Current.DetermineIfDictionaryCanBePopulated(state.Current.ReturnValue); } state.Current.CollectionPropertyInitialized = true; } else if (state.Current.IsProcessingObject(ClassType.Object)) { if (classInfo.CreateObject == null) { ThrowHelper.ThrowNotSupportedException_DeserializeCreateObjectDelegateIsNull(classInfo.Type); } state.Current.ReturnValue = classInfo.CreateObject(); } else { // Only dictionaries or objects are valid given the `StartObject` token. ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(classInfo.Type); } }
private static void HandleStartArray( JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { if (state.Current.SkipProperty) { // The array is not being applied to the object. state.Push(); state.Current.Drain = true; return; } JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo; if (jsonPropertyInfo == null) { jsonPropertyInfo = state.Current.JsonClassInfo.CreateRootObject(options); } else if (state.Current.JsonClassInfo.ClassType == ClassType.Unknown) { jsonPropertyInfo = state.Current.JsonClassInfo.CreatePolymorphicProperty(jsonPropertyInfo, typeof(object), options); } // Verify that we have a valid enumerable. Type arrayType = jsonPropertyInfo.RuntimePropertyType; if (!typeof(IEnumerable).IsAssignableFrom(arrayType)) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(arrayType); } Debug.Assert(state.Current.IsProcessingCollection()); if (state.Current.CollectionPropertyInitialized) { // A nested json array so push a new stack frame. Type elementType = jsonPropertyInfo.ElementClassInfo.Type; state.Push(); state.Current.Initialize(elementType, options); } state.Current.CollectionPropertyInitialized = true; // We should not be processing custom converters here (converters are of ClassType.Value). Debug.Assert(state.Current.JsonClassInfo.ClassType != ClassType.Value); // Set or replace the existing enumerable value. object value = ReadStackFrame.CreateEnumerableValue(ref reader, ref state); // If value is not null, then we don't have a converter so apply the value. if (value != null) { if (state.Current.ReturnValue != null) { state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value); } else { // Primitive arrays being returned without object state.Current.SetReturnValue(value); } } }
public JsonPropertyInfo GetProperty(ReadOnlySpan <byte> propertyName, ref ReadStackFrame frame) { JsonPropertyInfo info = null; // Keep a local copy of the cache in case it changes by another thread. PropertyRef[] localPropertyRefsSorted = _propertyRefsSorted; ulong key = GetKey(propertyName); // If there is an existing cache, then use it. if (localPropertyRefsSorted != null) { // Start with the current property index, and then go forwards\backwards. int propertyIndex = frame.PropertyIndex; int count = localPropertyRefsSorted.Length; int iForward = Math.Min(propertyIndex, count); int iBackward = iForward - 1; while (true) { if (iForward < count) { PropertyRef propertyRef = localPropertyRefsSorted[iForward]; if (TryIsPropertyRefEqual(propertyRef, propertyName, key, ref info)) { return(info); } ++iForward; if (iBackward >= 0) { propertyRef = localPropertyRefsSorted[iBackward]; if (TryIsPropertyRefEqual(propertyRef, propertyName, key, ref info)) { return(info); } --iBackward; } } else if (iBackward >= 0) { PropertyRef propertyRef = localPropertyRefsSorted[iBackward]; if (TryIsPropertyRefEqual(propertyRef, propertyName, key, ref info)) { return(info); } --iBackward; } else { // Property was not found. break; } } } // No cached item was found. Try the main list which has all of the properties. string stringPropertyName = JsonHelpers.Utf8GetString(propertyName); if (!PropertyCache.TryGetValue(stringPropertyName, out info)) { info = JsonPropertyInfo.s_missingProperty; } Debug.Assert(info != null); // Three code paths to get here: // 1) info == s_missingProperty. Property not found. // 2) key == info.PropertyNameKey. Exact match found. // 3) key != info.PropertyNameKey. Match found due to case insensitivity. Debug.Assert(info == JsonPropertyInfo.s_missingProperty || key == info.PropertyNameKey || Options.PropertyNameCaseInsensitive); // Check if we should add this to the cache. // Only cache up to a threshold length and then just use the dictionary when an item is not found in the cache. int cacheCount = 0; if (localPropertyRefsSorted != null) { cacheCount = localPropertyRefsSorted.Length; } // Do a quick check for the stable (after warm-up) case. if (cacheCount < PropertyNameCountCacheThreshold) { // Do a slower check for the warm-up case. if (frame.PropertyRefCache != null) { cacheCount += frame.PropertyRefCache.Count; } // Check again to append the cache up to the threshold. if (cacheCount < PropertyNameCountCacheThreshold) { if (frame.PropertyRefCache == null) { frame.PropertyRefCache = new List <PropertyRef>(); } PropertyRef propertyRef = new PropertyRef(key, info); frame.PropertyRefCache.Add(propertyRef); } } return(info); }
public void Push() { if (_continuationCount == 0) { if (_count == 0) { // The first stack frame is held in Current. _count = 1; } else { JsonTypeInfo jsonTypeInfo; JsonNumberHandling?numberHandling = Current.NumberHandling; ConverterStrategy converterStrategy = Current.JsonTypeInfo.PropertyInfoForTypeInfo.ConverterStrategy; if (converterStrategy == ConverterStrategy.Object) { if (Current.JsonPropertyInfo != null) { jsonTypeInfo = Current.JsonPropertyInfo.RuntimeTypeInfo; } else { jsonTypeInfo = Current.CtorArgumentState !.JsonParameterInfo !.RuntimeTypeInfo; } } else if (converterStrategy == ConverterStrategy.Value) { // Although ConverterStrategy.Value doesn't push, a custom custom converter may re-enter serialization. jsonTypeInfo = Current.JsonPropertyInfo !.RuntimeTypeInfo; } else { Debug.Assert(((ConverterStrategy.Enumerable | ConverterStrategy.Dictionary) & converterStrategy) != 0); jsonTypeInfo = Current.JsonTypeInfo.ElementTypeInfo !; } AddCurrent(); Current.Reset(); Current.JsonTypeInfo = jsonTypeInfo; Current.JsonPropertyInfo = jsonTypeInfo.PropertyInfoForTypeInfo; // Allow number handling on property to win over handling on type. Current.NumberHandling = numberHandling ?? Current.JsonPropertyInfo.NumberHandling; } } else if (_continuationCount == 1) { // No need for a push since there is only one stack frame. Debug.Assert(_count == 1); _continuationCount = 0; } else { // A continuation; adjust the index. Current = _previous[_count - 1]; // Check if we are done. if (_count == _continuationCount) { _continuationCount = 0; } else { _count++; } } SetConstructorArgumentState(); }
public JsonParameterInfo?GetParameter( ReadOnlySpan <byte> propertyName, ref ReadStackFrame frame, out byte[] utf8PropertyName) { ParameterRef parameterRef; ulong key = GetKey(propertyName); // Keep a local copy of the cache in case it changes by another thread. ParameterRef[]? localParameterRefsSorted = _parameterRefsSorted; // If there is an existing cache, then use it. if (localParameterRefsSorted != null) { // Start with the current parameter index, and then go forwards\backwards. int parameterIndex = frame.CtorArgumentState !.ParameterIndex; int count = localParameterRefsSorted.Length; int iForward = Math.Min(parameterIndex, count); int iBackward = iForward - 1; while (true) { if (iForward < count) { parameterRef = localParameterRefsSorted[iForward]; if (IsParameterRefEqual(parameterRef, propertyName, key)) { utf8PropertyName = parameterRef.NameFromJson; return(parameterRef.Info); } ++iForward; if (iBackward >= 0) { parameterRef = localParameterRefsSorted[iBackward]; if (IsParameterRefEqual(parameterRef, propertyName, key)) { utf8PropertyName = parameterRef.NameFromJson; return(parameterRef.Info); } --iBackward; } } else if (iBackward >= 0) { parameterRef = localParameterRefsSorted[iBackward]; if (IsParameterRefEqual(parameterRef, propertyName, key)) { utf8PropertyName = parameterRef.NameFromJson; return(parameterRef.Info); } --iBackward; } else { // Property was not found. break; } } } // No cached item was found. Try the main dictionary which has all of the parameters. Debug.Assert(ParameterCache != null); if (ParameterCache.TryGetValue(JsonHelpers.Utf8GetString(propertyName), out JsonParameterInfo? info)) { if (Options.PropertyNameCaseInsensitive) { if (propertyName.SequenceEqual(info.NameAsUtf8Bytes)) { Debug.Assert(key == GetKey(info.NameAsUtf8Bytes.AsSpan())); // Use the existing byte[] reference instead of creating another one. utf8PropertyName = info.NameAsUtf8Bytes !; } else { // Make a copy of the original Span. utf8PropertyName = propertyName.ToArray(); } } else { Debug.Assert(key == GetKey(info.NameAsUtf8Bytes !.AsSpan())); utf8PropertyName = info.NameAsUtf8Bytes !; } } else { Debug.Assert(info == null); // Make a copy of the original Span. utf8PropertyName = propertyName.ToArray(); } // Check if we should add this to the cache. // Only cache up to a threshold length and then just use the dictionary when an item is not found in the cache. int cacheCount = 0; if (localParameterRefsSorted != null) { cacheCount = localParameterRefsSorted.Length; } // Do a quick check for the stable (after warm-up) case. if (cacheCount < ParameterNameCountCacheThreshold) { // Do a slower check for the warm-up case. if (frame.CtorArgumentState !.ParameterRefCache != null) { cacheCount += frame.CtorArgumentState.ParameterRefCache.Count; } // Check again to append the cache up to the threshold. if (cacheCount < ParameterNameCountCacheThreshold) { if (frame.CtorArgumentState.ParameterRefCache == null) { frame.CtorArgumentState.ParameterRefCache = new List <ParameterRef>(); } parameterRef = new ParameterRef(key, info !, utf8PropertyName); frame.CtorArgumentState.ParameterRefCache.Add(parameterRef); } } return(info); }
public void Pop() { Debug.Assert(_index > 0); Current = _previous[--_index]; }
private static void HandleStartDictionary(JsonSerializerOptions options, ref ReadStack state) { Debug.Assert(!state.Current.IsProcessingEnumerable()); JsonPropertyInfo?jsonPropertyInfo = state.Current.JsonPropertyInfo; if (jsonPropertyInfo == null) { jsonPropertyInfo = state.Current.JsonClassInfo !.CreateRootProperty(options); } Debug.Assert(jsonPropertyInfo != null); // A nested object or dictionary, so push new frame. if (state.Current.CollectionPropertyInitialized) { state.Push(); state.Current.JsonClassInfo = jsonPropertyInfo.ElementClassInfo !; state.Current.InitializeJsonPropertyInfo(); JsonClassInfo classInfo = state.Current.JsonClassInfo; if (state.Current.IsProcessingDictionary()) { object?dictValue = ReadStackFrame.CreateDictionaryValue(ref state); // If value is not null, then we don't have a converter so apply the value. if (dictValue != null) { state.Current.ReturnValue = dictValue; state.Current.DetermineIfDictionaryCanBePopulated(state.Current.ReturnValue); } state.Current.CollectionPropertyInitialized = true; } else if (state.Current.IsProcessingObject(ClassType.Object)) { if (classInfo.CreateObject == null) { ThrowHelper.ThrowNotSupportedException_DeserializeCreateObjectDelegateIsNull(classInfo.Type); } state.Current.ReturnValue = classInfo.CreateObject(); } else { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(classInfo.Type); } return; } state.Current.CollectionPropertyInitialized = true; object?value = ReadStackFrame.CreateDictionaryValue(ref state); if (value != null) { state.Current.DetermineIfDictionaryCanBePopulated(value); if (state.Current.ReturnValue != null) { Debug.Assert(state.Current.JsonPropertyInfo != null); state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value); } else { // A dictionary is being returned directly, or a nested dictionary. state.Current.ReturnValue = value; } } }
public bool TryGetParameter( ReadOnlySpan <byte> propertyName, ref ReadStackFrame frame, out JsonParameterInfo?jsonParameterInfo) { JsonParameterInfo?info = null; // Keep a local copy of the cache in case it changes by another thread. ParameterRef[]? localParameterRefsSorted = _parameterRefsSorted; ulong key = GetKey(propertyName); // If there is an existing cache, then use it. if (localParameterRefsSorted != null) { // Start with the current parameter index, and then go forwards\backwards. int parameterIndex = frame.CtorArgumentState !.ParameterIndex; int count = localParameterRefsSorted.Length; int iForward = Math.Min(parameterIndex, count); int iBackward = iForward - 1; while (true) { if (iForward < count) { ParameterRef parameterRef = localParameterRefsSorted[iForward]; if (TryIsParameterRefEqual(parameterRef, propertyName, key, ref info)) { jsonParameterInfo = info; return(true); } ++iForward; if (iBackward >= 0) { parameterRef = localParameterRefsSorted[iBackward]; if (TryIsParameterRefEqual(parameterRef, propertyName, key, ref info)) { jsonParameterInfo = info; return(true); } --iBackward; } } else if (iBackward >= 0) { ParameterRef parameterRef = localParameterRefsSorted[iBackward]; if (TryIsParameterRefEqual(parameterRef, propertyName, key, ref info)) { jsonParameterInfo = info; return(true); } --iBackward; } else { // Property was not found. break; } } } string propertyNameAsString = JsonHelpers.Utf8GetString(propertyName); Debug.Assert(ParameterCache != null); if (!ParameterCache.TryGetValue(propertyNameAsString, out info)) { // Constructor parameter not found. We'll check if it's a property next. jsonParameterInfo = null; return(false); } jsonParameterInfo = info; Debug.Assert(info != null); // Two code paths to get here: // 1) key == info.PropertyNameKey. Exact match found. // 2) key != info.PropertyNameKey. Match found due to case insensitivity. // TODO: recheck these conditions Debug.Assert(key == info.ParameterNameKey || propertyNameAsString.Equals(info.NameAsString, StringComparison.OrdinalIgnoreCase)); // Check if we should add this to the cache. // Only cache up to a threshold length and then just use the dictionary when an item is not found in the cache. int cacheCount = 0; if (localParameterRefsSorted != null) { cacheCount = localParameterRefsSorted.Length; } // Do a quick check for the stable (after warm-up) case. if (cacheCount < ParameterNameCountCacheThreshold) { // Do a slower check for the warm-up case. if (frame.CtorArgumentState !.ParameterRefCache != null) { cacheCount += frame.CtorArgumentState.ParameterRefCache.Count; } // Check again to append the cache up to the threshold. if (cacheCount < ParameterNameCountCacheThreshold) { if (frame.CtorArgumentState.ParameterRefCache == null) { frame.CtorArgumentState.ParameterRefCache = new List <ParameterRef>(); } ParameterRef parameterRef = new ParameterRef(key, jsonParameterInfo); frame.CtorArgumentState.ParameterRefCache.Add(parameterRef); } } return(true); }
internal JsonPropertyInfo GetProperty(ReadOnlySpan <byte> propertyName, ref ReadStackFrame frame) { JsonPropertyInfo info = null; // If we're not trying to build the cache locally, and there is an existing cache, then use it. if (_propertyRefsSorted != null) { ulong key = GetKey(propertyName); // First try sorted lookup. int propertyIndex = frame.PropertyIndex; // This .Length is consistent no matter what json data intialized _propertyRefsSorted. int count = _propertyRefsSorted.Length; if (count != 0) { int iForward = Math.Min(propertyIndex, count); int iBackward = iForward - 1; while (iForward < count || iBackward >= 0) { if (iForward < count) { if (TryIsPropertyRefEqual(_propertyRefsSorted[iForward], propertyName, key, ref info)) { return(info); } ++iForward; } if (iBackward >= 0) { if (TryIsPropertyRefEqual(_propertyRefsSorted[iBackward], propertyName, key, ref info)) { return(info); } --iBackward; } } } } // Try the main list which has all of the properties in a consistent order. // We could get here even when hasPropertyCache==true if there is a race condition with different json // property ordering and _propertyRefsSorted is re-assigned while in the loop above. string stringPropertyName = JsonHelpers.Utf8GetString(propertyName); if (PropertyCache.TryGetValue(stringPropertyName, out info)) { // For performance, only add to cache up to a threshold and then just use the dictionary. int count; if (_propertyRefsSorted != null) { count = _propertyRefsSorted.Length; } else { count = 0; } // Do a quick check for the stable (after warm-up) case. if (count < PropertyNameCountCacheThreshold) { if (frame.PropertyRefCache != null) { count += frame.PropertyRefCache.Count; } // Check again to fill up to the limit. if (count < PropertyNameCountCacheThreshold) { if (frame.PropertyRefCache == null) { frame.PropertyRefCache = new List <PropertyRef>(); } ulong key = info.PropertyNameKey; PropertyRef propertyRef = new PropertyRef(key, info); frame.PropertyRefCache.Add(propertyRef); } } } return(info); }