mirror of
https://github.com/Sei-Lisa/LSL-PyOptimizer
synced 2025-07-03 00:18:20 +00:00
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:
parent
36947b7b12
commit
6738615360
5 changed files with 359 additions and 329 deletions
|
@ -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}
|
||||||
|
|
|
@ -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,8 +1104,9 @@ 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:
|
||||||
|
if 'Fn' in sym and ('SEF' in sym or lslcommon.IsCalc):
|
||||||
# It's side-effect free if the children are and the function
|
# It's side-effect free if the children are and the function
|
||||||
# is marked as SEF.
|
# is marked as SEF.
|
||||||
if SEFargs:
|
if SEFargs:
|
||||||
|
@ -1215,204 +1142,49 @@ class foldconst(object):
|
||||||
args[argnum][i] = lslfuncs.v2f(args[argnum][i])
|
args[argnum][i] = lslfuncs.v2f(args[argnum][i])
|
||||||
del argtypes
|
del argtypes
|
||||||
try:
|
try:
|
||||||
|
# May raise ELSLCantCompute
|
||||||
if name[:10] == 'llDetected':
|
if name[:10] == 'llDetected':
|
||||||
value = fn(*args, event=self.CurEvent)
|
value = fn(*args, event=self.CurEvent)
|
||||||
else:
|
else:
|
||||||
value = fn(*args)
|
value = fn(*args)
|
||||||
except lslfuncs.ELSLCantCompute:
|
finally:
|
||||||
# Don't transform the tree if function is not computable
|
|
||||||
return
|
|
||||||
|
|
||||||
del args
|
del args
|
||||||
|
|
||||||
if not self.foldtabs:
|
if not self.foldtabs:
|
||||||
generatesTabs = (
|
generatesTabs = (
|
||||||
isinstance(value, unicode) and '\t' in value
|
isinstance(value, unicode) and '\t' in value
|
||||||
or type(value) == list and any(isinstance(x, unicode) and '\t' in x for x in value)
|
or type(value) == list
|
||||||
|
and any(isinstance(x, unicode)
|
||||||
|
and '\t' in x for x in value)
|
||||||
)
|
)
|
||||||
if generatesTabs:
|
if generatesTabs:
|
||||||
if self.warntabs:
|
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'))
|
warning(u"Can't optimize call to %s"
|
||||||
return
|
u" because it would generate a tab"
|
||||||
parent[index] = {'nt':'CONST', 't':node['t'], 'value':value, 'SEF':True}
|
u" character (you can force the "
|
||||||
elif self.optlistlength and name == 'llGetListLength':
|
u" optimization with the 'foldtabs'"
|
||||||
# Convert llGetListLength(expr) to (expr != [])
|
u" option, or disable this warning by"
|
||||||
node = {'nt':'CONST', 't':'list', 'value':[]}
|
u" disabling the 'warntabs' option)."
|
||||||
parent[index] = node = {'nt':'!=', 't':'integer',
|
% name.decode('utf8'))
|
||||||
'ch':[child[0], node]}
|
raise lslfuncs.ELSLCantCompute()
|
||||||
# SEF if the list is
|
# Replace with a constant
|
||||||
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]
|
|
||||||
|
|
||||||
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':
|
|
||||||
|
|
||||||
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}
|
|
||||||
|
|
||||||
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]:
|
elif SEFargs and 'SEF' in self.symtab[0][name]:
|
||||||
# The function is marked as SEF in the symbol table, and the
|
# The function is marked as SEF in the symbol table, and the
|
||||||
# arguments are all side-effect-free. The result is SEF.
|
# arguments are all side-effect-free. The result is SEF.
|
||||||
node['SEF'] = True
|
node['SEF'] = True
|
||||||
|
|
||||||
|
except lslfuncs.ELSLCantCompute:
|
||||||
|
# Don't transform the tree if function is not computable
|
||||||
|
pass
|
||||||
|
|
||||||
|
# At this point, we have resolved whether the function is SEF,
|
||||||
|
# or whether the function resolves to a constant.
|
||||||
|
OptimizeFunc(self, parent, index)
|
||||||
|
|
||||||
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)):
|
||||||
|
|
|
@ -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"")
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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':
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue