Separate library function optimization into a different file.

No other functional changes. This required quite some reorganization affecting many files. As a side effect, PythonType2LSL and LSLType2Python aren't duplicate anymore.
This commit is contained in:
Sei Lisa 2017-08-25 20:11:16 +02:00
parent 36947b7b12
commit 6738615360
5 changed files with 359 additions and 329 deletions

View file

@ -45,3 +45,13 @@ LSO = False
IsCalc = False IsCalc = False
DataPath = '' DataPath = ''
# Conversion of LSL types to Python types and vice versa.
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}

View file

@ -18,89 +18,15 @@
# Constant folding and simplification of expressions and statements. # Constant folding and simplification of expressions and statements.
import lslcommon import lslcommon
from lslcommon import Key, Vector, Quaternion from lslcommon import Vector, Quaternion
import lslfuncs import lslfuncs
from lslfuncs import NULL_KEY, ZERO_VECTOR, ZERO_ROTATION from lslfuncs import ZERO_VECTOR, ZERO_ROTATION
import math import math
from lslparse import warning from lslparse import warning
from lslfuncopt import OptimizeParams from lslfuncopt import OptimizeFunc, OptimizeArgs, FuncOptSetup
class foldconst(object): class foldconst(object):
# Type of each entry in llGetObjectDetails. Last: 38.
objDetailsTypes = 'issvrvkkkiiififfffkiiiiiiffkiviiksiisii'
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
}
# 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}
def isLocalVar(self, node): def isLocalVar(self, node):
name = node['name'] name = node['name']
scope = node['scope'] scope = node['scope']
@ -166,7 +92,7 @@ class foldconst(object):
return False return False
if type(nodeOrConst) == dict: if type(nodeOrConst) == dict:
return nodeOrConst['t'] return nodeOrConst['t']
return self.PythonType2LSL[type(nodeOrConst)] return lslcommon.PythonType2LSL[type(nodeOrConst)]
def FoldAndRemoveEmptyStmts(self, lst): def FoldAndRemoveEmptyStmts(self, lst):
"""Utility function for elimination of useless expressions in FOR""" """Utility function for elimination of useless expressions in FOR"""
@ -478,7 +404,7 @@ class foldconst(object):
parent[index] = {'nt':'CONST', 't':node['t'], 'SEF':True, parent[index] = {'nt':'CONST', 't':node['t'], 'SEF':True,
'value':lslfuncs.typecast( 'value':lslfuncs.typecast(
child[0]['value'], self.LSL2PythonType[node['t']])} child[0]['value'], lslcommon.LSLType2Python[node['t']])}
# Remove casts of a type to the same type (NOP in Mono) # Remove casts of a type to the same type (NOP in Mono)
# This is not an optimization by itself, but it simplifies the job, # This is not an optimization by itself, but it simplifies the job,
@ -770,7 +696,7 @@ class foldconst(object):
if rnt == 'CONST' and len(rval['value']) == 1: if rnt == 'CONST' and len(rval['value']) == 1:
# list + [constant] -> list + constant # list + [constant] -> list + constant
rval['value'] = rval['value'][0] rval['value'] = rval['value'][0]
rtype = rval['t'] = self.PythonType2LSL[type(rval['value'])] rtype = rval['t'] = lslcommon.PythonType2LSL[type(rval['value'])]
return return
if (lnt == 'LIST' and len(lval['ch']) == 1 if (lnt == 'LIST' and len(lval['ch']) == 1
@ -786,7 +712,7 @@ class foldconst(object):
if lnt == 'CONST' and len(lval['value']) == 1: if lnt == 'CONST' and len(lval['value']) == 1:
# [constant] + list -> constant + list # [constant] + list -> constant + list
lval['value'] = lval['value'][0] lval['value'] = lval['value'][0]
ltype = lval['t'] = self.PythonType2LSL[type(lval['value'])] ltype = lval['t'] = lslcommon.PythonType2LSL[type(lval['value'])]
return return
return return
@ -1178,241 +1104,87 @@ class foldconst(object):
CONSTargs = False CONSTargs = False
sym = self.symtab[0][name] sym = self.symtab[0][name]
OptimizeParams(node, sym) OptimizeArgs(node, sym)
if 'Fn' in sym and ('SEF' in sym and sym['SEF'] or lslcommon.IsCalc): try:
# It's side-effect free if the children are and the function if 'Fn' in sym and ('SEF' in sym or lslcommon.IsCalc):
# is marked as SEF. # It's side-effect free if the children are and the function
if SEFargs: # is marked as SEF.
node['SEF'] = True if SEFargs:
if CONSTargs: node['SEF'] = True
# Call it if CONSTargs:
fn = sym['Fn'] # Call it
args = [arg['value'] for arg in child] fn = sym['Fn']
argtypes = sym['ParamTypes'] args = [arg['value'] for arg in child]
assert(len(args) == len(argtypes)) argtypes = sym['ParamTypes']
for argnum in range(len(args)): assert(len(args) == len(argtypes))
# Adapt types of params for argnum in range(len(args)):
if argtypes[argnum] == 'string': # Adapt types of params
args[argnum] = lslfuncs.fs(args[argnum]) if argtypes[argnum] == 'string':
elif argtypes[argnum] == 'key': args[argnum] = lslfuncs.fs(args[argnum])
args[argnum] = lslfuncs.fk(args[argnum]) elif argtypes[argnum] == 'key':
elif argtypes[argnum] == 'float': args[argnum] = lslfuncs.fk(args[argnum])
args[argnum] = lslfuncs.ff(args[argnum]) elif argtypes[argnum] == 'float':
elif argtypes[argnum] == 'vector': args[argnum] = lslfuncs.ff(args[argnum])
args[argnum] = lslfuncs.v2f(args[argnum]) elif argtypes[argnum] == 'vector':
elif argtypes[argnum] == 'quaternion': args[argnum] = lslfuncs.v2f(args[argnum])
args[argnum] = lslfuncs.q2f(args[argnum]) elif argtypes[argnum] == 'quaternion':
elif argtypes[argnum] == 'list': args[argnum] = lslfuncs.q2f(args[argnum])
# ensure vectors and quaternions passed to elif argtypes[argnum] == 'list':
# functions have only float components # ensure vectors and quaternions passed to
assert type(args[argnum]) == list # functions have only float components
# make a shallow copy assert type(args[argnum]) == list
args[argnum] = args[argnum][:] # make a shallow copy
for i in range(len(args[argnum])): args[argnum] = args[argnum][:]
if type(args[argnum][i]) == Quaternion: for i in range(len(args[argnum])):
args[argnum][i] = lslfuncs.q2f(args[argnum][i]) if type(args[argnum][i]) == Quaternion:
elif type(args[argnum][i]) == Vector: args[argnum][i] = lslfuncs.q2f(args[argnum][i])
args[argnum][i] = lslfuncs.v2f(args[argnum][i]) elif type(args[argnum][i]) == Vector:
del argtypes args[argnum][i] = lslfuncs.v2f(args[argnum][i])
try: del argtypes
if name[:10] == 'llDetected': try:
value = fn(*args, event=self.CurEvent) # May raise ELSLCantCompute
else: if name[:10] == 'llDetected':
value = fn(*args) value = fn(*args, event=self.CurEvent)
except lslfuncs.ELSLCantCompute: else:
# Don't transform the tree if function is not computable value = fn(*args)
return finally:
del args
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:
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'))
return
parent[index] = {'nt':'CONST', 't':node['t'], 'value':value, 'SEF':True}
elif self.optlistlength and name == 'llGetListLength':
# Convert llGetListLength(expr) to (expr != [])
node = {'nt':'CONST', 't':'list', 'value':[]}
parent[index] = node = {'nt':'!=', 't':'integer',
'ch':[child[0], node]}
# SEF if the list is
node['SEF'] = 'SEF' in child[0]
elif (name == 'llDumpList2String'
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'
del child[1]
del node['name']
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]
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:
warning(u"Can't optimize call to %s"
u" because it would generate a tab"
u" character (you can force the "
u" optimization with the 'foldtabs'"
u" option, or disable this warning by"
u" disabling the 'warntabs' option)."
% name.decode('utf8'))
raise lslfuncs.ELSLCantCompute()
# Replace with a constant
parent[index] = {'nt':'CONST', 't':node['t'], parent[index] = {'nt':'CONST', 't':node['t'],
'value':const, 'SEF':True} 'value':value, 'SEF':True}
return return
if listarg['nt'] == 'FNCALL' and listarg['name'] == 'llGetObjectDetails': elif SEFargs and 'SEF' in self.symtab[0][name]:
# 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
listarg = listarg['ch'][1] # make it the list argument of llGetObjectDetails except lslfuncs.ELSLCantCompute:
value = self.GetListNodeElement(listarg, idx) # Don't transform the tree if function is not computable
tvalue = self.TypeFromNodeOrConst(value) pass
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 # At this point, we have resolved whether the function is SEF,
# and replace node with a constant if that's the case # or whether the function resolves to a constant.
if (value is False OptimizeFunc(self, parent, index)
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}
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
del listarg, idx, value, tvalue, const
elif SEFargs and 'SEF' in self.symtab[0][name]:
# 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 return
if nt == 'PRINT': if nt == 'PRINT':
@ -1754,11 +1526,7 @@ class foldconst(object):
tree = self.tree tree = self.tree
self.CurEvent = None self.CurEvent = None
# Patch the default values list for LSO FuncOptSetup()
if lslcommon.LSO:
self.defaultListVals['llList2Key'] = Key(NULL_KEY)
else:
self.defaultListVals['llList2Key'] = Key(u"")
# Constant folding pass. It does some other optimizations along the way. # Constant folding pass. It does some other optimizations along the way.
for idx in xrange(len(tree)): for idx in xrange(len(tree)):

View file

@ -15,13 +15,15 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with LSL PyOptimizer. If not, see <http://www.gnu.org/licenses/>. # along with LSL PyOptimizer. If not, see <http://www.gnu.org/licenses/>.
# Transform function parameters to shorter equivalents where possible. # Optimize calls to LSL library functions and parameters where possible
# This is dependent on the LSL function library. # This is dependent on the LSL function library.
from lslcommon import Key#, Vector, Quaternion import lslcommon
from lslcommon import Key, Vector, Quaternion
import lslfuncs import lslfuncs
def OptimizeParams(node, sym): def OptimizeArgs(node, sym):
"""Transform function arguments to shorter equivalents where possible."""
assert node['nt'] == 'FNCALL' assert node['nt'] == 'FNCALL'
params = node['ch'] params = node['ch']
name = node['name'] name = node['name']
@ -42,3 +44,263 @@ def OptimizeParams(node, sym):
if not lslfuncs.cond(Key(params[i]['value'])): if not lslfuncs.cond(Key(params[i]['value'])):
params[i]['value'] = u"" params[i]['value'] = u""
params[i]['type'] = 'string' params[i]['type'] = 'string'
# Type of each entry in llGetObjectDetails. Last: 38.
objDetailsTypes = 'issvrvkkkiiififfffkiiiiiiffkiviiksiisii'
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
}
# 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.))}
# The 'self' parameter here is the constant folding object.
def OptimizeFunc(self, parent, index):
"""Look for possible optimizations taking advantage of the specific LSL
library function semantics.
"""
node = parent[index]
assert node['nt'] == 'FNCALL'
name = node['name']
child = node['ch']
if self.optlistlength and name == 'llGetListLength':
# Convert llGetListLength(expr) to (expr != [])
node = {'nt':'CONST', 't':'list', 'value':[]}
parent[index] = node = {'nt':'!=', 't':'integer',
'ch':[child[0], node]}
# SEF if the list is
node['SEF'] = 'SEF' in child[0]
if name == 'llDumpList2String':
if (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'
del child[1]
del node['name']
return
if (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 listCompat:
const = lslfuncs.InternalTypecast(const,
lslcommon.LSLType2Python[node['t']],
InList=True, f32=True)
else:
const = 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.
finaltype = objDetailsTypes[const:const+1]
if (name == 'llList2Key' # checked via listCompat
or (name == 'llList2Integer'
and finaltype in ('s', 'i')) # won't work for floats
or (name == 'llList2Float'
and finaltype in ('s', 'i')) # won't work for floats
) and (node['t'][0] + finaltype) in 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] + objDetailsTypes[const])
not in listCompat
) and 'SEF' in node:
parent[index] = {'nt':'CONST', 't':node['t'],
'value':defaultListVals[name],
'SEF':True}
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 primParamsArgs
or param < 0
or param >= len(primParamsTypes)
or primParamsTypes[param] is False
):
# Can't process this list.
returntypes = '!'
break
returntypes += 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 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 listCompat \
and 'SEF' in node:
parent[index] = {'nt':'CONST',
't':node['t'],
'value':defaultListVals[name],
'SEF':True}
return
del returntypes
del listarg, idx, value, tvalue, const
def FuncOptSetup():
# Patch the default values list for LSO
if lslcommon.LSO:
defaultListVals['llList2Key'] = Key(lslfuncs.NULL_KEY)
else:
defaultListVals['llList2Key'] = Key(u"")

View file

@ -37,13 +37,6 @@ class optimizer(foldconst, renamer, deadcode):
'==','!=','|','^','&','||','&&')) '==','!=','|','^','&','||','&&'))
assign_ops = frozenset(('=','+=','-=','*=','/=','%=','&=','|=','^=','<<=','>>=')) assign_ops = frozenset(('=','+=','-=','*=','/=','%=','&=','|=','^=','<<=','>>='))
LSL2PythonType = {'integer':int, 'float':float, 'string':unicode, 'key':lslfuncs.Key,
'vector':lslfuncs.Vector, 'rotation':lslfuncs.Quaternion, 'list':list}
PythonType2LSL = {int: 'integer', float: 'float',
unicode: 'string', Key: 'key', Vector: 'vector',
Quaternion: 'rotation', list: 'list'}
def Cast(self, value, newtype): def Cast(self, value, newtype):
"""Return a CAST node if the types are not equal, otherwise the """Return a CAST node if the types are not equal, otherwise the
value unchanged. value unchanged.

View file

@ -199,10 +199,6 @@ class parser(object):
switch_keywords = frozenset(('switch', 'case', 'break', 'default')) switch_keywords = frozenset(('switch', 'case', 'break', 'default'))
types = frozenset(('integer','float','string','key','vector', types = frozenset(('integer','float','string','key','vector',
'quaternion','rotation','list')) 'quaternion','rotation','list'))
PythonType2LSL = {int: 'integer', float: 'float',
unicode: 'string', Key: 'key', Vector: 'vector',
Quaternion: 'rotation', list: 'list'}
PythonType2LSLToken = {int:'INTEGER_VALUE', float:'FLOAT_VALUE', PythonType2LSLToken = {int:'INTEGER_VALUE', float:'FLOAT_VALUE',
unicode:'STRING_VALUE', Key:'KEY_VALUE', Vector:'VECTOR_VALUE', unicode:'STRING_VALUE', Key:'KEY_VALUE', Vector:'VECTOR_VALUE',
Quaternion:'ROTATION_VALUE', list:'LIST_VALUE'} Quaternion:'ROTATION_VALUE', list:'LIST_VALUE'}
@ -2069,7 +2065,8 @@ list lazy_list_set(list L, integer i, list v)
while self.tok[0] == 'STRING_VALUE': while self.tok[0] == 'STRING_VALUE':
val += self.tok[1] val += self.tok[1]
self.NextToken() self.NextToken()
return {'nt':'CONST', 't':self.PythonType2LSL[type(val)], 'value':val} return {'nt':'CONST', 't':lslcommon.PythonType2LSL[type(val)],
'value':val}
if tok[0] == 'IDENT': if tok[0] == 'IDENT':
sym = self.FindSymbolPartial(tok[1]) sym = self.FindSymbolPartial(tok[1])
if sym is None or sym['Kind'] != 'v': if sym is None or sym['Kind'] != 'v':