2017-08-09 19:45:46 +02:00
# (C) Copyright 2015-2017 Sei Lisa. All rights reserved.
2015-03-05 23:18:41 +01:00
#
# This file is part of LSL PyOptimizer.
#
# LSL PyOptimizer is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# LSL PyOptimizer is distributed in the hope that it will be useful,
# 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
# along with LSL PyOptimizer. If not, see <http://www.gnu.org/licenses/>.
# Constant folding and simplification of expressions and statements.
2014-08-13 14:02:05 +02:00
2016-12-21 06:01:03 +01:00
import lslcommon
2017-08-23 11:55:37 +02:00
from lslcommon import Key , Vector , Quaternion
2014-08-13 14:02:05 +02:00
import lslfuncs
2017-08-23 11:55:37 +02:00
from lslfuncs import NULL_KEY , ZERO_VECTOR , ZERO_ROTATION
2015-03-06 23:26:58 +01:00
import math
2014-08-13 14:02:05 +02:00
from lslparse import warning
2017-08-25 19:31:07 +02:00
from lslfuncopt import OptimizeParams
2014-08-13 14:02:05 +02:00
2014-08-13 14:19:58 +02:00
class foldconst ( object ) :
2014-08-13 14:02:05 +02:00
Optimize some llList2XXXX functions.
- Resolve for lists that aren't constants, but that are SEF, and reference a constant element, e.g. llList2String([llGetKey(), 5], 1) -> 5.
- Make it work with invalid conversions, if they are SEF.
- Simplify invalid conversions when the list argument is a llGetObjectDetails call and SEF.
- Simplify llList2String(llGetObjectDetails(id, [single_element]), 0) (or -1) to (string)llGetObjectDetails...; same with llList2Key and llList2Integer, converting it to (integer)((string)llGetObjectDetails...).
- Add TODO item to do it similarly for llGet[Link]PrimitiveParams.
Adds auxiliary functions to return a node or constant from a list in any of its forms, its length, and a constant for a node or constant.
Adds some utility members for LSL<->Python type conversion (one of them duplicated from lslparse.py).
Needs maintenance of the types returned by llGetObjectDetails.
2017-08-23 14:37:47 +02:00
# Type of each entry in llGetObjectDetails. Last: 38.
objDetailsTypes = ' issvrvkkkiiififfffkiiiiiiffkiviiksiisii '
2017-08-24 22:58:32 +02:00
primParamsTypes = \
( False , False # 0 (unassigned) and 1=PRIM_TYPE_LEGACY
, ' i ' # 2=PRIM_MATERIAL
, ' i ' # 3=PRIM_PHYSICS
, ' i ' # 4=PRIM_TEMP_ON_REZ
, ' i ' # 5=PRIM_PHANTOM
, ' v ' # 6=PRIM_POSITION
, ' v ' # 7=PRIM_SIZE
, ' r ' # 8=PRIM_ROTATION
, ' i* ' # 9=PRIM_TYPE
, False , False , False , False # 10, 11, 12, 13 (unassigned)
, False , False , False # 14, 15, 16 (unassigned)
, ' svvf ' # 17=PRIM_TEXTURE
, ' vf ' # 18=PRIM_COLOR
, ' ii ' # 19=PRIM_BUMP_SHINY
, ' i ' # 20=PRIM_FULLBRIGHT
, ' iiffffv ' # 21=PRIM_FLEXIBLE
, ' i ' # 22=PRIM_TEXGEN
, ' ivfff ' # 23=PRIM_POINT_LIGHT
, False # 24 (unassigned)
, ' f ' # 25=PRIM_GLOW
, ' svf ' # 26=PRIM_TEXT
, ' s ' # 27=PRIM_NAME
, ' s ' # 28=PRIM_DESC
, ' r ' # 29=PRIM_ROT_LOCAL
, ' i ' # 30=PRIM_PHYSICS_SHAPE_TYPE
, False # 31 (unassigned)
, ' vff ' # 32=PRIM_OMEGA
, ' v ' # 33=PRIM_POS_LOCAL
, ' ' # 34=PRIM_LINK_TARGET
, ' v ' # 35=PRIM_SLICE
, ' svvfvii ' # 36=PRIM_SPECULAR
, ' svvf ' # 37=PRIM_NORMAL
, ' ii ' # 38=PRIM_ALPHA_MODE
, ' i ' # 39=PRIM_ALLOW_UNSIT
, ' i ' # 40=PRIM_SCRIPTED_SIT_ONLY
, ' ivv ' # 41=PRIM_SIT_TARGET
)
# Primitive Params with arguments. F=face, L=link.
primParamsArgs = \
{ 17 : ' F ' # 17=PRIM_TEXTURE
, 18 : ' F ' # 18=PRIM_COLOR
, 19 : ' F ' # 19=PRIM_BUMP_SHINY
, 20 : ' F ' # 20=PRIM_FULLBRIGHT
, 22 : ' F ' # PRIM_TEXGEN
, 25 : ' F ' # PRIM_GLOW
, 34 : ' L ' # PRIM_LINK_TARGET
, 36 : ' F ' # PRIM_SPECULAR
, 37 : ' F ' # PRIM_NORMAL
, 38 : ' F ' # PRIM_ALPHA_MODE
}
Optimize some llList2XXXX functions.
- Resolve for lists that aren't constants, but that are SEF, and reference a constant element, e.g. llList2String([llGetKey(), 5], 1) -> 5.
- Make it work with invalid conversions, if they are SEF.
- Simplify invalid conversions when the list argument is a llGetObjectDetails call and SEF.
- Simplify llList2String(llGetObjectDetails(id, [single_element]), 0) (or -1) to (string)llGetObjectDetails...; same with llList2Key and llList2Integer, converting it to (integer)((string)llGetObjectDetails...).
- Add TODO item to do it similarly for llGet[Link]PrimitiveParams.
Adds auxiliary functions to return a node or constant from a list in any of its forms, its length, and a constant for a node or constant.
Adds some utility members for LSL<->Python type conversion (one of them duplicated from lslparse.py).
Needs maintenance of the types returned by llGetObjectDetails.
2017-08-23 14:37:47 +02:00
# Compatibility: list extraction function / input type (by type's first
# letter), e.g. 'si' means llList2String can extract an integer.
listCompat = frozenset ( { ' ss ' , ' sk ' , ' si ' , ' sf ' , ' sv ' , ' sr ' , ' ks ' , ' kk ' ,
' is ' , ' ii ' , ' if ' , ' fs ' , ' fi ' , ' ff ' , ' vv ' , ' rr ' } )
defaultListVals = { ' llList2Integer ' : 0 , ' llList2Float ' : 0.0 ,
' llList2String ' : u ' ' ,
# llList2Key is set programmatically in FoldScript
#'llList2Key':Key(u''),
' llList2Vector ' : Vector ( ( 0. , 0. , 0. ) ) ,
' llList2Rot ' : Quaternion ( ( 0. , 0. , 0. , 1. ) ) }
PythonType2LSL = { int : ' integer ' , float : ' float ' ,
unicode : ' string ' , Key : ' key ' , Vector : ' vector ' ,
Quaternion : ' rotation ' , list : ' list ' }
LSLType2Python = { ' integer ' : int , ' float ' : float ,
' string ' : unicode , ' key ' : Key , ' vector ' : Vector ,
' rotation ' : Quaternion , ' list ' : list }
2015-03-27 02:12:32 +01:00
def isLocalVar ( self , node ) :
name = node [ ' name ' ]
scope = node [ ' scope ' ]
return self . symtab [ scope ] [ name ] [ ' Kind ' ] == ' v ' \
and ' Loc ' not in self . symtab [ scope ] [ name ]
2017-01-05 02:55:06 +01:00
Optimize some llList2XXXX functions.
- Resolve for lists that aren't constants, but that are SEF, and reference a constant element, e.g. llList2String([llGetKey(), 5], 1) -> 5.
- Make it work with invalid conversions, if they are SEF.
- Simplify invalid conversions when the list argument is a llGetObjectDetails call and SEF.
- Simplify llList2String(llGetObjectDetails(id, [single_element]), 0) (or -1) to (string)llGetObjectDetails...; same with llList2Key and llList2Integer, converting it to (integer)((string)llGetObjectDetails...).
- Add TODO item to do it similarly for llGet[Link]PrimitiveParams.
Adds auxiliary functions to return a node or constant from a list in any of its forms, its length, and a constant for a node or constant.
Adds some utility members for LSL<->Python type conversion (one of them duplicated from lslparse.py).
Needs maintenance of the types returned by llGetObjectDetails.
2017-08-23 14:37:47 +02:00
def GetListNodeLength ( self , node ) :
""" Get the length of a list that is expressed as a CONST, LIST or CAST
node , or False if it can ' t be determined.
"""
assert node [ ' t ' ] == ' list '
nt = node [ ' nt ' ]
if nt == ' CAST ' :
if node [ ' ch ' ] [ 0 ] [ ' t ' ] == ' list ' :
return self . GetListNodeLength ( node [ ' ch ' ] [ 0 ] )
return 1
if nt == ' CONST ' : # constant list
return len ( node [ ' value ' ] )
if nt == ' LIST ' : # list constructor
return len ( node [ ' ch ' ] )
return False
def GetListNodeElement ( self , node , index ) :
""" Get an element of a list expressed as a CONST, LIST or CAST node.
If the index is out of range , return False ; otherwise the result can be
either a node or a constant .
"""
assert node [ ' t ' ] == ' list '
nt = node [ ' nt ' ]
if nt == ' CAST ' :
# (list)list_expr should have been handled in CAST
assert node [ ' ch ' ] [ 0 ] [ ' t ' ] != ' list '
if index == 0 or index == - 1 :
return node [ ' ch ' ] [ 0 ]
return False
if nt == ' CONST ' :
try :
return node [ ' value ' ] [ index ]
except IndexError :
pass
return False
if nt == ' LIST ' :
try :
return node [ ' ch ' ] [ index ]
except IndexError :
return False
return False
def ConstFromNodeOrConst ( self , nodeOrConst ) :
""" Return the constant if the value is a node and represents a constant,
or if the value is directly a constant , and False otherwise .
"""
if type ( nodeOrConst ) == dict :
if nodeOrConst [ ' nt ' ] == ' CONST ' :
return nodeOrConst [ ' value ' ]
return False
return nodeOrConst
def TypeFromNodeOrConst ( self , nodeOrConst ) :
""" Return the LSL type of a node or constant. """
if nodeOrConst is False :
return False
if type ( nodeOrConst ) == dict :
return nodeOrConst [ ' t ' ]
return self . PythonType2LSL [ type ( nodeOrConst ) ]
2014-08-13 14:02:05 +02:00
def FoldAndRemoveEmptyStmts ( self , lst ) :
""" Utility function for elimination of useless expressions in FOR """
idx = 0
while idx < len ( lst ) :
self . FoldTree ( lst , idx )
self . FoldStmt ( lst , idx )
# If eliminated, it must be totally removed. A ';' won't do.
if lst [ idx ] [ ' nt ' ] == ' ; ' :
del lst [ idx ]
else :
idx + = 1
2017-01-05 02:55:06 +01:00
def DoesSomething ( self , node ) :
""" Tell if a subtree does something or is just empty statements
( a pure combination of ' ; ' and ' {} ' )
2017-02-04 04:08:18 +01:00
Not to be confused with lslparse . does_something which includes labels ,
and applies to a block ' s statement list, not to a node.
2017-01-05 02:55:06 +01:00
"""
if node [ ' nt ' ] != ' ; ' :
if node [ ' nt ' ] == ' {} ' :
2017-02-04 04:08:18 +01:00
for subnode in node [ ' ch ' ] :
if self . DoesSomething ( subnode ) :
return True
2017-01-05 02:55:06 +01:00
else :
return True
return False
2014-08-13 14:02:05 +02:00
def FoldStmt ( self , parent , index ) :
""" Simplify a statement. """
node = parent [ index ]
if node [ ' nt ' ] == ' EXPR ' :
node = node [ ' ch ' ] [ 0 ]
# If the statement is side-effect-free, remove it as it does nothing.
if ' SEF ' in node :
# Side-effect free means that a statement does nothing except
# wasting CPU, and can thus be removed without affecting the
# program. But side effect freedom is propagated from the
# constituents of the statement, e.g. function calls in expressions
# or substatements in FOR, or even individual variables.
#
# Many library functions like llSameGroup or llGetVel() are
# side-effect free. Many other functions like llSleep() or
# llSetScale() are not. User functions may or may not be.
#
# Assignments do have side effects, except those of the form x = x.
# Pre- and post-increment and decrement also have side effects.
# Type casts do not add side effects. Neither do binary operators.
parent [ index ] = { ' nt ' : ' ; ' , ' t ' : None , ' SEF ' : True }
return
# Post-increments take more space than pre-increments.
if node [ ' nt ' ] in ( ' V++ ' , ' V-- ' ) :
node [ ' nt ' ] = ' ++V ' if node [ ' nt ' ] == ' V++ ' else ' --V ' ;
2015-03-13 23:48:24 +01:00
# Function calls are SEF if both the function and the args are SEF.
# If the statement is a function call and the function is marked as SEF
# at this point, it means the arguments are not SEF. Replace the node
# in that case with a block.
if node [ ' nt ' ] == ' FNCALL ' and ' SEF ' in self . symtab [ 0 ] [ node [ ' name ' ] ] and ' Loc ' in self . symtab [ 0 ] [ node [ ' name ' ] ] :
parent [ index ] = { ' nt ' : ' {} ' , ' t ' : None , ' ch ' :
[ { ' nt ' : ' EXPR ' , ' t ' : x [ ' t ' ] , ' ch ' : [ x ] } for x in node [ ' ch ' ] ] }
self . FoldTree ( parent , index )
return
2017-08-17 01:58:30 +02:00
def ExpandCondition ( self , parent , index ) :
""" IF, FOR, WHILE and DO...WHILE conditions accept several types, not
just integer . However , leaving them as - is generates longer code than if
we expand them and let the optimizer optimize , for float , vector and
rotation , and no matter the optimization in the case of list .
"""
ctyp = parent [ index ] [ ' t ' ]
# Under LSO, this would break the fact that 1-element lists count as
# false, so we don't do it for LSO lists.
if ctyp in ( ' float ' , ' vector ' , ' rotation ' ) or ctyp == ' list ' and not lslcommon . LSO :
parent [ index ] = { ' nt ' : ' != ' , ' t ' : ' integer ' , ' ch ' : [ parent [ index ] ,
{ ' nt ' : ' CONST ' , ' t ' : ctyp , ' value ' :
0.0 if ctyp == ' float '
2017-08-23 11:55:37 +02:00
else ZERO_VECTOR if ctyp == ' vector '
else ZERO_ROTATION if ctyp == ' rotation '
2017-08-17 01:58:30 +02:00
else [ ] } ] }
parent [ index ] [ ' SEF ' ] = ' SEF ' in parent [ index ] [ ' ch ' ] [ 0 ]
2015-02-28 05:14:52 +01:00
def IsBool ( self , node ) :
""" Some operators return 0 or 1, and that allows simplification of
2015-03-02 23:28:14 +01:00
boolean expressions . This function returns whether we know for sure
that the result is boolean .
"""
2015-03-28 23:49:10 +01:00
nt = node [ ' nt ' ]
if nt in ( ' < ' , ' ! ' , ' > ' , ' <= ' , ' >= ' , ' == ' , ' || ' , ' && ' ) \
or nt == ' != ' and node [ ' ch ' ] [ 0 ] [ ' t ' ] != ' list ' \
2017-08-02 01:05:17 +02:00
or nt == ' & ' and ( self . IsBool ( node [ ' ch ' ] [ 0 ] ) or self . IsBool ( node [ ' ch ' ] [ 1 ] ) ) \
or nt in ( ' | ' , ' ^ ' , ' * ' ) and self . IsBool ( node [ ' ch ' ] [ 0 ] ) and self . IsBool ( node [ ' ch ' ] [ 1 ] ) \
2015-03-28 23:49:10 +01:00
or nt == ' CONST ' and node [ ' t ' ] == ' integer ' and node [ ' value ' ] in ( 0 , 1 ) :
return True
if nt == ' FNCALL ' :
sym = self . symtab [ 0 ] [ node [ ' name ' ] ]
2015-06-17 02:51:46 +02:00
if sym [ ' Type ' ] == ' integer ' and ' min ' in sym and ' max ' in sym \
2015-03-28 23:49:10 +01:00
and sym [ ' min ' ] == 0 and sym [ ' max ' ] == 1 :
return True
return False
2015-02-28 05:14:52 +01:00
2015-03-29 03:47:54 +02:00
def FoldCond ( self , parent , index , ParentIsNegation = False ) :
2014-08-13 14:02:05 +02:00
""" When we know that the parent is interested only in the truth value
of the node , we can perform further optimizations . This function deals
with them .
"""
node = parent [ index ]
2015-02-28 05:14:52 +01:00
nt = node [ ' nt ' ]
if nt in ( ' CONST ' , ' IDENT ' , ' FLD ' ) :
2014-08-13 14:02:05 +02:00
if node [ ' nt ' ] == ' CONST ' :
node [ ' t ' ] = ' integer '
2017-01-04 00:41:08 +01:00
node [ ' value ' ] = 1 if lslfuncs . cond ( node [ ' value ' ] ) else 0
2014-08-13 14:02:05 +02:00
return # Nothing to do if it's already simplified.
2015-02-28 05:14:52 +01:00
child = node [ ' ch ' ] if ' ch ' in node else None
2017-08-15 13:07:30 +02:00
if nt == ' ! ' :
self . FoldCond ( child , 0 , True )
if child [ 0 ] [ ' nt ' ] == ' ! ' :
# bool(!!a) equals bool(a)
parent [ index ] = child [ 0 ] [ ' ch ' ] [ 0 ]
return
2015-02-28 05:14:52 +01:00
if nt == ' NEG ' :
# bool(-a) equals bool(a)
parent [ index ] = child [ 0 ]
2017-08-15 13:07:30 +02:00
self . FoldCond ( parent , index , ParentIsNegation )
2015-02-28 05:14:52 +01:00
return
if nt in self . binary_ops and child [ 0 ] [ ' t ' ] == child [ 1 ] [ ' t ' ] == ' integer ' :
if nt == ' != ' :
if child [ 0 ] [ ' nt ' ] == ' CONST ' and child [ 0 ] [ ' value ' ] == 1 \
or child [ 1 ] [ ' nt ' ] == ' CONST ' and child [ 1 ] [ ' value ' ] == 1 :
# a != 1 -> a - 1 (which FoldTree will transform to ~-a)
node [ ' nt ' ] = ' - '
else :
# This converts != to ^; FoldTree will simplify ^-1 to ~
# and optimize out ^0.
node [ ' nt ' ] = ' ^ '
self . FoldTree ( parent , index )
2015-03-28 23:47:45 +01:00
return
if nt == ' == ' :
2015-02-28 05:14:52 +01:00
if child [ 0 ] [ ' nt ' ] == ' CONST ' and - 1 < = child [ 0 ] [ ' value ' ] < = 1 \
or child [ 1 ] [ ' nt ' ] == ' CONST ' and - 1 < = child [ 1 ] [ ' value ' ] < = 1 :
# Transform a==b into !(a-b) if either a or b are in [-1, 1]
parent [ index ] = { ' nt ' : ' ! ' , ' t ' : ' integer ' , ' ch ' : [ node ] }
node [ ' nt ' ] = ' - '
self . FoldTree ( parent , index )
2015-03-28 23:47:45 +01:00
return
2015-02-28 05:14:52 +01:00
2015-02-28 18:30:04 +01:00
if nt == ' | ' :
2015-03-28 20:16:06 +01:00
# In a boolean context, the operands count as booleans.
self . FoldCond ( child , 0 )
self . FoldCond ( child , 1 )
2015-03-29 00:16:17 +01:00
# Deal with operands in any order
2015-02-28 18:30:04 +01:00
a , b = 0 , 1
2015-03-28 23:49:10 +01:00
# Put constant in child[b] if present
if child [ b ] [ ' nt ' ] != ' CONST ' :
2015-02-28 18:30:04 +01:00
a , b = 1 , 0
if child [ b ] [ ' nt ' ] == ' CONST ' and child [ b ] [ ' value ' ] and ' SEF ' in child [ a ] :
parent [ index ] = child [ b ]
2015-03-28 23:49:10 +01:00
child [ b ] [ ' value ' ] = - 1
2015-02-28 18:30:04 +01:00
return
2015-03-28 23:49:10 +01:00
2015-03-29 03:47:54 +02:00
# Check if the operands are a negation ('!') or can be inverted
# without adding more than 1 byte and are boolean.
# We only support '<' and some cases of '&' (are there more?)
2015-03-29 05:43:38 +02:00
Invertible = [ False , False ]
for a in ( 0 , 1 ) :
Invertible [ a ] = child [ a ] [ ' nt ' ] == ' ! '
if child [ a ] [ ' nt ' ] == ' < ' \
and child [ a ] [ ' ch ' ] [ 0 ] [ ' t ' ] == child [ a ] [ ' ch ' ] [ 1 ] [ ' t ' ] == ' integer ' :
if child [ a ] [ ' ch ' ] [ 0 ] [ ' nt ' ] == ' CONST ' \
and child [ a ] [ ' ch ' ] [ 0 ] [ ' value ' ] != 2147483647 \
or child [ a ] [ ' ch ' ] [ 1 ] [ ' nt ' ] == ' CONST ' \
and child [ a ] [ ' ch ' ] [ 1 ] [ ' value ' ] != int ( - 2147483648 ) :
Invertible [ a ] = True
# Deal with our optimization of a<0 -> a&0x80000000 (see below)
if child [ a ] [ ' nt ' ] == ' & ' and (
child [ a ] [ ' ch ' ] [ 0 ] [ ' nt ' ] == ' CONST ' and child [ a ] [ ' ch ' ] [ 0 ] [ ' value ' ] == int ( - 2147483648 )
or child [ a ] [ ' ch ' ] [ 1 ] [ ' nt ' ] == ' CONST ' and child [ a ] [ ' ch ' ] [ 1 ] [ ' value ' ] == int ( - 2147483648 )
) :
Invertible [ a ] | = ParentIsNegation
if ( Invertible [ 0 ] or Invertible [ 1 ] ) and ParentIsNegation :
# !(!a|b) -> a&-!b or a&!b
2015-04-21 04:56:09 +02:00
# This deals with the part after the first !, transforming
# it into (!a|!!b) so that the outer node can optimize the
# negated version to a simple &.
2015-03-29 05:43:38 +02:00
for a in ( 0 , 1 ) :
if not Invertible [ a ] :
child [ a ] = { ' nt ' : ' ! ' , ' t ' : ' integer ' ,
' ch ' : [ { ' nt ' : ' ! ' , ' t ' : ' integer ' , ' ch ' : [ child [ a ] ] } ]
}
Invertible [ a ] = True
if Invertible [ 0 ] and Invertible [ 1 ] :
2015-03-29 03:47:54 +02:00
# Both operands are negated, or negable.
# Make them a negation if they aren't already.
for a in ( 0 , 1 ) :
if child [ a ] [ ' nt ' ] == ' < ' :
if child [ a ] [ ' ch ' ] [ 0 ] [ ' nt ' ] == ' CONST ' :
child [ a ] [ ' ch ' ] [ 0 ] [ ' value ' ] + = 1
else :
child [ a ] [ ' ch ' ] [ 1 ] [ ' value ' ] - = 1
child [ a ] [ ' ch ' ] [ 0 ] , child [ a ] [ ' ch ' ] [ 1 ] = \
child [ a ] [ ' ch ' ] [ 1 ] , child [ a ] [ ' ch ' ] [ 0 ]
child [ a ] = { ' nt ' : ' ! ' , ' t ' : ' integer ' , ' ch ' : [ child [ a ] ] }
elif child [ a ] [ ' nt ' ] == ' & ' :
child [ a ] = { ' nt ' : ' ! ' , ' t ' : ' integer ' ,
' ch ' : [ { ' nt ' : ' ! ' , ' t ' : ' integer ' , ' ch ' : [ child [ a ] ] } ]
}
self . FoldTree ( child [ a ] [ ' ch ' ] , 0 )
2015-03-28 23:49:10 +01:00
# If they are boolean, the expression can be turned into
# !(a&b) which hopefully will have a ! uptree if it came
2015-03-29 00:16:17 +01:00
# from a '&&' and cancel out (if not, we still remove one
2015-03-28 23:49:10 +01:00
# ! so it's good). If one is bool, another transformation
# can be performed: !nonbool|!bool -> !(nonbool&-bool)
# which is still a gain.
2015-03-29 03:47:54 +02:00
# Deal with operands in any order
2015-03-28 23:49:10 +01:00
a , b = 0 , 1
# Put the bool in child[b]['ch'][0].
if not self . IsBool ( child [ b ] [ ' ch ' ] [ 0 ] ) :
a , b = 1 , 0
if self . IsBool ( child [ b ] [ ' ch ' ] [ 0 ] ) :
if not self . IsBool ( child [ a ] [ ' ch ' ] [ 0 ] ) :
2015-03-29 03:47:54 +02:00
child [ b ] [ ' ch ' ] [ 0 ] = { ' nt ' : ' NEG ' , ' t ' : ' integer ' ,
2015-03-28 23:49:10 +01:00
' ch ' : [ child [ b ] [ ' ch ' ] [ 0 ] ] }
node = parent [ index ] = { ' nt ' : ' ! ' , ' t ' : ' integer ' ,
' ch ' : [ { ' nt ' : ' & ' , ' t ' : ' integer ' ,
' ch ' : [ child [ 0 ] [ ' ch ' ] [ 0 ] ,
child [ 1 ] [ ' ch ' ] [ 0 ] ]
} ]
}
# Fold the node we've just synthesized
# (this deals with SEF)
self . FoldTree ( parent , index )
2015-02-28 05:14:52 +01:00
2015-03-29 00:16:17 +01:00
return
2015-03-29 03:47:54 +02:00
# This optimization turns out to be counter-productive for some
# common cases, e.g. it turns i >= 0 into !(i & 0x80000000)
# instead of the more optimal (integer)-1 < i. So we revert it
# where appropriate (in FoldTree, case '!', and above, case '|').
2015-03-29 00:16:17 +01:00
if nt == ' < ' :
if child [ 1 ] [ ' nt ' ] == ' CONST ' and child [ 1 ] [ ' value ' ] == 0 :
nt = node [ ' nt ' ] = ' & '
2015-03-29 03:47:54 +02:00
child [ 1 ] [ ' value ' ] = int ( - 2147483648 )
2015-03-29 00:16:17 +01:00
# Fall through to check & 0x80000000
2015-03-03 17:49:57 +01:00
2015-03-29 00:16:17 +01:00
if nt == ' & ' :
# Deal with operands in any order
a , b = 0 , 1
# Put constant in child[b], if present
if child [ b ] [ ' nt ' ] != ' CONST ' :
a , b = 1 , 0
2015-03-29 03:47:54 +02:00
if child [ b ] [ ' nt ' ] == ' CONST ' and child [ b ] [ ' value ' ] == int ( - 2147483648 ) \
2015-03-29 00:16:17 +01:00
and child [ a ] [ ' nt ' ] == ' FNCALL ' :
sym = self . symtab [ 0 ] [ child [ a ] [ ' name ' ] ]
if ' min ' in sym and sym [ ' min ' ] == - 1 :
node = parent [ index ] = { ' nt ' : ' ~ ' , ' t ' : ' integer ' ,
' ch ' : [ child [ a ] ] }
2015-03-29 03:47:54 +02:00
self . FoldTree ( parent , index )
2015-03-29 00:16:17 +01:00
return
2014-08-13 14:02:05 +02:00
def CopyNode ( self , node ) :
2015-03-29 00:16:17 +01:00
''' This is mainly for simple_expr so no need to go deeper than 1 level
'''
2014-08-13 14:02:05 +02:00
ret = node . copy ( )
if ' ch ' in ret :
new = [ ]
for subnode in ret [ ' ch ' ] :
new . append ( self . CopyNode ( subnode ) )
ret [ ' ch ' ] = new
return ret
def FoldTree ( self , parent , index ) :
""" Recursively traverse the tree to fold constants, changing it in
place .
Also optimizes away IF , WHILE , etc .
"""
node = parent [ index ]
nt = node [ ' nt ' ]
child = node [ ' ch ' ] if ' ch ' in node else None
if nt == ' CONST ' :
# Job already done. But mark as side-effect free.
node [ ' SEF ' ] = True
return
if nt == ' CAST ' :
self . FoldTree ( child , 0 )
if ' SEF ' in child [ 0 ] :
node [ ' SEF ' ] = True
2015-03-13 22:13:00 +01:00
if child [ 0 ] [ ' nt ' ] == ' CONST ' :
2014-08-13 14:02:05 +02:00
# Enable key constants. We'll typecast them back on output, but
# this enables some optimizations.
#if node['t'] != 'key': # key constants not possible
parent [ index ] = { ' nt ' : ' CONST ' , ' t ' : node [ ' t ' ] , ' SEF ' : True ,
' value ' : lslfuncs . typecast (
child [ 0 ] [ ' value ' ] , self . LSL2PythonType [ node [ ' t ' ] ] ) }
2017-08-23 11:58:05 +02:00
# Remove casts of a type to the same type (NOP in Mono)
# This is not an optimization by itself, but it simplifies the job,
# by not needing to look into nested casts like (key)((key)...)
while node [ ' nt ' ] == ' CAST ' and child [ 0 ] [ ' t ' ] == node [ ' t ' ] :
parent [ index ] = node = child [ 0 ]
if ' ch ' not in node :
break
child = node [ ' ch ' ]
2014-08-13 14:02:05 +02:00
return
if nt == ' NEG ' :
self . FoldTree ( child , 0 )
2015-03-29 06:09:26 +02:00
if child [ 0 ] [ ' nt ' ] == ' + ' and ( child [ 0 ] [ ' ch ' ] [ 0 ] [ ' nt ' ] == ' NEG '
or child [ 0 ] [ ' ch ' ] [ 1 ] [ ' nt ' ] == ' NEG ' ) :
node = parent [ index ] = child [ 0 ]
child = node [ ' ch ' ]
for a in ( 0 , 1 ) :
if child [ a ] [ ' nt ' ] == ' NEG ' :
child [ a ] = child [ a ] [ ' ch ' ] [ 0 ]
else :
child [ a ] = { ' nt ' : ' NEG ' , ' t ' : child [ a ] [ ' t ' ] , ' ch ' : [ child [ a ] ] }
self . FoldTree ( child , a )
return
2014-08-13 14:02:05 +02:00
if child [ 0 ] [ ' nt ' ] == ' NEG ' :
2015-03-28 23:47:45 +01:00
# Double negation: - - expr -> expr
2014-12-13 14:16:28 +01:00
node = parent [ index ] = child [ 0 ] [ ' ch ' ] [ 0 ]
child = node [ ' ch ' ] if ' ch ' in node else None
2014-08-13 14:02:05 +02:00
elif child [ 0 ] [ ' nt ' ] == ' CONST ' :
node = parent [ index ] = child [ 0 ]
node [ ' value ' ] = lslfuncs . neg ( node [ ' value ' ] )
2014-12-13 14:16:28 +01:00
child = None
2014-08-13 14:02:05 +02:00
elif ' SEF ' in child [ 0 ] :
# propagate Side Effect Free flag
node [ ' SEF ' ] = True
2014-12-13 14:16:28 +01:00
if child and node [ ' nt ' ] == ' NEG ' and child [ 0 ] [ ' nt ' ] == ' ~ ' :
track = child [ 0 ] [ ' ch ' ] [ 0 ]
const = 1
while track [ ' nt ' ] == ' NEG ' and track [ ' ch ' ] [ 0 ] [ ' nt ' ] == ' ~ ' :
const + = 1
track = track [ ' ch ' ] [ 0 ] [ ' ch ' ] [ 0 ]
if const > 2 :
# -~-~-~expr -> expr+3
node = { ' nt ' : ' CONST ' , ' t ' : ' integer ' , ' SEF ' : True , ' value ' : const }
node = { ' nt ' : ' + ' , ' t ' : ' integer ' , ' ch ' : [ node , track ] }
2015-02-28 00:43:26 +01:00
if ' SEF ' in track :
node [ ' SEF ' ] = True
parent [ index ] = node
2014-12-13 14:16:28 +01:00
2014-08-13 14:02:05 +02:00
return
if nt == ' ! ' :
self . FoldTree ( child , 0 )
2015-03-29 03:47:54 +02:00
self . FoldCond ( child , 0 , True )
2015-04-21 04:56:09 +02:00
# !! does *not* cancel out (unless in cond)
2014-08-13 14:02:05 +02:00
subexpr = child [ 0 ]
2015-03-29 03:47:54 +02:00
snt = subexpr [ ' nt ' ]
2017-08-14 21:41:08 +02:00
if snt == ' FNCALL ' and subexpr [ ' name ' ] == ' llStringLength ' :
# !llStringLength(expr) -> expr == ""
parent [ index ] = { ' nt ' : ' == ' , ' t ' : ' integer ' ,
' ch ' : [ subexpr [ ' ch ' ] [ 0 ] ,
{ ' nt ' : ' CONST ' , ' t ' : ' string ' ,
' value ' : u " " } ] }
# new node is SEF if the argument to llStringLength is
if ' SEF ' in subexpr [ ' ch ' ] [ 0 ] :
parent [ index ] [ ' SEF ' ] = True
return
2014-08-13 14:02:05 +02:00
if ' SEF ' in subexpr :
node [ ' SEF ' ] = True
if subexpr [ ' nt ' ] == ' CONST ' :
node = parent [ index ] = subexpr
node [ ' value ' ] = int ( not node [ ' value ' ] )
2015-03-29 03:47:54 +02:00
return
if snt == ' < ' :
lop = subexpr [ ' ch ' ] [ 0 ]
rop = subexpr [ ' ch ' ] [ 1 ]
if lop [ ' nt ' ] == ' CONST ' and lop [ ' t ' ] == rop [ ' t ' ] == ' integer ' \
and lop [ ' value ' ] < 2147483647 :
lop [ ' value ' ] + = 1
subexpr [ ' ch ' ] [ 0 ] , subexpr [ ' ch ' ] [ 1 ] = subexpr [ ' ch ' ] [ 1 ] , subexpr [ ' ch ' ] [ 0 ]
parent [ index ] = subexpr # remove !
return
if rop [ ' nt ' ] == ' CONST ' and lop [ ' t ' ] == rop [ ' t ' ] == ' integer ' \
and rop [ ' value ' ] > int ( - 2147483648 ) :
rop [ ' value ' ] - = 1
subexpr [ ' ch ' ] [ 0 ] , subexpr [ ' ch ' ] [ 1 ] = subexpr [ ' ch ' ] [ 1 ] , subexpr [ ' ch ' ] [ 0 ]
parent [ index ] = subexpr # remove !
return
if snt == ' & ' :
a , b = 0 , 1
if subexpr [ ' ch ' ] [ b ] [ ' nt ' ] != ' CONST ' :
a , b = 1 , 0
if subexpr [ ' ch ' ] [ b ] [ ' nt ' ] == ' CONST ' and subexpr [ ' ch ' ] [ b ] [ ' value ' ] == int ( - 2147483648 ) :
# !(i & 0x80000000) -> -1 < i (because one of our
# optimizations can be counter-productive, see FoldCond)
subexpr [ ' nt ' ] = ' < '
subexpr [ ' ch ' ] [ b ] [ ' value ' ] = - 1
subexpr [ ' ch ' ] = [ subexpr [ ' ch ' ] [ b ] , subexpr [ ' ch ' ] [ a ] ]
parent [ index ] = subexpr
return
2014-08-13 14:02:05 +02:00
return
if nt == ' ~ ' :
self . FoldTree ( child , 0 )
subexpr = child [ 0 ]
if ' SEF ' in subexpr :
node [ ' SEF ' ] = True
if subexpr [ ' nt ' ] == ' ~ ' :
# Double negation: ~~expr
parent [ index ] = subexpr [ ' ch ' ] [ 0 ]
elif subexpr [ ' nt ' ] == ' CONST ' :
node = parent [ index ] = child [ 0 ]
node [ ' value ' ] = ~ node [ ' value ' ]
return
if nt in self . binary_ops :
# RTL evaluation
self . FoldTree ( child , 1 )
self . FoldTree ( child , 0 )
if ' SEF ' in child [ 0 ] and ' SEF ' in child [ 1 ] :
# Propagate SEF flag if both sides are side-effect free.
node [ ' SEF ' ] = True
2015-02-12 07:47:35 +01:00
optype = node [ ' t ' ]
lval = child [ 0 ]
ltype = lval [ ' t ' ]
lnt = lval [ ' nt ' ]
rval = child [ 1 ]
rtype = rval [ ' t ' ]
rnt = rval [ ' nt ' ]
if lnt == rnt == ' CONST ' :
op1 = lval [ ' value ' ]
op2 = rval [ ' value ' ]
2014-08-13 14:02:05 +02:00
if nt == ' + ' :
2015-02-28 20:37:18 +01:00
if ltype == rtype == ' string ' and not self . addstrings :
return
result = lslfuncs . add ( op1 , op2 )
2014-08-13 14:02:05 +02:00
elif nt == ' - ' :
result = lslfuncs . sub ( op1 , op2 )
elif nt == ' * ' :
result = lslfuncs . mul ( op1 , op2 )
elif nt == ' / ' :
2015-02-11 05:43:13 +01:00
try :
result = lslfuncs . div ( op1 , op2 )
except lslfuncs . ELSLMathError :
return
2014-08-13 14:02:05 +02:00
elif nt == ' % ' :
2015-02-11 05:43:13 +01:00
try :
result = lslfuncs . mod ( op1 , op2 )
except lslfuncs . ELSLMathError :
return
2014-08-13 14:02:05 +02:00
elif nt == ' << ' :
result = lslfuncs . S32 ( op1 << ( op2 & 31 ) )
elif nt == ' >> ' :
result = lslfuncs . S32 ( op1 >> ( op2 & 31 ) )
elif nt == ' == ' or nt == ' != ' :
result = lslfuncs . compare ( op1 , op2 , Eq = ( nt == ' == ' ) )
elif nt in ( ' < ' , ' <= ' , ' > ' , ' >= ' ) :
if nt in ( ' > ' , ' <= ' ) :
result = lslfuncs . less ( op2 , op1 )
else :
result = lslfuncs . less ( op1 , op2 )
if nt in ( ' >= ' , ' <= ' ) :
result = 1 - result
elif nt == ' | ' :
result = op1 | op2
elif nt == ' ^ ' :
result = op1 ^ op2
elif nt == ' & ' :
result = op1 & op2
elif nt == ' || ' :
result = int ( bool ( op1 ) or bool ( op2 ) )
elif nt == ' && ' :
result = int ( bool ( op1 ) and bool ( op2 ) )
else :
assert False , ' Internal error: Operator not found: ' + nt # pragma: no cover
parent [ index ] = { ' nt ' : ' CONST ' , ' t ' : node [ ' t ' ] , ' SEF ' : True , ' value ' : result }
return
# Simplifications for particular operands
if nt == ' - ' :
if optype in ( ' vector ' , ' rotation ' ) :
if lnt == ' CONST ' and all ( component == 0 for component in lval [ ' value ' ] ) :
# Change <0,0,0[,0]>-expr -> -expr
parent [ index ] = { ' nt ' : ' NEG ' , ' t ' : node [ ' t ' ] , ' ch ' : [ rval ] }
if ' SEF ' in rval :
parent [ index ] [ ' SEF ' ] = True
elif rnt == ' CONST ' and all ( component == 0 for component in rval [ ' value ' ] ) :
# Change expr-<0,0,0[,0]> -> expr
parent [ index ] = lval
return
# Change - to + - for int/float
nt = node [ ' nt ' ] = ' + '
if child [ 1 ] [ ' nt ' ] == ' CONST ' :
rval [ ' value ' ] = lslfuncs . neg ( rval [ ' value ' ] )
else :
rnt = ' NEG '
RSEF = ' SEF ' in rval
rval = child [ 1 ] = { ' nt ' : rnt , ' t ' : rval [ ' t ' ] , ' ch ' : [ rval ] }
2015-03-29 06:09:26 +02:00
self . FoldTree ( child , 1 )
2014-08-13 14:02:05 +02:00
if RSEF :
rval [ ' SEF ' ] = True
# rtype unchanged
# Fall through to simplify it as '+'
if nt == ' + ' :
# Tough one. Remove neutral elements for the diverse types,
# and more.
2014-08-19 19:45:47 +02:00
2015-02-28 20:37:18 +01:00
# Addition of integers, strings, and lists is associative.
2015-02-12 07:47:35 +01:00
# Addition of floats, vectors and rotations would be, except
# for FP precision.
2015-02-27 19:18:28 +01:00
# TODO: associative addition of lists
# Associative lists are trickier, because unlike the others,
# the types of the operands may not be lists
# so e.g. list+(integer+integer) != (list+integer)+integer.
2015-02-28 20:37:18 +01:00
if optype == ' integer ' or optype == ' string ' and self . addstrings :
2015-02-12 07:47:35 +01:00
if lnt == ' + ' and rnt == ' CONST ' and lval [ ' ch ' ] [ 1 ] [ ' nt ' ] == ' CONST ' :
# (var + ct1) + ct2 -> var + (ct1 + ct2)
child [ 1 ] = { ' nt ' : ' + ' , ' t ' : optype , ' ch ' : [ lval [ ' ch ' ] [ 1 ] , rval ] , ' SEF ' : True }
lval = child [ 0 ] = lval [ ' ch ' ] [ 0 ]
lnt = lval [ ' nt ' ]
ltype = lval [ ' t ' ]
rtype = optype
# Fold the RHS again now that we have it constant
self . FoldTree ( child , 1 )
rval = child [ 1 ]
rnt = rval [ ' nt ' ]
2014-08-13 14:02:05 +02:00
if optype == ' list ' and not ( ltype == rtype == ' list ' ) :
2014-08-17 16:31:46 +02:00
if lnt == ' CONST ' and not lval [ ' value ' ] :
# [] + nonlist -> (list)nonlist
parent [ index ] = self . Cast ( rval , optype )
2017-08-14 21:23:09 +02:00
# node is SEF if rval is
parent [ index ] [ ' SEF ' ] = ' SEF ' in rval
2014-08-13 14:02:05 +02:00
return
if optype in ( ' vector ' , ' rotation ' ) :
# not much to do with vectors or quaternions either
2015-02-28 21:46:19 +01:00
if lnt == ' CONST ' and all ( x == 0 for x in lval [ ' value ' ] ) :
2014-08-13 14:02:05 +02:00
# Change <0,0,0[,0]>+expr -> expr
parent [ index ] = rval
2015-02-28 21:46:19 +01:00
elif rnt == ' CONST ' and all ( x == 0 for x in rval [ ' value ' ] ) :
2014-08-13 14:02:05 +02:00
# Change expr+<0,0,0[,0]> -> expr
parent [ index ] = lval
return
# Can't be key, as no combo of addition operands returns key
2014-08-17 16:31:46 +02:00
# All these types evaluate to boolean False when they are
2014-08-13 14:02:05 +02:00
# the neutral addition element.
if optype in ( ' string ' , ' float ' , ' list ' ) :
if lnt == ' CONST ' and not lval [ ' value ' ] :
2014-08-17 16:31:46 +02:00
# 0. + expr -> expr
2014-08-13 14:02:05 +02:00
# "" + expr -> expr
# [] + expr -> expr
parent [ index ] = self . Cast ( rval , optype )
2017-08-14 21:23:09 +02:00
# node is SEF if rval is
parent [ index ] [ ' SEF ' ] = ' SEF ' in rval
2017-08-09 16:41:36 +02:00
return
if rnt == ' CONST ' and not rval [ ' value ' ] :
2014-08-17 16:31:46 +02:00
# expr + 0. -> expr
2014-08-13 14:02:05 +02:00
# expr + "" -> expr
# expr + [] -> expr
parent [ index ] = self . Cast ( lval , optype )
2017-08-14 21:23:09 +02:00
# node is SEF if lval is
parent [ index ] [ ' SEF ' ] = ' SEF ' in lval
2017-08-09 16:41:36 +02:00
return
if ltype == rtype == ' list ' :
if ( rnt == ' LIST ' and len ( rval [ ' ch ' ] ) == 1
or rnt == ' CAST ' ) :
# list + (list)element -> list + element
# list + [element] -> list + element
while True :
# Remove nested typecasts: (list)(list)x -> x
rval = parent [ index ] [ ' ch ' ] [ 1 ] = rval [ ' ch ' ] [ 0 ]
if rval [ ' nt ' ] != ' CAST ' or rval [ ' t ' ] != ' list ' :
break
return
if rnt == ' CONST ' and len ( rval [ ' value ' ] ) == 1 :
# list + [constant] -> list + constant
rval [ ' value ' ] = rval [ ' value ' ] [ 0 ]
rtype = rval [ ' t ' ] = self . PythonType2LSL [ type ( rval [ ' value ' ] ) ]
return
if ( lnt == ' LIST ' and len ( lval [ ' ch ' ] ) == 1
or lnt == ' CAST ' ) :
# (list)element + list -> element + list
# [element] + list -> element + list
while True :
# Remove nested typecasts: (list)(list)x -> x
lval = parent [ index ] [ ' ch ' ] [ 0 ] = lval [ ' ch ' ] [ 0 ]
if lval [ ' nt ' ] != ' CAST ' or lval [ ' t ' ] != ' list ' :
break
return
if lnt == ' CONST ' and len ( lval [ ' value ' ] ) == 1 :
# [constant] + list -> constant + list
lval [ ' value ' ] = lval [ ' value ' ] [ 0 ]
ltype = lval [ ' t ' ] = self . PythonType2LSL [ type ( lval [ ' value ' ] ) ]
return
2014-08-13 14:02:05 +02:00
return
# Must be two integers. This allows for a number of
# optimizations. First the most obvious ones.
if lnt == ' CONST ' and lval [ ' value ' ] == 0 :
parent [ index ] = rval
return
if rnt == ' CONST ' and rval [ ' value ' ] == 0 :
parent [ index ] = lval
return
if lnt != ' CONST ' != rnt :
# Neither is const. Two chances to optimize.
# 1. -expr + -expr -> -(expr + expr) (saves 1 byte)
# 2. lvalue + -lvalue -> 0
# There may be other possibilities for optimization,
# e.g. (type)ident + -(type)ident but we only do lvalues
# here. Note these are integers, no NaN involved.
# TODO: Compare the subtrees if they are SEF. If they are
# the same subtree, they can cancel out.
if lnt == rnt == ' NEG ' :
node = { ' nt ' : ' + ' , ' t ' : optype , ' ch ' : [ lval [ ' ch ' ] [ 0 ] , rval [ ' ch ' ] [ 0 ] ] }
SEF = ' SEF ' in lval [ ' ch ' ] [ 0 ] and ' SEF ' in rval [ ' ch ' ] [ 0 ]
if SEF :
node [ ' SEF ' ] = True
node = { ' nt ' : ' NEG ' , ' t ' : optype , ' ch ' : [ node ] }
if SEF :
node [ ' SEF ' ] = True
parent [ index ] = node
return
if lnt == ' NEG ' :
# Swap to treat always as expr + -expr for simplicity.
lnt , lval , rnt , rval = rnt , rval , lnt , lval
if lnt == ' IDENT ' and rnt == ' NEG ' and rval [ ' ch ' ] [ 0 ] [ ' nt ' ] == ' IDENT ' \
and lval [ ' name ' ] == rval [ ' ch ' ] [ 0 ] [ ' name ' ] :
# Replace with 0
parent [ index ] = { ' nt ' : ' CONST ' , ' SEF ' : True , ' t ' : optype , ' value ' : 0 }
return
2014-08-19 19:45:47 +02:00
if lnt == ' + ' and ( lval [ ' ch ' ] [ 0 ] [ ' nt ' ] == ' CONST '
2014-12-13 14:16:28 +01:00
or lval [ ' ch ' ] [ 1 ] [ ' nt ' ] == ' CONST ' ) :
# We have expr + const + const or const + expr + const.
2014-08-19 19:45:47 +02:00
# Addition of integers mod 2^32 is associative and
# commutative, so constants can be merged.
if lval [ ' ch ' ] [ 0 ] [ ' nt ' ] == ' CONST ' :
rval [ ' value ' ] = lslfuncs . S32 ( rval [ ' value ' ] + lval [ ' ch ' ] [ 0 ] [ ' value ' ] )
lval = child [ 0 ] = lval [ ' ch ' ] [ 1 ]
else :
rval [ ' value ' ] = lslfuncs . S32 ( rval [ ' value ' ] + lval [ ' ch ' ] [ 1 ] [ ' value ' ] )
lval = child [ 0 ] = lval [ ' ch ' ] [ 0 ]
lnt = lval [ ' nt ' ]
2015-02-28 00:43:26 +01:00
if rnt == ' + ' and ( rval [ ' ch ' ] [ 0 ] [ ' nt ' ] == ' CONST '
or rval [ ' ch ' ] [ 1 ] [ ' nt ' ] == ' CONST ' ) :
2014-12-13 14:16:28 +01:00
# const + (expr + const) or const + (const + expr)
# same as above, join them
2015-02-28 20:39:23 +01:00
# FIXME: Isn't this covered by the associative sum above?
2014-12-13 14:16:28 +01:00
2015-03-05 06:20:11 +01:00
pass # TODO: implement const + (expr + const) or const + (const + expr)
2014-12-13 14:16:28 +01:00
2014-08-13 14:02:05 +02:00
if rnt == ' CONST ' :
# Swap the vars to deal with const in lval always
lval , lnt , rval , rnt = rval , rnt , lval , lnt
RSEF = ' SEF ' in rval
2015-02-27 20:05:14 +01:00
if lval [ ' value ' ] == - 1 or lval [ ' value ' ] == - 2 :
if rnt == ' NEG ' : # Cancel the NEG
2015-02-28 00:43:26 +01:00
node = { ' nt ' : ' ~ ' , ' t ' : optype , ' ch ' : rval [ ' ch ' ] }
2015-02-27 20:05:14 +01:00
if RSEF :
node [ ' SEF ' ] = True
else : # Add the NEG
2015-02-27 20:12:17 +01:00
node = { ' nt ' : ' NEG ' , ' t ' : optype , ' ch ' : [ rval ] }
2015-02-27 20:05:14 +01:00
if RSEF :
node [ ' SEF ' ] = True
node = { ' nt ' : ' ~ ' , ' t ' : optype , ' ch ' : [ node ] }
if RSEF :
node [ ' SEF ' ] = True
if lval [ ' value ' ] == - 2 :
node = { ' nt ' : ' NEG ' , ' t ' : optype , ' ch ' : [ node ] }
2014-08-13 14:02:05 +02:00
if RSEF :
node [ ' SEF ' ] = True
node = { ' nt ' : ' ~ ' , ' t ' : optype , ' ch ' : [ node ] }
if RSEF :
node [ ' SEF ' ] = True
parent [ index ] = node
return
2015-02-27 20:05:14 +01:00
if lval [ ' value ' ] == 1 or lval [ ' value ' ] == 2 :
if rnt == ' ~ ' : # Cancel the ~
2015-02-27 20:12:17 +01:00
node = { ' nt ' : ' NEG ' , ' t ' : optype , ' ch ' : rval [ ' ch ' ] }
2014-08-13 14:02:05 +02:00
if RSEF :
node [ ' SEF ' ] = True
2015-02-27 20:05:14 +01:00
else :
2015-02-28 00:43:26 +01:00
node = { ' nt ' : ' ~ ' , ' t ' : optype , ' ch ' : [ rval ] }
2014-08-13 14:02:05 +02:00
if RSEF :
node [ ' SEF ' ] = True
node = { ' nt ' : ' NEG ' , ' t ' : optype , ' ch ' : [ node ] }
if RSEF :
node [ ' SEF ' ] = True
2015-02-27 20:05:14 +01:00
if lval [ ' value ' ] == 2 :
2014-08-13 14:02:05 +02:00
node = { ' nt ' : ' ~ ' , ' t ' : optype , ' ch ' : [ node ] }
if RSEF :
node [ ' SEF ' ] = True
2015-02-27 20:05:14 +01:00
node = { ' nt ' : ' NEG ' , ' t ' : optype , ' ch ' : [ node ] }
if RSEF :
node [ ' SEF ' ] = True
2014-08-13 14:02:05 +02:00
parent [ index ] = node
return
# More than 2 becomes counter-productive.
return
2014-08-19 19:45:47 +02:00
if nt == ' << ' and child [ 1 ] [ ' nt ' ] == ' CONST ' :
2014-08-13 14:02:05 +02:00
# Transforming << into multiply saves some bytes.
if child [ 1 ] [ ' value ' ] & 31 :
# x << 3 --> x * 8
2015-02-28 05:14:52 +01:00
# we have {<<, something, {CONST n}}
# we transform it into {*, something, {CONST n}}
2015-03-28 14:43:11 +01:00
nt = node [ ' nt ' ] = ' * '
2014-08-13 14:02:05 +02:00
child [ 1 ] [ ' value ' ] = 1 << ( child [ 1 ] [ ' value ' ] & 31 )
2015-02-28 05:14:52 +01:00
# Fall through to optimize product
2014-08-13 14:02:05 +02:00
else : # x << 0 --> x
parent [ index ] = child [ 0 ]
2015-02-28 05:14:52 +01:00
return
2015-02-28 18:30:04 +01:00
if nt == ' % ' \
and child [ 1 ] [ ' nt ' ] == ' CONST ' \
and child [ 1 ] [ ' t ' ] == ' integer ' \
and abs ( child [ 1 ] [ ' value ' ] ) == 1 :
# a%1 -> a&0
# a%-1 -> a&0
# (SEF analysis performed below)
nt = node [ ' nt ' ] = ' & '
child [ 1 ] [ ' value ' ] = 0
if nt in ( ' * ' , ' / ' ) :
2015-03-03 01:46:53 +01:00
# Extract signs outside
if child [ 0 ] [ ' nt ' ] == ' NEG ' or child [ 1 ] [ ' nt ' ] == ' NEG ' :
a , b = 0 , 1
if child [ b ] [ ' nt ' ] == ' NEG ' :
a , b = 1 , 0
child [ a ] = child [ a ] [ ' ch ' ] [ 0 ]
parent [ index ] = node = { ' nt ' : ' NEG ' , ' t ' : node [ ' t ' ] , ' ch ' : [ node ] }
if ' SEF ' in node [ ' ch ' ] [ 0 ] :
node [ ' SEF ' ] = True
# Fold the new expression
self . FoldTree ( parent , index )
return
2015-03-02 23:28:14 +01:00
2015-02-28 18:30:04 +01:00
# Deal with operands in any order
a , b = 0 , 1
if child [ a ] [ ' nt ' ] == ' CONST ' and child [ a ] [ ' t ' ] in ( ' float ' , ' integer ' ) :
a , b = 1 , 0
if child [ b ] [ ' nt ' ] == ' CONST ' :
val = child [ b ] [ ' value ' ]
# Optimize out signs if possible.
# Note that (-intvar)*floatconst needs cornermath because
# -intvar could equal intvar if intvar = -2147483648,
# so the sign is a no-op and pushing it to floatconst would
# make the result be different.
if child [ a ] [ ' nt ' ] == ' NEG ' \
and ( self . cornermath
or child [ a ] [ ' t ' ] != ' integer '
or child [ b ] [ ' t ' ] != ' float '
) :
# Expression is of the form (-float)*const or (-float)/const or const/(-float)
2015-03-29 03:47:54 +02:00
if val != int ( - 2147483648 ) or child [ a ] [ ' t ' ] == ' integer ' : # can't be optimized otherwise
2015-02-28 18:30:04 +01:00
child [ a ] = child [ a ] [ ' ch ' ] [ 0 ] # remove NEG
child [ b ] [ ' value ' ] = val = - val
# Five optimizations corresponding to -2, -1, 0, 1, 2
# for product, and two for division:
# expr * 1 -> expr
# expr * 0 -> 0 if side-effect free
# expr * -1 -> -expr
2015-03-03 02:16:00 +01:00
# ident * 2 -> ident + ident (only if ident is local)
# ident * -2 -> -(ident + ident) (only if ident is local)
2015-02-28 18:30:04 +01:00
# expr/1 -> expr
# expr/-1 -> -expr
if nt == ' * ' and child [ b ] [ ' t ' ] in ( ' float ' , ' integer ' ) \
and val in ( - 2 , - 1 , 0 , 1 , 2 ) \
or nt == ' / ' and b == 1 and val in ( - 1 , 1 ) :
if val == 1 :
parent [ index ] = child [ a ]
return
if val == 0 :
if ' SEF ' in child [ a ] :
parent [ index ] = child [ b ]
return
if val == - 1 :
# Note 0.0*-1 equals -0.0 in LSL, so this is safe
node = parent [ index ] = { ' nt ' : ' NEG ' , ' t ' : node [ ' t ' ] , ' ch ' : [ child [ a ] ] }
if ' SEF ' in child [ a ] :
node [ ' SEF ' ] = True
return
# only -2, 2 remain
2015-03-27 02:12:32 +01:00
if child [ a ] [ ' nt ' ] == ' IDENT ' and self . isLocalVar ( child [ a ] ) :
2015-03-03 00:49:14 +01:00
child [ b ] = child [ a ] . copy ( )
node [ ' nt ' ] = ' + '
if val == - 2 :
parent [ index ] = { ' nt ' : ' NEG ' , ' t ' : node [ ' t ' ] , ' ch ' : [ node ] }
if ' SEF ' in node :
parent [ index ] [ ' SEF ' ] = True
2015-02-28 18:30:04 +01:00
return
return
2015-02-28 05:14:52 +01:00
2015-03-28 20:23:47 +01:00
if nt in ( ' <= ' , ' >= ' ) or nt == ' != ' and child [ 0 ] [ ' t ' ] != ' list ' :
# Except for list != list, all these comparisons are compiled
# as !(a>b) etc. so we transform them here in order to reduce
# the number of cases to check.
# a<=b --> !(a>b); a>=b --> !(a<b); a!=b --> !(a==b)
2015-03-03 02:16:00 +01:00
node [ ' nt ' ] = { ' <= ' : ' > ' , ' >= ' : ' < ' , ' != ' : ' == ' } [ nt ]
2015-04-21 06:09:57 +02:00
parent [ index ] = { ' nt ' : ' ! ' , ' t ' : node [ ' t ' ] , ' ch ' : [ node ] }
2015-03-29 03:47:54 +02:00
self . FoldTree ( parent , index )
2015-04-21 06:09:57 +02:00
return
2015-03-28 20:23:47 +01:00
if nt == ' > ' :
# Invert the inequalities to avoid doubling the cases to check.
# a>b --> b<a
nt = node [ ' nt ' ] = ' < '
child [ 1 ] , child [ 0 ] = child [ 0 ] , child [ 1 ]
# fall through to check for '<'
if nt == ' < ' :
# Convert 2147483647<i and i<-2147483648 to i&0
if child [ 0 ] [ ' t ' ] == child [ 1 ] [ ' t ' ] == ' integer ' \
and ( child [ 0 ] [ ' nt ' ] == ' CONST ' and child [ 0 ] [ ' value ' ] == 2147483647
2015-03-29 03:47:54 +02:00
or child [ 1 ] [ ' nt ' ] == ' CONST ' and child [ 1 ] [ ' value ' ] == int ( - 2147483648 ) ) :
2015-03-28 20:23:47 +01:00
a , b = 0 , 1
# Put the constant in child[b]
if child [ a ] [ ' nt ' ] == ' CONST ' :
a , b = 1 , 0
2015-03-03 02:16:00 +01:00
nt = node [ ' nt ' ] = ' & '
child [ b ] [ ' value ' ] = 0
# fall through to check for '&'
2015-03-28 20:23:47 +01:00
else :
return
2015-02-28 20:39:23 +01:00
2015-02-28 18:30:04 +01:00
if nt in ( ' & ' , ' | ' ) :
# Deal with operands in any order
a , b = 0 , 1
2015-03-28 20:23:47 +01:00
# Put constant in child[b]
if child [ b ] [ ' nt ' ] != ' CONST ' :
2015-02-28 18:30:04 +01:00
a , b = 1 , 0
if child [ b ] [ ' nt ' ] == ' CONST ' :
val = child [ b ] [ ' value ' ]
2017-08-02 01:40:48 +02:00
if nt == ' | ' and val == 0 or nt == ' & ' and ( val == - 1 or val == 1 and self . IsBool ( child [ a ] ) ) :
2015-02-28 18:30:04 +01:00
# a|0 -> a
# a&-1 -> a
2017-08-02 01:40:48 +02:00
# a&1 -> a if a is boolean
2015-02-28 18:30:04 +01:00
parent [ index ] = child [ a ]
return
2017-08-02 01:40:48 +02:00
if nt == ' | ' and ( val == - 1 or val == 1 and self . IsBool ( child [ a ] ) ) or nt == ' & ' and val == 0 :
2015-02-28 18:30:04 +01:00
# a|-1 -> -1 if a is SEF
2017-08-02 01:40:48 +02:00
# a|1 -> 1 if a is bool and SEF
2015-02-28 18:30:04 +01:00
# a&0 -> 0 if a is SEF
if ' SEF ' in child [ a ] :
parent [ index ] = child [ b ]
2015-03-28 20:23:47 +01:00
return
2015-02-28 05:14:52 +01:00
if nt == ' ^ ' :
2015-02-28 18:30:04 +01:00
a , b = 0 , 1
if child [ a ] [ ' nt ' ] == ' CONST ' :
a , b = 1 , 0
if child [ b ] [ ' nt ' ] == ' CONST ' and child [ b ] [ ' value ' ] in ( 0 , - 1 ) :
if child [ b ] [ ' value ' ] == 0 :
parent [ index ] = child [ a ]
2015-02-28 05:14:52 +01:00
else :
node [ ' nt ' ] = ' ~ '
2015-02-28 18:30:04 +01:00
node [ ' ch ' ] = [ child [ a ] ]
2015-02-28 05:14:52 +01:00
return
if nt == ' && ' or nt == ' || ' :
2015-02-28 18:30:04 +01:00
SEF = ' SEF ' in node
2015-02-28 05:14:52 +01:00
if nt == ' || ' :
parent [ index ] = node = { ' nt ' : ' ! ' , ' t ' : ' integer ' , ' ch ' : [
{ ' nt ' : ' ! ' , ' t ' : ' integer ' , ' ch ' : [
{ ' nt ' : ' | ' , ' t ' : ' integer ' , ' ch ' : [ child [ 0 ] , child [ 1 ] ] }
] } ] }
if SEF :
node [ ' SEF ' ] = node [ ' ch ' ] [ 0 ] [ ' SEF ' ] = node [ ' ch ' ] [ 0 ] [ ' ch ' ] [ 0 ] [ ' SEF ' ] = True
else :
parent [ index ] = node = { ' nt ' : ' ! ' , ' t ' : ' integer ' , ' ch ' : [
{ ' nt ' : ' | ' , ' t ' : ' integer ' , ' ch ' : [
{ ' nt ' : ' ! ' , ' t ' : ' integer ' , ' ch ' : [ child [ 0 ] ] }
,
{ ' nt ' : ' ! ' , ' t ' : ' integer ' , ' ch ' : [ child [ 1 ] ] }
] } ] }
if SEF :
node [ ' SEF ' ] = node [ ' ch ' ] [ 0 ] [ ' SEF ' ] = True
2015-02-28 18:30:04 +01:00
if ' SEF ' in node [ ' ch ' ] [ 0 ] [ ' ch ' ] [ 0 ] [ ' ch ' ] [ 0 ] :
node [ ' ch ' ] [ 0 ] [ ' ch ' ] [ 0 ] [ ' SEF ' ] = True
if ' SEF ' in node [ ' ch ' ] [ 0 ] [ ' ch ' ] [ 1 ] [ ' ch ' ] [ 0 ] :
node [ ' ch ' ] [ 0 ] [ ' ch ' ] [ 1 ] [ ' SEF ' ] = True
# Make another pass with the substitution
2015-02-28 05:14:52 +01:00
self . FoldTree ( parent , index )
return
2014-08-13 14:02:05 +02:00
return
if nt in self . assign_ops :
# Transform the whole thing into a regular assignment, as there are
# no gains and it simplifies the optimization.
# An assignment has no side effects only if it's of the form x = x.
if nt != ' = ' :
# Replace the node with the expression alone
2015-02-28 00:43:26 +01:00
# e.g. a += b -> a + b
2014-08-13 14:02:05 +02:00
node [ ' nt ' ] = nt [ : - 1 ]
2015-02-28 00:43:26 +01:00
# Linden Craziness: int *= float; is valid (but no other
# int op= float is). It's actually performed as
# i = (integer)(i + (f));
# This breaks equivalence of x op= y as x = x op (y) so we add
# the explicit type cast here.
2014-08-13 14:02:05 +02:00
if nt == ' *= ' and child [ 0 ] [ ' t ' ] == ' integer ' and child [ 1 ] [ ' t ' ] == ' float ' :
node [ ' t ' ] = ' float ' # Addition shall return float.
node = self . Cast ( node , ' integer ' )
# And wrap it in an assignment.
child = [ child [ 0 ] . copy ( ) , node ]
node = parent [ index ] = { ' nt ' : ' = ' , ' t ' : child [ 0 ] [ ' t ' ] , ' ch ' : child }
# We have a regular assignment either way now. Simplify the RHS.
self . FoldTree ( node [ ' ch ' ] , 1 )
if child [ 0 ] [ ' nt ' ] == child [ 1 ] [ ' nt ' ] == ' IDENT ' \
and child [ 1 ] [ ' name ' ] == child [ 0 ] [ ' name ' ] \
and child [ 1 ] [ ' scope ' ] == child [ 0 ] [ ' scope ' ] \
or child [ 0 ] [ ' nt ' ] == child [ 1 ] [ ' nt ' ] == ' FLD ' \
and child [ 1 ] [ ' ch ' ] [ 0 ] [ ' name ' ] == child [ 0 ] [ ' ch ' ] [ 0 ] [ ' name ' ] \
and child [ 1 ] [ ' ch ' ] [ 0 ] [ ' scope ' ] == child [ 0 ] [ ' ch ' ] [ 0 ] [ ' scope ' ] \
and child [ 1 ] [ ' fld ' ] == child [ 0 ] [ ' fld ' ] :
node [ ' SEF ' ] = True
self . FoldStmt ( parent , index )
return
if nt == ' IDENT ' or nt == ' FLD ' :
node [ ' SEF ' ] = True
if self . globalmode :
ident = child [ 0 ] if nt == ' FLD ' else node
# Resolve constant values so they can be optimized
sym = self . symtab [ ident [ ' scope ' ] ] [ ident [ ' name ' ] ]
defn = self . tree [ sym [ ' Loc ' ] ]
assert defn [ ' name ' ] == ident [ ' name ' ]
# Assume we already were there
if ' ch ' in defn :
val = defn [ ' ch ' ] [ 0 ]
if val [ ' nt ' ] != ' CONST ' or ident [ ' t ' ] == ' key ' :
return
val = val . copy ( )
else :
val = { ' nt ' : ' CONST ' , ' t ' : defn [ ' t ' ] ,
' value ' : self . DefaultValues [ defn [ ' t ' ] ] }
if nt == ' FLD ' :
val = { ' nt ' : ' CONST ' , ' t ' : ' float ' ,
' value ' : val [ ' value ' ] [ ' xyzs ' . index ( node [ ' fld ' ] ) ] }
parent [ index ] = val
return
if nt == ' FNCALL ' :
2017-08-23 11:55:37 +02:00
name = node [ ' name ' ]
2014-08-13 14:02:05 +02:00
SEFargs = True
CONSTargs = True
for idx in xrange ( len ( child ) - 1 , - 1 , - 1 ) :
self . FoldTree ( child , idx )
# Function is not SEF if any argument is not SEF
if ' SEF ' not in child [ idx ] :
SEFargs = False
# Function is not a constant if any argument is not a constant
if child [ idx ] [ ' nt ' ] != ' CONST ' :
CONSTargs = False
2017-08-23 11:55:37 +02:00
sym = self . symtab [ 0 ] [ name ]
2016-12-21 06:01:03 +01:00
OptimizeParams ( node , sym )
2017-01-08 05:53:10 +01:00
if ' Fn ' in sym and ( ' SEF ' in sym and sym [ ' SEF ' ] or lslcommon . IsCalc ) :
# It's side-effect free if the children are and the function
# is marked as SEF.
2014-08-13 14:02:05 +02:00
if SEFargs :
node [ ' SEF ' ] = True
if CONSTargs :
# Call it
2016-12-21 06:01:03 +01:00
fn = sym [ ' Fn ' ]
2016-12-22 01:24:44 +01:00
args = [ arg [ ' value ' ] for arg in child ]
argtypes = sym [ ' ParamTypes ' ]
assert ( len ( args ) == len ( argtypes ) )
for argnum in range ( len ( args ) ) :
# Adapt types of params
if argtypes [ argnum ] == ' string ' :
args [ argnum ] = lslfuncs . fs ( args [ argnum ] )
elif argtypes [ argnum ] == ' key ' :
args [ argnum ] = lslfuncs . fk ( args [ argnum ] )
elif argtypes [ argnum ] == ' float ' :
args [ argnum ] = lslfuncs . ff ( args [ argnum ] )
elif argtypes [ argnum ] == ' vector ' :
args [ argnum ] = lslfuncs . v2f ( args [ argnum ] )
elif argtypes [ argnum ] == ' quaternion ' :
args [ argnum ] = lslfuncs . q2f ( args [ argnum ] )
elif argtypes [ argnum ] == ' list ' :
# ensure vectors and quaternions passed to
# functions have only float components
assert type ( args [ argnum ] ) == list
# make a shallow copy
args [ argnum ] = args [ argnum ] [ : ]
for i in range ( len ( args [ argnum ] ) ) :
2017-08-23 11:55:37 +02:00
if type ( args [ argnum ] [ i ] ) == Quaternion :
2016-12-22 01:24:44 +01:00
args [ argnum ] [ i ] = lslfuncs . q2f ( args [ argnum ] [ i ] )
2017-08-23 11:55:37 +02:00
elif type ( args [ argnum ] [ i ] ) == Vector :
2016-12-22 01:24:44 +01:00
args [ argnum ] [ i ] = lslfuncs . v2f ( args [ argnum ] [ i ] )
del argtypes
2015-02-11 05:43:13 +01:00
try :
2017-08-23 11:55:37 +02:00
if name [ : 10 ] == ' llDetected ' :
2016-12-21 06:01:03 +01:00
value = fn ( * args , event = self . CurEvent )
2015-02-11 05:43:13 +01:00
else :
2016-12-21 06:01:03 +01:00
value = fn ( * args )
2015-02-11 05:43:13 +01:00
except lslfuncs . ELSLCantCompute :
# Don't transform the tree if function is not computable
2016-12-25 00:25:49 +01:00
return
2016-12-22 01:24:44 +01:00
del args
if not self . foldtabs :
generatesTabs = (
isinstance ( value , unicode ) and ' \t ' in value
or type ( value ) == list and any ( isinstance ( x , unicode ) and ' \t ' in x for x in value )
)
if generatesTabs :
if self . warntabs :
2017-08-23 11:55:37 +02:00
warning ( u " Can ' t optimize call to %s because it would generate a tab character (you can force the optimization with the ' foldtabs ' option, or disable this warning by disabling the ' warntabs ' option). " % name . decode ( ' utf8 ' ) )
2016-12-22 01:24:44 +01:00
return
2017-01-17 01:00:04 +01:00
parent [ index ] = { ' nt ' : ' CONST ' , ' t ' : node [ ' t ' ] , ' value ' : value , ' SEF ' : True }
2017-08-23 11:55:37 +02:00
elif self . optlistlength and name == ' llGetListLength ' :
2015-07-13 07:16:32 +02:00
# Convert llGetListLength(expr) to (expr != [])
2014-08-13 14:02:05 +02:00
node = { ' nt ' : ' CONST ' , ' t ' : ' list ' , ' value ' : [ ] }
2017-08-15 12:52:48 +02:00
parent [ index ] = node = { ' nt ' : ' != ' , ' t ' : ' integer ' ,
2017-06-15 21:11:54 +02:00
' ch ' : [ child [ 0 ] , node ] }
2017-08-14 21:23:09 +02:00
# SEF if the list is
node [ ' SEF ' ] = ' SEF ' in child [ 0 ]
2017-08-23 11:55:37 +02:00
elif ( name == ' llDumpList2String '
2017-05-31 14:39:42 +02:00
and child [ 1 ] [ ' nt ' ] == ' CONST '
and child [ 1 ] [ ' t ' ] in ( ' string ' , ' key ' )
and child [ 1 ] [ ' value ' ] == u " " ) :
# Convert llDumpList2String(expr, "") to (string)(expr)
node [ ' nt ' ] = ' CAST '
2017-06-15 21:11:54 +02:00
del child [ 1 ]
2017-05-31 14:39:42 +02:00
del node [ ' name ' ]
Optimize some llList2XXXX functions.
- Resolve for lists that aren't constants, but that are SEF, and reference a constant element, e.g. llList2String([llGetKey(), 5], 1) -> 5.
- Make it work with invalid conversions, if they are SEF.
- Simplify invalid conversions when the list argument is a llGetObjectDetails call and SEF.
- Simplify llList2String(llGetObjectDetails(id, [single_element]), 0) (or -1) to (string)llGetObjectDetails...; same with llList2Key and llList2Integer, converting it to (integer)((string)llGetObjectDetails...).
- Add TODO item to do it similarly for llGet[Link]PrimitiveParams.
Adds auxiliary functions to return a node or constant from a list in any of its forms, its length, and a constant for a node or constant.
Adds some utility members for LSL<->Python type conversion (one of them duplicated from lslparse.py).
Needs maintenance of the types returned by llGetObjectDetails.
2017-08-23 14:37:47 +02:00
elif ( name in ( ' llList2String ' , ' llList2Key ' , ' llList2Integer ' ,
' llList2Float ' , ' llList2Vector ' , ' llList2Rot ' )
and child [ 1 ] [ ' nt ' ] == ' CONST ' ) :
# 2nd arg to llList2XXXX must be integer
assert child [ 1 ] [ ' t ' ] == ' integer '
listarg = child [ 0 ]
idx = child [ 1 ] [ ' value ' ]
value = self . GetListNodeElement ( listarg , idx )
tvalue = self . TypeFromNodeOrConst ( value )
const = self . ConstFromNodeOrConst ( value )
if const is not False and ' SEF ' in node :
# Managed to get a constant from a list, even if the
# list wasn't constant. Handle the type conversion.
if ( node [ ' t ' ] [ 0 ] + tvalue [ 0 ] ) in self . listCompat :
const = lslfuncs . InternalTypecast ( const ,
self . LSLType2Python [ node [ ' t ' ] ] ,
InList = True , f32 = True )
else :
const = self . defaultListVals [ name ]
parent [ index ] = { ' nt ' : ' CONST ' , ' t ' : node [ ' t ' ] ,
' value ' : const , ' SEF ' : True }
return
if listarg [ ' nt ' ] == ' FNCALL ' and listarg [ ' name ' ] == ' llGetObjectDetails ' :
listarg = listarg [ ' ch ' ] [ 1 ] # make it the list argument of llGetObjectDetails
value = self . GetListNodeElement ( listarg , idx )
tvalue = self . TypeFromNodeOrConst ( value )
const = self . ConstFromNodeOrConst ( value )
if type ( const ) == int and self . GetListNodeLength ( listarg ) == 1 :
# Some of these can be handled with a typecast to string.
if name == ' llList2String ' :
# turn the node into a cast of arg 0 to string
node [ ' nt ' ] = ' CAST '
del child [ 1 ]
del node [ ' name ' ]
return
# The other ones that support cast to string then to
# the final type in some cases (depending on the
# list type, which we know) are key/int/float.
if ( name == ' llList2Key ' # checked via listCompat
or ( name == ' llList2Integer '
and self . objDetailsTypes [ const : const + 1 ]
in ( ' s ' , ' i ' ) ) # won't work for floats
or ( name == ' llList2Float '
and self . objDetailsTypes [ const : const + 1 ]
in ( ' s ' , ' i ' ) ) # won't work for floats
) and ( node [ ' t ' ] [ 0 ]
+ self . objDetailsTypes [ const : const + 1 ]
) in self . listCompat :
# -> (key)((string)llGetObjectDetails...)
# or (integer)((string)llGetObjectDetails...)
node [ ' nt ' ] = ' CAST '
del child [ 1 ]
del node [ ' name ' ]
child [ 0 ] = { ' nt ' : ' CAST ' , ' t ' : ' string ' ,
' ch ' : [ child [ 0 ] ] }
if ' SEF ' in child [ 0 ] [ ' ch ' ] [ 0 ] :
child [ 0 ] [ ' SEF ' ] = True
return
# Check for type incompatibility or index out of range
# and replace node with a constant if that's the case
if ( value is False
or type ( const ) == int
and ( node [ ' t ' ] [ 0 ] + self . objDetailsTypes [ const ] )
not in self . listCompat
) and ' SEF ' in node :
parent [ index ] = { ' nt ' : ' CONST ' , ' t ' : node [ ' t ' ] ,
' value ' : self . defaultListVals [ name ] ,
' SEF ' : True }
2017-08-24 22:58:32 +02:00
elif listarg [ ' nt ' ] == ' FNCALL ' and listarg [ ' name ' ] in (
' llGetPrimitiveParams ' , ' llGetLinkPrimitiveParams ' ) :
# We're going to work with the primitive params list.
listarg = listarg [ ' ch ' ] [
0 if listarg [ ' name ' ] == ' llGetPrimitiveParams '
else 1 ]
length = self . GetListNodeLength ( listarg )
if length is not False :
# Construct a list (string) of return types.
# A '*' in the list means the type can't be
# determined past this point (used with PRIM_TYPE).
i = 0
returntypes = ' '
while i < length :
param = self . GetListNodeElement ( listarg , i )
param = self . ConstFromNodeOrConst ( param )
if ( param is False
or type ( param ) != int
# Parameters with arguments have
# side effects (errors).
# We could check whether there's a face
# argument and the face is 0, which is
# guaranteed to exist, but it's not worth
# the effort.
or param in self . primParamsArgs
or param < 0
or param > = len ( self . primParamsTypes )
or self . primParamsTypes [ param ] is False
) :
# Can't process this list.
returntypes = ' ! '
break
returntypes + = self . primParamsTypes [ param ]
i + = 1
if returntypes != ' ! ' :
if ( len ( returntypes ) == 1
and returntypes != ' * '
and idx in ( 0 , - 1 )
) :
if name == ' llList2String ' :
node [ ' nt ' ] = ' CAST '
del child [ 1 ]
del node [ ' name ' ]
return
if ( ( name == ' llList2Key '
or name == ' llList2Integer '
and returntypes in ( ' s ' , ' i ' )
or name == ' llList2Float '
and returntypes in ( ' s ' , ' i ' )
)
and ( node [ ' t ' ] [ 0 ] + returntypes )
in self . listCompat
) :
node [ ' nt ' ] = ' CAST '
del child [ 1 ]
del node [ ' name ' ]
child [ 0 ] = { ' nt ' : ' CAST ' , ' t ' : ' string ' ,
' ch ' : [ child [ 0 ] ] }
if ' SEF ' in child [ 0 ] [ ' ch ' ] [ 0 ] :
child [ 0 ] [ ' SEF ' ] = True
return
if ( returntypes . find ( ' * ' ) == - 1
or idx > = 0 and idx < returntypes . find ( ' * ' )
or idx < 0 and idx > returntypes . rfind ( ' * ' )
- len ( returntypes )
) :
# Check for type incompatibility or index
# out of range.
if idx < 0 :
# s[-1:0] doesn't return the last char
# so we have to compensate
idx + = len ( returntypes )
if ( node [ ' t ' ] [ 0 ] + returntypes [ idx : idx + 1 ] ) \
not in self . listCompat \
and ' SEF ' in node :
parent [ index ] = { ' nt ' : ' CONST ' ,
' t ' : node [ ' t ' ] ,
' value ' : self . defaultListVals [ name ] ,
' SEF ' : True }
return
del returntypes
Optimize some llList2XXXX functions.
- Resolve for lists that aren't constants, but that are SEF, and reference a constant element, e.g. llList2String([llGetKey(), 5], 1) -> 5.
- Make it work with invalid conversions, if they are SEF.
- Simplify invalid conversions when the list argument is a llGetObjectDetails call and SEF.
- Simplify llList2String(llGetObjectDetails(id, [single_element]), 0) (or -1) to (string)llGetObjectDetails...; same with llList2Key and llList2Integer, converting it to (integer)((string)llGetObjectDetails...).
- Add TODO item to do it similarly for llGet[Link]PrimitiveParams.
Adds auxiliary functions to return a node or constant from a list in any of its forms, its length, and a constant for a node or constant.
Adds some utility members for LSL<->Python type conversion (one of them duplicated from lslparse.py).
Needs maintenance of the types returned by llGetObjectDetails.
2017-08-23 14:37:47 +02:00
del listarg , idx , value , tvalue , const
2017-08-23 11:55:37 +02:00
elif SEFargs and ' SEF ' in self . symtab [ 0 ] [ name ] :
2014-08-13 14:02:05 +02:00
# The function is marked as SEF in the symbol table, and the
# arguments are all side-effect-free. The result is SEF.
node [ ' SEF ' ] = True
return
if nt == ' PRINT ' :
self . FoldTree ( child , 0 )
# PRINT is considered to have side effects. If it's there, assume
# there's a reason.
return
if nt == ' EXPR ' :
self . FoldTree ( child , 0 )
if ' SEF ' in child [ 0 ] :
node [ ' SEF ' ] = True
return
if nt == ' FNDEF ' :
2015-02-11 05:43:13 +01:00
# used when folding llDetected* function calls
if ' scope ' in node :
# function definition
self . CurEvent = None
else :
# event definition
self . CurEvent = node [ ' name ' ]
2014-08-13 14:02:05 +02:00
self . FoldTree ( child , 0 )
2015-03-13 23:46:23 +01:00
# TODO: This works, but analysis of code paths is DCR's thing
# and this is incomplete, e.g. x(){{return;}} is not detected.
while ' ch ' in child [ 0 ] and child [ 0 ] [ ' ch ' ] :
last = child [ 0 ] [ ' ch ' ] [ - 1 ]
if last [ ' nt ' ] != ' RETURN ' or ' ch ' in last :
break
del child [ 0 ] [ ' ch ' ] [ - 1 ]
2014-08-13 14:02:05 +02:00
if ' SEF ' in child [ 0 ] :
node [ ' SEF ' ] = True
if node [ ' name ' ] in self . symtab [ 0 ] :
# Mark the symbol table entry if it's not an event.
self . symtab [ 0 ] [ node [ ' name ' ] ] [ ' SEF ' ] = True
return
if nt in ( ' VECTOR ' , ' ROTATION ' , ' LIST ' ) :
isconst = True
issef = True
2017-01-23 01:18:50 +01:00
for idx in xrange ( len ( child ) ) :
2014-08-13 14:02:05 +02:00
self . FoldTree ( child , idx )
if child [ idx ] [ ' nt ' ] != ' CONST ' :
isconst = False
if ' SEF ' not in child [ idx ] :
issef = False
if isconst :
2017-08-23 11:55:37 +02:00
value = [ x [ ' value ' ] for x in child ]
2014-08-13 14:02:05 +02:00
if nt == ' VECTOR ' :
2017-08-23 11:55:37 +02:00
value = Vector ( [ lslfuncs . ff ( x ) for x in value ] )
2014-08-13 14:02:05 +02:00
elif nt == ' ROTATION ' :
2017-08-23 11:55:37 +02:00
value = Quaternion ( [ lslfuncs . ff ( x ) for x in value ] )
2015-03-13 22:13:00 +01:00
parent [ index ] = { ' nt ' : ' CONST ' , ' SEF ' : True , ' t ' : node [ ' t ' ] ,
' value ' : value }
return
2014-08-13 14:02:05 +02:00
if issef :
node [ ' SEF ' ] = True
return
if nt == ' STDEF ' :
for idx in xrange ( len ( child ) ) :
self . FoldTree ( child , idx )
return
if nt == ' {} ' :
idx = 0
issef = True
while idx < len ( child ) :
self . FoldTree ( child , idx )
self . FoldStmt ( child , idx )
if ' SEF ' not in child [ idx ] :
issef = False
if child [ idx ] [ ' nt ' ] == ' ; ' \
2015-03-13 23:45:56 +01:00
or child [ idx ] [ ' nt ' ] == ' {} ' and not child [ idx ] [ ' ch ' ] :
2014-08-13 14:02:05 +02:00
del child [ idx ]
else :
if ' StSw ' in child [ idx ] :
node [ ' StSw ' ] = True
idx + = 1
if issef :
node [ ' SEF ' ] = True
return
if nt == ' IF ' :
2015-03-05 06:20:11 +01:00
# TODO: Swap IF/ELSE if both present and cond starts with !
2017-08-17 01:58:30 +02:00
self . ExpandCondition ( child , 0 )
2014-08-13 14:02:05 +02:00
self . FoldTree ( child , 0 )
self . FoldCond ( child , 0 )
if child [ 0 ] [ ' nt ' ] == ' CONST ' :
# We might be able to remove one of the branches.
if child [ 0 ] [ ' value ' ] :
self . FoldTree ( child , 1 )
# If it has a state switch, the if() must be preserved
# (but the else branch may be removed).
if ' StSw ' in child [ 1 ] :
# TODO: Get rid of StSw craziness and make another pass
# to put them under conditionals if present (if bald
# state switches are present, it means they are the
# result of optimization so they must be wrapped in an
# IF statement). The current approach leaves unnecessary
# IFs behind.
2017-01-05 02:55:06 +01:00
if len ( child ) == 3 and child [ 2 ] [ ' nt ' ] != ' @ ' :
2014-08-13 14:02:05 +02:00
del child [ 2 ] # Delete ELSE if present
return
else :
self . FoldStmt ( child , 1 )
2017-01-05 02:55:06 +01:00
if len ( child ) == 3 and child [ 2 ] [ ' nt ' ] == ' @ ' :
# Corner case. The label is in the same scope as
# this statement, so it must be preserved just in
# case it's jumped to.
return
2014-08-13 14:02:05 +02:00
parent [ index ] = child [ 1 ]
return
elif len ( child ) == 3 :
self . FoldTree ( child , 2 )
self . FoldStmt ( child , 2 )
2017-01-05 02:55:06 +01:00
if child [ 1 ] [ ' nt ' ] == ' @ ' :
# Corner case. The label is in the same scope as this
# statement, so it must be preserved just in case it's
# jumped to.
if not self . DoesSomething ( child [ 2 ] ) :
del child [ 2 ]
return
2014-08-13 14:02:05 +02:00
parent [ index ] = child [ 2 ]
return
else :
# No ELSE branch, replace the statement with an empty one.
2017-01-05 02:55:06 +01:00
if child [ 1 ] [ ' nt ' ] == ' @ ' :
# Corner case. The label is in the same scope as this
# statement, so it must be preserved just in case it's
# jumped to.
parent [ index ] = child [ 1 ]
return
2014-08-13 14:02:05 +02:00
parent [ index ] = { ' nt ' : ' ; ' , ' t ' : None , ' SEF ' : True }
return
else :
self . FoldTree ( child , 1 )
self . FoldStmt ( child , 1 )
if len ( child ) > 2 :
self . FoldTree ( child , 2 )
self . FoldStmt ( child , 2 )
2017-01-05 02:55:06 +01:00
if not self . DoesSomething ( child [ 2 ] ) :
2014-08-13 14:02:05 +02:00
# no point in "... else ;" - remove else branch
del child [ 2 ]
if all ( ' SEF ' in subnode for subnode in child ) :
node [ ' SEF ' ] = True
return
if nt == ' WHILE ' :
# Loops are not considered side-effect free. If the expression is
# TRUE, it's definitely not SEF. If it's FALSE, it will be optimized
# anyway. Otherwise we just don't know if it may be infinite, even
# if every component is SEF.
2017-08-17 01:58:30 +02:00
self . ExpandCondition ( child , 0 )
2014-08-13 14:02:05 +02:00
self . FoldTree ( child , 0 )
self . FoldCond ( child , 0 )
if child [ 0 ] [ ' nt ' ] == ' CONST ' :
# See if the whole WHILE can be eliminated.
if child [ 0 ] [ ' value ' ] :
# Endless loop which must be kept.
# Recurse on the statement.
self . FoldTree ( child , 1 )
self . FoldStmt ( child , 1 )
else :
2017-01-05 02:55:06 +01:00
if child [ 1 ] [ ' nt ' ] == ' @ ' :
# Corner case. The label is in the same scope as this
# statement, so it must be preserved just in case it's
# jumped to.
parent [ index ] = child [ 1 ]
else :
# Whole statement can be removed.
parent [ index ] = { ' nt ' : ' ; ' , ' t ' : None , ' SEF ' : True }
2014-08-13 14:02:05 +02:00
return
else :
self . FoldTree ( child , 1 )
self . FoldStmt ( child , 1 )
return
if nt == ' DO ' :
self . FoldTree ( child , 0 ) # This one is always executed.
self . FoldStmt ( child , 0 )
2017-08-17 01:58:30 +02:00
self . ExpandCondition ( child , 1 )
2014-08-13 14:02:05 +02:00
self . FoldTree ( child , 1 )
self . FoldCond ( child , 1 )
# See if the latest part is a constant.
if child [ 1 ] [ ' nt ' ] == ' CONST ' :
if not child [ 1 ] [ ' value ' ] :
# Only one go. Replace with the statement(s).
parent [ index ] = child [ 0 ]
return
if nt == ' FOR ' :
assert child [ 0 ] [ ' nt ' ] == ' EXPRLIST '
assert child [ 2 ] [ ' nt ' ] == ' EXPRLIST '
self . FoldAndRemoveEmptyStmts ( child [ 0 ] [ ' ch ' ] )
2017-08-17 01:58:30 +02:00
self . ExpandCondition ( child , 1 ) # Condition.
self . FoldTree ( child , 1 )
2014-08-13 14:02:05 +02:00
self . FoldCond ( child , 1 )
if child [ 1 ] [ ' nt ' ] == ' CONST ' :
# FOR is delicate. It can have multiple expressions at start.
# And if there is more than one, these expressions will need a
# new block, which means new scope, which is dangerous.
# They are expressions, no declarations or labels allowed, thus
2017-01-04 00:41:08 +01:00
# no new identifiers may be created in the new scope, but it
# still feels dodgy.
2014-08-13 14:02:05 +02:00
if child [ 1 ] [ ' value ' ] :
# Endless loop. Traverse the loop and the iterator.
self . FoldTree ( child , 3 )
self . FoldStmt ( child , 3 )
self . FoldAndRemoveEmptyStmts ( child [ 2 ] [ ' ch ' ] )
else :
# Convert expression list to code block.
exprlist = [ ]
for expr in child [ 0 ] [ ' ch ' ] :
# Fold into expression statements.
exprlist . append ( { ' nt ' : ' EXPR ' , ' t ' : expr [ ' t ' ] , ' ch ' : [ expr ] } )
2017-01-05 02:55:06 +01:00
if ( exprlist or child [ 2 ] [ ' ch ' ] ) and child [ 3 ] [ ' nt ' ] == ' @ ' :
# Corner case. We can't optimize this to one single
# statement, so we leave it as-is.
self . FoldTree ( child , 3 )
self . FoldStmt ( child , 3 )
self . FoldAndRemoveEmptyStmts ( child [ 2 ] [ ' ch ' ] )
return
2014-08-13 14:02:05 +02:00
# returns type None, as FOR does
if exprlist :
# We're in the case where there are expressions. If any
# remain, they are not SEF (or they would have been
# removed earlier) so don't mark this node as SEF.
parent [ index ] = { ' nt ' : ' {} ' , ' t ' : None , ' ch ' : exprlist }
else :
2017-01-05 02:55:06 +01:00
if child [ 3 ] [ ' nt ' ] == ' @ ' :
# Corner case. The label is in the same scope as
# this statement, so it must be preserved. Also,
# jumping inside the loop would execute the
# iterator, so we fold it.
self . FoldAndRemoveEmptyStmts ( child [ 2 ] [ ' ch ' ] )
if not child [ 2 ] [ ' ch ' ] :
# if there's something in the 2nd list,
# preserve the whole statement, otherwise
# replace it with the label
parent [ index ] = child [ 3 ]
else :
parent [ index ] = { ' nt ' : ' ; ' , ' t ' : None , ' SEF ' : True }
2014-08-13 14:02:05 +02:00
return
else :
self . FoldTree ( child , 3 )
self . FoldStmt ( child , 3 )
self . FoldAndRemoveEmptyStmts ( child [ 2 ] [ ' ch ' ] )
return
if nt == ' RETURN ' :
if child :
self . FoldTree ( child , 0 )
return
if nt == ' DECL ' :
if child :
# Check if child is a simple_expr. If it is, then we keep the
# original attached to the folded node to use it in the output.
if child [ 0 ] . pop ( ' Simple ' , False ) :
orig = self . CopyNode ( child [ 0 ] )
self . FoldTree ( child , 0 )
child [ 0 ] [ ' orig ' ] = orig
else :
self . FoldTree ( child , 0 )
# Remove assignment if integer zero.
if node [ ' t ' ] == ' integer ' and child [ 0 ] [ ' nt ' ] == ' CONST ' \
and not child [ 0 ] [ ' value ' ] :
del node [ ' ch ' ]
return
else :
# Add assignment if vector, rotation or float.
if node [ ' t ' ] in ( ' float ' , ' vector ' , ' rotation ' ) :
typ = node [ ' t ' ]
node [ ' ch ' ] = [ { ' nt ' : ' CONST ' , ' t ' : typ , ' SEF ' : True ,
' value ' : 0.0 if typ == ' float ' else
2017-08-23 11:55:37 +02:00
ZERO_VECTOR if typ == ' vector ' else
ZERO_ROTATION } ]
2014-08-13 14:02:05 +02:00
# Declarations always have side effects.
return
if nt == ' STSW ' :
# State switch always has side effects.
node [ ' StSw ' ] = True
return
2015-03-13 06:38:35 +01:00
if nt == ' SUBIDX ' :
# Recurse to every child. It's SEF if all children are.
idx = 0
issef = True
while idx < len ( child ) :
self . FoldTree ( child , idx )
if ' SEF ' not in child [ idx ] :
issef = False
idx + = 1
if issef :
node [ ' SEF ' ] = True
return
2014-08-13 14:02:05 +02:00
if nt == ' ; ' :
node [ ' SEF ' ] = True
return
2015-03-03 17:59:51 +01:00
if nt in ( ' JUMP ' , ' @ ' , ' V++ ' , ' V-- ' , ' --V ' , ' ++V ' , ' LAMBDA ' ) :
# Except LAMBDA, these all have side effects, as in, can't be
# eliminated as statements.
# LAMBDA can't be eliminated without scrolling Loc's.
2014-08-13 14:02:05 +02:00
return
assert False , ' Internal error: This should not happen, node type = ' \
+ nt # pragma: no cover
2015-03-06 23:26:58 +01:00
def IsValidGlobalIdOrConst ( self , node ) :
2016-01-06 01:21:04 +01:00
# nan can't be represented as a simple constant; all others are valid
2015-03-06 23:26:58 +01:00
return not ( node [ ' nt ' ] == ' CONST ' and node [ ' t ' ] == ' float '
2016-01-06 01:21:04 +01:00
and math . isnan ( node [ ' value ' ] ) )
2015-03-06 23:26:58 +01:00
2014-08-13 14:02:05 +02:00
def IsValidGlobalConstant ( self , decl ) :
if ' ch ' not in decl :
return True
expr = decl [ ' ch ' ] [ 0 ]
if expr [ ' nt ' ] in ( ' CONST ' , ' IDENT ' ) :
2015-03-06 23:26:58 +01:00
return self . IsValidGlobalIdOrConst ( expr )
2014-08-13 14:02:05 +02:00
if expr [ ' nt ' ] not in ( ' VECTOR ' , ' ROTATION ' , ' LIST ' ) :
return False
2015-03-06 23:26:58 +01:00
return all ( elem [ ' nt ' ] in ( ' CONST ' , ' IDENT ' )
and self . IsValidGlobalIdOrConst ( elem )
for elem in expr [ ' ch ' ] )
2014-08-13 14:02:05 +02:00
2015-03-06 23:26:58 +01:00
def FoldScript ( self , warningpass = True ) :
2014-08-13 14:02:05 +02:00
""" Optimize the symbolic table symtab in place. Requires a table of
predefined functions for folding constants .
"""
self . globalmode = False
2014-08-13 14:19:58 +02:00
tree = self . tree
2015-02-11 05:43:13 +01:00
self . CurEvent = None
2014-08-13 14:19:58 +02:00
Optimize some llList2XXXX functions.
- Resolve for lists that aren't constants, but that are SEF, and reference a constant element, e.g. llList2String([llGetKey(), 5], 1) -> 5.
- Make it work with invalid conversions, if they are SEF.
- Simplify invalid conversions when the list argument is a llGetObjectDetails call and SEF.
- Simplify llList2String(llGetObjectDetails(id, [single_element]), 0) (or -1) to (string)llGetObjectDetails...; same with llList2Key and llList2Integer, converting it to (integer)((string)llGetObjectDetails...).
- Add TODO item to do it similarly for llGet[Link]PrimitiveParams.
Adds auxiliary functions to return a node or constant from a list in any of its forms, its length, and a constant for a node or constant.
Adds some utility members for LSL<->Python type conversion (one of them duplicated from lslparse.py).
Needs maintenance of the types returned by llGetObjectDetails.
2017-08-23 14:37:47 +02:00
# Patch the default values list for LSO
if lslcommon . LSO :
self . defaultListVals [ ' llList2Key ' ] = Key ( NULL_KEY )
else :
self . defaultListVals [ ' llList2Key ' ] = Key ( u " " )
2014-08-13 14:02:05 +02:00
# Constant folding pass. It does some other optimizations along the way.
for idx in xrange ( len ( tree ) ) :
if tree [ idx ] [ ' nt ' ] == ' DECL ' :
self . globalmode = True
self . FoldTree ( tree , idx )
self . globalmode = False
2015-03-06 23:26:58 +01:00
if warningpass and not self . IsValidGlobalConstant ( tree [ idx ] ) :
2016-12-20 19:22:48 +01:00
warning ( u " Expression in globals doesn ' t resolve to a simple constant. " )
2014-08-13 14:02:05 +02:00
else :
self . FoldTree ( tree , idx )