- Backend update from GZDoom

* scriptable CVARs.
* GLES update
* various ZScript improvements.
This commit is contained in:
Christoph Oelckers 2023-02-11 12:06:58 +01:00
parent 565f1ed416
commit 8c99d7b034
31 changed files with 1022 additions and 276 deletions

View file

@ -108,7 +108,7 @@ template<typename T>
using expand_types_vm =
std::conditional_t<std::is_same_v<T, uint8_t> || std::is_same_v<T, uint16_t>, uint32_t , /* expand 8/16-bit to 32-bit */
std::conditional_t<std::is_same_v<T, float> , double , /* expand float to double */
std::conditional_t<std::is_same_v<T, FString> , const FString & , T>>>; /* change String to String ref */
std::conditional_t<std::is_same_v<T, FString> , const FString & , T>>>; /* change String to String ref */
template<typename M> unsigned int MapCountUsed(M * self)
{
@ -118,13 +118,22 @@ template<typename M> unsigned int MapCountUsed(M * self)
template<typename M> expand_types_vm<typename M::ValueType> MapGet(M * self,expand_types_vm<typename M::KeyType> key)
{
typename M::ValueType * v = self->CheckKey(key);
if (v) {
return *v;
if (v)
{
if constexpr(std::is_same_v<typename M::ValueType, DObject*>)
{
return GC::ReadBarrier(*v);
}
else
{
return *v;
}
}
else
{
typename M::ValueType n {};
self->Insert(key,n);
self->info->rev++; // invalidate iterators
return n;
}
}
@ -139,6 +148,38 @@ template<typename M> void MapGetString(M * self,expand_types_vm<typename M::KeyT
{
out = FString();
self->Insert(key,out);
self->info->rev++; // invalidate iterators
}
}
template<typename M> expand_types_vm<typename M::ValueType> MapGetIfExists(M * self,expand_types_vm<typename M::KeyType> key)
{
typename M::ValueType * v = self->CheckKey(key);
if (v) {
if constexpr(std::is_same_v<typename M::ValueType, DObject*>)
{
return GC::ReadBarrier(*v);
}
else
{
return *v;
}
}
else
{
return {};
}
}
template<typename M> void MapGetIfExistsString(M * self,expand_types_vm<typename M::KeyType> key, FString &out)
{
FString * v = self->CheckKey(key);
if (v) {
out = *v;
}
else
{
out = FString();
}
}
@ -147,6 +188,37 @@ template<typename M> int MapCheckKey(M * self, expand_types_vm<typename M::KeyTy
return self->CheckKey(key) != nullptr;
}
template<typename M> expand_types_vm<typename M::ValueType> MapCheckValue(M * self,expand_types_vm<typename M::KeyType> key, int &exists)
{
typename M::ValueType * v = self->CheckKey(key);
if ((exists = !!v)) {
if constexpr(std::is_same_v<typename M::ValueType, DObject*>)
{
return GC::ReadBarrier(*v);
}
else
{
return *v;
}
}
else
{
return {};
}
}
template<typename M> void MapCheckValueString(M * self,expand_types_vm<typename M::KeyType> key, FString &out, int &exists)
{
FString * v = self->CheckKey(key);
if ((exists = !!v)) {
out = *v;
}
else
{
out = FString();
}
}
//==========================================================================
//
@ -238,7 +310,14 @@ template<typename I> void MapIteratorGetKeyString(I * self, FString &out)
template<typename I> expand_types_vm<typename I::ValueType> MapIteratorGetValue(I * self)
{
return self->GetValue();
if constexpr(std::is_same_v<typename I::ValueType, DObject*>)
{
return GC::ReadBarrier(self->GetValue());
}
else
{
return self->GetValue();
}
}
template<typename I> void MapIteratorGetValueString(I * self, FString &out)
@ -272,6 +351,25 @@ template<typename I> void MapIteratorSetValue(I * self, expand_types_vm<typename
//
//==========================================================================
template<int N, typename T, typename U> void SetValType(T & ret,U & val){
if constexpr(std::is_same_v<U, DObject*>)
{
ret[N].SetObject(val);
}
else if constexpr(std::is_same_v<U, void*>)
{
ret[N].SetPointer(val);
}
else if constexpr(std::is_same_v<U, uint32_t>)
{
ret[N].SetInt(val);
}
else if constexpr(std::is_same_v<U, double>)
{
ret[N].SetFloat(val);
}
}
#define PARAM_VOIDPOINTER(X) PARAM_POINTER( X , void )
#define PARAM_OBJPOINTER(X) PARAM_OBJECT( X , DObject )
@ -345,6 +443,23 @@ template<typename I> void MapIteratorSetValue(I * self, expand_types_vm<typename
PARAM_SELF_STRUCT_PROLOGUE( name ); \
PARAM_KEY( key ); \
ACTION_RETURN_VALUE( MapGet(self, key) ); \
} \
DEFINE_ACTION_FUNCTION_NATIVE( name, GetIfExists, MapGetIfExists< name >) \
{ \
PARAM_SELF_STRUCT_PROLOGUE( name ); \
PARAM_KEY( key ); \
ACTION_RETURN_VALUE( MapGetIfExists(self, key) ); \
} \
DEFINE_ACTION_FUNCTION_NATIVE( name, CheckValue, MapCheckValue< name >) \
{ \
PARAM_SELF_STRUCT_PROLOGUE( name ); \
PARAM_KEY( key ); \
int exists; \
expand_types_vm<value_type> out; \
out = MapCheckValue(self, key, exists); \
if (numret > 1) ret[1].SetInt(exists); \
if (numret > 0) SetValType<0>(ret, out); \
return numret; \
}
#define DEF_MAP_X_S( name, key_type, PARAM_KEY ) \
@ -356,6 +471,25 @@ template<typename I> void MapIteratorSetValue(I * self, expand_types_vm<typename
FString out; \
MapGetString(self, key, out); \
ACTION_RETURN_STRING( out ); \
} \
DEFINE_ACTION_FUNCTION_NATIVE( name, GetIfExists, MapGetIfExistsString< name >) \
{ \
PARAM_SELF_STRUCT_PROLOGUE( name ); \
PARAM_KEY( key ); \
FString out; \
MapGetIfExistsString(self, key, out); \
ACTION_RETURN_STRING( out ); \
} \
DEFINE_ACTION_FUNCTION_NATIVE( name, CheckValue, MapCheckValueString< name >) \
{ \
PARAM_SELF_STRUCT_PROLOGUE( name ); \
PARAM_KEY( key ); \
int exists; \
FString out; \
MapCheckValueString(self, key, out, exists); \
if (numret > 1) ret[1].SetInt(exists); \
if (numret > 0) ret[0].SetString(out); \
return numret; \
}
#define COMMA ,

View file

@ -211,6 +211,7 @@ void FScopeBarrier::AddFlags(int flags1, int flags2, const char* name)
// these are for vmexec.h
void FScopeBarrier::ValidateNew(PClass* cls, int outerside)
{
if (cls->VMType == nullptr) ThrowAbortException(X_OTHER,"Cannot instantiate invalid class %s", cls->TypeName.GetChars());
int innerside = FScopeBarrier::SideFromObjectFlags(cls->VMType->ScopeFlags);
if ((outerside != innerside) && (innerside != FScopeBarrier::Side_PlainData)) // "cannot construct ui class ... from data context"
ThrowAbortException(X_OTHER, "Cannot construct %s class %s from %s context", FScopeBarrier::StringFromSide(innerside), cls->TypeName.GetChars(), FScopeBarrier::StringFromSide(outerside));

View file

@ -137,7 +137,6 @@ PField::PField()
{
}
PField::PField(FName name, PType *type, uint32_t flags, size_t offset, int bitvalue)
: PSymbol(name), Offset(offset), Type(type), Flags(flags)
{
@ -331,10 +330,12 @@ PSymbol *PSymbolTable::AddSymbol (PSymbol *sym)
//
//==========================================================================
PField *PSymbolTable::AddField(FName name, PType *type, uint32_t flags, unsigned &Size, unsigned *Align)
PField *PSymbolTable::AddField(FName name, PType *type, uint32_t flags, unsigned &Size, unsigned *Align, int fileno)
{
PField *field = Create<PField>(name, type, flags);
field->mDefFileNo = fileno;
// The new field is added to the end of this struct, alignment permitting.
field->Offset = (Size + (type->Align - 1)) & ~(type->Align - 1);
@ -365,10 +366,12 @@ PField *PSymbolTable::AddField(FName name, PType *type, uint32_t flags, unsigned
//
//==========================================================================
PField *PSymbolTable::AddNativeField(FName name, PType *type, size_t address, uint32_t flags, int bitvalue)
PField *PSymbolTable::AddNativeField(FName name, PType *type, size_t address, uint32_t flags, int bitvalue, int fileno)
{
PField *field = Create<PField>(name, type, flags | VARF_Native | VARF_Transient, address, bitvalue);
field->mDefFileNo = fileno;
if (AddSymbol(field) == nullptr)
{ // name is already in use
field->Destroy();

View file

@ -78,6 +78,7 @@ public:
uint32_t Flags;
int BitValue;
FString DeprecationMessage;
int mDefFileNo = 0;
protected:
PField();
};
@ -212,8 +213,8 @@ struct PSymbolTable
// a symbol with the same name is already in the table. This symbol is
// not copied and will be freed when the symbol table is destroyed.
PSymbol *AddSymbol (PSymbol *sym);
PField *AddField(FName name, PType *type, uint32_t flags, unsigned &Size, unsigned *Align = nullptr);
PField *AddNativeField(FName name, PType *type, size_t address, uint32_t flags, int bitvalue);
PField *AddField(FName name, PType *type, uint32_t flags, unsigned &Size, unsigned *Align = nullptr, int fileno = 0);
PField *AddNativeField(FName name, PType *type, size_t address, uint32_t flags, int bitvalue, int fileno = 0);
bool ReadFields(FSerializer &ar, void *addr, const char *TypeName) const;
void WriteFields(FSerializer &ar, const void *addr, const void *def = nullptr) const;

View file

@ -1985,12 +1985,114 @@ PStaticArray *NewStaticArray(PType *type)
//
//==========================================================================
enum OverrideFunctionRetType {
OFN_RET_VOID,
OFN_RET_VAL,
OFN_RET_KEY,
OFN_RET_BOOL,
OFN_RET_VAL_BOOL,
OFN_RET_INT,
};
enum OverrideFunctionArgType {
OFN_ARG_VOID,
OFN_ARG_KEY,
OFN_ARG_VAL,
OFN_ARG_KEY_VAL,
OFN_ARG_ELEM,
OFN_ARG_INT_ELEM,
};
template<OverrideFunctionRetType RetType, OverrideFunctionArgType ArgType , int ExtraFlags = 0, class MT>
void CreateOverrideFunction(MT *self, FName name)
{
auto Fn = Create<PFunction>(self->BackingType, name);
auto NativeFn = FindFunction(self->BackingType, name.GetChars());
assert(NativeFn);
assert(NativeFn->VMPointer);
TArray<PType*> ret;
TArray<PType*> args;
TArray<uint32_t> argflags;
TArray<FName> argnames;
if constexpr(RetType == OFN_RET_VAL)
{
ret.Push(self->ValueType);
}
else if constexpr(RetType == OFN_RET_KEY)
{
ret.Push(self->KeyType);
}
else if constexpr(RetType == OFN_RET_BOOL)
{
ret.Push(TypeBool);
}
else if constexpr(RetType == OFN_RET_VAL_BOOL)
{
ret.Push(self->ValueType);
ret.Push(TypeBool);
}
else if constexpr(RetType == OFN_RET_INT)
{
ret.Push(TypeSInt32);
}
args.Push(NewPointer(self->BackingType));
argnames.Push(NAME_self);
argflags.Push(VARF_Implicit | VARF_ReadOnly);
if constexpr(ArgType == OFN_ARG_KEY)
{
args.Push(self->KeyType);
argflags.Push(0);
argnames.Push(NAME_Key);
}
else if constexpr(ArgType == OFN_ARG_VAL)
{
args.Push(self->ValueType);
argflags.Push(0);
argnames.Push(NAME_Value);
}
else if constexpr(ArgType == OFN_ARG_KEY_VAL)
{
args.Push(self->KeyType);
args.Push(self->ValueType);
argflags.Push(0);
argflags.Push(0);
argnames.Push(NAME_Key);
argnames.Push(NAME_Value);
}
else if constexpr(ArgType == OFN_ARG_ELEM)
{
args.Push(self->ElementType);
argflags.Push(0);
argnames.Push(NAME_Item);
}
else if constexpr(ArgType == OFN_ARG_INT_ELEM)
{
args.Push(TypeSInt32);
args.Push(self->ElementType);
argflags.Push(0);
argflags.Push(0);
argnames.Push(NAME_Index);
argnames.Push(NAME_Item);
}
Fn->AddVariant(NewPrototype(ret, args), argflags, argnames, *NativeFn->VMPointer, VARF_Method | VARF_Native | ExtraFlags, SUF_ACTOR | SUF_OVERLAY | SUF_WEAPON | SUF_ITEM);
self->FnOverrides.Insert(name, Fn);
}
PDynArray::PDynArray(PType *etype,PStruct *backing)
: ElementType(etype), BackingType(backing)
{
mDescriptiveName.Format("DynArray<%s>", etype->DescriptiveName());
Size = sizeof(FArray);
Align = alignof(FArray);
CreateOverrideFunction<OFN_RET_INT , OFN_ARG_ELEM , VARF_ReadOnly> (this, NAME_Find);
CreateOverrideFunction<OFN_RET_INT , OFN_ARG_ELEM > (this, NAME_Push);
CreateOverrideFunction<OFN_RET_VOID , OFN_ARG_INT_ELEM > (this, NAME_Insert);
}
//==========================================================================
@ -2226,89 +2328,19 @@ PDynArray *NewDynArray(PType *type)
//
//==========================================================================
enum OverrideFunctionRetType {
OFN_RET_VOID,
OFN_RET_VAL,
OFN_RET_KEY,
OFN_RET_BOOL,
};
enum OverrideFunctionArgType {
OFN_ARG_VOID,
OFN_ARG_KEY,
OFN_ARG_VAL,
OFN_ARG_KEY_VAL,
};
template<class MT, OverrideFunctionRetType RetType, OverrideFunctionArgType ArgType >
void CreateOverrideFunction(MT *self, FName name)
{
auto Fn = Create<PFunction>(self->BackingType, name);
auto NativeFn = FindFunction(self->BackingType, name.GetChars());
assert(NativeFn);
assert(NativeFn->VMPointer);
TArray<PType*> ret;
TArray<PType*> args;
TArray<uint32_t> argflags;
TArray<FName> argnames;
if constexpr(RetType == OFN_RET_VAL)
{
ret.Push(self->ValueType);
}
else if constexpr(RetType == OFN_RET_KEY)
{
ret.Push(self->KeyType);
}
else if constexpr(RetType == OFN_RET_BOOL)
{
ret.Push(TypeBool);
}
args.Push(NewPointer(self->BackingType));
argnames.Push(NAME_self);
argflags.Push(VARF_Implicit | VARF_ReadOnly);
if constexpr(ArgType == OFN_ARG_KEY)
{
args.Push(self->KeyType);
argflags.Push(0);
argnames.Push(NAME_Key);
}
else if constexpr(ArgType == OFN_ARG_VAL)
{
args.Push(self->ValueType);
argflags.Push(0);
argnames.Push(NAME_Value);
}
else if constexpr(ArgType == OFN_ARG_KEY_VAL)
{
args.Push(self->KeyType);
args.Push(self->ValueType);
argflags.Push(0);
argflags.Push(0);
argnames.Push(NAME_Key);
argnames.Push(NAME_Value);
}
Fn->AddVariant(NewPrototype(ret, args), argflags, argnames, *NativeFn->VMPointer, VARF_Method | VARF_Native,SUF_ACTOR | SUF_OVERLAY | SUF_WEAPON | SUF_ITEM);
self->FnOverrides.Insert(name, Fn);
}
PMap::PMap(PType *keytype, PType *valtype, PStruct *backing, int backing_class)
: KeyType(keytype), ValueType(valtype), BackingType(backing), BackingClass((decltype(BackingClass)) backing_class)
{
mDescriptiveName.Format("Map<%s, %s>", keytype->DescriptiveName(), valtype->DescriptiveName());
Size = sizeof(ZSFMap);
Align = alignof(ZSFMap);
CreateOverrideFunction<PMap, OFN_RET_VAL, OFN_ARG_KEY>(this, NAME_Get);
CreateOverrideFunction<PMap, OFN_RET_BOOL, OFN_ARG_KEY>(this, NAME_CheckKey);
CreateOverrideFunction<PMap, OFN_RET_VOID, OFN_ARG_KEY_VAL>(this, NAME_Insert);
CreateOverrideFunction<PMap, OFN_RET_VOID, OFN_ARG_KEY>(this, NAME_InsertNew);
CreateOverrideFunction<PMap, OFN_RET_VOID, OFN_ARG_KEY>(this, NAME_Remove);
CreateOverrideFunction< OFN_RET_VAL , OFN_ARG_KEY > (this, NAME_Get);
CreateOverrideFunction< OFN_RET_VAL , OFN_ARG_KEY , VARF_ReadOnly> (this, NAME_GetIfExists);
CreateOverrideFunction< OFN_RET_BOOL , OFN_ARG_KEY , VARF_ReadOnly> (this, NAME_CheckKey);
CreateOverrideFunction< OFN_RET_VAL_BOOL , OFN_ARG_KEY , VARF_ReadOnly> (this, NAME_CheckValue);
CreateOverrideFunction< OFN_RET_VOID , OFN_ARG_KEY_VAL > (this, NAME_Insert);
CreateOverrideFunction< OFN_RET_VOID , OFN_ARG_KEY > (this, NAME_InsertNew);
CreateOverrideFunction< OFN_RET_VOID , OFN_ARG_KEY > (this, NAME_Remove);
}
//==========================================================================
@ -2770,9 +2802,9 @@ PMapIterator::PMapIterator(PType *keytype, PType *valtype, PStruct *backing, int
mDescriptiveName.Format("MapIterator<%s, %s>", keytype->DescriptiveName(), valtype->DescriptiveName());
Size = sizeof(ZSFMap);
Align = alignof(ZSFMap);
CreateOverrideFunction<PMapIterator, OFN_RET_KEY, OFN_ARG_VOID>(this, NAME_GetKey);
CreateOverrideFunction<PMapIterator, OFN_RET_VAL, OFN_ARG_VOID>(this, NAME_GetValue);
CreateOverrideFunction<PMapIterator, OFN_RET_VOID, OFN_ARG_VAL>(this, NAME_SetValue);
CreateOverrideFunction<OFN_RET_KEY, OFN_ARG_VOID>(this, NAME_GetKey);
CreateOverrideFunction<OFN_RET_VAL, OFN_ARG_VOID>(this, NAME_GetValue);
CreateOverrideFunction<OFN_RET_VOID, OFN_ARG_VAL>(this, NAME_SetValue);
}
//==========================================================================
@ -3009,12 +3041,13 @@ PMapIterator *NewMapIterator(PType *keyType, PType *valueType)
//
//==========================================================================
PStruct::PStruct(FName name, PTypeBase *outer, bool isnative)
PStruct::PStruct(FName name, PTypeBase *outer, bool isnative, int fileno)
: PContainerType(name, outer)
{
mDescriptiveName.Format("%sStruct<%s>", isnative? "Native" : "", name.GetChars());
Size = 0;
isNative = isnative;
mDefFileNo = fileno;
}
//==========================================================================
@ -3155,7 +3188,7 @@ PField *PStruct::AddField(FName name, PType *type, uint32_t flags)
PField *PStruct::AddNativeField(FName name, PType *type, size_t address, uint32_t flags, int bitvalue)
{
return Symbols.AddNativeField(name, type, address, flags, bitvalue);
return Symbols.AddNativeField(name, type, address, flags, bitvalue, mDefFileNo);
}
//==========================================================================
@ -3166,14 +3199,14 @@ PField *PStruct::AddNativeField(FName name, PType *type, size_t address, uint32_
//
//==========================================================================
PStruct *NewStruct(FName name, PTypeBase *outer, bool native)
PStruct *NewStruct(FName name, PTypeBase *outer, bool native, int fileno)
{
size_t bucket;
if (outer == nullptr) outer = Namespaces.GlobalNamespace;
PType *stype = TypeTable.FindType(NAME_Struct, (intptr_t)outer, name.GetIndex(), &bucket);
if (stype == nullptr)
{
stype = new PStruct(name, outer, native);
stype = new PStruct(name, outer, native, fileno);
TypeTable.AddType(stype, NAME_Struct, (intptr_t)outer, name.GetIndex(), bucket);
}
return static_cast<PStruct *>(stype);
@ -3271,7 +3304,7 @@ PPrototype *NewPrototype(const TArray<PType *> &rettypes, const TArray<PType *>
//
//==========================================================================
PClassType::PClassType(PClass *cls)
PClassType::PClassType(PClass *cls, int fileno)
{
assert(cls->VMType == nullptr);
Descriptor = cls;
@ -3284,6 +3317,7 @@ PClassType::PClassType(PClass *cls)
ScopeFlags = ParentType->ScopeFlags;
}
cls->VMType = this;
mDefFileNo = fileno;
mDescriptiveName.Format("Class<%s>", cls->TypeName.GetChars());
}
@ -3295,7 +3329,7 @@ PClassType::PClassType(PClass *cls)
PField *PClassType::AddField(FName name, PType *type, uint32_t flags)
{
return Descriptor->AddField(name, type, flags);
return Descriptor->AddField(name, type, flags, mDefFileNo);
}
//==========================================================================
@ -3306,7 +3340,7 @@ PField *PClassType::AddField(FName name, PType *type, uint32_t flags)
PField *PClassType::AddNativeField(FName name, PType *type, size_t address, uint32_t flags, int bitvalue)
{
auto field = Symbols.AddNativeField(name, type, address, flags, bitvalue);
auto field = Symbols.AddNativeField(name, type, address, flags, bitvalue, mDefFileNo);
if (field != nullptr) Descriptor->Fields.Push(field);
return field;
}
@ -3317,13 +3351,13 @@ PField *PClassType::AddNativeField(FName name, PType *type, size_t address, uint
//
//==========================================================================
PClassType *NewClassType(PClass *cls)
PClassType *NewClassType(PClass *cls, int fileno)
{
size_t bucket;
PType *ptype = TypeTable.FindType(NAME_Object, 0, cls->TypeName.GetIndex(), &bucket);
if (ptype == nullptr)
{
ptype = new PClassType(cls);
ptype = new PClassType(cls, fileno);
TypeTable.AddType(ptype, NAME_Object, 0, cls->TypeName.GetIndex(), bucket);
}
return static_cast<PClassType *>(ptype);

View file

@ -512,6 +512,8 @@ public:
PType *ElementType;
PStruct *BackingType;
TMap<FName,PFunction*> FnOverrides;
bool IsMatch(intptr_t id1, intptr_t id2) const override;
void GetTypeIDs(intptr_t &id1, intptr_t &id2) const override;
@ -596,13 +598,14 @@ public:
class PStruct : public PContainerType
{
public:
PStruct(FName name, PTypeBase *outer, bool isnative = false);
PStruct(FName name, PTypeBase *outer, bool isnative = false, int fileno = 0);
bool isNative;
bool isOrdered = false;
// Some internal structs require explicit construction and destruction of fields the VM cannot handle directly so use these two functions for it.
VMFunction *mConstructor = nullptr;
VMFunction *mDestructor = nullptr;
int mDefFileNo;
PField *AddField(FName name, PType *type, uint32_t flags=0) override;
PField *AddNativeField(FName name, PType *type, size_t address, uint32_t flags = 0, int bitvalue = 0) override;
@ -635,8 +638,9 @@ class PClassType : public PContainerType
public:
PClass *Descriptor;
PClassType *ParentType;
int mDefFileNo;
PClassType(PClass *cls = nullptr);
PClassType(PClass *cls = nullptr, int fileno = 0);
PField *AddField(FName name, PType *type, uint32_t flags = 0) override;
PField *AddNativeField(FName name, PType *type, size_t address, uint32_t flags = 0, int bitvalue = 0) override;
};
@ -657,9 +661,9 @@ PPointer *NewPointer(PType *type, bool isconst = false);
PPointer *NewPointer(PClass *type, bool isconst = false);
PClassPointer *NewClassPointer(PClass *restrict);
PEnum *NewEnum(FName name, PTypeBase *outer);
PStruct *NewStruct(FName name, PTypeBase *outer, bool native = false);
PStruct *NewStruct(FName name, PTypeBase *outer, bool native = false, int fileno = 0);
PPrototype *NewPrototype(const TArray<PType *> &rettypes, const TArray<PType *> &argtypes);
PClassType *NewClassType(PClass *cls);
PClassType *NewClassType(PClass *cls, int fileno);
// Built-in types -----------------------------------------------------------