2026-05-12 10:32:42 -07:00
|
|
|
/* Copyright (C) 2026 Wildfire Games.
|
2023-12-02 16:30:12 -08:00
|
|
|
* This file is part of 0 A.D.
|
2010-01-09 11:20:14 -08:00
|
|
|
*
|
2023-12-02 16:30:12 -08:00
|
|
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
2010-01-09 11:20:14 -08:00
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
2023-12-02 16:30:12 -08:00
|
|
|
* 0 A.D. is distributed in the hope that it will be useful,
|
2010-01-09 11:20:14 -08:00
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
2023-12-02 16:30:12 -08:00
|
|
|
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
2010-01-09 11:20:14 -08:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "precompiled.h"
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
#include "Interface.h"
|
2010-01-09 11:20:14 -08:00
|
|
|
|
|
|
|
|
#include "lib/debug.h"
|
2025-08-04 10:04:30 -07:00
|
|
|
#include "lib/file/vfs/vfs_path.h"
|
2025-08-03 08:30:17 -07:00
|
|
|
#include "lib/file/vfs/vfs_util.h"
|
2010-08-04 14:15:41 -07:00
|
|
|
#include "lib/utf8.h"
|
2010-01-09 11:20:14 -08:00
|
|
|
#include "ps/CLogger.h"
|
2025-08-04 10:04:30 -07:00
|
|
|
#include "ps/CStr.h"
|
2011-01-12 04:29:00 -08:00
|
|
|
#include "ps/Filesystem.h"
|
2010-05-05 15:36:35 -07:00
|
|
|
#include "ps/Profile.h"
|
2025-08-04 10:04:30 -07:00
|
|
|
#include "ps/Profiler2.h"
|
|
|
|
|
#include "ps/ThreadUtil.h"
|
2023-07-07 13:12:16 -07:00
|
|
|
#include "scriptinterface/FunctionWrapper.h"
|
2025-01-15 11:38:37 -08:00
|
|
|
#include "scriptinterface/ModuleLoader.h"
|
2023-07-07 13:12:16 -07:00
|
|
|
#include "scriptinterface/Object.h"
|
2026-05-12 10:32:42 -07:00
|
|
|
#include "scriptinterface/Context.h"
|
2023-07-07 13:12:16 -07:00
|
|
|
#include "scriptinterface/ScriptStats.h"
|
|
|
|
|
#include "scriptinterface/StructuredClone.h"
|
2010-01-09 11:20:14 -08:00
|
|
|
|
2025-08-04 10:04:30 -07:00
|
|
|
#include <cstdio>
|
|
|
|
|
#include <cstring>
|
|
|
|
|
#include <js/CallAndConstruct.h>
|
|
|
|
|
#include <js/Class.h>
|
|
|
|
|
#include <js/ComparisonOperators.h>
|
|
|
|
|
#include <js/CompilationAndEvaluation.h>
|
|
|
|
|
#include <js/CompileOptions.h>
|
|
|
|
|
#include <js/GCVector.h>
|
|
|
|
|
#include <js/GlobalObject.h>
|
|
|
|
|
#include <js/PropertyAndElement.h>
|
|
|
|
|
#include <js/PropertyDescriptor.h>
|
|
|
|
|
#include <js/Realm.h>
|
|
|
|
|
#include <js/RealmOptions.h>
|
|
|
|
|
#include <js/SourceText.h>
|
|
|
|
|
#include <jsapi.h>
|
|
|
|
|
#include <mozilla/Maybe.h>
|
2021-03-03 14:26:49 -08:00
|
|
|
#include <string>
|
2025-08-04 10:04:30 -07:00
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
namespace JS { class Compartment; }
|
|
|
|
|
namespace mozilla { union Utf8Unit; }
|
2010-01-09 11:20:14 -08:00
|
|
|
|
2011-02-21 01:23:51 -08:00
|
|
|
#define BOOST_MULTI_INDEX_DISABLE_SERIALIZATION
|
2010-01-09 11:20:14 -08:00
|
|
|
#include <boost/preprocessor/punctuation/comma_if.hpp>
|
|
|
|
|
#include <boost/preprocessor/repetition/repeat.hpp>
|
2010-05-21 17:38:33 -07:00
|
|
|
#include <boost/random/linear_congruential.hpp>
|
2011-02-20 18:16:12 -08:00
|
|
|
#include <boost/flyweight.hpp>
|
|
|
|
|
#include <boost/flyweight/key_value.hpp>
|
|
|
|
|
#include <boost/flyweight/no_locking.hpp>
|
|
|
|
|
#include <boost/flyweight/no_tracking.hpp>
|
2010-01-09 11:20:14 -08:00
|
|
|
|
2012-02-29 19:55:05 -08:00
|
|
|
/**
|
|
|
|
|
* @file
|
|
|
|
|
* Abstractions of various SpiderMonkey features.
|
|
|
|
|
* Engine code should be using functions of these interfaces rather than
|
|
|
|
|
* directly accessing the underlying JS api.
|
|
|
|
|
*/
|
|
|
|
|
|
2010-01-09 11:20:14 -08:00
|
|
|
struct ScriptInterface_impl
|
|
|
|
|
{
|
2026-05-12 10:32:42 -07:00
|
|
|
ScriptInterface_impl(const char* nativeScopeName, Script::Context& context,
|
2025-02-05 06:15:53 -08:00
|
|
|
JS::Compartment* compartment, std::function<bool(const VfsPath&)> allowModule);
|
2010-01-09 11:20:14 -08:00
|
|
|
~ScriptInterface_impl();
|
|
|
|
|
|
2015-01-24 06:46:52 -08:00
|
|
|
// Take care to keep this declaration before heap rooted members. Destructors of heap rooted
|
2020-11-14 02:57:50 -08:00
|
|
|
// members have to be called before the context destructor.
|
2026-05-12 10:32:42 -07:00
|
|
|
Script::Context& m_context;
|
2015-01-24 06:46:52 -08:00
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
friend Script::Request;
|
2020-11-13 05:18:22 -08:00
|
|
|
private:
|
|
|
|
|
JSContext* m_cx;
|
2020-11-14 00:46:32 -08:00
|
|
|
JS::PersistentRootedObject m_glob; // global scope object
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
boost::rand48* m_rng;
|
|
|
|
|
JS::PersistentRootedObject m_nativeScope; // native function scope object
|
2025-01-15 11:38:37 -08:00
|
|
|
Script::ModuleLoader m_ModuleLoader;
|
2010-01-09 11:20:14 -08:00
|
|
|
};
|
|
|
|
|
|
2021-05-03 09:07:26 -07:00
|
|
|
/**
|
2026-05-12 10:32:42 -07:00
|
|
|
* Constructor for Script::Request - here because it needs access into ScriptInterface_impl.
|
2021-05-03 09:07:26 -07:00
|
|
|
*/
|
2026-05-12 10:32:42 -07:00
|
|
|
Script::Request::Request(const Script::Interface& scriptInterface) :
|
2021-05-15 06:54:58 -07:00
|
|
|
cx(scriptInterface.m->m_cx),
|
|
|
|
|
glob(scriptInterface.m->m_glob),
|
|
|
|
|
nativeScope(scriptInterface.m->m_nativeScope),
|
|
|
|
|
m_ScriptInterface(scriptInterface)
|
2020-11-13 05:18:22 -08:00
|
|
|
{
|
2021-05-15 06:54:58 -07:00
|
|
|
m_FormerRealm = JS::EnterRealm(cx, scriptInterface.m->m_glob);
|
2020-11-14 00:46:32 -08:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
Script::Request::~Request()
|
2020-11-14 00:46:32 -08:00
|
|
|
{
|
2021-05-15 06:54:58 -07:00
|
|
|
JS::LeaveRealm(cx, m_FormerRealm);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
Script::Request::Request(JSContext* cx) : Script::Request(Script::Interface::CmptPrivate::GetScriptInterface(cx))
|
2021-05-15 06:54:58 -07:00
|
|
|
{
|
2020-11-13 05:18:22 -08:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
JS::Value Script::Request::globalValue() const
|
2020-11-13 05:18:22 -08:00
|
|
|
{
|
2021-05-03 09:07:26 -07:00
|
|
|
return JS::ObjectValue(*glob);
|
2020-11-13 05:18:22 -08:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
const Script::Interface& Script::Request::GetScriptInterface() const
|
2021-05-15 06:54:58 -07:00
|
|
|
{
|
|
|
|
|
return m_ScriptInterface;
|
|
|
|
|
}
|
2021-05-03 09:07:26 -07:00
|
|
|
|
2010-01-09 11:20:14 -08:00
|
|
|
namespace
|
|
|
|
|
{
|
|
|
|
|
|
2020-11-18 06:39:04 -08:00
|
|
|
JSClassOps global_classops = {
|
2016-09-02 09:26:54 -07:00
|
|
|
nullptr, nullptr,
|
|
|
|
|
nullptr, nullptr,
|
|
|
|
|
nullptr, nullptr, nullptr,
|
2024-10-25 01:24:14 -07:00
|
|
|
nullptr, nullptr,
|
2015-01-24 06:46:52 -08:00
|
|
|
JS_GlobalObjectTraceHook
|
2010-01-09 11:20:14 -08:00
|
|
|
};
|
|
|
|
|
|
2020-11-18 06:39:04 -08:00
|
|
|
JSClass global_class = {
|
|
|
|
|
"global", JSCLASS_GLOBAL_FLAGS, &global_classops
|
|
|
|
|
};
|
|
|
|
|
|
2010-01-09 11:20:14 -08:00
|
|
|
// Functions in the global namespace:
|
|
|
|
|
|
2017-08-28 03:27:36 -07:00
|
|
|
bool print(JSContext* cx, uint argc, JS::Value* vp)
|
2010-01-09 11:20:14 -08:00
|
|
|
{
|
2014-07-12 09:55:09 -07:00
|
|
|
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
|
2026-05-12 10:32:42 -07:00
|
|
|
Script::Request rq(cx);
|
2020-11-13 05:18:22 -08:00
|
|
|
|
2014-07-12 09:55:09 -07:00
|
|
|
for (uint i = 0; i < args.length(); ++i)
|
2010-01-09 11:20:14 -08:00
|
|
|
{
|
2013-11-10 08:13:40 -08:00
|
|
|
std::wstring str;
|
2021-05-13 02:43:33 -07:00
|
|
|
if (!Script::FromJSVal(rq, args[i], str))
|
2015-01-24 06:46:52 -08:00
|
|
|
return false;
|
2015-02-13 17:45:13 -08:00
|
|
|
debug_printf("%s", utf8_from_wstring(str).c_str());
|
2010-01-09 11:20:14 -08:00
|
|
|
}
|
|
|
|
|
fflush(stdout);
|
2014-07-12 09:55:09 -07:00
|
|
|
args.rval().setUndefined();
|
2015-01-24 06:46:52 -08:00
|
|
|
return true;
|
2010-05-31 15:44:59 -07:00
|
|
|
}
|
|
|
|
|
|
2017-08-28 03:27:36 -07:00
|
|
|
bool logmsg(JSContext* cx, uint argc, JS::Value* vp)
|
2010-07-06 13:31:05 -07:00
|
|
|
{
|
2014-07-12 09:55:09 -07:00
|
|
|
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
|
|
|
|
|
if (args.length() < 1)
|
2010-08-23 07:30:13 -07:00
|
|
|
{
|
2014-07-12 09:55:09 -07:00
|
|
|
args.rval().setUndefined();
|
2015-01-24 06:46:52 -08:00
|
|
|
return true;
|
2010-08-23 07:30:13 -07:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
Script::Request rq(cx);
|
2010-07-06 13:31:05 -07:00
|
|
|
std::wstring str;
|
2021-05-13 02:43:33 -07:00
|
|
|
if (!Script::FromJSVal(rq, args[0], str))
|
2015-01-24 06:46:52 -08:00
|
|
|
return false;
|
2015-01-22 12:37:38 -08:00
|
|
|
LOGMESSAGE("%s", utf8_from_wstring(str));
|
2014-07-12 09:55:09 -07:00
|
|
|
args.rval().setUndefined();
|
2015-01-24 06:46:52 -08:00
|
|
|
return true;
|
2010-07-06 13:31:05 -07:00
|
|
|
}
|
|
|
|
|
|
2017-08-28 03:27:36 -07:00
|
|
|
bool warn(JSContext* cx, uint argc, JS::Value* vp)
|
2010-05-31 15:44:59 -07:00
|
|
|
{
|
2014-07-12 09:55:09 -07:00
|
|
|
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
|
|
|
|
|
if (args.length() < 1)
|
2010-08-23 07:30:13 -07:00
|
|
|
{
|
2014-07-12 09:55:09 -07:00
|
|
|
args.rval().setUndefined();
|
2015-01-24 06:46:52 -08:00
|
|
|
return true;
|
2010-08-23 07:30:13 -07:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
Script::Request rq(cx);
|
2010-05-31 15:44:59 -07:00
|
|
|
std::wstring str;
|
2021-05-13 02:43:33 -07:00
|
|
|
if (!Script::FromJSVal(rq, args[0], str))
|
2015-01-24 06:46:52 -08:00
|
|
|
return false;
|
2015-01-22 12:37:38 -08:00
|
|
|
LOGWARNING("%s", utf8_from_wstring(str));
|
2014-07-12 09:55:09 -07:00
|
|
|
args.rval().setUndefined();
|
2015-01-24 06:46:52 -08:00
|
|
|
return true;
|
2010-05-31 15:44:59 -07:00
|
|
|
}
|
|
|
|
|
|
2017-08-28 03:27:36 -07:00
|
|
|
bool error(JSContext* cx, uint argc, JS::Value* vp)
|
2010-05-31 15:44:59 -07:00
|
|
|
{
|
2014-07-12 09:55:09 -07:00
|
|
|
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
|
|
|
|
|
if (args.length() < 1)
|
2010-08-23 07:30:13 -07:00
|
|
|
{
|
2014-07-12 09:55:09 -07:00
|
|
|
args.rval().setUndefined();
|
2015-01-24 06:46:52 -08:00
|
|
|
return true;
|
2010-08-23 07:30:13 -07:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
Script::Request rq(cx);
|
2010-05-31 15:44:59 -07:00
|
|
|
std::wstring str;
|
2021-05-13 02:43:33 -07:00
|
|
|
if (!Script::FromJSVal(rq, args[0], str))
|
2015-01-24 06:46:52 -08:00
|
|
|
return false;
|
2015-01-22 12:37:38 -08:00
|
|
|
LOGERROR("%s", utf8_from_wstring(str));
|
2014-07-12 09:55:09 -07:00
|
|
|
args.rval().setUndefined();
|
2015-01-24 06:46:52 -08:00
|
|
|
return true;
|
2010-01-09 11:20:14 -08:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
JS::Value deepcopy(const Script::Request& rq, JS::HandleValue val)
|
2011-05-12 16:50:42 -07:00
|
|
|
{
|
2021-03-02 12:01:14 -08:00
|
|
|
if (val.isNullOrUndefined())
|
2011-05-12 16:50:42 -07:00
|
|
|
{
|
2021-03-02 12:01:14 -08:00
|
|
|
ScriptException::Raise(rq, "deepcopy requires one argument.");
|
|
|
|
|
return JS::UndefinedValue();
|
2011-05-12 16:50:42 -07:00
|
|
|
}
|
|
|
|
|
|
2021-05-10 04:51:32 -07:00
|
|
|
JS::RootedValue ret(rq.cx, Script::DeepCopy(rq, val));
|
|
|
|
|
if (ret.isNullOrUndefined())
|
2021-03-02 12:01:14 -08:00
|
|
|
{
|
|
|
|
|
ScriptException::Raise(rq, "deepcopy StructureClone copy failed.");
|
|
|
|
|
return JS::UndefinedValue();
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
2011-05-12 16:50:42 -07:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
JS::Value deepfreeze(const Script::Interface& scriptInterface, JS::HandleValue val)
|
2017-09-03 05:50:45 -07:00
|
|
|
{
|
2026-05-12 10:32:42 -07:00
|
|
|
Script::Request rq(scriptInterface);
|
2021-03-02 12:01:14 -08:00
|
|
|
if (!val.isObject())
|
2017-09-03 05:50:45 -07:00
|
|
|
{
|
Improve JS Exception handling.
- Check for pending exceptions after function calls and script
executions.
- Call LOGERROR instead of JS_ReportError when there is a conversion
error in FromJSVal, since that can only be called from C++ (where JS
errors don't really make sense). Instead, C++ callers of FromJSVal
should handle the failure and, themselves, either report an error or
simply do something else.
- Wrap JS_ReportError since that makes updating it later easier.
This isn't a systematical fix since ToJSVal also ought return a boolean
for failures, and we probably should trigger errors instead of warnings
on 'implicit' conversions, rather a preparation diff.
Part of the SM52 migration, stage: SM45 compatible (actually SM52
incompatible, too).
Based on a patch by: Itms
Comments by: Vladislavbelov, Stan`
Refs #742, #4893
Differential Revision: https://code.wildfiregames.com/D3093
This was SVN commit r24187.
2020-11-15 10:29:17 -08:00
|
|
|
ScriptException::Raise(rq, "deepfreeze requires exactly one object as an argument.");
|
2021-03-02 12:01:14 -08:00
|
|
|
return JS::UndefinedValue();
|
2017-09-03 05:50:45 -07:00
|
|
|
}
|
|
|
|
|
|
2024-11-17 07:25:17 -08:00
|
|
|
Script::DeepFreezeObject(rq, val);
|
2021-03-02 12:01:14 -08:00
|
|
|
return val;
|
2017-09-03 05:50:45 -07:00
|
|
|
}
|
|
|
|
|
|
2021-03-02 12:01:14 -08:00
|
|
|
void ProfileStart(const std::string& regionName)
|
2011-03-02 16:16:14 -08:00
|
|
|
{
|
2011-11-29 12:32:43 -08:00
|
|
|
const char* name = "(ProfileStart)";
|
2011-03-02 16:16:14 -08:00
|
|
|
|
2021-03-02 12:01:14 -08:00
|
|
|
typedef boost::flyweight<
|
|
|
|
|
std::string,
|
|
|
|
|
boost::flyweights::no_tracking,
|
|
|
|
|
boost::flyweights::no_locking
|
|
|
|
|
> StringFlyweight;
|
2011-03-02 16:16:14 -08:00
|
|
|
|
2021-03-02 12:01:14 -08:00
|
|
|
if (!regionName.empty())
|
|
|
|
|
name = StringFlyweight(regionName).get().c_str();
|
2011-03-02 16:16:14 -08:00
|
|
|
|
2021-01-10 00:39:54 -08:00
|
|
|
if (CProfileManager::IsInitialised() && Threading::IsMainThread())
|
2011-03-02 16:16:14 -08:00
|
|
|
g_Profiler.StartScript(name);
|
2011-11-29 12:32:43 -08:00
|
|
|
|
|
|
|
|
g_Profiler2.RecordRegionEnter(name);
|
2011-03-02 16:16:14 -08:00
|
|
|
}
|
|
|
|
|
|
2021-03-02 12:01:14 -08:00
|
|
|
void ProfileStop()
|
2011-03-02 16:16:14 -08:00
|
|
|
{
|
2021-01-10 00:39:54 -08:00
|
|
|
if (CProfileManager::IsInitialised() && Threading::IsMainThread())
|
2011-03-02 16:16:14 -08:00
|
|
|
g_Profiler.Stop();
|
2011-11-29 12:32:43 -08:00
|
|
|
|
2016-06-22 06:38:05 -07:00
|
|
|
g_Profiler2.RecordRegionLeave();
|
2011-03-02 16:16:14 -08:00
|
|
|
}
|
|
|
|
|
|
2021-03-02 12:01:14 -08:00
|
|
|
void ProfileAttribute(const std::string& attr)
|
2016-06-25 06:12:35 -07:00
|
|
|
{
|
|
|
|
|
const char* name = "(ProfileAttribute)";
|
|
|
|
|
|
2021-03-02 12:01:14 -08:00
|
|
|
typedef boost::flyweight<
|
|
|
|
|
std::string,
|
|
|
|
|
boost::flyweights::no_tracking,
|
|
|
|
|
boost::flyweights::no_locking
|
|
|
|
|
> StringFlyweight;
|
2016-06-25 06:12:35 -07:00
|
|
|
|
2021-03-02 12:01:14 -08:00
|
|
|
if (!attr.empty())
|
|
|
|
|
name = StringFlyweight(attr).get().c_str();
|
2016-06-25 06:12:35 -07:00
|
|
|
|
2016-06-26 09:51:52 -07:00
|
|
|
g_Profiler2.RecordAttribute("%s", name);
|
2016-06-25 06:12:35 -07:00
|
|
|
}
|
|
|
|
|
|
2010-05-21 17:38:33 -07:00
|
|
|
// Math override functions:
|
|
|
|
|
|
2012-05-13 16:40:06 -07:00
|
|
|
// boost::uniform_real is apparently buggy in Boost pre-1.47 - for integer generators
|
|
|
|
|
// it returns [min,max], not [min,max). The bug was fixed in 1.47.
|
|
|
|
|
// We need consistent behaviour, so manually implement the correct version:
|
|
|
|
|
static double generate_uniform_real(boost::rand48& rng, double min, double max)
|
|
|
|
|
{
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
double n = (double)(rng() - rng.min());
|
|
|
|
|
double d = (double)(rng.max() - rng.min()) + 1.0;
|
|
|
|
|
ENSURE(d > 0 && n >= 0 && n <= d);
|
|
|
|
|
double r = n / d * (max - min) + min;
|
|
|
|
|
if (r < max)
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-15 06:54:58 -07:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
bool Script::Interface::MathRandom(double& nbr) const
|
2010-05-21 17:38:33 -07:00
|
|
|
{
|
2021-05-15 06:54:58 -07:00
|
|
|
if (m->m_rng == nullptr)
|
2015-01-24 06:46:52 -08:00
|
|
|
return false;
|
2021-05-15 06:54:58 -07:00
|
|
|
nbr = generate_uniform_real(*(m->m_rng), 0.0, 1.0);
|
2015-01-24 06:46:52 -08:00
|
|
|
return true;
|
2010-05-21 17:38:33 -07:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
bool Script::Interface::Math_random(JSContext* cx, uint argc, JS::Value* vp)
|
2014-03-28 13:26:32 -07:00
|
|
|
{
|
2021-05-15 06:54:58 -07:00
|
|
|
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
|
|
|
|
|
double r;
|
2026-05-12 10:32:42 -07:00
|
|
|
if (!Script::Interface::CmptPrivate::GetScriptInterface(cx).MathRandom(r))
|
2014-03-28 13:26:32 -07:00
|
|
|
return false;
|
2021-05-15 06:54:58 -07:00
|
|
|
|
|
|
|
|
args.rval().setNumber(r);
|
2014-03-28 13:26:32 -07:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
ScriptInterface_impl::ScriptInterface_impl(const char* nativeScopeName, Script::Context& context,
|
2025-02-05 06:15:53 -08:00
|
|
|
JS::Compartment* compartment, std::function<bool(const VfsPath&)> allowModule) :
|
2023-11-19 12:13:19 -08:00
|
|
|
m_context(context), m_cx(context.GetGeneralJSContext()), m_glob(context.GetGeneralJSContext()),
|
2025-02-05 06:15:53 -08:00
|
|
|
m_nativeScope(context.GetGeneralJSContext()),
|
|
|
|
|
m_ModuleLoader{std::move(allowModule)}
|
2010-01-09 11:20:14 -08:00
|
|
|
{
|
2020-11-30 01:03:20 -08:00
|
|
|
JS::RealmCreationOptions creationOpt;
|
2016-09-02 09:48:41 -07:00
|
|
|
// Keep JIT code during non-shrinking GCs. This brings a quite big performance improvement.
|
2020-11-18 06:39:04 -08:00
|
|
|
creationOpt.setPreserveJitCode(true);
|
2020-12-06 06:03:02 -08:00
|
|
|
// Enable uneval
|
|
|
|
|
creationOpt.setToSourceEnabled(true);
|
Run the AI in the same Compartment as the simulation. Let the AI access Sim data.
This is a paradigm change for AI computation.
Historically, the AI was intended to be run in a separate thread from
the simulation. The idea was that slow AI wouldn't stop the renderer
from being smooth.
In that original design, the AI received a copy of the game world and
used that to run its logic. This meant the simulation could safely do
whatever it wanted in the meantime. This copy was done via AIProxy &
AIInterface.
This design ended up having significant flaws:
- The copying impacts the simulation negatively, particularly because
AIProxy subscribes to a lot of messages (sometimes sent exclusively to
it). This time cannot be threaded, and impacts MP games without AIs.
- Copying the data is increasingly difficult. Modifiers are a headache,
LOS is not implemented. Lots of logic is duplicated.
The intended benefits of the design also failed to realise somewhat:
- The AI was never threaded, and in fact, it is probably better to try
and thread Sim + AI from the renderer than just the AI, at which point
threading the AI specifically brings little benefit.
The new design is much simpler and straighforward, but this has some
side-effects:
- The AI can now change the simulation. This can be used for cheating,
or possibly for a tutorial AI.
- The AI runs in the same GC zone as the simulation, which may lead to
more frequent Sim GCs (but overall we might expect a reduction in
temporary objects).
- The AI state was essentially cached, so replacing some functions with
Engine.QueryInterface might be slower. The tradeoff should be balanced
by lower AIProxy computation times.
Future work:
- Threading some specific AI tasks could still be worthwhile, but should
be done in specific worker threads, allowed to run over several turns if
needed.
Technical note: the AI 'global' is in its own Realm, which means name
collisions with the same are not possible.
Other notes:
- The RL Interface uses the AI Interface and thus will gradually lose
some data there. Given that the RL Interface can now request data
however, this should be dine.
Refs #5962, #2370
Differential Revision: https://code.wildfiregames.com/D3769
This was SVN commit r26274.
2022-01-30 05:33:34 -08:00
|
|
|
|
|
|
|
|
if (compartment)
|
|
|
|
|
creationOpt.setExistingCompartment(compartment);
|
|
|
|
|
else
|
|
|
|
|
// This is the default behaviour.
|
|
|
|
|
creationOpt.setNewCompartmentAndZone();
|
|
|
|
|
|
2020-11-30 01:03:20 -08:00
|
|
|
JS::RealmOptions opt(creationOpt, JS::RealmBehaviors{});
|
2011-07-16 16:24:14 -07:00
|
|
|
|
2020-11-14 00:46:32 -08:00
|
|
|
m_glob = JS_NewGlobalObject(m_cx, &global_class, nullptr, JS::OnNewGlobalHookOption::FireOnNewGlobalHook, opt);
|
2010-01-09 11:20:14 -08:00
|
|
|
|
2020-11-30 01:03:20 -08:00
|
|
|
JSAutoRealm autoRealm(m_cx, m_glob);
|
2020-11-14 00:46:32 -08:00
|
|
|
|
2020-11-30 01:03:20 -08:00
|
|
|
ENSURE(JS::InitRealmStandardClasses(m_cx));
|
2020-11-14 00:46:32 -08:00
|
|
|
|
|
|
|
|
JS_DefineProperty(m_cx, m_glob, "global", m_glob, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
|
2010-01-09 11:20:14 -08:00
|
|
|
|
2021-03-02 12:01:14 -08:00
|
|
|
// These first 4 actually use CallArgs & thus don't use ScriptFunction
|
2020-11-14 00:46:32 -08:00
|
|
|
JS_DefineFunction(m_cx, m_glob, "print", ::print, 0, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
|
|
|
|
|
JS_DefineFunction(m_cx, m_glob, "log", ::logmsg, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
|
|
|
|
|
JS_DefineFunction(m_cx, m_glob, "warn", ::warn, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
|
|
|
|
|
JS_DefineFunction(m_cx, m_glob, "error", ::error, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
|
2021-03-02 12:01:14 -08:00
|
|
|
ScriptFunction::Register<deepcopy>(m_cx, m_glob, "clone");
|
|
|
|
|
ScriptFunction::Register<deepfreeze>(m_cx, m_glob, "deepfreeze");
|
2011-03-02 16:16:14 -08:00
|
|
|
|
2021-03-02 12:01:14 -08:00
|
|
|
m_nativeScope = JS_DefineObject(m_cx, m_glob, nativeScopeName, nullptr, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
|
|
|
|
|
|
|
|
|
|
ScriptFunction::Register<&ProfileStart>(m_cx, m_nativeScope, "ProfileStart");
|
|
|
|
|
ScriptFunction::Register<&ProfileStop>(m_cx, m_nativeScope, "ProfileStop");
|
|
|
|
|
ScriptFunction::Register<&ProfileAttribute>(m_cx, m_nativeScope, "ProfileAttribute");
|
2014-03-28 13:26:32 -07:00
|
|
|
|
2023-11-19 12:13:19 -08:00
|
|
|
m_context.RegisterRealm(JS::GetObjectRealmOrNull(m_glob));
|
2010-01-09 11:20:14 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ScriptInterface_impl::~ScriptInterface_impl()
|
|
|
|
|
{
|
2023-11-19 12:13:19 -08:00
|
|
|
m_context.UnRegisterRealm(JS::GetObjectRealmOrNull(m_glob));
|
2010-01-09 11:20:14 -08:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
Script::Interface::Interface(const char* nativeScopeName, const char* debugName, Script::Context& context,
|
2025-02-05 06:15:53 -08:00
|
|
|
std::function<bool(const VfsPath&)> allowModule) :
|
|
|
|
|
m{std::make_unique<ScriptInterface_impl>(nativeScopeName, context, nullptr,
|
|
|
|
|
std::move(allowModule))}
|
Run the AI in the same Compartment as the simulation. Let the AI access Sim data.
This is a paradigm change for AI computation.
Historically, the AI was intended to be run in a separate thread from
the simulation. The idea was that slow AI wouldn't stop the renderer
from being smooth.
In that original design, the AI received a copy of the game world and
used that to run its logic. This meant the simulation could safely do
whatever it wanted in the meantime. This copy was done via AIProxy &
AIInterface.
This design ended up having significant flaws:
- The copying impacts the simulation negatively, particularly because
AIProxy subscribes to a lot of messages (sometimes sent exclusively to
it). This time cannot be threaded, and impacts MP games without AIs.
- Copying the data is increasingly difficult. Modifiers are a headache,
LOS is not implemented. Lots of logic is duplicated.
The intended benefits of the design also failed to realise somewhat:
- The AI was never threaded, and in fact, it is probably better to try
and thread Sim + AI from the renderer than just the AI, at which point
threading the AI specifically brings little benefit.
The new design is much simpler and straighforward, but this has some
side-effects:
- The AI can now change the simulation. This can be used for cheating,
or possibly for a tutorial AI.
- The AI runs in the same GC zone as the simulation, which may lead to
more frequent Sim GCs (but overall we might expect a reduction in
temporary objects).
- The AI state was essentially cached, so replacing some functions with
Engine.QueryInterface might be slower. The tradeoff should be balanced
by lower AIProxy computation times.
Future work:
- Threading some specific AI tasks could still be worthwhile, but should
be done in specific worker threads, allowed to run over several turns if
needed.
Technical note: the AI 'global' is in its own Realm, which means name
collisions with the same are not possible.
Other notes:
- The RL Interface uses the AI Interface and thus will gradually lose
some data there. Given that the RL Interface can now request data
however, this should be dine.
Refs #5962, #2370
Differential Revision: https://code.wildfiregames.com/D3769
This was SVN commit r26274.
2022-01-30 05:33:34 -08:00
|
|
|
{
|
|
|
|
|
// Profiler stats table isn't thread-safe, so only enable this on the main thread
|
|
|
|
|
if (Threading::IsMainThread())
|
|
|
|
|
{
|
|
|
|
|
if (g_ScriptStatsTable)
|
|
|
|
|
g_ScriptStatsTable->Add(this, debugName);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
Script::Request rq(this);
|
Run the AI in the same Compartment as the simulation. Let the AI access Sim data.
This is a paradigm change for AI computation.
Historically, the AI was intended to be run in a separate thread from
the simulation. The idea was that slow AI wouldn't stop the renderer
from being smooth.
In that original design, the AI received a copy of the game world and
used that to run its logic. This meant the simulation could safely do
whatever it wanted in the meantime. This copy was done via AIProxy &
AIInterface.
This design ended up having significant flaws:
- The copying impacts the simulation negatively, particularly because
AIProxy subscribes to a lot of messages (sometimes sent exclusively to
it). This time cannot be threaded, and impacts MP games without AIs.
- Copying the data is increasingly difficult. Modifiers are a headache,
LOS is not implemented. Lots of logic is duplicated.
The intended benefits of the design also failed to realise somewhat:
- The AI was never threaded, and in fact, it is probably better to try
and thread Sim + AI from the renderer than just the AI, at which point
threading the AI specifically brings little benefit.
The new design is much simpler and straighforward, but this has some
side-effects:
- The AI can now change the simulation. This can be used for cheating,
or possibly for a tutorial AI.
- The AI runs in the same GC zone as the simulation, which may lead to
more frequent Sim GCs (but overall we might expect a reduction in
temporary objects).
- The AI state was essentially cached, so replacing some functions with
Engine.QueryInterface might be slower. The tradeoff should be balanced
by lower AIProxy computation times.
Future work:
- Threading some specific AI tasks could still be worthwhile, but should
be done in specific worker threads, allowed to run over several turns if
needed.
Technical note: the AI 'global' is in its own Realm, which means name
collisions with the same are not possible.
Other notes:
- The RL Interface uses the AI Interface and thus will gradually lose
some data there. Given that the RL Interface can now request data
however, this should be dine.
Refs #5962, #2370
Differential Revision: https://code.wildfiregames.com/D3769
This was SVN commit r26274.
2022-01-30 05:33:34 -08:00
|
|
|
m_CmptPrivate.pScriptInterface = this;
|
|
|
|
|
JS::SetRealmPrivate(JS::GetObjectRealmOrNull(rq.glob), (void*)&m_CmptPrivate);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
Script::Interface::Interface(const char* nativeScopeName, const char* debugName,
|
|
|
|
|
const Script::Interface& neighbor, std::function<bool(const VfsPath& path)> allowModule)
|
2010-01-09 11:20:14 -08:00
|
|
|
{
|
2026-05-12 10:32:42 -07:00
|
|
|
Script::Request nrq(neighbor);
|
Run the AI in the same Compartment as the simulation. Let the AI access Sim data.
This is a paradigm change for AI computation.
Historically, the AI was intended to be run in a separate thread from
the simulation. The idea was that slow AI wouldn't stop the renderer
from being smooth.
In that original design, the AI received a copy of the game world and
used that to run its logic. This meant the simulation could safely do
whatever it wanted in the meantime. This copy was done via AIProxy &
AIInterface.
This design ended up having significant flaws:
- The copying impacts the simulation negatively, particularly because
AIProxy subscribes to a lot of messages (sometimes sent exclusively to
it). This time cannot be threaded, and impacts MP games without AIs.
- Copying the data is increasingly difficult. Modifiers are a headache,
LOS is not implemented. Lots of logic is duplicated.
The intended benefits of the design also failed to realise somewhat:
- The AI was never threaded, and in fact, it is probably better to try
and thread Sim + AI from the renderer than just the AI, at which point
threading the AI specifically brings little benefit.
The new design is much simpler and straighforward, but this has some
side-effects:
- The AI can now change the simulation. This can be used for cheating,
or possibly for a tutorial AI.
- The AI runs in the same GC zone as the simulation, which may lead to
more frequent Sim GCs (but overall we might expect a reduction in
temporary objects).
- The AI state was essentially cached, so replacing some functions with
Engine.QueryInterface might be slower. The tradeoff should be balanced
by lower AIProxy computation times.
Future work:
- Threading some specific AI tasks could still be worthwhile, but should
be done in specific worker threads, allowed to run over several turns if
needed.
Technical note: the AI 'global' is in its own Realm, which means name
collisions with the same are not possible.
Other notes:
- The RL Interface uses the AI Interface and thus will gradually lose
some data there. Given that the RL Interface can now request data
however, this should be dine.
Refs #5962, #2370
Differential Revision: https://code.wildfiregames.com/D3769
This was SVN commit r26274.
2022-01-30 05:33:34 -08:00
|
|
|
JS::Compartment* comp = JS::GetCompartmentForRealm(JS::GetCurrentRealmOrNull(nrq.cx));
|
2025-02-05 06:15:53 -08:00
|
|
|
m = std::make_unique<ScriptInterface_impl>(nativeScopeName, neighbor.GetContext(), comp,
|
|
|
|
|
std::move(allowModule));
|
Run the AI in the same Compartment as the simulation. Let the AI access Sim data.
This is a paradigm change for AI computation.
Historically, the AI was intended to be run in a separate thread from
the simulation. The idea was that slow AI wouldn't stop the renderer
from being smooth.
In that original design, the AI received a copy of the game world and
used that to run its logic. This meant the simulation could safely do
whatever it wanted in the meantime. This copy was done via AIProxy &
AIInterface.
This design ended up having significant flaws:
- The copying impacts the simulation negatively, particularly because
AIProxy subscribes to a lot of messages (sometimes sent exclusively to
it). This time cannot be threaded, and impacts MP games without AIs.
- Copying the data is increasingly difficult. Modifiers are a headache,
LOS is not implemented. Lots of logic is duplicated.
The intended benefits of the design also failed to realise somewhat:
- The AI was never threaded, and in fact, it is probably better to try
and thread Sim + AI from the renderer than just the AI, at which point
threading the AI specifically brings little benefit.
The new design is much simpler and straighforward, but this has some
side-effects:
- The AI can now change the simulation. This can be used for cheating,
or possibly for a tutorial AI.
- The AI runs in the same GC zone as the simulation, which may lead to
more frequent Sim GCs (but overall we might expect a reduction in
temporary objects).
- The AI state was essentially cached, so replacing some functions with
Engine.QueryInterface might be slower. The tradeoff should be balanced
by lower AIProxy computation times.
Future work:
- Threading some specific AI tasks could still be worthwhile, but should
be done in specific worker threads, allowed to run over several turns if
needed.
Technical note: the AI 'global' is in its own Realm, which means name
collisions with the same are not possible.
Other notes:
- The RL Interface uses the AI Interface and thus will gradually lose
some data there. Given that the RL Interface can now request data
however, this should be dine.
Refs #5962, #2370
Differential Revision: https://code.wildfiregames.com/D3769
This was SVN commit r26274.
2022-01-30 05:33:34 -08:00
|
|
|
|
2010-10-31 15:00:28 -07:00
|
|
|
// Profiler stats table isn't thread-safe, so only enable this on the main thread
|
2021-01-10 00:39:54 -08:00
|
|
|
if (Threading::IsMainThread())
|
2010-10-31 15:00:28 -07:00
|
|
|
{
|
|
|
|
|
if (g_ScriptStatsTable)
|
|
|
|
|
g_ScriptStatsTable->Add(this, debugName);
|
|
|
|
|
}
|
2016-11-23 06:09:58 -08:00
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
Script::Request rq(this);
|
2020-11-13 08:44:15 -08:00
|
|
|
m_CmptPrivate.pScriptInterface = this;
|
2021-05-10 04:51:32 -07:00
|
|
|
JS::SetRealmPrivate(JS::GetObjectRealmOrNull(rq.glob), (void*)&m_CmptPrivate);
|
2010-01-09 11:20:14 -08:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
Script::Interface::~Interface()
|
2010-01-09 11:20:14 -08:00
|
|
|
{
|
2024-07-08 12:07:04 -07:00
|
|
|
m->m_context.RunJobs();
|
2021-01-10 00:39:54 -08:00
|
|
|
if (Threading::IsMainThread())
|
2010-10-31 15:00:28 -07:00
|
|
|
{
|
|
|
|
|
if (g_ScriptStatsTable)
|
|
|
|
|
g_ScriptStatsTable->Remove(this);
|
|
|
|
|
}
|
2010-01-09 11:20:14 -08:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
const Script::Interface& Script::Interface::CmptPrivate::GetScriptInterface(JSContext *cx)
|
2010-01-09 11:20:14 -08:00
|
|
|
{
|
2021-05-15 06:54:58 -07:00
|
|
|
CmptPrivate* pCmptPrivate = (CmptPrivate*)JS::GetRealmPrivate(JS::GetCurrentRealmOrNull(cx));
|
|
|
|
|
ENSURE(pCmptPrivate);
|
|
|
|
|
return *pCmptPrivate->pScriptInterface;
|
2010-01-09 11:20:14 -08:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
void* Script::Interface::CmptPrivate::GetCBData(JSContext *cx)
|
2010-01-09 11:20:14 -08:00
|
|
|
{
|
2021-05-10 04:51:32 -07:00
|
|
|
CmptPrivate* pCmptPrivate = (CmptPrivate*)JS::GetRealmPrivate(JS::GetCurrentRealmOrNull(cx));
|
2021-05-15 06:54:58 -07:00
|
|
|
return pCmptPrivate ? pCmptPrivate->pCBData : nullptr;
|
2010-01-09 11:20:14 -08:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
void Script::Interface::SetCallbackData(void* pCBData)
|
2021-05-15 06:54:58 -07:00
|
|
|
{
|
|
|
|
|
m_CmptPrivate.pCBData = pCBData;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <>
|
2026-05-12 10:32:42 -07:00
|
|
|
void* Script::Interface::ObjectFromCBData<void>(const Script::Request& rq)
|
2021-05-15 06:54:58 -07:00
|
|
|
{
|
2026-05-12 10:32:42 -07:00
|
|
|
return Script::Interface::CmptPrivate::GetCBData(rq.cx);
|
2021-05-15 06:54:58 -07:00
|
|
|
}
|
2014-01-30 05:21:36 -08:00
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
bool Script::Interface::LoadGlobalScripts()
|
2010-05-21 17:38:33 -07:00
|
|
|
{
|
2012-07-02 19:16:45 -07:00
|
|
|
// Ignore this failure in tests
|
|
|
|
|
if (!g_VFS)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// Load and execute *.js in the global scripts directory
|
|
|
|
|
VfsPaths pathnames;
|
|
|
|
|
vfs::GetPathnames(g_VFS, L"globalscripts/", L"*.js", pathnames);
|
2015-07-29 16:44:12 -07:00
|
|
|
for (const VfsPath& path : pathnames)
|
|
|
|
|
if (!LoadGlobalScriptFile(path))
|
2012-07-02 19:16:45 -07:00
|
|
|
{
|
2015-07-29 16:44:12 -07:00
|
|
|
LOGERROR("LoadGlobalScripts: Failed to load script %s", path.string8());
|
2012-07-02 19:16:45 -07:00
|
|
|
return false;
|
|
|
|
|
}
|
2014-03-28 13:26:32 -07:00
|
|
|
|
2012-07-02 19:16:45 -07:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
bool Script::Interface::ReplaceNondeterministicRNG(boost::rand48& rng)
|
2012-07-02 19:16:45 -07:00
|
|
|
{
|
2026-05-12 10:32:42 -07:00
|
|
|
Script::Request rq(this);
|
2020-11-13 05:18:22 -08:00
|
|
|
JS::RootedValue math(rq.cx);
|
2020-11-14 00:46:32 -08:00
|
|
|
JS::RootedObject global(rq.cx, rq.glob);
|
2020-11-13 05:18:22 -08:00
|
|
|
if (JS_GetProperty(rq.cx, global, "Math", &math) && math.isObject())
|
2012-03-20 11:13:27 -07:00
|
|
|
{
|
2020-11-13 05:18:22 -08:00
|
|
|
JS::RootedObject mathObj(rq.cx, &math.toObject());
|
|
|
|
|
JS::RootedFunction random(rq.cx, JS_DefineFunction(rq.cx, mathObj, "random", Math_random, 0,
|
2015-01-24 06:46:52 -08:00
|
|
|
JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT));
|
2012-07-02 19:16:45 -07:00
|
|
|
if (random)
|
2012-03-05 13:20:39 -08:00
|
|
|
{
|
2014-03-28 13:26:32 -07:00
|
|
|
m->m_rng = &rng;
|
|
|
|
|
return true;
|
2012-03-05 13:20:39 -08:00
|
|
|
}
|
2012-03-02 16:06:16 -08:00
|
|
|
}
|
2012-07-02 19:16:45 -07:00
|
|
|
|
Improve JS Exception handling.
- Check for pending exceptions after function calls and script
executions.
- Call LOGERROR instead of JS_ReportError when there is a conversion
error in FromJSVal, since that can only be called from C++ (where JS
errors don't really make sense). Instead, C++ callers of FromJSVal
should handle the failure and, themselves, either report an error or
simply do something else.
- Wrap JS_ReportError since that makes updating it later easier.
This isn't a systematical fix since ToJSVal also ought return a boolean
for failures, and we probably should trigger errors instead of warnings
on 'implicit' conversions, rather a preparation diff.
Part of the SM52 migration, stage: SM45 compatible (actually SM52
incompatible, too).
Based on a patch by: Itms
Comments by: Vladislavbelov, Stan`
Refs #742, #4893
Differential Revision: https://code.wildfiregames.com/D3093
This was SVN commit r24187.
2020-11-15 10:29:17 -08:00
|
|
|
ScriptException::CatchPending(rq);
|
2015-01-22 12:31:30 -08:00
|
|
|
LOGERROR("ReplaceNondeterministicRNG: failed to replace Math.random");
|
2012-07-02 19:16:45 -07:00
|
|
|
return false;
|
2010-05-21 17:38:33 -07:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
JSContext* Script::Interface::GetGeneralJSContext() const
|
2010-08-02 12:23:58 -07:00
|
|
|
{
|
2023-11-19 12:13:19 -08:00
|
|
|
return m->m_context.GetGeneralJSContext();
|
2010-08-02 12:23:58 -07:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
Script::Context& Script::Interface::GetContext() const
|
2014-01-04 02:14:53 -08:00
|
|
|
{
|
2020-11-14 02:57:50 -08:00
|
|
|
return m->m_context;
|
2014-01-04 02:14:53 -08:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
Script::ModuleLoader& Script::Interface::GetModuleLoader() const
|
2025-01-15 11:38:37 -08:00
|
|
|
{
|
|
|
|
|
return m->m_ModuleLoader;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
void Script::Interface::CallConstructor(JS::HandleValue ctor, JS::HandleValueArray argv, JS::MutableHandleValue out) const
|
2010-01-09 11:20:14 -08:00
|
|
|
{
|
2026-05-12 10:32:42 -07:00
|
|
|
Script::Request rq(this);
|
2014-03-28 13:26:32 -07:00
|
|
|
if (!ctor.isObject())
|
2010-01-09 11:20:14 -08:00
|
|
|
{
|
2015-01-22 12:31:30 -08:00
|
|
|
LOGERROR("CallConstructor: ctor is not an object");
|
2014-08-02 09:30:15 -07:00
|
|
|
out.setNull();
|
|
|
|
|
return;
|
2010-01-09 11:20:14 -08:00
|
|
|
}
|
|
|
|
|
|
2022-01-12 08:51:32 -08:00
|
|
|
JS::RootedObject objOut(rq.cx);
|
|
|
|
|
if (!JS::Construct(rq.cx, ctor, argv, &objOut))
|
|
|
|
|
out.setNull();
|
|
|
|
|
else
|
|
|
|
|
out.setObjectOrNull(objOut);
|
2011-01-12 04:29:00 -08:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
void Script::Interface::DefineCustomObjectType(JSClass *clasp, JSNative constructor, uint minArgs, JSPropertySpec *ps, JSFunctionSpec *fs, JSPropertySpec *static_ps, JSFunctionSpec *static_fs)
|
2014-01-04 02:14:53 -08:00
|
|
|
{
|
2026-05-12 10:32:42 -07:00
|
|
|
Script::Request rq(this);
|
2014-01-04 02:14:53 -08:00
|
|
|
std::string typeName = clasp->name;
|
|
|
|
|
|
|
|
|
|
if (m_CustomObjectTypes.find(typeName) != m_CustomObjectTypes.end())
|
|
|
|
|
{
|
|
|
|
|
// This type already exists
|
|
|
|
|
throw PSERROR_Scripting_DefineType_AlreadyExists();
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-14 00:46:32 -08:00
|
|
|
JS::RootedObject global(rq.cx, rq.glob);
|
2024-10-25 01:22:43 -07:00
|
|
|
JS::RootedObject obj(rq.cx, JS_InitClass(rq.cx, global,
|
|
|
|
|
clasp, nullptr, clasp->name,
|
Upgrade SpiderMonkey to version 45.0.2, refs #4893.
- Various build changes, in particular NSPR is not needed on Unix
anymore
- Add js/Initialization.h to source/scriptinterface/ScriptEngine.h
- Use nullptr instead of JS::NullPtr(), see
https://bugzilla.mozilla.org/show_bug.cgi?id=1164602
- Remove `JS::RuntimeOptionsRef.varObjFix`, see
https://bugzilla.mozilla.org/show_bug.cgi?id=1171177
- Remove uses of `AutoIdArray`, see
https://bugzilla.mozilla.org/show_bug.cgi?id=1191529
- `JS_InternUCStringN` has been renamed, see
https://bugzilla.mozilla.org/show_bug.cgi?id=1178581
- `JS::Evaluate` now takes scope chains explicitly, see
https://bugzilla.mozilla.org/show_bug.cgi?id=1097987
- Array functions (such as `JS_IsArrayObject`) are fallible and output
to params, see https://bugzilla.mozilla.org/show_bug.cgi?id=f3d35d8
- Remove `JSCLASS_CACHED_PROTO_WIDTH` workaround in our code, see
https://bugzilla.mozilla.org/show_bug.cgi?id=1236373
- Remove compile'n go (`setCompileAndGo`) and replace it by
`setIsRunOnce` which will become the default in the future, see
https://bugzilla.mozilla.org/show_bug.cgi?id=679939
- Mark shared memory in direct access operations
(`JS_GetUint16ArrayData` and `JS_GetUint8ArrayData`), see
https://bugzilla.mozilla.org/show_bug.cgi?id=1176214
- Use new `JS::ObjectOpResult`, see
https://bugzilla.mozilla.org/show_bug.cgi?id=1113369
Thanks to wraitii, elexis, Krinkle and historic_bruno for contributions
and comments, and to gentz, madpilot, s0600204 and Stan for testing and
indirect contributions.
Differential Revision: https://code.wildfiregames.com/D1510
This was SVN commit r22627.
2019-08-07 15:37:43 -07:00
|
|
|
constructor, minArgs, // Constructor, min args
|
|
|
|
|
ps, fs, // Properties, methods
|
|
|
|
|
static_ps, static_fs)); // Constructor properties, methods
|
2014-01-04 02:14:53 -08:00
|
|
|
|
2020-11-24 07:47:03 -08:00
|
|
|
if (obj == nullptr)
|
Improve JS Exception handling.
- Check for pending exceptions after function calls and script
executions.
- Call LOGERROR instead of JS_ReportError when there is a conversion
error in FromJSVal, since that can only be called from C++ (where JS
errors don't really make sense). Instead, C++ callers of FromJSVal
should handle the failure and, themselves, either report an error or
simply do something else.
- Wrap JS_ReportError since that makes updating it later easier.
This isn't a systematical fix since ToJSVal also ought return a boolean
for failures, and we probably should trigger errors instead of warnings
on 'implicit' conversions, rather a preparation diff.
Part of the SM52 migration, stage: SM45 compatible (actually SM52
incompatible, too).
Based on a patch by: Itms
Comments by: Vladislavbelov, Stan`
Refs #742, #4893
Differential Revision: https://code.wildfiregames.com/D3093
This was SVN commit r24187.
2020-11-15 10:29:17 -08:00
|
|
|
{
|
|
|
|
|
ScriptException::CatchPending(rq);
|
2014-01-04 02:14:53 -08:00
|
|
|
throw PSERROR_Scripting_DefineType_CreationFailed();
|
Improve JS Exception handling.
- Check for pending exceptions after function calls and script
executions.
- Call LOGERROR instead of JS_ReportError when there is a conversion
error in FromJSVal, since that can only be called from C++ (where JS
errors don't really make sense). Instead, C++ callers of FromJSVal
should handle the failure and, themselves, either report an error or
simply do something else.
- Wrap JS_ReportError since that makes updating it later easier.
This isn't a systematical fix since ToJSVal also ought return a boolean
for failures, and we probably should trigger errors instead of warnings
on 'implicit' conversions, rather a preparation diff.
Part of the SM52 migration, stage: SM45 compatible (actually SM52
incompatible, too).
Based on a patch by: Itms
Comments by: Vladislavbelov, Stan`
Refs #742, #4893
Differential Revision: https://code.wildfiregames.com/D3093
This was SVN commit r24187.
2020-11-15 10:29:17 -08:00
|
|
|
}
|
2014-01-04 02:14:53 -08:00
|
|
|
|
2016-09-02 09:53:22 -07:00
|
|
|
CustomType& type = m_CustomObjectTypes[typeName];
|
2014-01-04 02:14:53 -08:00
|
|
|
|
2020-11-13 05:18:22 -08:00
|
|
|
type.m_Prototype.init(rq.cx, obj);
|
2014-01-04 02:14:53 -08:00
|
|
|
type.m_Class = clasp;
|
|
|
|
|
type.m_Constructor = constructor;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
JSObject* Script::Interface::CreateCustomObject(const std::string& typeName) const
|
2014-01-04 02:14:53 -08:00
|
|
|
{
|
2025-01-19 06:19:25 -08:00
|
|
|
std::unordered_map<std::string, CustomType>::const_iterator it = m_CustomObjectTypes.find(typeName);
|
2014-01-04 02:14:53 -08:00
|
|
|
|
|
|
|
|
if (it == m_CustomObjectTypes.end())
|
|
|
|
|
throw PSERROR_Scripting_TypeDoesNotExist();
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
Script::Request rq(this);
|
2020-11-13 05:18:22 -08:00
|
|
|
JS::RootedObject prototype(rq.cx, it->second.m_Prototype.get());
|
|
|
|
|
return JS_NewObjectWithGivenProto(rq.cx, it->second.m_Class, prototype);
|
2014-01-04 02:14:53 -08:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
bool Script::Interface::SetGlobal_(const char* name, JS::HandleValue value, bool replace, bool constant, bool enumerate)
|
2010-01-09 11:20:14 -08:00
|
|
|
{
|
2026-05-12 10:32:42 -07:00
|
|
|
Script::Request rq(this);
|
2020-11-14 00:46:32 -08:00
|
|
|
JS::RootedObject global(rq.cx, rq.glob);
|
2019-07-19 14:58:58 -07:00
|
|
|
|
|
|
|
|
bool found;
|
2020-11-13 05:18:22 -08:00
|
|
|
if (!JS_HasProperty(rq.cx, global, name, &found))
|
2019-07-19 14:58:58 -07:00
|
|
|
return false;
|
|
|
|
|
if (found)
|
2010-01-24 09:24:35 -08:00
|
|
|
{
|
2023-01-10 09:06:47 -08:00
|
|
|
JS::Rooted<mozilla::Maybe<JS::PropertyDescriptor>> desc(rq.cx);
|
|
|
|
|
if (!JS_GetOwnPropertyDescriptor(rq.cx, global, name, &desc) || !desc.isSome())
|
2015-01-24 06:46:52 -08:00
|
|
|
return false;
|
2019-07-19 14:58:58 -07:00
|
|
|
|
2023-01-10 09:06:47 -08:00
|
|
|
if (!desc->writable())
|
2010-01-24 09:24:35 -08:00
|
|
|
{
|
2019-07-19 14:58:58 -07:00
|
|
|
if (!replace)
|
|
|
|
|
{
|
Improve JS Exception handling.
- Check for pending exceptions after function calls and script
executions.
- Call LOGERROR instead of JS_ReportError when there is a conversion
error in FromJSVal, since that can only be called from C++ (where JS
errors don't really make sense). Instead, C++ callers of FromJSVal
should handle the failure and, themselves, either report an error or
simply do something else.
- Wrap JS_ReportError since that makes updating it later easier.
This isn't a systematical fix since ToJSVal also ought return a boolean
for failures, and we probably should trigger errors instead of warnings
on 'implicit' conversions, rather a preparation diff.
Part of the SM52 migration, stage: SM45 compatible (actually SM52
incompatible, too).
Based on a patch by: Itms
Comments by: Vladislavbelov, Stan`
Refs #742, #4893
Differential Revision: https://code.wildfiregames.com/D3093
This was SVN commit r24187.
2020-11-15 10:29:17 -08:00
|
|
|
ScriptException::Raise(rq, "SetGlobal \"%s\" called multiple times", name);
|
2019-07-19 14:58:58 -07:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This is not supposed to happen, unless the user has called SetProperty with constant = true on the global object
|
|
|
|
|
// instead of using SetGlobal.
|
2023-01-10 09:06:47 -08:00
|
|
|
if (!desc->configurable())
|
2019-07-19 14:58:58 -07:00
|
|
|
{
|
Improve JS Exception handling.
- Check for pending exceptions after function calls and script
executions.
- Call LOGERROR instead of JS_ReportError when there is a conversion
error in FromJSVal, since that can only be called from C++ (where JS
errors don't really make sense). Instead, C++ callers of FromJSVal
should handle the failure and, themselves, either report an error or
simply do something else.
- Wrap JS_ReportError since that makes updating it later easier.
This isn't a systematical fix since ToJSVal also ought return a boolean
for failures, and we probably should trigger errors instead of warnings
on 'implicit' conversions, rather a preparation diff.
Part of the SM52 migration, stage: SM45 compatible (actually SM52
incompatible, too).
Based on a patch by: Itms
Comments by: Vladislavbelov, Stan`
Refs #742, #4893
Differential Revision: https://code.wildfiregames.com/D3093
This was SVN commit r24187.
2020-11-15 10:29:17 -08:00
|
|
|
ScriptException::Raise(rq, "The global \"%s\" is permanent and cannot be hotloaded", name);
|
2019-07-19 14:58:58 -07:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOGMESSAGE("Hotloading new value for global \"%s\".", name);
|
2020-11-13 05:18:22 -08:00
|
|
|
ENSURE(JS_DeleteProperty(rq.cx, global, name));
|
2010-01-24 09:24:35 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-13 08:37:41 -08:00
|
|
|
uint attrs = 0;
|
|
|
|
|
if (constant)
|
2019-07-19 14:58:58 -07:00
|
|
|
attrs |= JSPROP_READONLY;
|
2019-01-13 08:37:41 -08:00
|
|
|
if (enumerate)
|
|
|
|
|
attrs |= JSPROP_ENUMERATE;
|
|
|
|
|
|
2020-11-13 05:18:22 -08:00
|
|
|
return JS_DefineProperty(rq.cx, global, name, value, attrs);
|
2014-03-28 13:26:32 -07:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
bool Script::Interface::GetGlobalProperty(const Script::Request& rq, const std::string& name, JS::MutableHandleValue out)
|
2020-12-17 09:51:18 -08:00
|
|
|
{
|
|
|
|
|
// Try to get the object as a property of the global object.
|
|
|
|
|
JS::RootedObject global(rq.cx, rq.glob);
|
|
|
|
|
if (!JS_GetProperty(rq.cx, global, name.c_str(), out))
|
|
|
|
|
{
|
|
|
|
|
out.set(JS::NullHandleValue);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!out.isNullOrUndefined())
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
// Some objects, such as const definitions, or Class definitions, are hidden inside closures.
|
|
|
|
|
// We must fetch those from the correct lexical scope.
|
|
|
|
|
//JS::RootedValue glob(cx);
|
|
|
|
|
JS::RootedObject lexical_environment(rq.cx, JS_GlobalLexicalEnvironment(rq.glob));
|
|
|
|
|
if (!JS_GetProperty(rq.cx, lexical_environment, name.c_str(), out))
|
|
|
|
|
{
|
|
|
|
|
out.set(JS::NullHandleValue);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!out.isNullOrUndefined())
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
out.set(JS::NullHandleValue);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
bool Script::Interface::SetPrototype(JS::HandleValue objVal, JS::HandleValue protoVal)
|
2010-01-09 11:20:14 -08:00
|
|
|
{
|
2026-05-12 10:32:42 -07:00
|
|
|
Script::Request rq(this);
|
2015-01-24 06:46:52 -08:00
|
|
|
if (!objVal.isObject() || !protoVal.isObject())
|
2010-01-09 11:20:14 -08:00
|
|
|
return false;
|
2020-11-13 05:18:22 -08:00
|
|
|
JS::RootedObject obj(rq.cx, &objVal.toObject());
|
|
|
|
|
JS::RootedObject proto(rq.cx, &protoVal.toObject());
|
|
|
|
|
return JS_SetPrototype(rq.cx, obj, proto);
|
2010-01-09 11:20:14 -08:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
bool Script::Interface::LoadScript(const VfsPath& filename, const std::string& code) const
|
2010-01-09 11:20:14 -08:00
|
|
|
{
|
2026-05-12 10:32:42 -07:00
|
|
|
Script::Request rq(this);
|
2020-11-14 00:46:32 -08:00
|
|
|
JS::RootedObject global(rq.cx, rq.glob);
|
2020-11-30 01:03:20 -08:00
|
|
|
|
2015-01-24 06:46:52 -08:00
|
|
|
// CompileOptions does not copy the contents of the filename string pointer.
|
|
|
|
|
// Passing a temporary string there will cause undefined behaviour, so we create a separate string to avoid the temporary.
|
2015-12-18 18:55:30 -08:00
|
|
|
std::string filenameStr = filename.string8();
|
2015-01-24 06:46:52 -08:00
|
|
|
|
2020-11-13 05:18:22 -08:00
|
|
|
JS::CompileOptions options(rq.cx);
|
2020-12-26 23:51:30 -08:00
|
|
|
// Set the line to 0 because CompileFunction silently adds a `(function() {` as the first line,
|
|
|
|
|
// and errors get misreported.
|
|
|
|
|
// TODO: it would probably be better to not implicitly introduce JS scopes.
|
|
|
|
|
options.setFileAndLine(filenameStr.c_str(), 0);
|
2019-08-08 02:05:42 -07:00
|
|
|
options.setIsRunOnce(false);
|
2025-03-29 06:34:43 -07:00
|
|
|
options.setForceStrictMode();
|
2011-03-23 14:15:22 -07:00
|
|
|
|
2020-11-30 01:03:20 -08:00
|
|
|
JS::SourceText<mozilla::Utf8Unit> src;
|
|
|
|
|
ENSURE(src.init(rq.cx, code.c_str(), code.length(), JS::SourceOwnership::Borrowed));
|
|
|
|
|
JS::RootedObjectVector emptyScopeChain(rq.cx);
|
|
|
|
|
JS::RootedFunction func(rq.cx, JS::CompileFunction(rq.cx, emptyScopeChain, options, NULL, 0, NULL, src));
|
|
|
|
|
if (func == nullptr)
|
Improve JS Exception handling.
- Check for pending exceptions after function calls and script
executions.
- Call LOGERROR instead of JS_ReportError when there is a conversion
error in FromJSVal, since that can only be called from C++ (where JS
errors don't really make sense). Instead, C++ callers of FromJSVal
should handle the failure and, themselves, either report an error or
simply do something else.
- Wrap JS_ReportError since that makes updating it later easier.
This isn't a systematical fix since ToJSVal also ought return a boolean
for failures, and we probably should trigger errors instead of warnings
on 'implicit' conversions, rather a preparation diff.
Part of the SM52 migration, stage: SM45 compatible (actually SM52
incompatible, too).
Based on a patch by: Itms
Comments by: Vladislavbelov, Stan`
Refs #742, #4893
Differential Revision: https://code.wildfiregames.com/D3093
This was SVN commit r24187.
2020-11-15 10:29:17 -08:00
|
|
|
{
|
|
|
|
|
ScriptException::CatchPending(rq);
|
2010-04-14 11:57:50 -07:00
|
|
|
return false;
|
Improve JS Exception handling.
- Check for pending exceptions after function calls and script
executions.
- Call LOGERROR instead of JS_ReportError when there is a conversion
error in FromJSVal, since that can only be called from C++ (where JS
errors don't really make sense). Instead, C++ callers of FromJSVal
should handle the failure and, themselves, either report an error or
simply do something else.
- Wrap JS_ReportError since that makes updating it later easier.
This isn't a systematical fix since ToJSVal also ought return a boolean
for failures, and we probably should trigger errors instead of warnings
on 'implicit' conversions, rather a preparation diff.
Part of the SM52 migration, stage: SM45 compatible (actually SM52
incompatible, too).
Based on a patch by: Itms
Comments by: Vladislavbelov, Stan`
Refs #742, #4893
Differential Revision: https://code.wildfiregames.com/D3093
This was SVN commit r24187.
2020-11-15 10:29:17 -08:00
|
|
|
}
|
2010-04-14 11:57:50 -07:00
|
|
|
|
2020-11-13 05:18:22 -08:00
|
|
|
JS::RootedValue rval(rq.cx);
|
Improve JS Exception handling.
- Check for pending exceptions after function calls and script
executions.
- Call LOGERROR instead of JS_ReportError when there is a conversion
error in FromJSVal, since that can only be called from C++ (where JS
errors don't really make sense). Instead, C++ callers of FromJSVal
should handle the failure and, themselves, either report an error or
simply do something else.
- Wrap JS_ReportError since that makes updating it later easier.
This isn't a systematical fix since ToJSVal also ought return a boolean
for failures, and we probably should trigger errors instead of warnings
on 'implicit' conversions, rather a preparation diff.
Part of the SM52 migration, stage: SM45 compatible (actually SM52
incompatible, too).
Based on a patch by: Itms
Comments by: Vladislavbelov, Stan`
Refs #742, #4893
Differential Revision: https://code.wildfiregames.com/D3093
This was SVN commit r24187.
2020-11-15 10:29:17 -08:00
|
|
|
if (JS_CallFunction(rq.cx, nullptr, func, JS::HandleValueArray::empty(), &rval))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
ScriptException::CatchPending(rq);
|
|
|
|
|
return false;
|
2010-01-09 11:20:14 -08:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
bool Script::Interface::LoadGlobalScript(const VfsPath& filename, const std::string& code) const
|
2011-04-14 22:23:51 -07:00
|
|
|
{
|
2026-05-12 10:32:42 -07:00
|
|
|
Script::Request rq(this);
|
2015-12-18 18:55:30 -08:00
|
|
|
// CompileOptions does not copy the contents of the filename string pointer.
|
|
|
|
|
// Passing a temporary string there will cause undefined behaviour, so we create a separate string to avoid the temporary.
|
|
|
|
|
std::string filenameStr = filename.string8();
|
2011-04-14 22:23:51 -07:00
|
|
|
|
2020-11-13 05:18:22 -08:00
|
|
|
JS::RootedValue rval(rq.cx);
|
|
|
|
|
JS::CompileOptions opts(rq.cx);
|
2020-11-30 01:03:20 -08:00
|
|
|
opts.setFileAndLine(filenameStr.c_str(), 1);
|
2025-03-29 06:34:43 -07:00
|
|
|
opts.setForceStrictMode();
|
2020-11-30 01:03:20 -08:00
|
|
|
|
|
|
|
|
JS::SourceText<mozilla::Utf8Unit> src;
|
|
|
|
|
ENSURE(src.init(rq.cx, code.c_str(), code.length(), JS::SourceOwnership::Borrowed));
|
|
|
|
|
if (JS::Evaluate(rq.cx, opts, src, &rval))
|
Improve JS Exception handling.
- Check for pending exceptions after function calls and script
executions.
- Call LOGERROR instead of JS_ReportError when there is a conversion
error in FromJSVal, since that can only be called from C++ (where JS
errors don't really make sense). Instead, C++ callers of FromJSVal
should handle the failure and, themselves, either report an error or
simply do something else.
- Wrap JS_ReportError since that makes updating it later easier.
This isn't a systematical fix since ToJSVal also ought return a boolean
for failures, and we probably should trigger errors instead of warnings
on 'implicit' conversions, rather a preparation diff.
Part of the SM52 migration, stage: SM45 compatible (actually SM52
incompatible, too).
Based on a patch by: Itms
Comments by: Vladislavbelov, Stan`
Refs #742, #4893
Differential Revision: https://code.wildfiregames.com/D3093
This was SVN commit r24187.
2020-11-15 10:29:17 -08:00
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
ScriptException::CatchPending(rq);
|
|
|
|
|
return false;
|
2011-04-14 22:23:51 -07:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
bool Script::Interface::LoadGlobalScriptFile(const VfsPath& path) const
|
2011-01-12 04:29:00 -08:00
|
|
|
{
|
2011-05-25 03:39:13 -07:00
|
|
|
if (!VfsFileExists(path))
|
2011-01-12 04:29:00 -08:00
|
|
|
{
|
2015-01-22 12:36:24 -08:00
|
|
|
LOGERROR("File '%s' does not exist", path.string8());
|
2011-01-12 04:29:00 -08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CVFSFile file;
|
|
|
|
|
|
|
|
|
|
PSRETURN ret = file.Load(g_VFS, path);
|
|
|
|
|
|
|
|
|
|
if (ret != PSRETURN_OK)
|
|
|
|
|
{
|
2015-01-22 12:36:24 -08:00
|
|
|
LOGERROR("Failed to load file '%s': %s", path.string8(), GetErrorString(ret));
|
2011-01-12 04:29:00 -08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-30 01:03:20 -08:00
|
|
|
CStr code = file.DecodeUTF8(); // assume it's UTF-8
|
2011-01-12 04:29:00 -08:00
|
|
|
|
2022-03-28 14:58:22 -07:00
|
|
|
return LoadGlobalScript(path, code);
|
2011-01-12 04:29:00 -08:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
bool Script::Interface::Eval(const char* code) const
|
2010-01-09 11:20:14 -08:00
|
|
|
{
|
2026-05-12 10:32:42 -07:00
|
|
|
Script::Request rq(this);
|
2020-11-13 05:18:22 -08:00
|
|
|
JS::RootedValue rval(rq.cx);
|
2016-11-23 06:09:58 -08:00
|
|
|
|
2020-11-13 05:18:22 -08:00
|
|
|
JS::CompileOptions opts(rq.cx);
|
2015-12-18 17:58:03 -08:00
|
|
|
opts.setFileAndLine("(eval)", 1);
|
2025-03-29 06:34:43 -07:00
|
|
|
opts.setForceStrictMode();
|
2020-11-30 01:03:20 -08:00
|
|
|
JS::SourceText<mozilla::Utf8Unit> src;
|
|
|
|
|
ENSURE(src.init(rq.cx, code, strlen(code), JS::SourceOwnership::Borrowed));
|
|
|
|
|
if (JS::Evaluate(rq.cx, opts, src, &rval))
|
Improve JS Exception handling.
- Check for pending exceptions after function calls and script
executions.
- Call LOGERROR instead of JS_ReportError when there is a conversion
error in FromJSVal, since that can only be called from C++ (where JS
errors don't really make sense). Instead, C++ callers of FromJSVal
should handle the failure and, themselves, either report an error or
simply do something else.
- Wrap JS_ReportError since that makes updating it later easier.
This isn't a systematical fix since ToJSVal also ought return a boolean
for failures, and we probably should trigger errors instead of warnings
on 'implicit' conversions, rather a preparation diff.
Part of the SM52 migration, stage: SM45 compatible (actually SM52
incompatible, too).
Based on a patch by: Itms
Comments by: Vladislavbelov, Stan`
Refs #742, #4893
Differential Revision: https://code.wildfiregames.com/D3093
This was SVN commit r24187.
2020-11-15 10:29:17 -08:00
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
ScriptException::CatchPending(rq);
|
|
|
|
|
return false;
|
2010-03-23 15:45:07 -07:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 10:32:42 -07:00
|
|
|
bool Script::Interface::Eval(const char* code, JS::MutableHandleValue rval) const
|
2010-03-23 15:45:07 -07:00
|
|
|
{
|
2026-05-12 10:32:42 -07:00
|
|
|
Script::Request rq(this);
|
2010-03-23 15:45:07 -07:00
|
|
|
|
2020-11-13 05:18:22 -08:00
|
|
|
JS::CompileOptions opts(rq.cx);
|
2015-12-18 17:58:03 -08:00
|
|
|
opts.setFileAndLine("(eval)", 1);
|
2025-03-29 06:34:43 -07:00
|
|
|
opts.setForceStrictMode();
|
2020-11-30 01:03:20 -08:00
|
|
|
JS::SourceText<mozilla::Utf8Unit> src;
|
|
|
|
|
ENSURE(src.init(rq.cx, code, strlen(code), JS::SourceOwnership::Borrowed));
|
|
|
|
|
if (JS::Evaluate(rq.cx, opts, src, rval))
|
Improve JS Exception handling.
- Check for pending exceptions after function calls and script
executions.
- Call LOGERROR instead of JS_ReportError when there is a conversion
error in FromJSVal, since that can only be called from C++ (where JS
errors don't really make sense). Instead, C++ callers of FromJSVal
should handle the failure and, themselves, either report an error or
simply do something else.
- Wrap JS_ReportError since that makes updating it later easier.
This isn't a systematical fix since ToJSVal also ought return a boolean
for failures, and we probably should trigger errors instead of warnings
on 'implicit' conversions, rather a preparation diff.
Part of the SM52 migration, stage: SM45 compatible (actually SM52
incompatible, too).
Based on a patch by: Itms
Comments by: Vladislavbelov, Stan`
Refs #742, #4893
Differential Revision: https://code.wildfiregames.com/D3093
This was SVN commit r24187.
2020-11-15 10:29:17 -08:00
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
ScriptException::CatchPending(rq);
|
|
|
|
|
return false;
|
2010-01-09 11:20:14 -08:00
|
|
|
}
|