Extended variable destruction
-
- Posts: 2655
- Joined: Aug 28, 2008 10:54
- Location: new york
Extended variable destruction
Why would a destructor that otherwise functions properly segfault on an object destruction called from an extended variable?
-
- Posts: 2655
- Joined: Aug 28, 2008 10:54
- Location: new york
Re: Extended variable destruction
The code appears to call the destructor and for some reason there appears to be extra empty variables. When I call this from an extended object, it segfaults. I dunno what's going on.
Code: Select all
#include once "Parsing.bas"
#ifndef NULL
#define NULL 0
#endif
#DEFINE ___DEBUG
#DEFINE DBLQUOTE ascii(34)
enum varType
_MALFORMED_ = 0
_NUMBER_ = 1
_STRING_ = 2
_BOOL_ = 3
_ARRAY_ = 4
_OBJECT_ = 5
_NULL_ = 6
end enum
enum JSON_BOOL
_FALSE_ = 0
_TRUE_ = 1
end enum
type variable_ as variable
type varArray
Public:
declare Destructor()
declare sub add( byval item as variable_ ptr )
declare function get( byval index as integer ) as variable_ ptr
declare function getLength() as integer
declare sub push( byval idx as integer, byval item as variable_ ptr )
declare sub push( byval idx as integer, byref item as variable_ )
declare sub pop( byval item as integer )
declare sub push_back( byval item as variable_ ptr )
declare sub clear()
as integer size
as variable_ ptr items(any)
end type
type varObjectField
Public:
declare Destructor()
as string key
as variable_ ptr value
end type
type varObject
Public:
declare Destructor()
declare sub add( byval key as string, byval item as variable_ ptr )
declare function get( byval key as string ) as variable_ ptr
declare function getKey( byval index as integer ) as string
declare function getValue( byval index as integer ) as variable_ ptr
declare function getSize() as integer
declare sub pop( byval idx as integer )
as integer size
as varObjectField ptr fields(any)
end type
type variable extends Object
Public:
declare Constructor()
declare Constructor( byref rhs as variable )
declare Constructor( byval json as string, byval n as string = "" )
declare Constructor( byval n as double )
declare Destructor()
declare Operator Let( byref rhs as variable )
declare Operator Let( byval json as string )
declare Operator Let( byval n as double )
declare sub init( byval s as string, byval n as string = "" )
declare function getType() as string
declare function getTypeNum() as vartype
declare function toString() as string
declare function getNumber() as double
declare function getString() as string
declare function getBoolean() as integer
declare function getArray() as vararray ptr
declare function getObject() as varobject ptr
as string lbl
as varType typ
as double number
as string strng
as integer bool
as varArray ptr array
as varObject ptr object
end type
Destructor varArray()
#IFDEF ___DEBUG
? "Array destructor"
#ENDIF
for i as integer = 0 to this.size-1
delete items(i)
next
erase items
End Destructor
sub varArray.add( byval item as variable_ ptr )
size += 1
redim preserve items(size-1)
items(size-1) = new variable_
*items(size-1) = *item
end sub
sub varArray.push( byval idx as integer, byval item as variable_ ptr )
if idx < 0 then exit sub
if idx > size-1 then exit sub
redim preserve items( size )
items( size ) = new variable_
for i as integer = size-1 to idx step -1
swap items(i), items(i+1)
next i
*items( idx ) = *item
size += 1
end sub
sub varArray.push( byval idx as integer, byref item as variable_ )
if idx < 0 then exit sub
if idx > size-1 then exit sub
redim preserve items( size )
for i as integer = size-1 to idx step -1
swap items(i), items(i+1)
next i
*items( idx ) = item
size += 1
end sub
sub varArray.push_back( byval item as variable_ ptr )
redim preserve items( size )
items( size ) = new variable
*items( size ) = *item
size += 1
end sub
sub varArray.pop( byval idx as integer )
if idx < 0 then exit sub
if idx > size-1 then exit sub
if size = 1 then
erase( items )
size = 0
exit sub
endif
for i as integer = idx+1 to size-1
items(i-1) = items(i)
next
size -= 1
redim preserve items( 0 to size-1 )
end sub
sub varArray.clear()
for i as integer = 0 to size-1
delete items(i)
next i
erase items
size = 0
end sub
function varArray.get( byval index as integer ) as variable_ ptr
if index >= size or index < 0 then return NULL
return items(index)
end function
function varArray.getLength() as integer
return size
end function
Destructor varObjectField()
#IFDEF ___DEBUG
? "Object field destructor"
#ENDIF
if value then delete value
end Destructor
Destructor varObject
#IFDEF ___DEBUG
? "Object destructor"
#ENDIF
for i as integer = 0 to this.size-1
if fields(i) then delete fields(i)
next i
erase fields
end Destructor
sub varObject.add( byval key as string, byval item as variable_ ptr )
size += 1
redim preserve fields(size-1)
fields(size-1) = new varObjectField
fields(size-1)->key = key
fields(size-1)->value = new variable_
*fields(size-1)->value = *item
end sub
function varObject.get( byval key as string ) as variable_ ptr
key = chr(34)+key+chr(34)
for i as integer = 0 to size-1
if fields(i)->key = key then return fields(i)->value
next i
return 0
end function
function varObject.getKey( byval index as integer ) as string
return fields(index)->key
end function
function varObject.getValue( byval index as integer ) as variable_ ptr
return fields(index)->value
end function
function varObject.getSize() as integer
return size
end function
sub varObject.pop( byval idx as integer )
if idx < 0 then exit sub
if idx > size-1 then exit sub
if size = 1 then
erase( fields )
size = 0
exit sub
endif
for i as integer = idx+1 to size-1
fields(i-1) = fields(i)
next
size -= 1
redim preserve fields( 0 to size-1 )
end sub
declare Operator = ( byref lhs as variable, byref rhs as variable ) as integer
Constructor variable()
end Constructor
Constructor variable( byref rhs as variable )
this.lbl = rhs.lbl
this.typ = rhs.typ
select case rhs.typ
case varType._BOOL_
this.bool = rhs.bool
case varType._NUMBER_
this.number = rhs.number
case varType._STRING_
this.strng = rhs.strng
case varType._OBJECT_
this.object = new varObject
redim this.object->fields( rhs.object->size - 1 )
this.object->size = rhs.object->size
if rhs.object->size then
for i as integer = 0 to rhs.object->size - 1
this.object->fields(i) = new varObjectField
this.object->fields(i)->key = rhs.object->fields(i)->key
this.object->fields(i)->value = new variable
*this.object->fields(i)->value = *rhs.object->fields(i)->value
next i
end if
case varType._ARRAY_
this.array = new varArray
if rhs.array->size then
redim this.array->items( rhs.array->size - 1 )
this.array->size = rhs.array->size
for i as integer = 0 to rhs.array->size - 1
this.array->items(i) = new variable
*this.array->items(i) = *rhs.array->items(i)
next i
end if
end select
End Constructor
Operator variable.Let( byref rhs as variable )
if @this <> @rhs then
this.lbl = rhs.lbl
this.typ = rhs.typ
select case rhs.typ
case varType._BOOL_
this.bool = rhs.bool
case varType._NUMBER_
this.number = rhs.number
case varType._STRING_
this.strng = rhs.strng
case varType._OBJECT_
this.object = new varObject
redim this.object->fields( rhs.object->size - 1 )
this.object->size = rhs.object->size
if rhs.object->size then
for i as integer = 0 to rhs.object->size - 1
this.object->fields(i) = new varObjectField
this.object->fields(i)->key = rhs.object->fields(i)->key
this.object->fields(i)->value = new variable
*this.object->fields(i)->value = *rhs.object->fields(i)->value
next i
end if
case varType._ARRAY_
this.array = new varArray
if rhs.array->size then
this.array->size = rhs.array->size
redim this.array->items( rhs.array->size - 1 )
for i as integer = 0 to rhs.array->size - 1
this.array->items(i) = new variable
*this.array->items(i) = *rhs.array->items(i)
next i
end if
case else
'? "Something is #%$@"
end select
end if
end Operator
sub copy_variable( byref var1 as variable, byref var2 as variable )
var1.lbl = var2.lbl
var1.typ = var2.typ
select case var2.typ
case _BOOL_
var1.bool = var2.bool
case _NUMBER_
var1.number = var2.number
case _STRING_
var1.strng = var2.strng
case _OBJECT_
var1.object = new varObject
redim var1.object->fields( var2.object->size - 1 )
for i as integer = 0 to var2.object->size - 1
var1.object->fields(i) = new varObjectField
var1.object->fields(i)->key = var2.object->fields(i)->key
*var1.object->fields(i)->value = *var2.object->fields(i)->value
next i
case _ARRAY_
var1.array = new varArray
redim var1.array->items( var2.array->size - 1 )
var1.array->size = var2.array->size
for i as integer = 0 to var2.array->size - 1
var1.array->items(i) = new variable
*var1.array->items(i) = *var2.array->items(i)
next i
end select
end sub
Constructor variable( byval json as string, byval n as string = "" )
init( json, n )
end Constructor
Constructor variable( byval num as double )
this.typ = _NUMBER_
this.number = num
end Constructor
Operator variable.Let( byval json as string )
init( json )
end Operator
Operator variable.Let( byval num as double )
this.typ = _NUMBER_
this.number = num
end Operator
Operator = ( byref lhs as varObjectField, byref rhs as varObjectField ) as integer
if lhs.key <> rhs.key then return 0
if ( *lhs.value = *rhs.value ) = 0 then return 0
return -1
end Operator
Operator = ( byref lhs as variable, byref rhs as variable ) as integer
if lhs.typ <> rhs.typ then return 0
select case lhs.typ
case varType._NUMBER_
if lhs.number <> rhs.number then return 0
case varType._STRING_
if lhs.strng <> rhs.strng then return 0
case varType._BOOL_
if lhs.bool <> rhs.bool then return 0
case varType._ARRAY_
if lhs.array->size <> rhs.array->size then return 0
for i as integer = 0 to lhs.array->size-1
if ( *lhs.array->items(i) = *rhs.array->items(i) ) = 0 then return 0
next i
case varType._OBJECT_
if lhs.object->size <> rhs.object->size then return 0
for i as integer = 0 to lhs.object->size-1
if ( *lhs.object->fields(i) = *rhs.object->fields(i) ) = 0 then return 0
next i
end select
return -1
end Operator
function array_has overload ( byref arr as variable, byref v as variable ) as integer
dim as integer res = 0
if arr.array = 0 then return 0
for i as integer = 0 to arr.array->size - 1
if *arr.array->items(i) = v then return i+1
if arr.array->items(i)->typ = _ARRAY_ then
res = array_has( *arr.array->items(i), v )
if res then return res
end if
next i
return res
end function
function array_has overload ( byref arr as variable, byval v as string ) as integer
dim as integer res = 0
if arr.array = 0 then return 0
for i as integer = 0 to arr.array->size - 1
if arr.array->items(i)->strng = v then return i+1
if arr.array->items(i)->typ = _ARRAY_ then
res = array_has( *arr.array->items(i), v )
if res then return res
end if
next i
return res
end function
function array_has_malformed( byref arr as variable ) as integer
dim as integer res = 0
if arr.array = 0 then return 0
for i as integer = 0 to arr.array->size - 1
if arr.array->items(i)->typ = _MALFORMED_ then return -1
if arr.array->items(i)->typ = _ARRAY_ then
res = array_has_malformed( *arr.array->items(i) )
if res then return res
end if
next i
return res
end function
Destructor variable()
#IFDEF ___DEBUG
? "Variable destructor"
? this.tostring
sleep
#ENDIF
delete this.object
delete this.array
end Destructor
sub variable.init( byval s as string, byval n as string = "" )
dim as integer l = len(s)
dim as integer arrayIsOpen = 0
dim as integer objectIsOpen = 0
dim as integer inQuotes = 0
this.lbl = n
for i as integer = 0 to l-1
dim as ubyte c = s[i]
if isWhite( c ) then continue for
if isNumberJson( c ) then
if arrayIsOpen or objectIsOpen then
this.typ = _MALFORMED_
this.strng = s
exit sub
endif
this.typ = _NUMBER_
dim as string inNum = ""
Do while IsNumberJson( s[i] )
inNum += ascii( s[i] )
if i > l then
this.typ = _MALFORMED_
this.strng = s
? "Error: i > l"
exit sub
endif
i+=1
loop
this.number = val(inNum)
endif
if c = 34 then
if arrayIsOpen or objectIsOpen then
this.typ = _MALFORMED_
this.strng = s
? "Array / Object is open"
exit sub
endif
i+=1
this.typ = _STRING_
dim as string inString = ""
dim as integer done = 0
Do
if s[i-1] <> asc("\") then
if s[i] <> 34 then
inString += ascii( s[i] )
i+=1
else
done = 1
endif
else
inString += ascii( s[i] )
i+=1
endif
if i >= l then
this.typ = _MALFORMED_
this.strng = s
? "Error: i > l"
done = 1
endif
loop until done = 1
if inString <> "" then this.strng = unEscape( inString )
if i <> l-1 then
this.typ = _MALFORMED_
this.strng = s
? "Error: i <> l-1", i, l-1
? instring
endif
endif
select case c
case asc("t"),asc("T"),asc("f"),asc("F"),asc("n"), asc("N")
if mid(s,i+1,4) = "true" then
this.typ = _BOOL_
this.bool = _TRUE_
i += 3
elseif mid(s,i+1,4) = "null" then
this.typ = _NULL_
i += 3
elseif mid(s,i+1,5) = "false" then
this.typ = _BOOL_
this.bool = _FALSE_
i += 4
endif
case asc("[")
arrayIsOpen = 1
this.typ = _ARRAY_
this.array = new varArray
dim as integer level = 1
dim as string instring = ""
i+=1
Do
dim as ubyte char2 = s[i]
dim as variable tmp
select case char2
case 34
if inQuotes = 0 then inQuotes = 1 else inQuotes = 0
instring += ascii( char2 )
case asc("[")
level += 1
instring += "["
Do
i+=1
if s[i] = asc("[") then level += 1
if s[i] = asc("]") then level -= 1
instring += ascii( s[i] )
loop until level = 1
if instring <> "" then
tmp.init( instring )
array->add( @tmp )
instring = ""
endif
case asc("]")
if instring <> "" then
if isWhite( instring[0] ) = 0 then
tmp.init( instring )
array->add( @tmp )
endif
endif
instring = ""
level -= 1
case asc(",")
if inQuotes = 0 then
if instring <> "" then
tmp.init( instring )
array->add( @tmp )
instring = ""
endif
else
instring += ascii( char2 )
endif
case asc("{")
dim as integer olevel = 1
instring += "{"
Do
i+=1
if s[i] = asc("{") then olevel += 1
if s[i] = asc("}") then olevel -= 1
instring += ascii( s[i] )
loop until olevel = 0
if instring <> "" then
tmp.init( instring )
array->add( @tmp )
instring = ""
endif
case else
instring += ascii( char2 )
end select
i+=1
loop until level = 0
arrayIsOpen = 0
case asc("{")
objectIsOpen = 1
this.typ = _OBJECT_
this.object = new varObject
dim as integer olevel = 1, alevel, inQuotes = 0
dim as string instring = "", key = ""
dim as variable tmp
Do
i+=1
dim as ubyte char2 = s[i]
if isWhite( char2 ) AND inQuotes = 0 then continue do
if inQuotes = 1 then
if char2 <> 34 then
instring += ascii( char2 )
continue do
endif
endif
select case char2
case asc(":")
key = instring
instring = ""
case asc("[")
alevel += 1
instring += ascii( char2 )
case asc("]")
alevel -= 1
instring += ascii( char2 )
case asc("{")
olevel += 1
instring += ascii( char2 )
Do
i+=1
char2 = s[i]
if char2 = 34 then
if inQuotes = 1 then inQuotes = 0 else inQuotes = 1
endif
if isWhite( char2 ) and inQuotes = 0 then Continue do
if char2 = asc("{") then olevel += 1
if char2 = asc("}") then olevel -= 1
if char2 = asc("[") then alevel += 1
if char2 = asc("]") then alevel -= 1
instring += ascii( char2 )
loop until olevel = 1 and alevel = 0
if instring <> "" then
tmp.init( instring )
object->add( key, @tmp )
instring = ""
endif
case asc("}")
olevel -= 1
if olevel = 0 then
if instring <> "" then
tmp.init( instring )
object->add( key, @tmp )
instring = ""
endif
endif
case asc(",")
if alevel = 0 then
if instring <> "" then
tmp.init( instring )
object->add( key, @tmp )
instring = ""
endif
else
instring += ascii( char2 )
endif
case 34
instring += ascii( char2 )
if inQuotes = 1 then inQuotes = 0 else inQuotes = 1
case else
instring += ascii( char2 )
end select
loop until olevel = 0
objectIsOpen = 0
end select
next i
end sub
function variable.getType() as string
select case typ
case _NUMBER_:
return "Number"
case _STRING_:
return "String"
case _BOOL_:
return "Boolean"
case _ARRAY_:
return "Array"
case _OBJECT_:
return "Object"
case _NULL_:
return "null"
case _MALFORMED_:
return "[MALFORMED]"
end select
end function
function variable.getTypeNum() as vartype
return typ
end function
function variable.toString() as string
select case typ
case _NUMBER_:
return number & ""
case _STRING_:
return strng
case _BOOL_:
if bool then return "true"
return "false"
case _ARRAY_:
return "ARRAY"
case _OBJECT_:
return "OBJECT"
case _NULL_:
return "null"
case _MALFORMED_:
return "[MALFORMED]"
end select
end function
function variable.getNumber() as double
if typ <> _NUMBER_ then return 0
return number
end function
function variable.getString() as string
if typ <> _STRING_ then return ""
return strng
end function
function variable.getBoolean() as integer
if typ <> _BOOL_ then return _FALSE_
return bool
end function
function variable.getArray() as vararray ptr
if typ <> _ARRAY_ then return NULL
return array
end function
function variable.getObject() as varobject ptr
if typ <> _OBJECT_ then return NULL
return object
end function
declare function stringize_object( byref v as variable, byval l as integer = 0 ) as string
declare function stringize_array( byref v as variable ) as string
'dim shared as string stringize_string
function stringize_array( byref v as variable ) as string
if v.typ <> varType._ARRAY_ then return ""
if v.array = 0 then return ""
dim as integer length = v.array->size
if length = 0 then return "[]"
var s = "["
for i as integer = 0 to length - 1
if v.array->items(i) <> 0 then
dim as variable tmp = *v.array->items(i)
select case tmp.typ
case _ARRAY_ :
s += stringize_array( tmp )
case _STRING_ :
s += DBLQUOTE
s += tmp.strng
s += DBLQUOTE
case _BOOL_ :
s += tmp.toString
case _NULL_ :
s += "null"
case _NUMBER_ :
s &= tmp.number
case _OBJECT_ :
s += stringize_object( tmp )
end select
else
s &= 0
end if
if i <> length-1 then s += "," else s += "]"
next i
return s
end function
function stringize_object( byref v as variable, byval l as integer = 0 ) as string
if v.typ <> varType._OBJECT_ then return ""
if v.object = 0 then return ""
dim as integer length = v.object->size
if length = 0 then return ""
dim as string s
s += !"\n"
for i2 as integer = 0 to l : s += !"\t" : next i2
s += "{"
for i as integer = 0 to length - 1
var tkey = v.object->fields(i)->key
s += !"\n\t"
for i2 as integer = 0 to l : s += !"\t" : next i2
s += tkey
if len(tkey) > 6 then s += !":\t" else s+= !":\t\t"
if v.object->fields(i)->value then
var tmp = *v.object->fields(i)->value
select case tmp.typ
case _ARRAY_ : s += stringize_array( tmp )
case _BOOL_ : s += tmp.toString
case _NULL_ : s += "NULL"
case _NUMBER_ : s &= tmp.number
case _OBJECT_ : s += stringize_object( tmp, l+1 )
case _STRING_ :
s += DBLQUOTE
s += tmp.strng
s += DBLQUOTE
end select
if i <> length-1 then
s += ","
else
s += !"\n"
for i2 as integer = 0 to l : s += !"\t" : next i2
s += "}"
endif
end if
next i
return s
end function
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
dim shared as string fake_json_word
fake_json_word = "{"
fake_json_word += DBLQUOTE + "txt" + DBLQUOTE + ": " + DBLQUOTE + "word" + DBLQUOTE + ","
fake_json_word += DBLQUOTE + "tag_tkns" + DBLQUOTE + ": [""NN""],"
fake_json_word += DBLQUOTE + "phone_tag" + DBLQUOTE + ": " + DBLQUOTE + "(W ER D)" + DBLQUOTE+ ","
fake_json_word += DBLQUOTE + "nsyls" + DBLQUOTE + ": 1,"
fake_json_word += DBLQUOTE + "accent" + DBLQUOTE + ": 1,"
fake_json_word += DBLQUOTE + "tags" + DBLQUOTE + ": [""stuff"",[""nested"",""array""],""more stuff""]"
fake_json_word += "}"
dim as variable v = fake_json_word
? stringize_object( v )
?:?
? stringize_array( *v.object->get("tags")->array->items(1) )
sleep
sleep
'dim as double t
't = timer
'? stringize_array2( dic )
'? timer-t
'sleep
''dim as variable dic2
'dic2.typ = _ARRAY_
'dic2.array = new varArray
'dic2.array->add( @dic )
'? dic2.array->items(0)->array->size
'sleep
'? dic2.array->size
'sleep
Re: Extended variable destruction
I get no error for the two configurations (without or with 'Extends Object'), and the console printings are exactly the same?
Re: Extended variable destruction
You are right to test sometime the pointer before 'Delete' it:
- For the two pointer arrays, no problem because by the sizing, they contain only non-null pointers.
- For the 3 elementary pointers, they are not necessarily used.
But that is not mandatory as is noted the documentation:
Calling Delete on a null pointer induces no action.
- For the two pointer arrays, no problem because by the sizing, they contain only non-null pointers.
- For the 3 elementary pointers, they are not necessarily used.
Code: Select all
Destructor varArray()
for i as integer = 0 to this.size-1
delete items(i)
next
erase items
End Destructor
Destructor varObject
for i as integer = 0 to this.size-1
delete fields(i)
next i
erase fields
end Destructor
Destructor varObjectField()
if value then delete value
End destructor
Destructor variable()
if array then delete array
if object then delete object
end Destructor
Calling Delete on a null pointer induces no action.
Re: Extended variable destruction
Seen from the user, it should be no behavior difference of a destructor for an alone type or for the same type but which 'Extends Object'.
-
- Posts: 2655
- Joined: Aug 28, 2008 10:54
- Location: new york
Re: Extended variable destruction [nevermind]
I just suck at memory management and understanding when destructors are called and whatnot. I have it working ok now.
Re: Extended variable destruction
By putting your main code in a Scope block as below, you can better highlight the destructors called during the different procedure callings (when local objects are destroyed at exits of procedures), and those called when the variable 'v' is destroyed (when it goes out of scope):
Code: Select all
.....
dim shared as string fake_json_word
fake_json_word = "{"
fake_json_word += DBLQUOTE + "txt" + DBLQUOTE + ": " + DBLQUOTE + "word" + DBLQUOTE + ","
fake_json_word += DBLQUOTE + "tag_tkns" + DBLQUOTE + ": [""NN""],"
fake_json_word += DBLQUOTE + "phone_tag" + DBLQUOTE + ": " + DBLQUOTE + "(W ER D)" + DBLQUOTE+ ","
fake_json_word += DBLQUOTE + "nsyls" + DBLQUOTE + ": 1,"
fake_json_word += DBLQUOTE + "accent" + DBLQUOTE + ": 1,"
fake_json_word += DBLQUOTE + "tags" + DBLQUOTE + ": [""stuff"",[""nested"",""array""],""more stuff""]"
fake_json_word += "}"
scope
dim as variable v = fake_json_word
? stringize_object( v )
?:?
? stringize_array( *v.object->get("tags")->array->items(1) )
print
print "--- end of scope"
print
end scope
print
print "--- end of program"
sleep