LCOV - code coverage report
Current view: top level - gjs - deprecation.cpp (source / functions) Hit Total Coverage
Test: gjs- Code Coverage Lines: 42 50 84.0 %
Date: 2024-02-27 17:05:05 Functions: 6 7 85.7 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 11 24 45.8 %

           Branch data     Line data    Source code
       1                 :            : /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
       2                 :            : // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
       3                 :            : // SPDX-FileCopyrightText: 2018 Philip Chimento <philip.chimento@gmail.com>
       4                 :            : 
       5                 :            : #include <config.h>
       6                 :            : 
       7                 :            : #include <cstddef>        // for size_t
       8                 :            : #include <functional>     // for hash<int>
       9                 :            : #include <sstream>
      10                 :            : #include <string>         // for string
      11                 :            : #include <string_view>
      12                 :            : #include <unordered_set>  // for unordered_set
      13                 :            : #include <utility>        // for move
      14                 :            : #include <vector>
      15                 :            : 
      16                 :            : #include <glib.h>  // for g_warning
      17                 :            : 
      18                 :            : #include <js/CharacterEncoding.h>
      19                 :            : #include <js/Conversions.h>
      20                 :            : #include <js/RootingAPI.h>
      21                 :            : #include <js/Stack.h>  // for CaptureCurrentStack, MaxFrames
      22                 :            : #include <js/TypeDecls.h>
      23                 :            : #include <js/Utility.h>  // for UniqueChars
      24                 :            : #include <js/Value.h>
      25                 :            : #include <js/friend/DumpFunctions.h>
      26                 :            : 
      27                 :            : #include "gjs/deprecation.h"
      28                 :            : #include "gjs/macros.h"
      29                 :            : 
      30                 :            : const char* messages[] = {
      31                 :            :     // None:
      32                 :            :     "(invalid message)",
      33                 :            : 
      34                 :            :     // ByteArrayInstanceToString:
      35                 :            :     "Some code called array.toString() on a Uint8Array instance. Previously "
      36                 :            :     "this would have interpreted the bytes of the array as a string, but that "
      37                 :            :     "is nonstandard. In the future this will return the bytes as "
      38                 :            :     "comma-separated digits. For the time being, the old behavior has been "
      39                 :            :     "preserved, but please fix your code anyway to use TextDecoder.\n"
      40                 :            :     "(Note that array.toString() may have been called implicitly.)",
      41                 :            : 
      42                 :            :     // DeprecatedGObjectProperty:
      43                 :            :     "The GObject property {}.{} is deprecated.",
      44                 :            : 
      45                 :            :     // ModuleExportedLetOrConst:
      46                 :            :     "Some code accessed the property '{}' on the module '{}'. That property "
      47                 :            :     "was defined with 'let' or 'const' inside the module. This was previously "
      48                 :            :     "supported, but is not correct according to the ES6 standard. Any symbols "
      49                 :            :     "to be exported from a module must be defined with 'var'. The property "
      50                 :            :     "access will work as previously for the time being, but please fix your "
      51                 :            :     "code anyway.",
      52                 :            : };
      53                 :            : 
      54                 :            : struct DeprecationEntry {
      55                 :            :     GjsDeprecationMessageId id;
      56                 :            :     std::string loc;
      57                 :            : 
      58                 :          4 :     DeprecationEntry(GjsDeprecationMessageId an_id, const char* a_loc)
      59                 :          8 :         : id(an_id), loc(a_loc) {}
      60                 :            : 
      61                 :          0 :     bool operator==(const DeprecationEntry& other) const {
      62   [ #  #  #  # ]:          0 :         return id == other.id && loc == other.loc;
      63                 :            :     }
      64                 :            : };
      65                 :            : 
      66                 :            : namespace std {
      67                 :            : template <>
      68                 :            : struct hash<DeprecationEntry> {
      69                 :          6 :     size_t operator()(const DeprecationEntry& key) const {
      70                 :          6 :         return hash<int>()(key.id) ^ hash<std::string>()(key.loc);
      71                 :            :     }
      72                 :            : };
      73                 :            : };  // namespace std
      74                 :            : 
      75                 :            : static std::unordered_set<DeprecationEntry> logged_messages;
      76                 :            : 
      77                 :            : GJS_JSAPI_RETURN_CONVENTION
      78                 :          4 : static JS::UniqueChars get_callsite(JSContext* cx) {
      79                 :          4 :     JS::RootedObject stack_frame(cx);
      80                 :          4 :     if (!JS::CaptureCurrentStack(cx, &stack_frame,
      81   [ +  -  +  -  :         12 :                                  JS::StackCapture(JS::MaxFrames(1))) ||
                   -  + ]
      82         [ -  + ]:          4 :         !stack_frame)
      83                 :          0 :         return nullptr;
      84                 :            : 
      85                 :          4 :     JS::RootedValue v_frame(cx, JS::ObjectValue(*stack_frame));
      86                 :          4 :     JS::RootedString frame_string(cx, JS::ToString(cx, v_frame));
      87         [ -  + ]:          4 :     if (!frame_string)
      88                 :          0 :         return nullptr;
      89                 :            : 
      90                 :          4 :     return JS_EncodeStringToUTF8(cx, frame_string);
      91                 :          4 : }
      92                 :            : 
      93                 :          4 : static void warn_deprecated_unsafe_internal(JSContext* cx,
      94                 :            :                                             const GjsDeprecationMessageId id,
      95                 :            :                                             const char* msg) {
      96                 :          4 :     JS::UniqueChars callsite(get_callsite(cx));
      97                 :          4 :     DeprecationEntry entry(id, callsite.get());
      98         [ +  - ]:          4 :     if (!logged_messages.count(entry)) {
      99                 :            :         JS::UniqueChars stack_dump =
     100                 :          4 :             JS::FormatStackDump(cx, false, false, false);
     101                 :          4 :         g_warning("%s\n%s", msg, stack_dump.get());
     102                 :          4 :         logged_messages.insert(std::move(entry));
     103                 :          4 :     }
     104                 :          4 : }
     105                 :            : 
     106                 :            : /* Note, this can only be called from the JS thread because it uses the full
     107                 :            :  * stack dump API and not the "safe" gjs_dumpstack() which can only print to
     108                 :            :  * stdout or stderr. Do not use this function during GC, for example. */
     109                 :          2 : void _gjs_warn_deprecated_once_per_callsite(JSContext* cx,
     110                 :            :                                             const GjsDeprecationMessageId id) {
     111                 :          2 :     warn_deprecated_unsafe_internal(cx, id, messages[id]);
     112                 :          2 : }
     113                 :            : 
     114                 :          2 : void _gjs_warn_deprecated_once_per_callsite(
     115                 :            :     JSContext* cx, GjsDeprecationMessageId id,
     116                 :            :     const std::vector<const char*>& args) {
     117                 :            :     // In C++20, use std::format() for this
     118                 :          2 :     std::string_view format_string{messages[id]};
     119                 :          2 :     std::stringstream message;
     120                 :            : 
     121                 :          2 :     size_t pos = 0;
     122                 :          2 :     size_t copied = 0;
     123                 :          2 :     size_t args_ptr = 0;
     124                 :          2 :     size_t nargs_given = args.size();
     125                 :            : 
     126         [ +  + ]:          6 :     while ((pos = format_string.find("{}", pos)) != std::string::npos) {
     127         [ -  + ]:          4 :         if (args_ptr >= nargs_given) {
     128                 :          0 :             g_critical("Only %zu format args passed for message ID %u",
     129                 :            :                        nargs_given, unsigned{id});
     130                 :          0 :             return;
     131                 :            :         }
     132                 :            : 
     133                 :          4 :         message << format_string.substr(copied, pos - copied);
     134                 :          4 :         message << args[args_ptr++];
     135                 :          4 :         pos = copied = pos + 2;  // skip over braces
     136                 :            :     }
     137         [ -  + ]:          2 :     if (args_ptr != nargs_given) {
     138                 :          0 :         g_critical("Excess %zu format args passed for message ID %u",
     139                 :            :                    nargs_given, unsigned{id});
     140                 :          0 :         return;
     141                 :            :     }
     142                 :            : 
     143                 :          2 :     message << format_string.substr(copied, std::string::npos);
     144                 :            : 
     145                 :          2 :     warn_deprecated_unsafe_internal(cx, id, message.str().c_str());
     146         [ +  - ]:          2 : }

Generated by: LCOV version 1.14