• Advertisement
  • Popular Tags

  • Popular Now

  • Advertisement
  • Similar Content

    • By PVS-studio team
      Recently a long-awaited event has happen - Unity Technologies uploaded the C# source code of the game engine, available for free download on Github. The code of the engine and the editor is available. Of course, we couldn't pass up, especially since lately we've not written so many articles about checking projects on C#. Unity allows to use the provided sources only for information purposes. We'll use them exactly in these ways. Let's try out the latest version PVS-Studio 6.23 on the Unity code.

      Introduction
      Previously we've written an article about checking Unity. At that time so much C#-code was not available for the analysis: some components, libraries and examples of usage. However, the author of the article managed to find quite interesting bugs.
      How did Unity please us this time? I'm saying "please" and hope not to offend the authors of the project. Especially since the amount of the source Unity C#-code, presented on GitHub, is about 400 thousand lines (excluding empty) in 2058 files with the extension "cs". It's a lot, and the analyzer had a quite considerable scope.
      Now about the results. Before the analysis, I've slightly simplified the work, having enabled the mode of the code display according to the CWE classification for the found bugs. I've also activated the warnings suppression mechanism of the third level of certainty (Low). These settings are available in the drop-down menu of PVS-Studio in Visual Studio development environment, and in the parameters of the analyzer. Getting rid of the warnings with low certainty, I made the analysis of the Unity source code. As a result, I got 181 warnings of the first level of certainty (High) and 506 warnings of the second level of certainty (Medium).
      I have not studied absolutely all the warnings, because there were quite a lot of them. Developers or enthusiasts can easily conduct an in-depth analysis by testing Unity themselves. To do this, PVS-Studio provides free trial and free modes of using. Companies can also buy our product and get quick and detailed support along with the license.
      Judging by the fact that I immediately managed to find couple of real bugs practically in every group of warnings with one or two attempts, there are a lot of them in Unity. And yes, they are diverse. Let's review the most interesting errors.

      Results of the check
      Something's wrong with the flags
      PVS-Studio warning: V3001 There are identical sub-expressions 'MethodAttributes.Public' to the left and to the right of the '|' operator. SyncListStructProcessor.cs 240
      MethodReference GenerateSerialization() { .... MethodDefinition serializeFunc = new MethodDefinition("SerializeItem", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Public | // <= MethodAttributes.HideBySig, Weaver.voidType); .... } When combining enumeration flags MethodAttributes, an error was made: the Public value was used twice. Perhaps, the reason for this is the wrong code formatting.
      A similar bug is also made in code of the method GenerateDeserialization:
      V3001 There are identical sub-expressions 'MethodAttributes.Public' to the left and to the right of the '|' operator. SyncListStructProcessor.cs 309 Copy-Paste
      PVS-Studio warning: V3001 There are identical sub-expressions 'format == RenderTextureFormat.ARGBFloat' to the left and to the right of the '||' operator. RenderTextureEditor.cs 87
      public static bool IsHDRFormat(RenderTextureFormat format) { Return (format == RenderTextureFormat.ARGBHalf || format == RenderTextureFormat.RGB111110Float || format == RenderTextureFormat.RGFloat || format == RenderTextureFormat.ARGBFloat || format == RenderTextureFormat.ARGBFloat || format == RenderTextureFormat.RFloat || format == RenderTextureFormat.RGHalf || format == RenderTextureFormat.RHalf); } I gave a piece of code, preliminary having formatted it, so the error is easily detected visually: the comparison with RenderTextureFormat.ARGBFloat is performed twice. In the original code, it looks differently:
      Probably, another value of enumeration RenderTextureFormat has to be used in one of two identical comparisons.
      Double work
      PVS-Studio warning: V3008 CWE-563 The 'fail' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 1633, 1632. UNetWeaver.cs 1633
      class Weaver { .... public static bool fail; .... static public bool IsValidTypeToGenerate(....) { .... if (....) { .... Weaver.fail = true; fail = true; return false; } return true; } .... } The true value is assigned twice to the value, as Weaver.fail and fail is one and the same static field of the Weaver class. Perhaps, there is no crucial error, but the code definitely needs attention.
      No options
      PVS-Studio warning: V3009 CWE-393 It's odd that this method always returns one and the same value of 'false'. ProjectBrowser.cs 1417
      // Returns true if we should early out of OnGUI bool HandleCommandEventsForTreeView() { .... if (....) { .... if (....) return false; .... } return false; } The method always returns false. Pay attention to the comment in the beginning.
      A developer forgot about the result
      PVS-Studio warning: V3010 CWE-252 The return value of function 'Concat' is required to be utilized. AnimationRecording.cs 455
      static public UndoPropertyModification[] Process(....) { .... discardedModifications.Concat(discardedRotationModifications); return discardedModifications.ToArray(); } When concatenating two arrays discardedModifications and discardedRotationModifications the author forgot to save the result. Probably a programmer assumed that the result would be expressed immediately in the array discardedModifications. But it is not so. As a result, the original array discardedModifications is returned from the method. The code needs to be corrected as follows:
      static public UndoPropertyModification[] Process(....) { .... return discardedModifications.Concat(discardedRotationModifications) .ToArray(); } Wrong variable was checked
      PVS-Studio warning: V3019 CWE-697 Possibly an incorrect variable is compared to null after type conversion using 'as' keyword. Check variables 'obj', 'newResolution'. GameViewSizesMenuItemProvider.cs 104
      private static GameViewSize CastToGameViewSize(object obj) { GameViewSize newResolution = obj as GameViewSize; if (obj == null) { Debug.LogError("Incorrect input"); return null; } return newResolution; } In this method, the developers forgot to consider a situation where the variable objis not equal to null, but it will not be able to cast to the GameViewSize type. Then the variable newResolution will be set to null, and the debug output will not be made. A correct variant of code will be like this:
      private static GameViewSize CastToGameViewSize(object obj) { GameViewSize newResolution = obj as GameViewSize; if (newResolution == null) { Debug.LogError("Incorrect input"); } return newResolution; } Deficiency
      PVS-Studio warning: V3020 CWE-670 An unconditional 'return' within a loop. PolygonCollider2DEditor.cs 96
      private void HandleDragAndDrop(Rect targetRect) { .... foreach (....) { .... if (....) { .... } return; } .... } The loop will execute only one iteration, after that the method terminates its work. Various scenarios are probable. For example, return must be inside the unit if, or somewhere before return, a directive continue is missing. It may well be that there is no error here, but then one should make the code more understandable.
      Unreachable code
      PVS-Studio warning: V3021 CWE-561 There are two 'if' statements with identical conditional expressions. The first 'if' statement contains method return. This means that the second 'if' statement is senseless CustomScriptAssembly.cs 179
      public bool IsCompatibleWith(....) { .... if (buildingForEditor) return IsCompatibleWithEditor(); if (buildingForEditor) buildTarget = BuildTarget.NoTarget; // Editor .... } Two identical checks, following one after another. It is clear that in case of buildingForEditor equality to the true value, the second check is meaningless, because the first method terminates its work. If the value buildingForEditor is false, neither then-brunch nor if operator will be executed. There is an erroneous construction that requires correction.
      Unconditional condition
      PVS-Studio warning: V3022 CWE-570 Expression 'index < 0 && index >= parameters.Length' is always false. AnimatorControllerPlayable.bindings.cs 287
      public AnimatorControllerParameter GetParameter(int index) { AnimatorControllerParameter[] param = parameters; if (index < 0 && index >= parameters.Length) throw new IndexOutOfRangeException( "Index must be between 0 and " + parameters.Length); return param[index]; } The condition of the index check is incorrect - the result will always be false. However, in case of passing the incorrect index to the GetParameter method, the exception IndexOutOfRangeException will still be thrown when attempting to access an array element in the return block. Although, the error message will be slightly different. One has to use || in a condition instead of the operator && so that the code worked the way a developer expected:
      public AnimatorControllerParameter GetParameter(int index) { AnimatorControllerParameter[] param = parameters; if (index < 0 || index >= parameters.Length) throw new IndexOutOfRangeException( "Index must be between 0 and " + parameters.Length); return param[index]; } Perhaps, due to the use of the Copy-Paste method, there is another the same error in the Unity code:
      PVS-Studio warning: V3022 CWE-570 Expression 'index < 0 && index >= parameters.Length' is always false. Animator.bindings.cs 711
      And another similar error associated with the incorrect condition of the check of the array index:
      PVS-Studio warning: V3022 CWE-570 Expression 'handle.valueIndex < 0 && handle.valueIndex >= list.Length' is always false. StyleSheet.cs 81
      static T CheckAccess<T>(T[] list, StyleValueType type, StyleValueHandle handle) { T value = default(T); if (handle.valueType != type) { Debug.LogErrorFormat(.... ); } else if (handle.valueIndex < 0 && handle.valueIndex >= list.Length) { Debug.LogError("Accessing invalid property"); } else { value = list[handle.valueIndex]; } return value; } And in this case, a release of the IndexOutOfRangeException exception is possible.As in the previous code fragments, one has to use the operator || instead of && to fix an error.
      Simply strange code
      Two warnings are issued for the code fragment below.
      PVS-Studio warning: V3022 CWE-571 Expression 'bRegisterAllDefinitions || (AudioSettings.GetSpatializerPluginName() == "GVR Audio Spatializer")' is always true. AudioExtensions.cs 463
      PVS-Studio warning: V3022 CWE-571 Expression 'bRegisterAllDefinitions || (AudioSettings.GetAmbisonicDecoderPluginName() == "GVR Audio Spatializer")' is always true. AudioExtensions.cs 467
      // This is where we register our built-in spatializer extensions. static private void RegisterBuiltinDefinitions() { bool bRegisterAllDefinitions = true; if (!m_BuiltinDefinitionsRegistered) { if (bRegisterAllDefinitions || (AudioSettings.GetSpatializerPluginName() == "GVR Audio Spatializer")) { } if (bRegisterAllDefinitions || (AudioSettings.GetAmbisonicDecoderPluginName() == "GVR Audio Spatializer")) { } m_BuiltinDefinitionsRegistered = true; } } It looks like an incomplete method. It is unclear why it has been left as such and why developers haven't commented the useless code blocks. All, that the method does at the moment:
      if (!m_BuiltinDefinitionsRegistered) { m_BuiltinDefinitionsRegistered = true; } Useless method
      PVS-Studio warning: V3022 CWE-570 Expression 'PerceptionRemotingPlugin.GetConnectionState() != HolographicStreamerConnectionState.Disconnected' is always false. HolographicEmulationWindow.cs 171
      private void Disconnect() { if (PerceptionRemotingPlugin.GetConnectionState() != HolographicStreamerConnectionState.Disconnected) PerceptionRemotingPlugin.Disconnect(); } To clarify the situation, it is necessary to look at the declaration of the methodPerceptionRemotingPlugin.GetConnectionState():
      internal static HolographicStreamerConnectionState GetConnectionState() { return HolographicStreamerConnectionState.Disconnected; } Thus, calling the Disconnect() method leads to nothing.
      One more error relates to the same method PerceptionRemotingPlugin.GetConnectionState():
      PVS-Studio warning: V3022 CWE-570 Expression 'PerceptionRemotingPlugin.GetConnectionState() == HolographicStreamerConnectionState.Connected' is always false. HolographicEmulationWindow.cs 177
      private bool IsConnectedToRemoteDevice() { return PerceptionRemotingPlugin.GetConnectionState() == HolographicStreamerConnectionState.Connected; } The result of the method is equivalent to the following:
      private bool IsConnectedToRemoteDevice() { return false; } As we can see, among the warnings V3022 many interesting ones were found. Probably, if one spends much time, he can increase the list. But let's move on.
      Not on the format
      PVS-Studio warning: V3025 CWE-685 Incorrect format. A different number of format items is expected while calling 'Format' function. Arguments not used: index. Physics2D.bindings.cs 2823
      public void SetPath(....) { if (index < 0) throw new ArgumentOutOfRangeException( String.Format("Negative path index is invalid.", index)); .... } There is no error in code, but as the saying goes, the code "smells". Probably, an earlier message was more informative, like this: "Negative path index {0} is invalid.". Then it was simplified, but developers forgot to remove the parameter index for the method Format. Of course, this is not the same as a forgotten parameter for the indicated output string specifier, i.e. the construction of the type String.Format("Negative path index {0} is invalid."). In such a case, an exception would be thrown. But in our case we also need neatness when refactoring. The code has to be fixed as follows:
      public void SetPath(....) { if (index < 0) throw new ArgumentOutOfRangeException( "Negative path index is invalid."); .... } Substring of the substring
      PVS-Studio warning: V3053 An excessive expression. Examine the substrings 'UnityEngine.' and 'UnityEngine.SetupCoroutine'. StackTrace.cs 43
      static bool IsSystemStacktraceType(object name) { string casted = (string)name; return casted.StartsWith("UnityEditor.") || casted.StartsWith("UnityEngine.") || casted.StartsWith("System.") || casted.StartsWith("UnityScript.Lang.") || casted.StartsWith("Boo.Lang.") || casted.StartsWith("UnityEngine.SetupCoroutine"); } Search of the substring "UnityEngine.SetupCoroutine" in the condition is meaningless, because before that the search for "UnityEngine." is performed. Therefore, the last check should be removed or one has to clarify the correctness of substrings.
      Another similar error:
      PVS-Studio warning: V3053 An excessive expression. Examine the substrings 'Windows.dll' and 'Windows.'. AssemblyHelper.cs 84
      static private bool CouldBelongToDotNetOrWindowsRuntime(string assemblyPath) { return assemblyPath.IndexOf("mscorlib.dll") != -1 || assemblyPath.IndexOf("System.") != -1 || assemblyPath.IndexOf("Windows.dll") != -1 || // <= assemblyPath.IndexOf("Microsoft.") != -1 || assemblyPath.IndexOf("Windows.") != -1 || // <= assemblyPath.IndexOf("WinRTLegacy.dll") != -1 || assemblyPath.IndexOf("platform.dll") != -1; } Size does matter
      PVS-Studio warning: V3063 CWE-571 A part of conditional expression is always true if it is evaluated: pageSize <= 1000. UNETInterface.cs 584
      public override bool IsValid() { .... return base.IsValid() && (pageSize >= 1 || pageSize <= 1000) && totalFilters <= 10; } Condition for a check of a valid page size is erroneous. Instead of the operator ||, one has to use &&. The corrected code:
      public override bool IsValid() { .... return base.IsValid() && (pageSize >= 1 && pageSize <= 1000) && totalFilters <= 10; } Possible division by zero
      PVS-Studio warning: V3064 CWE-369 Potential division by zero. Consider inspecting denominator '(float)(width - 1)'. ClothInspector.cs 249
      Texture2D GenerateColorTexture(int width) { .... for (int i = 0; i < width; i++) colors[i] = GetGradientColor(i / (float)(width - 1)); .... } The problem may occur when passing the value width = 1 into the method. In the method, it is not checked anyway. The method GenerateColorTexture is called in the code just once with the parameter 100:
      void OnEnable() { if (s_ColorTexture == null) s_ColorTexture = GenerateColorTexture(100); .... } So, there is no error here so far. But, just in case, in the method GenerateColorTexture the possibility of transferring incorrect width value should be provided.
      Paradoxical check
      PVS-Studio warning: V3080 CWE-476 Possible null dereference. Consider inspecting 'm_Parent'. EditorWindow.cs 449
      public void ShowPopup() { if (m_Parent == null) { .... Rect r = m_Parent.borderSize.Add(....); .... } } Probably, due to a typo, the execution of such code guarantees the use of the null reference m_Parent. The corrected code:
      public void ShowPopup() { if (m_Parent != null) { .... Rect r = m_Parent.borderSize.Add(....); .... } } The same error occurs later in the code:
      PVS-Studio warning: V3080 CWE-476 Possible null dereference. Consider inspecting 'm_Parent'. EditorWindow.cs 470
      internal void ShowWithMode(ShowMode mode) { if (m_Parent == null) { .... Rect r = m_Parent.borderSize.Add(....); .... } And here's another interesting bug that can lead to access by a null reference due to incorrect check:
      PVS-Studio warning: V3080 CWE-476 Possible null dereference. Consider inspecting 'objects'. TypeSelectionList.cs 48
      public TypeSelection(string typeName, Object[] objects) { System.Diagnostics.Debug.Assert(objects != null || objects.Length >= 1); .... } It seems to me that Unity developers quite often make errors related to misuse of operators || and && in conditions. In this case, if objects has a null value, then this will lead to a check of second part of the condition (objects != null || objects.Length >= 1), which will entail the unexpected throw of an exception. The error should be corrected as follows:
      public TypeSelection(string typeName, Object[] objects) { System.Diagnostics.Debug.Assert(objects != null && objects.Length >= 1); .... } Early nullifying
      PVS-Studio warning: V3080 CWE-476 Possible null dereference. Consider inspecting 'm_RowRects'. TreeViewControlGUI.cs 272
      public override void GetFirstAndLastRowVisible(....) { .... if (rowCount != m_RowRects.Count) { m_RowRects = null; throw new InvalidOperationException(string.Format("....", rowCount, m_RowRects.Count)); } .... } In this case, the exception throw (access by the null reference m_RowRects) will happen when generating the message string for another exception. Code might be fixed, for example, as follows:
      public override void GetFirstAndLastRowVisible(....) { .... if (rowCount != m_RowRects.Count) { var m_RowRectsCount = m_RowRects.Count; m_RowRects = null; throw new InvalidOperationException(string.Format("....", rowCount, m_RowRectsCount)); } .... } One more error when checking
      PVS-Studio warning: V3080 CWE-476 Possible null dereference. Consider inspecting 'additionalOptions'. MonoCrossCompile.cs 279
      static void CrossCompileAOT(....) { .... if (additionalOptions != null & additionalOptions.Trim().Length > 0) arguments += additionalOptions.Trim() + ","; .... } Due to the fact that the & operator is used in a condition, the second part of the condition will always be checked, regardless of the result of the check of the first part. In case if the variable additionalOptions has the null value, the exception throw is inevitable. The error has to be corrected, by using the operator && instead of &.
      As we can see, among the warnings with the number V3080 there are rather insidious errors.
      Late check
      PVS-Studio warning: V3095 CWE-476 The 'element' object was used before it was verified against null. Check lines: 101, 107. StyleContext.cs 101
      public override void OnBeginElementTest(VisualElement element, ....) { if (element.IsDirty(ChangeType.Styles)) { .... } if (element != null && element.styleSheets != null) { .... } .... } The variable element is used without preliminary check for null. While later in the code this check is performed. The code probably needs to be corrected as follows:
      public override void OnBeginElementTest(VisualElement element, ....) { if (element != null) { if (element.IsDirty(ChangeType.Styles)) { .... } if (element.styleSheets != null) { .... } } .... } In code there are 18 more errors. Let me give you a list of the first 10:
      V3095 CWE-476 The 'property' object was used before it was verified against null. Check lines: 5137, 5154. EditorGUI.cs 5137 V3095 CWE-476 The 'exposedPropertyTable' object was used before it was verified against null. Check lines: 152, 154. ExposedReferenceDrawer.cs 152 V3095 CWE-476 The 'rectObjs' object was used before it was verified against null. Check lines: 97, 99. RectSelection.cs 97 V3095 CWE-476 The 'm_EditorCache' object was used before it was verified against null. Check lines: 134, 140. EditorCache.cs 134 V3095 CWE-476 The 'setup' object was used before it was verified against null. Check lines: 43, 47. TreeViewExpandAnimator.cs 43 V3095 CWE-476 The 'response.job' object was used before it was verified against null. Check lines: 88, 99. AssetStoreClient.cs 88 V3095 CWE-476 The 'compilationTask' object was used before it was verified against null. Check lines: 1010, 1011. EditorCompilation.cs 1010 V3095 CWE-476 The 'm_GenericPresetLibraryInspector' object was used before it was verified against null. Check lines: 35, 36. CurvePresetLibraryInspector.cs 35 V3095 CWE-476 The 'Event.current' object was used before it was verified against null. Check lines: 574, 620. AvatarMaskInspector.cs 574 V3095 CWE-476 The 'm_GenericPresetLibraryInspector' object was used before it was verified against null. Check lines: 31, 32. ColorPresetLibraryInspector.cs 31 Wrong Equals method
      PVS-Studio warning: V3115 CWE-684 Passing 'null' to 'Equals' method should not result in 'NullReferenceException'. CurveEditorSelection.cs 74
      public override bool Equals(object _other) { CurveSelection other = (CurveSelection)_other; return other.curveID == curveID && other.key == key && other.type == type; } Overload of the Equals method was implemented carelessly. One has to take into account the possibility of obtaining null as a parameter, as this can lead to a throw of an exception, which hasn't been considered in the calling code. In addition, the situation, when _other can't be cast to the type CurveSelection, will lead to a throw of an exception. The code has to be fixed. A good example of the implementation of Object.equals overload is given in the documentation.
      In the code, there are other similar errors:
      V3115 CWE-684 Passing 'null' to 'Equals' method should not result in 'NullReferenceException'. SpritePackerWindow.cs 40 V3115 CWE-684 Passing 'null' to 'Equals' method should not result in 'NullReferenceException'. PlatformIconField.cs 28 V3115 CWE-684 Passing 'null' to 'Equals' method should not result in 'NullReferenceException'. ShapeEditor.cs 161 V3115 CWE-684 Passing 'null' to 'Equals' method should not result in 'NullReferenceException'. ActiveEditorTrackerBindings.gen.cs 33 V3115 CWE-684 Passing 'null' to 'Equals' method should not result in 'NullReferenceException'. ProfilerFrameDataView.bindings.cs 60 Once again about the check for null inequality
      PVS-Studio warning: V3125 CWE-476 The 'camera' object was used after it was verified against null. Check lines: 184, 180. ARBackgroundRenderer.cs 184
      protected void DisableARBackgroundRendering() { .... if (camera != null) camera.clearFlags = m_CameraClearFlags; // Command buffer camera.RemoveCommandBuffer(CameraEvent.BeforeForwardOpaque, m_CommandBuffer); camera.RemoveCommandBuffer(CameraEvent.BeforeGBuffer, m_CommandBuffer); } When the camera variable is used the first time, it is checked for null inequality. But further along the code the developers forget to do it. The correct variant could be like this:
      protected void DisableARBackgroundRendering() { .... if (camera != null) { camera.clearFlags = m_CameraClearFlags; // Command buffer camera.RemoveCommandBuffer(CameraEvent.BeforeForwardOpaque, m_CommandBuffer); camera.RemoveCommandBuffer(CameraEvent.BeforeGBuffer, m_CommandBuffer); } } Another similar error:
      PVS-Studio warning: V3125 CWE-476 The 'item' object was used after it was verified against null. Check lines: 88, 85. TreeViewForAudioMixerGroups.cs 88
      protected override Texture GetIconForItem(TreeViewItem item) { if (item != null && item.icon != null) return item.icon; if (item.id == kNoneItemID) // <= return k_AudioListenerIcon; return k_AudioGroupIcon; } An error, that in some cases leads to an access by a null link. The execution of the condition in the first block if enables the exit from the method. However, if this does not happen, then there is no guarantee that the reference item is non-zero. Here is the corrected version of the code:
      protected override Texture GetIconForItem(TreeViewItem item) { if (item != null) { if (item.icon != null) return item.icon; if (item.id == kNoneItemID) return k_AudioListenerIcon; } return k_AudioGroupIcon; } In the code there are 12 similar errors. Let me give you a list of the first 10:
      V3125 CWE-476 The 'element' object was used after it was verified against null. Check lines: 132, 107. StyleContext.cs 132 V3125 CWE-476 The 'mi.DeclaringType' object was used after it was verified against null. Check lines: 68, 49. AttributeHelper.cs 68 V3125 CWE-476 The 'label' object was used after it was verified against null. Check lines: 5016, 4999. EditorGUI.cs 5016 V3125 CWE-476 The 'Event.current' object was used after it was verified against null. Check lines: 277, 268. HostView.cs 277 V3125 CWE-476 The 'bpst' object was used after it was verified against null. Check lines: 96, 92. BuildPlayerSceneTreeView.cs 96 V3125 CWE-476 The 'state' object was used after it was verified against null. Check lines: 417, 404. EditorGUIExt.cs 417 V3125 CWE-476 The 'dock' object was used after it was verified against null. Check lines: 370, 365. WindowLayout.cs 370 V3125 CWE-476 The 'info' object was used after it was verified against null. Check lines: 234, 226. AssetStoreAssetInspector.cs 234 V3125 CWE-476 The 'platformProvider' object was used after it was verified against null. Check lines: 262, 222. CodeStrippingUtils.cs 262 V3125 CWE-476 The 'm_ControlPoints' object was used after it was verified against null. Check lines: 373, 361. EdgeControl.cs 373 The choice turned out to be small
      PVS-Studio warning: V3136 CWE-691 Constant expression in switch statement. HolographicEmulationWindow.cs 261
      void ConnectionStateGUI() { .... HolographicStreamerConnectionState connectionState = PerceptionRemotingPlugin.GetConnectionState(); switch (connectionState) { .... } .... } The method PerceptionRemotingPlugin.GetConnectionState() is to blame here. We have already come across it when we were analyzing the warnings V3022:
      internal static HolographicStreamerConnectionState GetConnectionState() { return HolographicStreamerConnectionState.Disconnected; } The method will return a constant. This code is very strange. It needs to be paid attention.

      Conclusions
      I think we can stop at this point, otherwise the article will become boring and overextended. Again, I listed the errors that I just couldn't miss. Sure, the Unity code contains a big number of the erroneous and incorrect constructions, that need to be fixed. The difficulty is that many of the issued warnings are very controversial and only the author of the code is able to make the exact "diagnosis" in each case.
      Generally speaking about the Unity project, we can say that it is rich for errors, but taking into account the size of its code base (400 thousand lines), it's not so bad. Nevertheless, I hope that the authors will not neglect the code analysis tools to improve the quality of their product.
      Use PVS-Studio and I wish you bugless code!
       
    • By fiyinDaniel
      Hi everyone,
      I'm a very proficient C++ programmer looking to build my portfolio by contributing to projects for free. I have been programming for about 4 years now. I have experience working with OpenGL and I'm willing to adapt to a wide variety of technologies as I learn really fast. I can also give 20 - 25 hrs weekly on the project. Contact me if you have something for me to work on.
      Thanks.
    • By Alexa Savchenko
      I publishing for manufacturing our ray tracing engines and products on graphics API (C++, Vulkan API, GLSL460, SPIR-V): https://github.com/world8th/satellite-oem
      For end users I have no more products or test products. Also, have one simple gltf viewer example (only source code).
      In 2016 year had idea for replacement of screen space reflections, but in 2018 we resolved to finally re-profile project as "basis of render engine". In Q3 of 2017 year finally merged to Vulkan API. 
       
       
    • By Mavericky
      Get the code HERE.
      This project is a nod to those 1990 pocket games that were all the rage in the day. The eTamagotchi features P2P networking showcasing how you can have a single application be both the client and the server at runtime. It features a random digimon (digital monster) that keeps track of all battles it has. The code is meant for demonstration and learning purposes only. The README lists each point that can be improved upon including gameplay mechanics.
       

    • By nxrighthere

      BenchmarkNet is a console application for testing the reliable UDP networking solutions.
      Features:
      Asynchronous simulation of a large number of clients Stable under high loads Simple and flexible simulation setup Detailed session information Supported networking libraries:
      ENet (C# Wrapper) UNet LiteNetLib Lidgren MiniUDP Hazel Photon Neutrino DarkRift More information and source code on GitHub.
      You can find the latest benchmark results on the wiki page.
  • Advertisement
  • Advertisement
Sign in to follow this  

FPL - a single header file platform abstraction library

Recommended Posts

Hi,

i wanted to present you my recent project i am working on which is a platform abstraction library.
This library is designed to be simple, non-bloated, can be included however you want and do not require any thirdparty libraries at all. It will simply abstract away all relevant platform specific things in a simple to use api without hiding any data. The main focus is game/media/simulation development, so you get a window, audio playback and a rendering context.

 

Why:

- Moving away from object orienation to handmade programming
- Most platform abstraction libraries are bloated, have a bad api, cannot be debugged, are slow, has bad memory management
- I dont want to write platform specific code anymore, just once for each platform with a fixed set of features and thats it.
- Learning C99 low level programming

 

Mindset:

- Its written in C99 but is C++ compatible
- There is just a single header file you have to include and thats it.
- C-Runtime support can be disabled
- The code style is simple and clean
- Its full open source, so you can use and extend it however you want
- Uses just the built-in operating system functions/libraries
- Works well with other libraries as well (ImGUI, Box2D, Glew, STL, etc.)
- Some features can be compiled out as needed (Window, OpenGL, etc.)
- Fixed and small memory footprint
- Easy to use api
- Can be debugged

 

Features:

- Window creation and handling
- OpenGL legacy and modern rendering context creation
- Memory allocation and deallocation
- Timing operations
- Threading and syncronisation
- File/Path IO
- String conversion functions
- Atomic operations
- Dynamic library loading
- Console out
- Gamepad input
- Audio playback

 

History:

Initially the project was fully written with 100% C/89 compability in mind without needing the c-runtime library, but as i see myself always fighting against C or missing any features like console output, i decided to move to a minimal C++ version.

After a while i upgraded to C++/11, but felt there was no need for C++ at all...

Now its written in C99 and it was the best choice ever.

 

Current state:

- Win32 support is complete
- Linux support is almost complete

 

Planned:

- More video drivers (Direct2D/3D/Vulcan)
- More audio drivers (XAudio, Wasapi)
- More platforms (MacOSX, maybe Android not sure)
- More docs & tutorials
- More tests

 

Source:

https://github.com/f1nalspace/final_game_tech

 

Feedback/Comments:

I would like to know what you think about, what is missing, how can i improve it, what do you want from it, etc.

 

Greetings,
Final

Edited by Finalspace

Share this post


Link to post
Share on other sites
Advertisement

I skimmed your code and there are two (maybe three) things that stuck out to me.

#define FPL_NULL 0

This is not how you should be defining NULL. Either use (void*)0 or nullptr if C++11 is available.

#if !FPL_ENABLE_C_RUNTIME_LIBRARY
void __stdcall WinMainCRTStartup() {
    int result = wWinMain(GetModuleHandleW(0), 0, 0, 0);
    ExitProcess(result);
}
#endif // !FPL_ENABLE_C_RUNTIME_LIBRARY

I read your comment further up the file about how you hate the CRT, but this is clearly taking it too far. I'll quote a section of ExitProcess()'s documentation:

Quote

Calling ExitProcess in a DLL can lead to unexpected application or system errors. Be sure to call ExitProcess from a DLL only if you know which applications or system components will load the DLL and that it is safe to call ExitProcess in this context.

And that's pretty much the point at which I stopped reading your code. Parts of it looked good, other parts looked really, really bad. A more important question to me is: Who needs this? Why does this exist? All of this stuff is already available in other libraries or game frameworks.

As a side note, why are people writing more and more of these "header only" libraries? The preprocessor seems like a flimsy way to configure how your code should compile. A more solid solution is to use a proper build system.

Share this post


Link to post
Share on other sites
51 minutes ago, TheComet said:

I skimmed your code and there are two (maybe three) things that stuck out to me.


#define FPL_NULL 0

This is not how you should be defining NULL. Either use (void*)0 or nullptr if C++11 is available.


#if !FPL_ENABLE_C_RUNTIME_LIBRARY
void __stdcall WinMainCRTStartup() {
    int result = wWinMain(GetModuleHandleW(0), 0, 0, 0);
    ExitProcess(result);
}
#endif // !FPL_ENABLE_C_RUNTIME_LIBRARY

I read your comment further up the file about how you hate the CRT, but this is clearly taking it too far. I'll quote a section of ExitProcess()'s documentation:

And that's pretty much the point at which I stopped reading your code. Parts of it looked good, other parts looked really, really bad. A more important question to me is: Who needs this? Why does this exist? All of this stuff is already available in other libraries or game frameworks.

As a side note, why are people writing more and more of these "header only" libraries? The preprocessor seems like a flimsy way to configure how your code should compile. A more solid solution is to use a proper build system.

I tried FPL_NULL (void *)0 - but i had trouble with it, so i simply set it to zero. But yes i agree, using nullptr on C++/11 would be better, but how do i detect if C++/11 is available - through preprocessor directives? Btw. FPL_NULL is internal only, so its not meant to be used outside.

 

That entire no CRT thing - i am not sure about this. Its a experiment to see if it works, because some people dont want to include even the C-Runtime Library at all. Also regards DLL´s and ExitProcess and need to think it more, as a matter of fact i havent created a library project yet - but this will come soon. When i see that no-CRT is support is too complicated, i will drop it. Right know i dont see any benefit disabling CRT, except for smalling out the executable size.

 

Why header library only? Because its simple and works all the time. No linking madness, no dll´s needed - besides from the standard system operating stuff which is included anyway.

 

Regards build-systems: I dont like build-systems at all, all i ever used was just awful: Slow, hard to configure, not working -> On any programming language the same bad experience.

But what i will do for sure, is making several simple build scripts (Batch, Shell) which just builds the samples in the simplest possible way for a set of compilers/platforms.

Edited by Finalspace

Share this post


Link to post
Share on other sites
52 minutes ago, Finalspace said:

C++/11 would be better, but how do i detect if C++/11 is available - through preprocessor directives?

You have it in your own code:

	#ifdef __cplusplus
	

Share this post


Link to post
Share on other sites
Quote

That entire no CRT thing - i am not sure about this. Its a experiment to see if it works, because some people dont want to include even the C-Runtime Library at all. 

I do not know a person or a product that does not link with CRT. Starting form small games, 3D DCCs, even some "serious software" uses it.
 

Quote

Why header library only? Because its simple and works all the time. No linking madness, no dll´s needed - besides from the standard system operating stuff which is included anyway.

A header and a *.cpp is equally simple to me.
 

#define fpl_globalvar static
#define fpl_inline inline
#define fpl_internal static
#if FPL_API_AS_PRIVATE
#    define fpl_api static
#else
#    define fpl_api extern

This is unnecessary code to me, it doesn't tell much to the reader and these have no functional benefit, actually it is wrong:
if I "fpl_globalvar int x;" in a *.cpp this will actually make the variable "private" to that *.cpp, making it inaccessible to other files.

Share this post


Link to post
Share on other sites
40 minutes ago, ongamex92 said:

I do not know a person or a product that does not link with CRT. Starting form small games, 3D DCCs, even some "serious software" uses it.
 

A header and a *.cpp is equally simple to me.
 


#define fpl_globalvar static
#define fpl_inline inline
#define fpl_internal static
#if FPL_API_AS_PRIVATE
#    define fpl_api static
#else
#    define fpl_api extern

This is unnecessary code to me, it doesn't tell much to the reader and these have no functional benefit, actually it is wrong:
if I "fpl_globalvar int x;" in a *.cpp this will actually make the variable "private" to that *.cpp, making it inaccessible to other files.

Insomniac Games and EA for example do not use the C standard library, they even dont use the STL.

Also there are others people in the industry who do not use the C standard library as well.

For myself, i dont care but its great when you have the option to not need to ship the C Runtime with your game (VC++ redist for example).

 

Static has more than one meaning, private scope for this file only, local persisting state, global variable, class field in c++, etc. Even though it just mean: Private in this file only. Techniqually there are no differences between all of them, but this way you see right away which scope its meant to be in.

 

Right, there are no difference between header and source files. C++ has no definition of a file, you just have translation units which gets compiled to object files - thats it. I dont want to force users to have a separate translation unit, just for the library - if they want, they can have one but its not required.

Edited by Finalspace

Share this post


Link to post
Share on other sites

It seems that this library is mostly useless for most people here, but for me its very useful and educational.

Edited by Finalspace

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement