1 /**
2  * Copyright (c) 2006-2019 LOVE Development Team
3  *
4  * This software is provided 'as-is', without any express or implied
5  * warranty.  In no event will the authors be held liable for any damages
6  * arising from the use of this software.
7  *
8  * Permission is granted to anyone to use this software for any purpose,
9  * including commercial applications, and to alter it and redistribute it
10  * freely, subject to the following restrictions:
11  *
12  * 1. The origin of this software must not be misrepresented; you must not
13  *    claim that you wrote the original software. If you use this software
14  *    in a product, an acknowledgment in the product documentation would be
15  *    appreciated but is not required.
16  * 2. Altered source versions must be plainly marked as such, and must not be
17  *    misrepresented as being the original software.
18  * 3. This notice may not be removed or altered from any source distribution.
19  **/
20 
21 #include "common/config.h"
22 #include "deprecation.h"
23 #include "thread/threads.h"
24 
25 #include <atomic>
26 #include <map>
27 
28 namespace love
29 {
30 
31 static std::map<std::string, DeprecationInfo> *deprecated = nullptr;
32 static std::vector<const DeprecationInfo *> *deprecatedList = nullptr;
33 
34 static std::atomic<int> initCount;
35 
36 static thread::Mutex *mutex = nullptr;
37 static bool outputEnabled = false;
38 
initDeprecation()39 void initDeprecation()
40 {
41 	if (initCount.fetch_add(1) == 0)
42 	{
43 		mutex = thread::newMutex();
44 
45 		// These are heap-allocated because we want to clear them on deinit,
46 		// and deinit may be called when the program is shutting down in the
47 		// middle of static variable cleanup (eg in the Math module destructor).
48 		// Calling std::map::clear() in that case was causing segfaults.
49 		deprecated = new std::map<std::string, DeprecationInfo>();
50 		deprecatedList = new std::vector<const DeprecationInfo *>();
51 	}
52 }
53 
deinitDeprecation()54 void deinitDeprecation()
55 {
56 	if (initCount.fetch_sub(1) == 1)
57 	{
58 		delete deprecated;
59 		delete deprecatedList;
60 		delete mutex;
61 
62 		deprecated = nullptr;
63 		deprecatedList = nullptr;
64 		mutex = nullptr;
65 	}
66 }
67 
printDeprecationNotice(const DeprecationInfo & info)68 static void printDeprecationNotice(const DeprecationInfo &info)
69 {
70 	std::string notice = getDeprecationNotice(info, true);
71 	printf("LOVE - Warning: %s\n", notice.c_str());
72 }
73 
setDeprecationOutputEnabled(bool enable)74 void setDeprecationOutputEnabled(bool enable)
75 {
76 	if (enable == outputEnabled)
77 		return;
78 
79 	outputEnabled = enable;
80 
81 	if (enable)
82 	{
83 		GetDeprecated deprecated;
84 
85 		for (const DeprecationInfo *info : deprecated.all)
86 		{
87 			if (info->uses == 1)
88 				printDeprecationNotice(*info);
89 		}
90 	}
91 }
92 
isDeprecationOutputEnabled()93 bool isDeprecationOutputEnabled()
94 {
95 	return outputEnabled;
96 }
97 
getDeprecationNotice(const DeprecationInfo & info,bool usewhere)98 std::string getDeprecationNotice(const DeprecationInfo &info, bool usewhere)
99 {
100 	std::string notice;
101 
102 	if (usewhere)
103 		notice += info.where;
104 
105 	notice += "Using deprecated ";
106 
107 	if (info.apiType == API_FUNCTION)
108 		notice += "function ";
109 	else if (info.apiType == API_METHOD)
110 		notice += "method ";
111 	else if (info.apiType == API_FIELD)
112 		notice += "field ";
113 	else if (info.apiType == API_CONSTANT)
114 		notice += "constant ";
115 	else
116 		notice += "API ";
117 
118 	notice += info.name;
119 
120 	if (info.type == DEPRECATED_REPLACED && !info.replacement.empty())
121 		notice += " (replaced by " + info.replacement + ")";
122 	else if (info.type == DEPRECATED_RENAMED && !info.replacement.empty())
123 		notice += " (renamed to " + info.replacement + ")";
124 
125 	return notice;
126 }
127 
GetDeprecated()128 GetDeprecated::GetDeprecated()
129 	: all(*deprecatedList)
130 {
131 	if (mutex != nullptr)
132 		mutex->lock();
133 }
134 
~GetDeprecated()135 GetDeprecated::~GetDeprecated()
136 {
137 	if (mutex != nullptr)
138 		mutex->unlock();
139 }
140 
MarkDeprecated(const char * name,APIType api)141 MarkDeprecated::MarkDeprecated(const char *name, APIType api)
142 	: MarkDeprecated(name, api, DEPRECATED_NO_REPLACEMENT, nullptr)
143 {
144 }
145 
MarkDeprecated(const char * name,APIType api,DeprecationType type,const char * replacement)146 MarkDeprecated::MarkDeprecated(const char *name, APIType api, DeprecationType type, const char *replacement)
147 	: info(nullptr)
148 {
149 	if (mutex != nullptr)
150 		mutex->lock();
151 
152 	auto it = deprecated->find(name);
153 
154 	if (it != deprecated->end())
155 	{
156 		it->second.uses++;
157 		info = &it->second;
158 	}
159 	else
160 	{
161 		DeprecationInfo newinfo = {};
162 
163 		newinfo.type = type;
164 		newinfo.apiType = api;
165 		newinfo.uses = 1;
166 		newinfo.name = name;
167 
168 		if (replacement != nullptr)
169 			newinfo.replacement = replacement;
170 
171 		auto inserted = deprecated->insert(std::make_pair(newinfo.name, newinfo));
172 
173 		info = &inserted.first->second;
174 		deprecatedList->push_back(info);
175 	}
176 }
177 
~MarkDeprecated()178 MarkDeprecated::~MarkDeprecated()
179 {
180 	if (outputEnabled && info != nullptr && info->uses == 1)
181 		printDeprecationNotice(*info);
182 
183 	if (mutex != nullptr)
184 		mutex->unlock();
185 }
186 
187 } // love
188