Line data Source code
1 : /* Copyright (C) 2021 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
4 : * 0 A.D. is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * 0 A.D. is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 :
19 : #include "precompiled.h"
20 :
21 : #include "ScriptExceptions.h"
22 :
23 : #include "ps/CLogger.h"
24 : #include "ps/CStr.h"
25 : #include "scriptinterface/FunctionWrapper.h"
26 : #include "scriptinterface/ScriptRequest.h"
27 :
28 1292 : bool ScriptException::IsPending(const ScriptRequest& rq)
29 : {
30 1292 : return JS_IsExceptionPending(rq.cx);
31 : }
32 :
33 253 : bool ScriptException::CatchPending(const ScriptRequest& rq)
34 : {
35 253 : if (!JS_IsExceptionPending(rq.cx))
36 244 : return false;
37 :
38 18 : JS::RootedValue excn(rq.cx);
39 9 : ENSURE(JS_GetPendingException(rq.cx, &excn));
40 9 : JS_ClearPendingException(rq.cx);
41 :
42 9 : if (excn.isUndefined())
43 : {
44 0 : LOGERROR("JavaScript error: (undefined)");
45 0 : return true;
46 : }
47 :
48 : // As of SM45/52, there is no way to recover a stack in case the thrown thing is not an Error object.
49 9 : if (!excn.isObject())
50 : {
51 0 : CStr error;
52 0 : Script::FromJSVal(rq, excn, error);
53 0 : LOGERROR("JavaScript error: %s", error);
54 0 : return true;
55 : }
56 :
57 18 : JS::RootedObject excnObj(rq.cx, &excn.toObject());
58 9 : JSErrorReport* report = JS_ErrorFromException(rq.cx, excnObj);
59 :
60 9 : if (!report)
61 : {
62 0 : CStr error;
63 0 : Script::FromJSVal(rq, excn, error);
64 0 : LOGERROR("JavaScript error: %s", error);
65 0 : return true;
66 : }
67 :
68 18 : std::stringstream msg;
69 9 : msg << "JavaScript error: ";
70 9 : if (report->filename)
71 : {
72 2 : msg << report->filename;
73 2 : msg << " line " << report->lineno << "\n";
74 : }
75 :
76 9 : msg << report->message().c_str();
77 :
78 18 : JS::RootedObject stackObj(rq.cx, ExceptionStackOrNull(excnObj));
79 18 : JS::RootedValue stackVal(rq.cx, JS::ObjectOrNullValue(stackObj));
80 :
81 9 : if (!stackVal.isNull())
82 : {
83 0 : std::string stackText;
84 0 : ScriptFunction::Call(rq, stackVal, "toString", stackText);
85 :
86 0 : std::istringstream stream(stackText);
87 0 : for (std::string line; std::getline(stream, line);)
88 0 : msg << "\n " << line;
89 : }
90 :
91 9 : LOGERROR("%s", msg.str().c_str());
92 9 : return true;
93 : }
94 :
95 3 : void ScriptException::Raise(const ScriptRequest& rq, const char* format, ...)
96 : {
97 : va_list ap;
98 3 : va_start(ap, format);
99 : // SM is single-threaded, so a static thread_local buffer needs no locking.
100 : thread_local static char buffer[256];
101 3 : vsprintf_s(buffer, ARRAY_SIZE(buffer), format, ap);
102 3 : va_end(ap);
103 : // Rather annoyingly, there are no va_list versions of this function, hence the preformatting above.
104 3 : JS_ReportErrorUTF8(rq.cx, "%s", buffer);
105 3 : }
|