Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    2 above: the information in refDocs.xml is used for several purposes, not just code generators. We don't want to hide this information about inheriting types, and the lookup to find the base type is too trivial to implement for us to consider adding some kind of shortcut.

    Comment


      I suspect that adding an attribute that specifies the root JavaScript type is also trivial to implement (whether string, number, boolean etc.), would greatly simplify code generation, and would also be useful in the documentation (I've wanted it since way before the TypeScript project). AFAIK, the whole concept of the baseType was introduced in this thread so I assumed that it was meant for code generation. I didn't realize it was being used for anything else, but you're right, I can easily add code to traverse the inheritance chain and find the atomic type, assuming there are no missing links in the chain, misspellings or other issues like we've seen in the inheritsFrom attribute.

      Comment


        The baseType is now also displayed by the SmartClient Reference and allows better type guessing in our Visual Tools. It is not just for code generation.

        You started out saying a root type would "greatly simplify code generation" but went on to say you could "easily add code to .. find the atomic type". Well, we agree with the latter assessment. This requires just a trivial helper method with a simple iteration inside of it.

        Note, although there are no breaks in the baseType inheritance chain and our tools would catch it if there were, we would recommend falling back to type "string" if something goes wrong in baseType lookup.

        Comment


          Currently, the fail-safe is any as that works in TypeScipt. When I said it would greatly simplify gode-generation, I was referring to not only the current issue, but also to all of the other special stuff (lists of string types, number types, Callback types etc.) that I've already put in place that would have been unnecessary if a rootType attribute existed and for FUTURE code generation if that ever happens for a new language or something. Yes, I've taken care of most of it now and will implement the iterator to handle inherited baseTypes. I'm just suggesting that a rootType would have been, and could be in the future, very useful. Also, for the user-readable documentation, I would like to see something that says it is really just a string, rather than click through a bunch of links just to find out that it's a string. A rootType attribute would be perfect for this.

          Comment


            Any system, doc viewer or code generator etc, can trivially look up the root type. We don't think it makes sense to add extra information to an already large file to obviate a trivial lookup. Let's stop kicking that dead horse..

            Not sure what that other complexity is that you're referring to. The information we've added over various enhancements should have allowed you to eliminate hardcoded lists of string or numeric types, classes missing inheritance information, etc. We eliminated all such extra information from our processes for generating SGWT APIs.

            Comment


              The Errors.txt file is generated every day (assuming everything works). The current list of string types without a baseType is listed here:

              Code:
              Attributes suspected as string types but do not have the baseType set: 47.
              === URL:32 ===
              1. attr:ActiveXControl.codeBase, valueType=URL
              2. attr:Canvas.appImgDir, valueType=URL
              3. attr:DataSource.dataURL, valueType=URL
              4. attr:DateChooser.nextMonthIcon, valueType=URL
              5. attr:DateChooser.nextMonthIconRTL, valueType=URL
              6. attr:DateChooser.nextYearIcon, valueType=URL
              7. attr:DateChooser.nextYearIconRTL, valueType=URL
              8. attr:DateChooser.prevMonthIcon, valueType=URL
              9. attr:DateChooser.prevMonthIconRTL, valueType=URL
              10. attr:DateChooser.prevYearIcon, valueType=URL
              11. attr:DateChooser.prevYearIconRTL, valueType=URL
              12. attr:DrawImage.src, valueType=URL
              13. attr:DynamicForm.action, valueType=URL
              14. attr:DynamicForm.validationURL, valueType=URL
              15. attr:Flashlet.codeBase, valueType=URL
              16. attr:Flashlet.pluginsPage, valueType=URL
              17. attr:Flashlet.src, valueType=URL
              18. attr:FusionChart.chartsBaseURL, valueType=URL
              19. attr:FusionChart.chartURL, valueType=URL
              20. attr:HTMLFlow.contentsURL, valueType=URL
              21. attr:ImgProperties.imgDir, valueType=URL
              22. attr:ImgProperties.src, valueType=URL
              23. attr:ImgTab.labelSkinImgDir, valueType=URL
              24. attr:ImgTab.skinImgDir, valueType=URL
              25. attr:ListGrid.headerMenuButtonIcon, valueType=URL
              26. attr:Mail.templateFile, valueType=URL
              27. attr:OperationBinding.dataURL, valueType=URL
              28. attr:RPCRequest.actionURL, valueType=URL
              29. attr:StretchImgButton.labelSkinImgDir, valueType=URL
              30. attr:SVG.pluginsPage, valueType=URL
              31. attr:SVG.src, valueType=URL
              32. attr:ViewLoader.viewURL, valueType=URL
              === DOMElement:2 ===
              1. attr:Canvas.htmlElement, valueType=DOMElement
              2. attr:DOMGrid.rootElement, valueType=DOMElement
              === CSSStyle:1 ===
              1. attr:Dialog.messageStyle, valueType=CSSStyle
              === URI:2 ===
              1. attr:SchemaSet.schemaNamespace, valueType=URI
              2. attr:WebService.serviceNamespace, valueType=URI
              === HTML:7 ===
              1. attr:GroupNode.groupTitle, valueType=HTML
              2. attr:Hilite.htmlAfter, valueType=HTML
              3. attr:Hilite.htmlBefore, valueType=HTML
              4. attr:Hilite.replacementValue, valueType=HTML
              5. attr:ListGridRecord.singleCellValue, valueType=HTML
              6. attr:MenuItem.title, valueType=HTML
              7. attr:TreeNode.title, valueType=HTML
              === Field:2 ===
              1. attr:FormulaBuilder.field, valueType=Field
              2. attr:SummaryBuilder.field, valueType=Field
              === FormItemClassname:1 ===
              1. attr:Operator.editorType, valueType=FormItemClassname
              For the one that have a lot of occurrences like URL, I would suggest to just make it a type with a baseType='string'

              Here's a section of my C# code that I maintain to deal with inconsistent types. I know that some of them may be fixed by now but this illustrates the issues I've found. With these lists, it mostly all works. The next guy to try code generation will end up making similar lists (minus the ones that are fixed of course).

              Code:
               
                     // To fix missing inheritsFrom attributes
                      Dictionary<string, string> dicInherits = new Dictionary<string, string>()
                          {
                                { "Layout", "Canvas"}
                              , { "VLayout", "Layout"}
                              , { "HLayout", "Layout" }
                              , { "PortalLayout", "Layout" }
                              , { "ToolStripButton", "Button" }  // Or is is stretchImgButton? Documentation is conflicted.
                              , { "SectionStack", "VLayout" }
                              , { "BaseWidget", "Class" }
                              , { "ToolStrip", "Layout" }
                              , { "RestDataSource", "DataSource" }
                              , { "HTMLPane", "HTMLFlow" }
                              , { "DataSource", "Class" }
                              , { "Menu", "ListGrid" }
                              , { "ResultSet", "Class" }
                              , { "ImgButton", "Img" }
                              , { "Portlet", "Window" }
                              , { "Time", "Class" }
                              , { "RPCRequest", "Class" }
                              , { "FormItem", "Class" }
                              , { "MathFunction", "Class" }
                          };
              
                      // These are all really just a JavaScript number
                      string[] numberTypes = { "Number", "PositiveInteger", "Integer", "Float", "double", "int" };
              
                      // Thease are all really just a JavaScript boolean
                      string[] boolTypes = { "Boolean", "bool" };
              
                      // Don't know how to deal with these so just make them any for now
                      string[] anyTypes = { "Transaction Object"
                              , "Object<String,SummaryFunction>"
                              , "Object"
                              , "object"
                              , "Any"
                              , "rect"
                              , "measure"
                              , "map"
                              , "discovertreesettings"
                              , "stringmethod"
                              , "map<string,string>"
                              , "RecordList"
                              , "record properties"
                              , "record"
                              , "function"
                              , "PrimaryKeys"
                              , "subClass"
                              , "Widget"
                              , "Widgets"
                              , "arguments"
                              , "objects"
                              , "button object"
                              , "button widget"
                              , "DataSourceRecord"
                              , "DataSourceRecords"
                              , "PKValue"
                              , "varies"
                              , "TimerEvent"
                             // , "Callback"
                      };
              
                      // These are all really Callbacks
                      string[] callbackTypes = { "rpccallback", "AnimationCallback", "PrintCanvasCallback", "DataURLCallback", "ClientOnlyDataSourceCallback", "CollapseSectionCallback", "ExpandSectionCallback", "GetFileVersionCallback", "HasFileCallback", "HideSectionCallback"
                                            , "PaletteNodeCallback", "ShowSectionCallback", "ValidationStatusCallback", "GetFileCallback", "MessagingCallback", "ProcessCallback", "LoadScreenCallback"
                                            , "RPCQueueCallback", "CanPlayCallback", "PlaybackCompleteCallback", "CanPlayCallback", "PlaybackCompleteCallback", "TabIndexUpDatedCallback","ShiftFocusCallback"
                                            , "ExportImageCallback"
                      };
              
                      // These are extra words that don't really refer to a type. They will be removed.
                      string[] nonTypes = { "Multiautochild", "Autochild", "instance" }; //, "| subClass" };
              
                      // Infrequent types that would be difficult to fit in a general rule
                      private Dictionary<string, string> weirdTypes = new Dictionary<string, string>()
                      {
                            {"Array of Array of int", "Array<Array<number>>" }
                          , {"Map<Prefix,URI>", "Map<string, string>" }
                          , {"Response", "RPCResponse | DSResponse" }
                          , {"Request", "RPCRequest | DSRequest" }
                      };
              
                      // These are things marked as Classes but do not inherit from Class.
                      string[] notClass = { "isc"
                              , "Date"
                              , "Browser"
                              , "Callbacks"
                              , "WSRequest"
                              , "ServerObject"
                              , "History"
                              , "String"
                              , "TooLStripSeparatorEditProxy"
                              , "SelectionOutline"
                              , "VisualBuilder"
                              , "EBay"
                              , "FileLoader"
                              , "FacetValue" };
              
                      // Not working for the reason listed
                      Dictionary<string, string> excludedClasses = new Dictionary<string, string> {
                                { "BooleanItem", "Extends CycleItem but CycleItem is not defined." }
                              , { "ComboBoxItem", "Interface 'ComboBoxItem' incorrectly extends interface 'TextItem'. Types of property 'canEditCriterion' are incompatible." }
                              , {"MultiComboBoxItem" , "Cannot find name 'ComboBoxItem'" }
                              // , {"MathFunction"      , "" }
                              , { "MultiFileItem"     , "Extends RelationItem but RelationItem is not defined" }
                              , { "PickListMenu"      , "Extends ScrollingMenu but ScrollingMenu is not defined" }
                              , { "RelativeDateItem"  , "Interface 'RelativeDateItem' incorrectly extends interface 'CanvasItem'. Types of property 'valueField' are incompatible." }
                              , { "RichTextItem"      , "Interface 'RichTextItem' incorrectly extends interface 'CanvasItem'. Types of property 'colSpan' are incompatible." }
                              , { "ToolbarItem"       , "Interface 'ToolbarItem' incorrectly extends interface 'CanvasItem'. Types of property 'colSpan' are incompatible." }
                      };
              
                      // Really just strings, hopefully they'll be putting in baseType='string' so we won't need this list anymore.
                      string[] stringTypes = { 
                                               "ListGridGroupState"
                                              , "RelativeDateString"
                                              , "CSSClass"
                                              , "CSSText"
                                              , "CSSColor"
                                              , "CSSClassName"
                                              , "CSSStyleName"
                                              , "Color"
                                              , "SCClassName"
                                              , "SCImgURL"
                                              , "DataPath"
                                              , "DateInputFormat"
                                              , "ListGridFieldState"
                                              , "ListGridSelectionState"
                                              , "ListGridSelectedState"
                                              , "ListGridSortState"
                                              , "ListGridViewState"
                                              , "TreeGridOpenState"
                                              , "TreeGridViewState"
                                              , "FormItemBaseStyle"
                                              , "VelocityExpression"
                                              , "XPathExpression"
                                              , "HTMLString"
                                              , "DetailViewerViewState"
                                              , "FormatString"
                                              , "character"
                                              , "AutoTestLocator"
                                              , "ListGridViewState"
                                             // , "identifier"
                                              , "align"
                                              , "stringMethod"
                                              , "String"
                                              , "global ID"
                                              , "HTML Element"   // Check this BEFORE HTML
                                              , "HTML"
                                              , "ElementName"
                                              , "XPath"
                                              , "URI"
                                              , "Prefix"
                                              , "Type"
                                              , "string Expression"
                                              , "String Expression"
                                              , "keyChar"
                                              , "animateShowEffectId"
                                              , "DOMElement", "DOM Element"
                                              , "datasource id", "URL", "id", "fieldNames", "fieldName", "Field", "Fields", "facetId", "facetIds", "facetValueGroupId"
                                              , "class object" /*, "KeyIdentifier", "keyidentifier",*/, "keyidentifer", "DSField"
                                              , "identifier", "string", "operationid", "formlayouttype", "domelement", "itemName"
                                              , "color", "urn", "valign", "classname", "cssclass" ,"CSSStyle", "name", "DataURLFormat", "FormItemClassname"};
              
                      // Skip these because they cause weird problems (redefined in subclasses?)
                      string[] excludedAttributes = { "valuesManager", "ValuesManager", "dataProperties", "State", "dataGradients", "activeAreaHTML"};

              Comment


                We're double-checking on URL, URI and HTML. All of those were intended to receive baseType markers. The other two are typos and we'll get those too.

                It looks like almost all of the "hardcoding" predates the many, many fixes we've announced in this thread. Do you need us to go back over those, or did you just not get around to updating after each fix?

                Comment


                  I'm sure many of them are unnecessary now but it's a pain to go test each thing every time there's an update so if the generator still works, I don't mess with it. At some point I will clean it up. Just showing, in general, the complexities that I've dealt with so far, some of which I am still dealing with. In general, the stuff I'd like you to go over is the stuff that I manage to get into the Errors.txt file. And by looking at the Progress page, I can see that many have gone down to 0, which is great.

                  As long as we're making progress though, it's all good. There are actually more things that could improve the docs but they're more nit-picky and I'm saving them until after the big stuff is fixed (inconsistent casing for example).

                  Comment


                    We definitely appreciate that before the fixes noted in this thread, some of those lists would have been necessary. As far as the "next guy", we believe it's the case that he won't need any such lists, and whenever you have time to check, that would be nice to confirm.

                    A couple of points on your lists (possibly covered before):

                    1. DOMElement is not a string type, this refers to native DOM elements as returned by standard DOM APIs like getElementById(). TypeScript surely has something appropriate to map this to.

                    2. Integer, Float etc are not just variants of Number, they convey that only integral or decimal values are expected and supported. If there is no corresponding TypeScript notion, we would suggest mapping to Number but then adding a note in the doc ("Decimal value expected and allowed" or similar)

                    3. similarly, "Boolean" vs "boolean" conveys whether null is allowed and expected (and the one typo of "bool" has been fixed for a long time)

                    4. any method defined on the "Callbacks" object can be assumed to be a callback. This is a SmartClient idiomatic way of defining things, but should not require a list of hardcoded callback names (which would be fragile)

                    5. "AutoChild" and "MultiAutoChild" are not really "extra" words - although you probably need to strip them out to figure out the TypeScript type, it makes sense to provide a link in the generated doc to the AutoChild / MultiAutoChild usage information, as we do in the SmartClient Reference

                    Comment


                      1. DOMElement is not a string type, this refers to native DOM elements as returned by standard DOM APIs like getElementById(). TypeScript surely has something appropriate to map this to.
                      Understood. TypeScript is mostly just decorated JavaScript. All the types we're talking about are native JavaScript types. There is no "TypeScript equivalent", there's just the JavaScript Element. To capture these types accurately, I now have the following additional list:

                      Code:
                       string[] elementTypes =
                      {
                         "DOMElement", "DOM Element", "domelement", "HTML Element"
                      };
                      I know that in some cases (maybe all?), the parameter can take an Element or a string. In those cases I would recommend setting the type as Element | string.

                      2. Integer, Float etc are not just variants of Number, they convey that only integral or decimal values are expected and supported. If there is no corresponding TypeScript notion, we would suggest mapping to Number but then adding a note in the doc ("Decimal value expected and allowed" or similar)
                      There is no TypeScript-specific notion of number, just the JavaScript number so these all map to number. Agreed that it should be documented on what type of number (int, float) but disagree on where to put that documentation because it could be as simple as "integer" or more complicated like "positive integer between 1 and 10". I suggest putting it in the description (i.e. "Decimal value expected and allowed") but I already know that's not going to happen. In the generated code, if the actual inferred JavaScript type is different than the SmartClient type, I put the original type in comments. For example:

                      Code:
                      readonly maxHeight?: number /* integer */;
                      3. similarly, "Boolean" vs "boolean" conveys whether null is allowed and expected (and the one typo of "bool" has been fixed for a long time)
                      Yes, bool is gone, thanks. If, however, I retain boolean and Boolean, a lot of stuff breaks because there are many subclasses that redefine a property from one to the other. I don't know if this is intentional or not as there are lots of casing issues throughout. If I just map everything to boolean, it works and I don't think it has any negative consequences. If you can think of some, please let me know.

                      4. any method defined on the "Callbacks" object can be assumed to be a callback. This is a SmartClient idiomatic way of defining things, but should not require a list of hardcoded callback names (which would be fragile)
                      Until now, I was not aware of the Callbacks object (or class?). I see now that it contains many callbacks with parameters. This is good because I can use it to generate more detailed, accurate definitions (although it will take some work). The docs have it defined as a Class but in the description says, "This object cannot be used....". So is it a Class or an Object? ... and if it's a Class, does it inheritFrom Class?

                      Agree that all of these lists are fragile and I wish I didn't have to make them in the first place. But, unfortunately, there is no field in the refDocs that just tells me what the actual JavaScript type is so I need to maintain (hopefully decreasing) lists in order to map the types to something the compiles.

                      Comment


                        5. "AutoChild" and "MultiAutoChild" are not really "extra" words - although you probably need to strip them out to figure out the TypeScript type, it makes sense to provide a link in the generated doc to the AutoChild / MultiAutoChild usage information, as we do in the SmartClient Reference
                        Understood they are not "extra" words to you but for the purposes of inferring the type, they are meaningless. They describe how/when a property is created but not what type it is. An AutoChild array of items is just an array of items. It just happens to be automatically created. And of course it makes sense to have a link to explain what they are. I would have, however, just added an attribute to the docItem that marks it as an AutoChild instead of putting it in the valueType field. But that's just me.

                        Comment


                          We use only "DOMElement" (not those other variations) and an API that takes DOMElement or String will have the type "DOMElement | String" (consistent with all other alternative types).

                          We don't have any plans to change from using informative type names like "Integer" and "Float", nor do we plan to inject thousands of copies of an identical description of what these types mean into the refDocs file. However, we could add offical @type declarations (with @baseType Number) so that you have a source for a description to generate into the TypeScript headers. This would also allow you to get rid of these last (very small) lists. The rest have been obsolete for some time.

                          We don't *think* we have any more instances of subclasses overriding with the wrong type (or different casing). This doesn't seem to be something you're flagging in Errors.txt.

                          "Callbacks" is neither a Class nor an Object, it's just a way of organizing the docs. This one item needs to be flagged as something to be ignored in terms of API generation.

                          Take a look at the AutoChild docs again: if something is documented as an AutoChild, it implies multiple related APIs exist, which are not necessarily explicitly, separately documented. So it's not just a usage hint. It's true that we could have a separate attribute "autoChild=true" but this doesn't materially change the work of a generator so we don't plan to change that now.





                          Comment


                            We use only "DOMElement" (not those other variations) and an API that takes DOMElement or String will have the type "DOMElement | String" (consistent with all other alternative types).
                            Code:
                            <docItems scVersionNumber="12.0" scVersion="12.0d" versionNumber="SNAPSHOT_v12.0d_2017-08-09" version="SNAPSHOT_v12.0d_2017-08-09/AllModules Deployment">
                            ...
                            <docItem type="method" definingClass="class:GridRenderer" ref="method:GridRenderer.getCellFromDomElement" description=" Given a pointer to an element in the DOM, this method will check whether this&amp;#010 element is contained within a cell of the gridRenderer, and if so return a&amp;#010 2 element array denoting the <code>[rowNum,colNum]</code> of the element&amp;#010 in question.&amp;#010" flags="A" name="getCellFromDomElement">
                            <returns type="Array" description="2 element array containing rowNum and colNum, or null if the element is not contained in any cell in this gridRenderer"/>
                            <groups>autoTest</groups>
                            <params optional="false" [B]type="DOM Element[/B]" description="DOM element to test" name="element"></params>
                            </docItem>
                            ...
                            <docItem ref="method:ActiveXControl.getPluginHandle" type="method" definingClass="class:ActiveXControl" description="  Returns a handle to the element for this ISC ActiveX control object.&amp;amp;#010" flags="" name="getPluginHandle">
                            <returns [B]type="HTML Element"[/B] description="pointer to the plugin element in the DOM"></returns>
                            </docItem>
                            
                            <docItem ref="method:Flashlet.getPluginHandle" type="method" definingClass="class:Flashlet" description=" &amp;amp;#010  Returns a handle to the flashlet DOM element (valid only after the component has been drawn).  &amp;amp;#010&amp;amp;#010" flags="A" name="getPluginHandle">
                            <returns [B]type="HTML Element"[/B] description="pointer to the plugin element in the DOM"></returns>
                            </docItem>
                            
                            <docItem ref="classMethod:EventHandler.getNativeMouseTarget" type="classMethod" definingClass="class:EventHandler" description=" Returns the natively reported target (or source) DOM element for the current mouse event.&amp;amp;#010 &amp;lt;b&amp;gt;NOTE:&amp;lt;/b&amp;gt; SmartClient cannot guarantee that the same element will&amp;amp;#010 be reported in all browser/platform configurations for all event types.&amp;amp;#010 If you wish to make use of this value, we recommend testing your use case &amp;amp;#010 in all target browser configurations.&amp;amp;#010&amp;amp;#010" flags="A" name="getNativeMouseTarget">
                            <returns [B]type="HTML Element"[/B] description="native DOM element over which the mouse event occurred"></returns>
                            </docItem>

                            we could add offical @type declarations (with @baseType Number) so that you have a source for a description to generate into the TypeScript headers.
                            I don't understand what this means but I want to. Please elaborate.

                            We don't *think* we have any more instances of subclasses overriding with the wrong type (or different casing). This doesn't seem to be something you're flagging in Errors.txt.
                            I will try to find these programmatically and put them in the Errors.txt file. In the meantime, here's the TypeScipt build output when I don't convert Boolean to boolean:

                            Code:
                            Build:Interface 'MenuItem' incorrectly extends interface 'ListGridRecord'.
                            Build:Interface 'CubeGrid' incorrectly extends interface 'ListGrid'.
                            Build:Interface 'DynamicFormProps' incorrectly extends interface 'CanvasProps'.
                            Build:Interface 'DynamicForm' incorrectly extends interface 'Canvas'.
                            Build:Interface 'EventCanvasProps' incorrectly extends interface 'VLayoutProps'.
                            Build:Interface 'EventCanvas' incorrectly extends interface 'VLayout'.
                            Build:Interface 'MenuProps' incorrectly extends interface 'ListGridProps'.
                            Build:Interface 'Menu' incorrectly extends interface 'ListGrid'.
                            Build:Interface 'MenuButtonProps' incorrectly extends interface 'ButtonProps'.
                            Build:Interface 'MenuButton' incorrectly extends interface 'Button'.
                            Build:Interface 'MiniDateRangeItemProps' incorrectly extends interface 'StaticTextItemProps'.
                            Build:Interface 'SelectItemProps' incorrectly extends interface 'FormItemProps'.
                            Build:Interface 'SelectItem' incorrectly extends interface 'FormItem'.
                            Build:Interface 'SliderProps' incorrectly extends interface 'CanvasProps'.
                            Build:Interface 'Slider' incorrectly extends interface 'Canvas'.
                            Build:Interface 'SplitbarProps' incorrectly extends interface 'StretchImgProps'.
                            Build:Interface 'Splitbar' incorrectly extends interface 'StretchImg'.
                            Build:Interface 'ToolbarProps' incorrectly extends interface 'LayoutProps'.
                            Build:Interface 'Toolbar' incorrectly extends interface 'Layout'.
                            Build:Interface 'ToolStripProps' incorrectly extends interface 'LayoutProps'.
                            Build:Interface 'ToolStrip' incorrectly extends interface 'Layout'.
                            Will revisit Callbacks and AutoChild.

                            Comment


                              I see that several Classes that did not inherit from Class are now tagged as "objects". This makes sense. I also see that for these objects, what used to be classMethods are now called staticMethods. All good. I've modified the generator to handle these. Thanks.

                              However, I also see that isA has been moved to its own object, which is fine, but the the methods are marked as classMethod. I think they should be staticMethod. This probably accounts for most of the decrease in the Methods Generated count which went from 3298 to 3278.


                              Click image for larger version

Name:	isA class methods.png
Views:	93
Size:	63.6 KB
ID:	248787

                              Comment


                                It looks like isA has been an object for a long time and many releases, so if there's any issue, it's just that the existing methods on it have been left as classMethods (since the concept of staticMethods didn't exist previously). Is that what you're saying?

                                Comment

                                Working...
                                X