Looping through Introspection.PropertyInfo BUG

My hardware : MacbookPro 2023, M2, 16GB, MacOS Sonoma 14.0 running Xojo 2023 Release 3.1

Try stepping through the following loop and you will see that each step takes twice longer than the previous.

For Each p As Introspection.PropertyInfo In properties
    // Any code
Next

Adding the following code in the loop, will crash Xojo randomly.

var d as new  dictionary
For Each p As Introspection.PropertyInfo In properties
    d.Value(p.name) =  p.Value(info)
Next

Share a complete sample so we can see exactly what you see instead of:

image

1 Like
  1. The code below will run, it crashes if you try to obtain the value for propertyTypes String/Text.
  2. Stepping through the loop with my hardware configuration gets longer with every step
  3. Viewing the values and variables at runtime is a bit off as well, note that I declare the dictionary dItem within the For loop. If it is declared outside the loop, it works but the way the values of the dictionary are shown in Xojo changes.
  • dItems declared INSIDE the For loop : Changes to the contents of the dictionary is correctly reflected in debug view, with the dItem selected and it’s content in view.
  • ditems declared OUTSIDE the For loop, even when using “ditems = new dictionary” inside the For loop, Xojo will not remove the previous content of the dictionary from the debug view when dItem is selected and it’s content in view. You must go back, click on the dictionary item again to view it’s content, and only then it’s shown as correctly being empty (immediately after ditems = new dictionary).

Abnormal behaviour.

Public Function GetProperties(info As Introspection.TypeInfo) As Dictionary()
  // Return a dictionary array, with all properties, type and values of a given class (info)
  
  // Get property info
  Var properties() As Introspection.PropertyInfo
  properties = info.GetProperties
  
  // Create a dictionary to hold our collected info
  var d() as  dictionary
  
  For Each p As Introspection.PropertyInfo In properties
    // Obtain the type info prior to getting property info, otherwise a crash might occur
    var pType as Introspection.TypeInfo
    ptype = p.PropertyType
    
    if pType.IsValueType = true then 
      // Create a key with the property's name and assign it's value
      var dItem as new Dictionary
      dItem.Value("name") = p.Name
      dItem.Value("isshared") = p.IsShared
      dItem.Value("isprotected") = p.IsProtected
      dItem.Value("iscomputed") = p.IsComputed
      dItem.Value("ispublic") = p.IsPublic
      dItem.value("isprimitive") = pType.IsPrimitive
      dItem.Value("isenum") = pType.IsEnum
      dItem.Value("variabletype") = ptype.FullName
      
      // Get the value of the property (will crash on string / text types)
      if ptype.FullName <> "string" and pType.FullName <> "Text" then
        dItem.Value("value") = p.Value(info)
        
        // Set an empty string / text value in those cases
      else
        dItem.Value("value") = "" 
        
      end if
      
      // Add the property's info to our dictionary array
      d.Add(dItem)
      
    end if
    
  Next
  
  // Return the properties and values as as dictionary
  return d
  
End Function

There’s a bug in your logic at this line (can have more):

       dItem.Value("value") = p.Value(info)

It raises a Xojo assertion

OS: Windows 11

Location: ObjectGlue.h(209)

Condition: RuntimeObjectIsa(obj, mClassPtr)

Message: Xojo.Introspection.ClassInfo

I guess you don’t intend to use that object as a key there

1 Like

Thank you sir! Much appreciated.

1 Like

The PropertyInfo iterator doesn’t always return a dictionary. I suggest:

  1. Wrap the entire block in a Try statement (to avoid an uncaught exception crash).
  2. Check the type before assigning it.

For example:

Var tmpStr As String
For Each p As Introspection.PropertyInfo In properties
    Var tmpVar As Variant = p.Value(sourceObject)
    // Any code
    If p.Name = "Dictionary" Then
        var d as new  dictionary
        For Each p As Introspection.PropertyInfo In properties
            d.Value(p.name) =  p.Value(info)
        Next
    Else
        Try
          Var vTypeInt As Integer = VarType(tmpVar)
          If vTypeInt > 4096 Then // It's an array
            Var elemTypeInt As Integer = vTypeInt - 4096
            Select Case elemTypeInt
            Case 8
              // It’s a string array
            Case 9
              // It’s an object array
            End Select
          Else
            // Attempt to assign to a string variable … ? … you decide
            // tmpStr = tmpVar.StringValue
          End If
        Catch e As TypeMismatchException
          ' Do nothing
        End Try
    End If
Catch ex As RuntimeException
    // Handle exception, or ignore it … up to you
End Try

Please note that I did not test the code above as it is only intended to give you ideas as to a solution. Hope that helps.

He’s not expecting it to return a dictionary - he’s instantiating an array of dictionaries and returning them.

As Rick pointed out, the problem was that the OP used the wrong variable in p.Value().

1 Like