LCOV - code coverage report
Current view: top level - source/scriptinterface - Object.h (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 71 92 77.2 %
Date: 2023-01-19 00:18:29 Functions: 19 192 9.9 %

          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             : #ifndef INCLUDED_SCRIPTINTERFACE_OBJECT
      19             : #define INCLUDED_SCRIPTINTERFACE_OBJECT
      20             : 
      21             : #include "ScriptConversions.h"
      22             : #include "ScriptRequest.h"
      23             : #include "ScriptTypes.h"
      24             : 
      25             : #include "ps/CLogger.h"
      26             : 
      27             : /**
      28             :  * Wraps SM APIs for manipulating JS objects.
      29             :  */
      30             : 
      31             : namespace Script
      32             : {
      33             : /**
      34             :  * Get the named property on the given object.
      35             :  */
      36             : template<typename PropType>
      37         203 : inline bool GetProperty(const ScriptRequest& rq, JS::HandleValue obj, PropType name, JS::MutableHandleValue out)
      38             : {
      39         203 :     if (!obj.isObject())
      40           0 :         return false;
      41         406 :     JS::RootedObject object(rq.cx, &obj.toObject());
      42             :     if constexpr (std::is_same_v<int, PropType>)
      43             :     {
      44          12 :         JS::RootedId id(rq.cx, INT_TO_JSID(name));
      45           6 :         return JS_GetPropertyById(rq.cx, object, id, out);
      46             :     }
      47             :     else if constexpr (std::is_same_v<const char*, PropType>)
      48         197 :         return JS_GetProperty(rq.cx, object, name, out);
      49             :     else
      50             :         return JS_GetUCProperty(rq.cx, object, name, wcslen(name), out);
      51             : }
      52             : 
      53             : template<typename T, typename PropType>
      54          82 : inline bool GetProperty(const ScriptRequest& rq, JS::HandleValue obj, PropType name, T& out)
      55             : {
      56         164 :     JS::RootedValue val(rq.cx);
      57          82 :     if (!GetProperty<PropType>(rq, obj, name, &val))
      58           0 :         return false;
      59          82 :     return FromJSVal(rq, val, out);
      60             : }
      61             : inline bool GetProperty(const ScriptRequest& rq, JS::HandleValue obj, const char* name, JS::MutableHandleObject out)
      62             : {
      63             :     JS::RootedValue val(rq.cx, JS::ObjectValue(*out.get()));
      64             :     if (!GetProperty(rq, obj, name, &val))
      65             :         return false;
      66             :     out.set(val.toObjectOrNull());
      67             :     return true;
      68             : }
      69             : 
      70             : template<typename T>
      71           0 : inline bool GetPropertyInt(const ScriptRequest& rq, JS::HandleValue obj, int name, T& out)
      72             : {
      73           0 :     return GetProperty(rq, obj, name, out);
      74             : }
      75           6 : inline bool GetPropertyInt(const ScriptRequest& rq, JS::HandleValue obj, int name, JS::MutableHandleValue out)
      76             : {
      77           6 :     return GetProperty(rq, obj, name, out);
      78             : }
      79             : /**
      80             :  * Check the named property has been defined on the given object.
      81             :  */
      82         150 : inline bool HasProperty(const ScriptRequest& rq, JS::HandleValue obj, const char* name)
      83             : {
      84         150 :     if (!obj.isObject())
      85           0 :         return false;
      86         300 :     JS::RootedObject object(rq.cx, &obj.toObject());
      87             : 
      88             :     bool found;
      89         150 :     if (!JS_HasProperty(rq.cx, object, name, &found))
      90           0 :         return false;
      91         150 :     return found;
      92             : }
      93             : 
      94             : /**
      95             :  * Set the named property on the given object.
      96             :  */
      97             : template<typename PropType>
      98          99 : inline bool SetProperty(const ScriptRequest& rq, JS::HandleValue obj, PropType name, JS::HandleValue value, bool constant = false, bool enumerable = true)
      99             : {
     100          99 :     uint attrs = 0;
     101          99 :     if (constant)
     102          98 :         attrs |= JSPROP_READONLY | JSPROP_PERMANENT;
     103          99 :     if (enumerable)
     104           1 :         attrs |= JSPROP_ENUMERATE;
     105             : 
     106          99 :     if (!obj.isObject())
     107           0 :         return false;
     108         198 :     JS::RootedObject object(rq.cx, &obj.toObject());
     109             :     if constexpr (std::is_same_v<int, PropType>)
     110             :     {
     111           2 :         JS::RootedId id(rq.cx, INT_TO_JSID(name));
     112           1 :         return JS_DefinePropertyById(rq.cx, object, id, value, attrs);
     113             :     }
     114             :     else if constexpr (std::is_same_v<const char*, PropType>)
     115          98 :         return JS_DefineProperty(rq.cx, object, name, value, attrs);
     116             :     else
     117             :         return JS_DefineUCProperty(rq.cx, object, name, value, attrs);
     118             : }
     119             : 
     120             : template<typename T, typename PropType>
     121          99 : inline bool SetProperty(const ScriptRequest& rq, JS::HandleValue obj, PropType name, const T& value, bool constant = false, bool enumerable = true)
     122             : {
     123         198 :     JS::RootedValue val(rq.cx);
     124          99 :     Script::ToJSVal(rq, &val, value);
     125         198 :     return SetProperty<PropType>(rq, obj, name, val, constant, enumerable);
     126             : }
     127             : 
     128             : template<typename T>
     129           1 : inline bool SetPropertyInt(const ScriptRequest& rq, JS::HandleValue obj, int name, const T& value, bool constant = false, bool enumerable = true)
     130             : {
     131           1 :     return SetProperty<T, int>(rq, obj, name, value, constant, enumerable);
     132             : }
     133             : 
     134             : template<typename T>
     135          65 : inline bool GetObjectClassName(const ScriptRequest& rq, JS::HandleObject obj, T& name)
     136             : {
     137         130 :     JS::RootedValue constructor(rq.cx, JS::ObjectOrNullValue(JS_GetConstructor(rq.cx, obj)));
     138         130 :     return constructor.isObject() && Script::HasProperty(rq, constructor, "name") && Script::GetProperty(rq, constructor, "name", name);
     139             : }
     140             : 
     141             : /**
     142             :  * Get the name of the object's class. Note that inheritance may lead to unexpected results.
     143             :  */
     144             : template<typename T>
     145           1 : inline bool GetObjectClassName(const ScriptRequest& rq, JS::HandleValue val, T& name)
     146             : {
     147           2 :     JS::RootedObject obj(rq.cx, val.toObjectOrNull());
     148           1 :     if (!obj)
     149           0 :         return false;
     150           1 :     return GetObjectClassName(rq, obj, name);
     151             : }
     152             : 
     153         294 : inline bool FreezeObject(const ScriptRequest& rq, JS::HandleValue objVal, bool deep)
     154             : {
     155         294 :     if (!objVal.isObject())
     156           0 :         return false;
     157             : 
     158         588 :     JS::RootedObject obj(rq.cx, &objVal.toObject());
     159             : 
     160         294 :     if (deep)
     161         294 :         return JS_DeepFreezeObject(rq.cx, obj);
     162             :     else
     163           0 :         return JS_FreezeObject(rq.cx, obj);
     164             : }
     165             : 
     166             : /**
     167             :  * Returns all properties of the object, both own properties and inherited.
     168             :  * This is essentially equivalent to calling Object.getOwnPropertyNames()
     169             :  * and recursing up the prototype chain.
     170             :  * NB: this does not return properties with symbol or numeric keys, as that would
     171             :  * require a variant in the vector, and it's not useful for now.
     172             :  * @param enumerableOnly - only return enumerable properties.
     173             :  */
     174          79 : inline bool EnumeratePropertyNames(const ScriptRequest& rq, JS::HandleValue objVal, bool enumerableOnly, std::vector<std::string>& out)
     175             : {
     176          79 :     if (!objVal.isObjectOrNull())
     177             :     {
     178           0 :         LOGERROR("EnumeratePropertyNames expected object type!");
     179           0 :         return false;
     180             :     }
     181             : 
     182         158 :     JS::RootedObject obj(rq.cx, &objVal.toObject());
     183         158 :     JS::RootedIdVector props(rq.cx);
     184             :     // This recurses up the prototype chain on its own.
     185          79 :     if (!js::GetPropertyKeys(rq.cx, obj, enumerableOnly? 0 : JSITER_HIDDEN, &props))
     186           0 :         return false;
     187             : 
     188          79 :     out.reserve(out.size() + props.length());
     189        1150 :     for (size_t i = 0; i < props.length(); ++i)
     190             :     {
     191        2142 :         JS::RootedId id(rq.cx, props[i]);
     192        2142 :         JS::RootedValue val(rq.cx);
     193        1071 :         if (!JS_IdToValue(rq.cx, id, &val))
     194           0 :             return false;
     195             : 
     196             :         // Ignore integer properties for now.
     197             :         // TODO: is this actually a thing in ECMAScript 6?
     198        1071 :         if (!val.isString())
     199           0 :             continue;
     200             : 
     201        2142 :         std::string propName;
     202        1071 :         if (!FromJSVal(rq, val, propName))
     203           0 :             return false;
     204             : 
     205        1071 :         out.emplace_back(std::move(propName));
     206             :     }
     207             : 
     208          79 :     return true;
     209             : }
     210             : 
     211             : /**
     212             :  * Create a plain object (i.e. {}). If it fails, returns undefined.
     213             :  */
     214           3 : inline JS::Value CreateObject(const ScriptRequest& rq)
     215             : {
     216           6 :     JS::RootedObject obj(rq.cx, JS_NewPlainObject(rq.cx));
     217           3 :     if (!obj)
     218           0 :         return JS::UndefinedValue();
     219           3 :     return JS::ObjectValue(*obj.get());
     220             : }
     221             : 
     222           3 : inline bool CreateObject(const ScriptRequest& rq, JS::MutableHandleValue objectValue)
     223             : {
     224           3 :     objectValue.set(CreateObject(rq));
     225           3 :     return !objectValue.isNullOrUndefined();
     226             : }
     227             : 
     228             : /**
     229             :  * Sets the given value to a new plain JS::Object, converts the arguments to JS::Values and sets them as properties.
     230             :  * This is static so that callers like ToJSVal can use it with the JSContext directly instead of having to obtain the instance using GetScriptInterfaceAndCBData.
     231             :  * Can throw an exception.
     232             :  */
     233             : template<typename T, typename... Args>
     234           0 : inline bool CreateObject(const ScriptRequest& rq, JS::MutableHandleValue objectValue, const char* propertyName, const T& propertyValue, Args const&... args)
     235             : {
     236           0 :     JS::RootedValue val(rq.cx);
     237           0 :     ToJSVal(rq, &val, propertyValue);
     238           0 :     return CreateObject(rq, objectValue, args...) && SetProperty(rq, objectValue, propertyName, val, false, true);
     239             : }
     240             : 
     241             : /**
     242             :  * Sets the given value to a new JS object or Null Value in case of out-of-memory.
     243             :  */
     244           1 : inline bool CreateArray(const ScriptRequest& rq, JS::MutableHandleValue objectValue, size_t length = 0)
     245             : {
     246           1 :     objectValue.setObjectOrNull(JS::NewArrayObject(rq.cx, length));
     247           1 :     return !objectValue.isNullOrUndefined();
     248             : }
     249             : 
     250             : } // namespace Script
     251             : 
     252             : #endif // INCLUDED_SCRIPTINTERFACE_Object

Generated by: LCOV version 1.13