Merge pull request #38119 from RandomShaper/imvu/new_dangling_variant_fix

Fix dangling Variants (3.2)
This commit is contained in:
Rémi Verschelde 2020-04-24 17:59:52 +02:00 committed by GitHub
commit 5d5848d2b1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 383 additions and 171 deletions

View file

@ -33,6 +33,7 @@
#include "core/class_db.h" #include "core/class_db.h"
#include "core/core_string_names.h" #include "core/core_string_names.h"
#include "core/message_queue.h" #include "core/message_queue.h"
#include "core/object_rc.h"
#include "core/os/os.h" #include "core/os/os.h"
#include "core/print_string.h" #include "core/print_string.h"
#include "core/resource.h" #include "core/resource.h"
@ -968,6 +969,37 @@ void Object::cancel_delete() {
_predelete_ok = true; _predelete_ok = true;
} }
#ifdef DEBUG_ENABLED
ObjectRC *Object::_use_rc() {
// The RC object is lazily created the first time it's requested;
// that way, there's no need to allocate and release it at all if this Object
// is not being referred by any Variant at all.
// Although when dealing with Objects from multiple threads some locking
// mechanism should be used, this at least makes safe the case of first
// assignment.
ObjectRC *rc = nullptr;
ObjectRC *const creating = reinterpret_cast<ObjectRC *>(1);
if (unlikely(_rc.compare_exchange_strong(rc, creating, std::memory_order_acq_rel))) {
// Not created yet
rc = memnew(ObjectRC(this));
_rc.store(rc, std::memory_order_release);
return rc;
}
// Spin-wait until we know it's created (or just return if it's already created)
for (;;) {
if (likely(rc != creating)) {
rc->increment();
return rc;
}
rc = _rc.load(std::memory_order_acquire);
}
}
#endif
void Object::set_script_and_instance(const RefPtr &p_script, ScriptInstance *p_instance) { void Object::set_script_and_instance(const RefPtr &p_script, ScriptInstance *p_instance) {
//this function is not meant to be used in any of these ways //this function is not meant to be used in any of these ways
@ -1944,6 +1976,9 @@ Object::Object() {
instance_binding_count = 0; instance_binding_count = 0;
memset(_script_instance_bindings, 0, sizeof(void *) * MAX_SCRIPT_INSTANCE_BINDINGS); memset(_script_instance_bindings, 0, sizeof(void *) * MAX_SCRIPT_INSTANCE_BINDINGS);
script_instance = NULL; script_instance = NULL;
#ifdef DEBUG_ENABLED
_rc.store(nullptr, std::memory_order_release);
#endif
#ifdef TOOLS_ENABLED #ifdef TOOLS_ENABLED
_edited = false; _edited = false;
@ -1957,6 +1992,15 @@ Object::Object() {
Object::~Object() { Object::~Object() {
#ifdef DEBUG_ENABLED
ObjectRC *rc = _rc.load(std::memory_order_acquire);
if (rc) {
if (rc->invalidate()) {
memfree(rc);
}
}
#endif
if (script_instance) if (script_instance)
memdelete(script_instance); memdelete(script_instance);
script_instance = NULL; script_instance = NULL;

View file

@ -34,11 +34,16 @@
#include "core/hash_map.h" #include "core/hash_map.h"
#include "core/list.h" #include "core/list.h"
#include "core/map.h" #include "core/map.h"
#include "core/object_id.h"
#include "core/os/rw_lock.h" #include "core/os/rw_lock.h"
#include "core/set.h" #include "core/set.h"
#include "core/variant.h" #include "core/variant.h"
#include "core/vmap.h" #include "core/vmap.h"
#ifdef DEBUG_ENABLED
#include <atomic> // For ObjectRC*
#endif
#define VARIANT_ARG_LIST const Variant &p_arg1 = Variant(), const Variant &p_arg2 = Variant(), const Variant &p_arg3 = Variant(), const Variant &p_arg4 = Variant(), const Variant &p_arg5 = Variant() #define VARIANT_ARG_LIST const Variant &p_arg1 = Variant(), const Variant &p_arg2 = Variant(), const Variant &p_arg3 = Variant(), const Variant &p_arg4 = Variant(), const Variant &p_arg5 = Variant()
#define VARIANT_ARG_PASS p_arg1, p_arg2, p_arg3, p_arg4, p_arg5 #define VARIANT_ARG_PASS p_arg1, p_arg2, p_arg3, p_arg4, p_arg5
#define VARIANT_ARG_DECLARE const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4, const Variant &p_arg5 #define VARIANT_ARG_DECLARE const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4, const Variant &p_arg5
@ -397,7 +402,7 @@ public: \
private: private:
class ScriptInstance; class ScriptInstance;
typedef uint64_t ObjectID; class ObjectRC;
class Object { class Object {
public: public:
@ -477,6 +482,9 @@ private:
int _predelete_ok; int _predelete_ok;
Set<Object *> change_receptors; Set<Object *> change_receptors;
ObjectID _instance_id; ObjectID _instance_id;
#ifdef DEBUG_ENABLED
std::atomic<ObjectRC *> _rc;
#endif
bool _predelete(); bool _predelete();
void _postinitialize(); void _postinitialize();
bool _can_translate; bool _can_translate;
@ -587,6 +595,10 @@ public:
return &ptr; return &ptr;
} }
#ifdef DEBUG_ENABLED
ObjectRC *_use_rc();
#endif
bool _is_gpl_reversed() const { return false; } bool _is_gpl_reversed() const { return false; }
_FORCE_INLINE_ ObjectID get_instance_id() const { return _instance_id; } _FORCE_INLINE_ ObjectID get_instance_id() const { return _instance_id; }

38
core/object_id.h Normal file
View file

@ -0,0 +1,38 @@
/*************************************************************************/
/* object_rc.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef OBJECTID_H
#define OBJECTID_H
#include "core/int_types.h"
typedef uint64_t ObjectID;
#endif

75
core/object_rc.h Normal file
View file

@ -0,0 +1,75 @@
/*************************************************************************/
/* object_rc.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef OBJECTRC_H
#define OBJECTRC_H
#ifdef DEBUG_ENABLED
#include "core/os/memory.h"
#include "core/typedefs.h"
#include <atomic>
class Object;
// Used to track Variants pointing to a non-Reference Object
class ObjectRC {
std::atomic<Object *> _ptr;
std::atomic<uint32_t> _users;
public:
_FORCE_INLINE_ void increment() {
_users.fetch_add(1, std::memory_order_relaxed);
}
_FORCE_INLINE_ bool decrement() {
return _users.fetch_sub(1, std::memory_order_relaxed) == 1;
}
_FORCE_INLINE_ bool invalidate() {
_ptr.store(nullptr, std::memory_order_release);
return decrement();
}
_FORCE_INLINE_ Object *get_ptr() {
return _ptr.load(std::memory_order_acquire);
}
_FORCE_INLINE_ ObjectRC(Object *p_object) {
// 1 (the Object) + 1 (the first user)
_users.store(2, std::memory_order_relaxed);
_ptr.store(p_object, std::memory_order_release);
}
};
#endif
#endif

View file

@ -33,6 +33,7 @@
#include "core/core_string_names.h" #include "core/core_string_names.h"
#include "core/io/marshalls.h" #include "core/io/marshalls.h"
#include "core/math/math_funcs.h" #include "core/math/math_funcs.h"
#include "core/object_rc.h"
#include "core/print_string.h" #include "core/print_string.h"
#include "core/resource.h" #include "core/resource.h"
#include "core/variant_parser.h" #include "core/variant_parser.h"
@ -790,7 +791,7 @@ bool Variant::is_zero() const {
} break; } break;
case OBJECT: { case OBJECT: {
return _get_obj().obj == NULL; return _OBJ_PTR(*this) == NULL;
} break; } break;
case NODE_PATH: { case NODE_PATH: {
@ -1000,6 +1001,11 @@ void Variant::reference(const Variant &p_variant) {
case OBJECT: { case OBJECT: {
memnew_placement(_data._mem, ObjData(p_variant._get_obj())); memnew_placement(_data._mem, ObjData(p_variant._get_obj()));
#ifdef DEBUG_ENABLED
if (_get_obj().rc) {
_get_obj().rc->increment();
}
#endif
} break; } break;
case NODE_PATH: { case NODE_PATH: {
@ -1114,8 +1120,19 @@ void Variant::clear() {
} break; } break;
case OBJECT: { case OBJECT: {
#ifdef DEBUG_ENABLED
if (_get_obj().rc) {
if (_get_obj().rc->decrement()) {
memfree(_get_obj().rc);
}
_get_obj().instance_id = 0;
} else {
_get_obj().ref.unref();
}
#else
_get_obj().obj = NULL; _get_obj().obj = NULL;
_get_obj().ref.unref(); _get_obj().ref.unref();
#endif
} break; } break;
case _RID: { case _RID: {
// not much need probably // not much need probably
@ -1580,19 +1597,17 @@ String Variant::stringify(List<const void *> &stack) const {
} break; } break;
case OBJECT: { case OBJECT: {
if (_get_obj().obj) { Object *obj = _OBJ_PTR(*this);
if (obj) {
return obj->to_string();
} else {
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null()) { if (ScriptDebugger::get_singleton() && _get_obj().instance_id != 0 && !ObjectDB::get_instance(_get_obj().instance_id)) {
//only if debugging! return "[Deleted Object]";
if (!ObjectDB::instance_validate(_get_obj().obj)) { }
return "[Deleted Object]";
};
};
#endif #endif
return _get_obj().obj->to_string();
} else
return "[Object:null]"; return "[Object:null]";
}
} break; } break;
default: { default: {
return "[" + get_type_name(type) + "]"; return "[" + get_type_name(type) + "]";
@ -1741,22 +1756,33 @@ Variant::operator RefPtr() const {
Variant::operator RID() const { Variant::operator RID() const {
if (type == _RID) if (type == _RID) {
return *reinterpret_cast<const RID *>(_data._mem); return *reinterpret_cast<const RID *>(_data._mem);
else if (type == OBJECT && !_get_obj().ref.is_null()) { } else if (type == OBJECT) {
return _get_obj().ref.get_rid(); if (!_get_obj().ref.is_null()) {
} else if (type == OBJECT && _get_obj().obj) { return _get_obj().ref.get_rid();
} else {
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
if (ScriptDebugger::get_singleton()) { Object *obj = _get_obj().rc->get_ptr();
ERR_FAIL_COND_V_MSG(!ObjectDB::instance_validate(_get_obj().obj), RID(), "Invalid pointer (object was deleted)."); if (unlikely(!obj)) {
}; if (ScriptDebugger::get_singleton() && _get_obj().instance_id != 0 && !ObjectDB::get_instance(_get_obj().instance_id)) {
WARN_PRINT("Attempted get RID on a deleted object.");
}
}
#else
Object *obj = _get_obj().obj;
if (unlikely(!obj)) {
return RID();
}
#endif #endif
Variant::CallError ce; Variant::CallError ce;
Variant ret = _get_obj().obj->call(CoreStringNames::get_singleton()->get_rid, NULL, 0, ce); Variant ret = obj->call(CoreStringNames::get_singleton()->get_rid, NULL, 0, ce);
if (ce.error == Variant::CallError::CALL_OK && ret.get_type() == Variant::_RID) { if (ce.error == Variant::CallError::CALL_OK && ret.get_type() == Variant::_RID) {
return ret; return ret;
} else {
return RID();
}
} }
return RID();
} else { } else {
return RID(); return RID();
} }
@ -1765,23 +1791,33 @@ Variant::operator RID() const {
Variant::operator Object *() const { Variant::operator Object *() const {
if (type == OBJECT) if (type == OBJECT)
return _get_obj().obj; return _OBJ_PTR(*this);
else else
return NULL; return NULL;
} }
Variant::operator Node *() const { Variant::operator Node *() const {
if (type == OBJECT) if (type == OBJECT) {
return Object::cast_to<Node>(_get_obj().obj); #ifdef DEBUG_ENABLED
else Object *obj = _get_obj().rc ? _get_obj().rc->get_ptr() : NULL;
return NULL; #else
Object *obj = _get_obj().obj;
#endif
return Object::cast_to<Node>(obj);
}
return NULL;
} }
Variant::operator Control *() const { Variant::operator Control *() const {
if (type == OBJECT) if (type == OBJECT) {
return Object::cast_to<Control>(_get_obj().obj); #ifdef DEBUG_ENABLED
else Object *obj = _get_obj().rc ? _get_obj().rc->get_ptr() : NULL;
return NULL; #else
Object *obj = _get_obj().obj;
#endif
return Object::cast_to<Control>(obj);
}
return NULL;
} }
Variant::operator Dictionary() const { Variant::operator Dictionary() const {
@ -2280,8 +2316,13 @@ Variant::Variant(const RefPtr &p_resource) {
type = OBJECT; type = OBJECT;
memnew_placement(_data._mem, ObjData); memnew_placement(_data._mem, ObjData);
#ifdef DEBUG_ENABLED
_get_obj().rc = NULL;
_get_obj().instance_id = 0;
#else
REF *ref = reinterpret_cast<REF *>(p_resource.get_data()); REF *ref = reinterpret_cast<REF *>(p_resource.get_data());
_get_obj().obj = ref->ptr(); _get_obj().obj = ref->ptr();
#endif
_get_obj().ref = p_resource; _get_obj().ref = p_resource;
} }
@ -2296,7 +2337,12 @@ Variant::Variant(const Object *p_object) {
type = OBJECT; type = OBJECT;
memnew_placement(_data._mem, ObjData); memnew_placement(_data._mem, ObjData);
#ifdef DEBUG_ENABLED
_get_obj().rc = p_object ? const_cast<Object *>(p_object)->_use_rc() : NULL;
_get_obj().instance_id = p_object ? p_object->get_instance_id() : 0;
#else
_get_obj().obj = const_cast<Object *>(p_object); _get_obj().obj = const_cast<Object *>(p_object);
#endif
} }
Variant::Variant(const Dictionary &p_dictionary) { Variant::Variant(const Dictionary &p_dictionary) {
@ -2608,6 +2654,11 @@ void Variant::operator=(const Variant &p_variant) {
case OBJECT: { case OBJECT: {
*reinterpret_cast<ObjData *>(_data._mem) = p_variant._get_obj(); *reinterpret_cast<ObjData *>(_data._mem) = p_variant._get_obj();
#ifdef DEBUG_ENABLED
if (_get_obj().rc) {
_get_obj().rc->increment();
}
#endif
} break; } break;
case NODE_PATH: { case NODE_PATH: {
@ -2805,7 +2856,7 @@ uint32_t Variant::hash() const {
} break; } break;
case OBJECT: { case OBJECT: {
return hash_djb2_one_64(make_uint64_t(_get_obj().obj)); return hash_djb2_one_64(make_uint64_t(_OBJ_PTR(*this)));
} break; } break;
case NODE_PATH: { case NODE_PATH: {

View file

@ -44,13 +44,14 @@
#include "core/math/transform_2d.h" #include "core/math/transform_2d.h"
#include "core/math/vector3.h" #include "core/math/vector3.h"
#include "core/node_path.h" #include "core/node_path.h"
#include "core/object_id.h"
#include "core/pool_vector.h" #include "core/pool_vector.h"
#include "core/ref_ptr.h" #include "core/ref_ptr.h"
#include "core/rid.h" #include "core/rid.h"
#include "core/ustring.h" #include "core/ustring.h"
class RefPtr;
class Object; class Object;
class ObjectRC;
class Node; // helper class Node; // helper
class Control; // helper class Control; // helper
@ -72,6 +73,13 @@ typedef PoolVector<Color> PoolColorArray;
#define GCC_ALIGNED_8 #define GCC_ALIGNED_8
#endif #endif
#ifdef DEBUG_ENABLED
// Ideally, an inline member of ObjectRC, but would cause circular includes
#define _OBJ_PTR(m_variant) ((m_variant)._get_obj().rc ? (m_variant)._get_obj().rc->get_ptr() : reinterpret_cast<Ref<Reference> *>((m_variant)._get_obj().ref.get_data())->ptr())
#else
#define _OBJ_PTR(m_variant) ((m_variant)._get_obj().obj)
#endif
class Variant { class Variant {
public: public:
// If this changes the table in variant_op must be updated // If this changes the table in variant_op must be updated
@ -127,7 +135,21 @@ private:
struct ObjData { struct ObjData {
#ifdef DEBUG_ENABLED
// Will be null for every type deriving from Reference as they have their
// own reference count mechanism
ObjectRC *rc;
// This is for allowing debug build to check for instance ID validity,
// so warnings are shown in debug builds when a stray Variant (one pointing
// to a released Object) would have happened.
// If it's zero, that means the Variant is has a legit null object value,
// thus not needing instance id validation.
ObjectID instance_id;
#else
Object *obj; Object *obj;
#endif
// Always initialized, but will be null if the Ref<> assigned was null
// or this Variant is not even holding a Reference-derived object
RefPtr ref; RefPtr ref;
}; };

View file

@ -35,6 +35,7 @@
#include "core/crypto/crypto_core.h" #include "core/crypto/crypto_core.h"
#include "core/io/compression.h" #include "core/io/compression.h"
#include "core/object.h" #include "core/object.h"
#include "core/object_rc.h"
#include "core/os/os.h" #include "core/os/os.h"
#include "core/script_language.h" #include "core/script_language.h"
@ -1094,22 +1095,18 @@ void Variant::call_ptr(const StringName &p_method, const Variant **p_args, int p
if (type == Variant::OBJECT) { if (type == Variant::OBJECT) {
//call object //call object
Object *obj = _get_obj().obj; Object *obj = _OBJ_PTR(*this);
if (!obj) { if (!obj) {
#ifdef DEBUG_ENABLED
if (ScriptDebugger::get_singleton() && _get_obj().instance_id != 0 && !ObjectDB::get_instance(_get_obj().instance_id)) {
WARN_PRINT("Attempted call on stray pointer object.");
}
#endif
r_error.error = CallError::CALL_ERROR_INSTANCE_IS_NULL; r_error.error = CallError::CALL_ERROR_INSTANCE_IS_NULL;
return; return;
} }
#ifdef DEBUG_ENABLED
if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null()) {
//only if debugging!
if (!ObjectDB::instance_validate(obj)) {
r_error.error = CallError::CALL_ERROR_INSTANCE_IS_NULL;
return;
}
}
#endif ret = obj->call(p_method, p_args, p_argcount, r_error);
ret = _get_obj().obj->call(p_method, p_args, p_argcount, r_error);
//else if (type==Variant::METHOD) { //else if (type==Variant::METHOD) {
@ -1275,18 +1272,16 @@ Variant Variant::construct(const Variant::Type p_type, const Variant **p_args, i
bool Variant::has_method(const StringName &p_method) const { bool Variant::has_method(const StringName &p_method) const {
if (type == OBJECT) { if (type == OBJECT) {
Object *obj = operator Object *(); Object *obj = _OBJ_PTR(*this);
if (!obj) if (!obj) {
return false;
#ifdef DEBUG_ENABLED
if (ScriptDebugger::get_singleton()) {
if (ObjectDB::instance_validate(obj)) {
#endif
return obj->has_method(p_method);
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
if (ScriptDebugger::get_singleton() && _get_obj().instance_id != 0 && !ObjectDB::get_instance(_get_obj().instance_id)) {
WARN_PRINT("Attempted method check on stray pointer object.");
} }
}
#endif #endif
return false;
}
return obj->has_method(p_method);
} }
const _VariantCall::TypeFunc &tf = _VariantCall::type_funcs[type]; const _VariantCall::TypeFunc &tf = _VariantCall::type_funcs[type];

View file

@ -32,6 +32,7 @@
#include "core/core_string_names.h" #include "core/core_string_names.h"
#include "core/object.h" #include "core/object.h"
#include "core/object_rc.h"
#include "core/script_language.h" #include "core/script_language.h"
#define CASE_TYPE_ALL(PREFIX, OP) \ #define CASE_TYPE_ALL(PREFIX, OP) \
@ -399,7 +400,7 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a,
CASE_TYPE(math, OP_EQUAL, NIL) { CASE_TYPE(math, OP_EQUAL, NIL) {
if (p_b.type == NIL) _RETURN(true); if (p_b.type == NIL) _RETURN(true);
if (p_b.type == OBJECT) if (p_b.type == OBJECT)
_RETURN(p_b._get_obj().obj == NULL); _RETURN(_OBJ_PTR(p_b) == NULL);
_RETURN(false); _RETURN(false);
} }
@ -416,9 +417,9 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a,
CASE_TYPE(math, OP_EQUAL, OBJECT) { CASE_TYPE(math, OP_EQUAL, OBJECT) {
if (p_b.type == OBJECT) if (p_b.type == OBJECT)
_RETURN((p_a._get_obj().obj == p_b._get_obj().obj)); _RETURN(_OBJ_PTR(p_a) == _OBJ_PTR(p_b));
if (p_b.type == NIL) if (p_b.type == NIL)
_RETURN(p_a._get_obj().obj == NULL); _RETURN(_OBJ_PTR(p_a) == NULL);
_RETURN_FAIL; _RETURN_FAIL;
} }
@ -486,7 +487,7 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a,
CASE_TYPE(math, OP_NOT_EQUAL, NIL) { CASE_TYPE(math, OP_NOT_EQUAL, NIL) {
if (p_b.type == NIL) _RETURN(false); if (p_b.type == NIL) _RETURN(false);
if (p_b.type == OBJECT) if (p_b.type == OBJECT)
_RETURN(p_b._get_obj().obj != NULL); _RETURN(_OBJ_PTR(p_b) != NULL);
_RETURN(true); _RETURN(true);
} }
@ -504,9 +505,9 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a,
CASE_TYPE(math, OP_NOT_EQUAL, OBJECT) { CASE_TYPE(math, OP_NOT_EQUAL, OBJECT) {
if (p_b.type == OBJECT) if (p_b.type == OBJECT)
_RETURN((p_a._get_obj().obj != p_b._get_obj().obj)); _RETURN((_OBJ_PTR(p_a) != _OBJ_PTR(p_b)));
if (p_b.type == NIL) if (p_b.type == NIL)
_RETURN(p_a._get_obj().obj != NULL); _RETURN(_OBJ_PTR(p_a) != NULL);
_RETURN_FAIL; _RETURN_FAIL;
} }
@ -589,7 +590,7 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a,
CASE_TYPE(math, OP_LESS, OBJECT) { CASE_TYPE(math, OP_LESS, OBJECT) {
if (p_b.type != OBJECT) if (p_b.type != OBJECT)
_RETURN_FAIL; _RETURN_FAIL;
_RETURN((p_a._get_obj().obj < p_b._get_obj().obj)); _RETURN(_OBJ_PTR(p_a) < _OBJ_PTR(p_b));
} }
CASE_TYPE(math, OP_LESS, ARRAY) { CASE_TYPE(math, OP_LESS, ARRAY) {
@ -643,7 +644,7 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a,
CASE_TYPE(math, OP_LESS_EQUAL, OBJECT) { CASE_TYPE(math, OP_LESS_EQUAL, OBJECT) {
if (p_b.type != OBJECT) if (p_b.type != OBJECT)
_RETURN_FAIL; _RETURN_FAIL;
_RETURN((p_a._get_obj().obj <= p_b._get_obj().obj)); _RETURN(_OBJ_PTR(p_a) <= _OBJ_PTR(p_b));
} }
DEFAULT_OP_NUM(math, OP_LESS_EQUAL, INT, <=, _int); DEFAULT_OP_NUM(math, OP_LESS_EQUAL, INT, <=, _int);
@ -693,7 +694,7 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a,
CASE_TYPE(math, OP_GREATER, OBJECT) { CASE_TYPE(math, OP_GREATER, OBJECT) {
if (p_b.type != OBJECT) if (p_b.type != OBJECT)
_RETURN_FAIL; _RETURN_FAIL;
_RETURN((p_a._get_obj().obj > p_b._get_obj().obj)); _RETURN(_OBJ_PTR(p_a) > _OBJ_PTR(p_b));
} }
CASE_TYPE(math, OP_GREATER, ARRAY) { CASE_TYPE(math, OP_GREATER, ARRAY) {
@ -747,7 +748,7 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a,
CASE_TYPE(math, OP_GREATER_EQUAL, OBJECT) { CASE_TYPE(math, OP_GREATER_EQUAL, OBJECT) {
if (p_b.type != OBJECT) if (p_b.type != OBJECT)
_RETURN_FAIL; _RETURN_FAIL;
_RETURN((p_a._get_obj().obj >= p_b._get_obj().obj)); _RETURN(_OBJ_PTR(p_a) >= _OBJ_PTR(p_b));
} }
DEFAULT_OP_NUM(math, OP_GREATER_EQUAL, INT, >=, _int); DEFAULT_OP_NUM(math, OP_GREATER_EQUAL, INT, >=, _int);
@ -1512,15 +1513,16 @@ void Variant::set_named(const StringName &p_index, const Variant &p_value, bool
} break; } break;
case OBJECT: { case OBJECT: {
Object *obj = _OBJ_PTR(*this);
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
if (!_get_obj().obj) { if (unlikely(!obj)) {
break; if (ScriptDebugger::get_singleton() && _get_obj().instance_id != 0 && !ObjectDB::get_instance(_get_obj().instance_id)) {
} else if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null() && !ObjectDB::instance_validate(_get_obj().obj)) { WARN_PRINT("Attempted set on a deleted object.");
}
break; break;
} }
#endif #endif
_get_obj().obj->set(p_index, p_value, &valid); obj->set(p_index, p_value, &valid);
} break; } break;
default: { default: {
@ -1677,23 +1679,19 @@ Variant Variant::get_named(const StringName &p_index, bool *r_valid) const {
} break; } break;
case OBJECT: { case OBJECT: {
Object *obj = _OBJ_PTR(*this);
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
if (!_get_obj().obj) { if (unlikely(!obj)) {
if (r_valid) if (r_valid)
*r_valid = false; *r_valid = false;
return "Instance base is null."; if (ScriptDebugger::get_singleton() && _get_obj().instance_id != 0 && !ObjectDB::get_instance(_get_obj().instance_id)) {
} else { WARN_PRINT("Attempted get on a deleted object.");
if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null() && !ObjectDB::instance_validate(_get_obj().obj)) {
if (r_valid)
*r_valid = false;
return "Attempted use of stray pointer object.";
} }
return Variant();
} }
#endif #endif
return _get_obj().obj->get(p_index, r_valid); return obj->get(p_index, r_valid);
} break; } break;
default: { default: {
@ -2167,29 +2165,24 @@ void Variant::set(const Variant &p_index, const Variant &p_value, bool *r_valid)
} break; } break;
case OBJECT: { case OBJECT: {
Object *obj = _get_obj().obj; Object *obj = _OBJ_PTR(*this);
//only if debugging! if (unlikely(!obj)) {
if (obj) {
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null()) { valid = false;
if (ScriptDebugger::get_singleton() && _get_obj().instance_id != 0 && !ObjectDB::get_instance(_get_obj().instance_id)) {
if (!ObjectDB::instance_validate(obj)) { WARN_PRINT("Attempted set on a deleted object.");
WARN_PRINT("Attempted use of stray pointer object.");
valid = false;
return;
}
} }
#endif #endif
if (p_index.get_type() != Variant::STRING) {
obj->setvar(p_index, p_value, r_valid);
return;
}
obj->set(p_index, p_value, r_valid);
return; return;
} }
if (p_index.get_type() != Variant::STRING) {
obj->setvar(p_index, p_value, r_valid);
return;
}
obj->set(p_index, p_value, r_valid);
return;
} break; } break;
case DICTIONARY: { case DICTIONARY: {
@ -2542,23 +2535,20 @@ Variant Variant::get(const Variant &p_index, bool *r_valid) const {
case _RID: { case _RID: {
} break; } break;
case OBJECT: { case OBJECT: {
Object *obj = _get_obj().obj; Object *obj = _OBJ_PTR(*this);
if (obj) { if (unlikely(!obj)) {
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null()) { valid = false;
//only if debugging! if (ScriptDebugger::get_singleton() && _get_obj().instance_id != 0 && !ObjectDB::get_instance(_get_obj().instance_id)) {
if (!ObjectDB::instance_validate(obj)) { WARN_PRINT("Attempted get on a deleted object.");
valid = false;
return "Attempted get on stray pointer.";
}
} }
#endif #endif
return Variant();
}
if (p_index.get_type() != Variant::STRING) { if (p_index.get_type() != Variant::STRING) {
return obj->getvar(p_index, r_valid); return obj->getvar(p_index, r_valid);
} } else {
return obj->get(p_index, r_valid); return obj->get(p_index, r_valid);
} }
@ -2606,34 +2596,26 @@ bool Variant::in(const Variant &p_index, bool *r_valid) const {
} break; } break;
case OBJECT: { case OBJECT: {
Object *obj = _get_obj().obj; Object *obj = _OBJ_PTR(*this);
if (obj) { if (unlikely(!obj)) {
bool valid = false;
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null()) { if (r_valid) {
//only if debugging! *r_valid = false;
if (!ObjectDB::instance_validate(obj)) { }
if (r_valid) { if (ScriptDebugger::get_singleton() && _get_obj().instance_id != 0 && !ObjectDB::get_instance(_get_obj().instance_id)) {
*r_valid = false; WARN_PRINT("Attempted 'in' on a deleted object.");
}
return true; // Attempted get on stray pointer.
}
} }
#endif #endif
return false;
if (p_index.get_type() != Variant::STRING) {
obj->getvar(p_index, &valid);
} else {
obj->get(p_index, &valid);
}
return valid;
} else {
if (r_valid)
*r_valid = false;
} }
return false;
bool result;
if (p_index.get_type() != Variant::STRING) {
obj->getvar(p_index, &result);
} else {
obj->get(p_index, &result);
}
return result;
} break; } break;
case DICTIONARY: { case DICTIONARY: {
@ -2880,21 +2862,17 @@ void Variant::get_property_list(List<PropertyInfo> *p_list) const {
} break; } break;
case OBJECT: { case OBJECT: {
Object *obj = _get_obj().obj; Object *obj = _OBJ_PTR(*this);
if (obj) { if (unlikely(!obj)) {
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null()) { if (ScriptDebugger::get_singleton() && _get_obj().instance_id != 0 && !ObjectDB::get_instance(_get_obj().instance_id)) {
//only if debugging! WARN_PRINT("Attempted get property list on a deleted object.");
if (!ObjectDB::instance_validate(obj)) {
WARN_PRINT("Attempted get_property list on stray pointer.");
return;
}
} }
#endif #endif
return;
obj->get_property_list(p_list);
} }
obj->get_property_list(p_list);
} break; } break;
case DICTIONARY: { case DICTIONARY: {
@ -2961,14 +2939,13 @@ bool Variant::iter_init(Variant &r_iter, bool &valid) const {
} break; } break;
case OBJECT: { case OBJECT: {
Object *obj = _OBJ_PTR(*this);
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
if (!_get_obj().obj) { if (unlikely(!obj)) {
valid = false;
return false;
}
if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null() && !ObjectDB::instance_validate(_get_obj().obj)) {
valid = false; valid = false;
if (ScriptDebugger::get_singleton() && _get_obj().instance_id != 0 && !ObjectDB::get_instance(_get_obj().instance_id)) {
WARN_PRINT("Attempted iteration start on a deleted object.");
}
return false; return false;
} }
#endif #endif
@ -2978,7 +2955,7 @@ bool Variant::iter_init(Variant &r_iter, bool &valid) const {
ref.push_back(r_iter); ref.push_back(r_iter);
Variant vref = ref; Variant vref = ref;
const Variant *refp[] = { &vref }; const Variant *refp[] = { &vref };
Variant ret = _get_obj().obj->call(CoreStringNames::get_singleton()->_iter_init, refp, 1, ce); Variant ret = obj->call(CoreStringNames::get_singleton()->_iter_init, refp, 1, ce);
if (ref.size() != 1 || ce.error != Variant::CallError::CALL_OK) { if (ref.size() != 1 || ce.error != Variant::CallError::CALL_OK) {
valid = false; valid = false;
@ -3129,14 +3106,13 @@ bool Variant::iter_next(Variant &r_iter, bool &valid) const {
} break; } break;
case OBJECT: { case OBJECT: {
Object *obj = _OBJ_PTR(*this);
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
if (!_get_obj().obj) { if (unlikely(!obj)) {
valid = false;
return false;
}
if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null() && !ObjectDB::instance_validate(_get_obj().obj)) {
valid = false; valid = false;
if (ScriptDebugger::get_singleton() && _get_obj().instance_id != 0 && !ObjectDB::get_instance(_get_obj().instance_id)) {
WARN_PRINT("Attempted iteration check next on a deleted object.");
}
return false; return false;
} }
#endif #endif
@ -3146,7 +3122,7 @@ bool Variant::iter_next(Variant &r_iter, bool &valid) const {
ref.push_back(r_iter); ref.push_back(r_iter);
Variant vref = ref; Variant vref = ref;
const Variant *refp[] = { &vref }; const Variant *refp[] = { &vref };
Variant ret = _get_obj().obj->call(CoreStringNames::get_singleton()->_iter_next, refp, 1, ce); Variant ret = obj->call(CoreStringNames::get_singleton()->_iter_next, refp, 1, ce);
if (ref.size() != 1 || ce.error != Variant::CallError::CALL_OK) { if (ref.size() != 1 || ce.error != Variant::CallError::CALL_OK) {
valid = false; valid = false;
@ -3288,21 +3264,20 @@ Variant Variant::iter_get(const Variant &r_iter, bool &r_valid) const {
} break; } break;
case OBJECT: { case OBJECT: {
Object *obj = _OBJ_PTR(*this);
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
if (!_get_obj().obj) { if (unlikely(!obj)) {
r_valid = false;
return Variant();
}
if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null() && !ObjectDB::instance_validate(_get_obj().obj)) {
r_valid = false; r_valid = false;
if (ScriptDebugger::get_singleton() && _get_obj().instance_id != 0 && !ObjectDB::get_instance(_get_obj().instance_id)) {
WARN_PRINT("Attempted iteration get next on a deleted object.");
}
return Variant(); return Variant();
} }
#endif #endif
Variant::CallError ce; Variant::CallError ce;
ce.error = Variant::CallError::CALL_OK; ce.error = Variant::CallError::CALL_OK;
const Variant *refp[] = { &r_iter }; const Variant *refp[] = { &r_iter };
Variant ret = _get_obj().obj->call(CoreStringNames::get_singleton()->_iter_get, refp, 1, ce); Variant ret = obj->call(CoreStringNames::get_singleton()->_iter_get, refp, 1, ce);
if (ce.error != Variant::CallError::CALL_OK) { if (ce.error != Variant::CallError::CALL_OK) {
r_valid = false; r_valid = false;

View file

@ -2884,15 +2884,15 @@ Dictionary RichTextLabel::parse_expressions_for_values(Vector<String> p_expressi
Vector<String> values = parts[1].split(",", false); Vector<String> values = parts[1].split(",", false);
RegEx color = RegEx(); RegEx color;
color.compile("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$"); color.compile("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$");
RegEx nodepath = RegEx(); RegEx nodepath;
nodepath.compile("^\\$"); nodepath.compile("^\\$");
RegEx boolean = RegEx(); RegEx boolean;
boolean.compile("^(true|false)$"); boolean.compile("^(true|false)$");
RegEx decimal = RegEx(); RegEx decimal;
decimal.compile("^-?^.?\\d+(\\.\\d+?)?$"); decimal.compile("^-?^.?\\d+(\\.\\d+?)?$");
RegEx numerical = RegEx(); RegEx numerical;
numerical.compile("^\\d+$"); numerical.compile("^\\d+$");
for (int j = 0; j < values.size(); j++) { for (int j = 0; j < values.size(); j++) {