diff --git a/lslopt/lslbasefuncs.py b/lslopt/lslbasefuncs.py index 5b599df..61e55fe 100644 --- a/lslopt/lslbasefuncs.py +++ b/lslopt/lslbasefuncs.py @@ -262,30 +262,67 @@ def zstr(s): return s return s.__class__(s[:zi]) +def fi(x): + """Force x to be an int""" + if type(x) != int or not (-2147483648 <= x <= 2147483647): + raise ELSLInvalidType + return x + def ff(x): """Force x to be a float""" + if int != type(x) != float: + raise ELSLInvalidType if type(x) != float: - x = float(x) + return InternalTypecast(x, float, False, True) return F32(x) def fk(k): """Force k to be a key""" + if unicode != type(k) != Key: + raise ELSLInvalidType if type(k) != Key: - k = Key(k) + k = InternalTypecast(k, Key, False, False) return k def fs(s): """Force s to be a string""" + if unicode != type(s) != Key: + raise ELSLInvalidType if type(s) != unicode: - s = unicode(s) + s = InternalTypecast(s, unicode, False, False) return s +def fl(L): + """Force l to be a list, and its elements to have sane types.""" + Lorig = L + if type(L) != list: + raise ELSLInvalidType + for i in xrange(len(L)): + t = type(L[i]) + if t not in Types: + raise ELSLInvalidType + if t == Vector: + # copy on write + if L is Lorig: + L = L[:] + L[i] = v2f(L[i]) + if t == Quaternion: + # copy on write + if L is Lorig: + L = L[:] + L[i] = q2f(L[i]) + return L + def q2f(q): + if type(q) != Quaternion: + raise ELSLInvalidType if type(q[0]) == type(q[1]) == type(q[2]) == type(q[3]) == float: return q return Quaternion((ff(q[0]), ff(q[1]), ff(q[2]), ff(q[3]))) def v2f(v): + if type(v) != Vector: + raise ELSLInvalidType if type(v[0]) == type(v[1]) == type(v[2]) == float: return v return Vector((ff(v[0]), ff(v[1]), ff(v[2]))) @@ -543,8 +580,8 @@ def InternalUTF8toString(s): # type check. Same for llGetSubString and llList2List. They are all joined into # one single function. def InternalGetDeleteSubSequence(val, start, end, isGet): - assert isinteger(start) - assert isinteger(end) + start = fi(start) + end = fi(end) L = len(val) # Python does much of the same thing as LSL here, which helps a lot @@ -800,27 +837,6 @@ def cond(x): return len(x) > 1 return bool(x) # works fine for int, float, string, list -def isinteger(x): - return type(x) == int - -def isfloat(x): - return type(x) == float - -def isvector(x): - return type(x) == Vector and len(x) == 3 and type(x[0]) == type(x[1]) == type(x[2]) == float - -def isrotation(x): - return type(x) == Quaternion and len(x) == 4 and type(x[0]) == type(x[1]) == type(x[2]) == type(x[3]) == float - -def isstring(x): - return type(x) == unicode - -def iskey(x): - return type(x) == Key - -def islist(x): - return type(x) == list - def reduce(t): t = F32(t) if not t.is_integer(): @@ -832,7 +848,7 @@ def reduce(t): # def llAbs(i): - assert isinteger(i) + i = fi(i) if i != -2147483648: return abs(i) if lslcommon.LSO: @@ -841,27 +857,27 @@ def llAbs(i): raise ELSLCantCompute def llAcos(f): - assert isfloat(f) + f = ff(f) try: return F32(math.acos(f)) except ValueError: return NaN def llAngleBetween(r1, r2): - assert isrotation(r1) - assert isrotation(r2) + r1 = q2f(r1) + r2 = q2f(r2) return llRot2Angle(div(qnz(r1), qnz(r2), f32=False)) def llAsin(f): - assert isfloat(f) + f = ff(f) try: return F32(math.asin(f)) except ValueError: return NaN def llAtan2(y, x): - assert isfloat(y) - assert isfloat(x) + y = ff(y) + x = ff(x) if math.isnan(x) and math.isnan(y): if math.copysign(1, x) == -1 and math.copysign(1, y) == -1: return -NaN @@ -873,9 +889,9 @@ def llAtan2(y, x): return F32(math.atan2(y, x)) def llAxes2Rot(fwd, left, up): - assert isvector(fwd) - assert isvector(left) - assert isvector(up) + fwd = v2f(fwd) + left = v2f(left) + up = v2f(up) # One of the hardest. @@ -912,8 +928,8 @@ def llAxes2Rot(fwd, left, up): def llAxisAngle2Rot(axis, angle): - assert isvector(axis) - assert isfloat(angle) + axis = v2f(axis) + angle = ff(angle) axis = llVecNorm(axis, f32=False) if axis == ZERO_VECTOR: angle = 0. @@ -924,7 +940,7 @@ def llAxisAngle2Rot(axis, angle): # NOTE: This one does not always return the same value in LSL. When it isn't # deterministic, it raises ELSLCantCompute. def llBase64ToInteger(s): - assert isstring(s) + s = fs(s) if len(s) > 8: return 0 s = b64_re.search(s).group() @@ -959,7 +975,7 @@ b64tos_re = re.compile( ) def llBase64ToString(s): - assert isstring(s) + s = fs(s) s = b64_re.search(s).group(0) # llUnescapeURL and llBase64ToString behave differently. @@ -995,7 +1011,7 @@ def llBase64ToString(s): return InternalUTF8toString(bytes(byteseq)) def llCSV2List(s): - assert isstring(s) + s = fs(s) bracketlevel = 0 lastwascomma = True # first space is eaten!!! @@ -1025,13 +1041,13 @@ def llCSV2List(s): return ret def llCeil(f): - assert isfloat(f) + f = ff(f) if math.isnan(f) or math.isinf(f) or f >= 2147483648.0 or f < -2147483648.0: return -2147483648 return int(math.ceil(f)) def llCos(f): - assert isfloat(f) + f = ff(f) if math.isinf(f): return Indet if -9223372036854775808.0 < f < 9223372036854775808.0: @@ -1040,21 +1056,21 @@ def llCos(f): def llDeleteSubList(lst, start, end): # This acts as llList2List if there's wraparound - assert islist(lst) + lst = fl(lst) return InternalGetDeleteSubSequence(lst, start, end, isGet=False) def llDeleteSubString(s, start, end): # This acts as llGetSubString if there's wraparound - assert isstring(s) + s = fs(s) return InternalGetDeleteSubSequence(s, start, end, isGet=False) def llDumpList2String(lst, sep): - assert islist(lst) - assert isstring(sep) + lst = fl(lst) + sep = fs(sep) return sep.join(InternalList2Strings(lst)) def llEscapeURL(s): - assert isstring(s) + s = fs(s) s = s.encode('utf8') # get bytes ret = u'' for c in s: @@ -1065,7 +1081,7 @@ def llEscapeURL(s): return ret def llEuler2Rot(v): - assert isvector(v) + v = v2f(v) c0 = math.cos(v[0]*0.5) s0 = math.sin(v[0]*0.5) c1 = math.cos(v[1]*0.5) @@ -1098,19 +1114,19 @@ def llEuler2Rot(v): return Quaternion(-f for f in r) if r[i] < 0 else Quaternion(r) def llFabs(f): - assert isfloat(f) + f = ff(f) if f == 0.0 or math.isnan(f): # llFabs(-0.0) is -0.0; llFabs(-nan) is -nan return f return math.fabs(f) def llFloor(f): - assert isfloat(f) + f = ff(f) if math.isnan(f) or math.isinf(f) or f >= 2147483648.0 or f < -2147483648.0: return -2147483648 return int(math.floor(f)) def llFrand(lim): - assert isfloat(lim) + lim = ff(lim) if math.isinf(lim): return 0. if abs(lim) < float.fromhex('0x1p-126'): @@ -1148,8 +1164,8 @@ def llGenerateKey(): raise ELSLCantCompute def llGetListEntryType(lst, pos): - assert islist(lst) - assert isinteger(pos) + lst = fl(lst) + pos = fi(pos) try: return Types[type(lst[pos])] except IndexError: @@ -1158,26 +1174,26 @@ def llGetListEntryType(lst, pos): raise ELSLInvalidType def llGetListLength(lst): - assert islist(lst) + lst = fl(lst) return len(lst) def llGetSubString(s, start, end): - assert isstring(s) + s = fs(s) return InternalGetDeleteSubSequence(s, start, end, isGet=True) def llInsertString(s, pos, src): - assert isstring(s) - assert isinteger(pos) - assert isstring(src) + s = fs(s) + pos = fi(pos) + src = fs(src) if pos < 0: pos = 0 # llInsertString does not support negative indices return s[:pos] + src + s[pos:] def llIntegerToBase64(x): - assert isinteger(x) + x = fi(x) return b64encode(chr((x>>24)&255) + chr((x>>16)&255) + chr((x>>8)&255) + chr(x&255)).decode('utf8') def llList2CSV(lst): - assert islist(lst) + lst = fl(lst) ret = [] for elem in lst: # This always uses LSO rules for float to string. @@ -1194,8 +1210,8 @@ def llList2CSV(lst): return ret def llList2Float(lst, pos): - assert islist(lst) - assert isinteger(pos) + lst = fl(lst) + pos = fi(pos) try: elem = lst[pos] if type(elem) == float: @@ -1207,8 +1223,8 @@ def llList2Float(lst, pos): return 0.0 def llList2Integer(lst, pos): - assert islist(lst) - assert isinteger(pos) + lst = fl(lst) + pos = fi(pos) try: elem = lst[pos] if type(elem) == int: @@ -1220,8 +1236,8 @@ def llList2Integer(lst, pos): return 0 def llList2Key(lst, pos): - assert islist(lst) - assert isinteger(pos) + lst = fl(lst) + pos = fi(pos) try: elem = lst[pos] if type(elem) == Key: @@ -1235,16 +1251,16 @@ def llList2Key(lst, pos): return Key(u'') def llList2List(lst, start, end): - assert islist(lst) - assert isinteger(start) - assert isinteger(end) + lst = fl(lst) + start = fi(start) + end = fi(end) return InternalGetDeleteSubSequence(lst, start, end, isGet=True) def llList2ListStrided(lst, start, end, stride): - assert islist(lst) - assert isinteger(start) - assert isinteger(end) - assert isinteger(stride) + lst = fl(lst) + start = fi(start) + end = fi(end) + stride = fi(stride) stride = abs(stride) if stride != 0 else 1 L = len(lst) if start < 0: start += L @@ -1260,13 +1276,13 @@ def llList2ListStrided(lst, start, end, stride): return lst[start:end+1:stride] def llList2Rot(lst, pos): - assert islist(lst) - assert isinteger(pos) + lst = fl(lst) + pos = fi(pos) try: elem = lst[pos] if type(elem) == Quaternion: # The list should not contain integer quaternion components, but - # we don't control that here. Instead we return the integer-less + # we don't err here if not. Instead we return the integer-less # quaternion when asked. return q2f(elem) except IndexError: @@ -1274,8 +1290,8 @@ def llList2Rot(lst, pos): return ZERO_ROTATION def llList2String(lst, pos): - assert islist(lst) - assert isinteger(pos) + lst = fl(lst) + pos = fi(pos) try: return InternalTypecast(lst[pos], unicode, InList=True, f32=True) except IndexError: @@ -1283,8 +1299,8 @@ def llList2String(lst, pos): return u'' def llList2Vector(lst, pos): - assert islist(lst) - assert isinteger(pos) + lst = fl(lst) + pos = fi(pos) try: elem = lst[pos] if type(elem) == Vector: @@ -1297,8 +1313,8 @@ def llList2Vector(lst, pos): return ZERO_VECTOR def llListFindList(lst, elems): - assert islist(lst) - assert islist(elems) + lst = fl(lst) + elems = fl(elems) # NaN is found in floats, but not in vectors L1 = len(lst) L2 = len(elems) @@ -1346,9 +1362,9 @@ def llListFindList(lst, elems): return -1 def llListInsertList(lst, elems, pos): - assert islist(lst) - assert islist(elems) - assert isinteger(pos) + lst = fl(lst) + elems = fl(elems) + pos = fi(pos) # Unlike llInsertString, this function does support negative indices. return lst[:pos] + elems + lst[pos:] @@ -1356,10 +1372,10 @@ def llListInsertList(lst, elems, pos): #def llListRandomize(x): def llListReplaceList(lst, elems, start, end): - assert islist(lst) - assert islist(elems) - assert isinteger(start) - assert isinteger(end) + lst = fl(lst) + elems = fl(elems) + start = fi(start) + end = fi(end) L = len(lst) if start < -L: # llListReplaceList([0,1,2,3],[5],-5,-5) should return [0,1,2,3] @@ -1374,9 +1390,9 @@ def llListReplaceList(lst, elems, start, end): return lst[:start] + elems + lst[end+1:] def llListSort(lst, stride, asc): - assert islist(lst) - assert isinteger(stride) - assert isinteger(asc) + lst = fl(lst) + stride = fi(stride) + asc = fi(asc) lst = lst[:] # make a copy L = len(lst) broken = u'\ufb1a' > u'\U0001d41a' # that happens on Windows @@ -1422,8 +1438,8 @@ def llListSort(lst, stride, asc): return lst def llListStatistics(op, lst): - assert isinteger(op) - assert islist(lst) + op = fi(op) + lst = fl(lst) nums = [] # Extract numbers in reverse order. LIST_STAT_MEDIAN uses that. @@ -1495,26 +1511,26 @@ def llListStatistics(op, lst): return 0.0 def llLog(f): - assert isfloat(f) + f = ff(f) if math.isinf(f) and f < 0 or math.isnan(f) or f <= 0.0: return 0.0 return F32(math.log(f)) def llLog10(f): - assert isfloat(f) + f = ff(f) if math.isinf(f) and f < 0 or math.isnan(f) or f <= 0.0: return 0.0 return F32(math.log10(f)) def llMD5String(s, salt): - assert isstring(s) - assert isinteger(salt) + s = fs(s) + salt = fi(salt) return hashlib.md5(zstr(s).encode('utf8') + b':' + bytes(salt)).hexdigest().decode('utf8') def llModPow(base, exp, mod): - assert isinteger(base) - assert isinteger(exp) - assert isinteger(mod) + base = fi(base) + exp = fi(exp) + mod = fi(mod) if not lslcommon.IsCalc: # This function has a delay, therefore it's not safe to compute it # unless in calculator mode. @@ -1544,9 +1560,9 @@ def llModPow(base, exp, mod): return S32(ret) def llParseString2List(s, exc, inc, KeepNulls=False): - assert isstring(s) - assert islist(exc) - assert islist(inc) + s = fs(s) + exc = fl(exc) + inc = fl(inc) if s == u'' and KeepNulls: return [s] exc = exc[:8] @@ -1569,8 +1585,8 @@ def llParseStringKeepNulls(s, exc, inc): return llParseString2List(s, exc, inc, KeepNulls=True) def llPow(base, exp): - assert isfloat(base) - assert isfloat(exp) + base = ff(base) + exp = ff(exp) try: # Python corner cases and LSL corner cases differ @@ -1599,19 +1615,19 @@ def llPow(base, exp): return Indet def llRot2Angle(r): - assert isrotation(r) + r = q2f(r) # Used by llAngleBetween. # Version based on research by Moon Metty, Miranda Umino and Strife Onizuka return F32(2.*math.atan2(math.sqrt(math.fsum((r[0]*r[0], r[1]*r[1], r[2]*r[2]))), abs(r[3]))); def llRot2Axis(r): - assert isrotation(r) + r = q2f(r) if r[3] < 0: return llVecNorm(Vector((-r[0], -r[1], -r[2]))) return llVecNorm(Vector((r[0], r[1], r[2]))) def llRot2Euler(r): - assert isrotation(r) + r = q2f(r) # Another one of the hardest. The formula for Z angle in the # singularity case was inspired by the viewer code. @@ -1634,23 +1650,23 @@ def llRot2Euler(r): ))) def llRot2Fwd(r): - assert isrotation(r) + r = q2f(r) v = Vector((1., 0., 0.)) return llVecNorm(mul(v, qnz(r), f32=False)) def llRot2Left(r): - assert isrotation(r) + r = q2f(r) v = Vector((0., 1., 0.)) return llVecNorm(mul(v, qnz(r), f32=False)) def llRot2Up(r): - assert isrotation(r) + r = q2f(r) v = Vector((0., 0., 1.)) return llVecNorm(mul(v, qnz(r), f32=False)) def llRotBetween(v1, v2): - assert isvector(v1) - assert isvector(v2) + v1 = v2f(v1) + v2 = v2f(v2) # Loosely based on the "Bad" reference implementation and # on SL source code (pre Moon Metty's changes). @@ -1685,17 +1701,17 @@ def llRotBetween(v1, v2): return Quaternion(F32((ortho[0] / m, ortho[1] / m, ortho[2] / m, 0.))) def llRound(f): - assert isfloat(f) + f = ff(f) if math.isnan(f) or math.isinf(f) or f >= 2147483647.5 or f < -2147483648.0: return -2147483648 return int(math.floor(F32(f+0.5))) def llSHA1String(s): - assert isstring(s) + s = fs(s) return hashlib.sha1(s.encode('utf8')).hexdigest().decode('utf8') def llSin(f): - assert isfloat(f) + f = ff(f) if math.isinf(f): return Indet if -9223372036854775808.0 < f < 9223372036854775808.0: @@ -1703,23 +1719,23 @@ def llSin(f): return f def llSqrt(f): - assert isfloat(f) + f = ff(f) if f < 0.0: return Indet # LSL and Python both produce -0.0 when the input is -0.0. return F32(math.sqrt(f)) def llStringLength(s): - assert isstring(s) + s = fs(s) return len(s) def llStringToBase64(s): - assert isstring(s) + s = fs(s) return b64encode(s.encode('utf8')).decode('utf8') def llStringTrim(s, mode): - assert isstring(s) - assert isinteger(mode) + s = fs(s) + mode = fi(mode) head = 0 length = len(s) tail = length-1 @@ -1732,12 +1748,12 @@ def llStringTrim(s, mode): return s[head:tail+1] def llSubStringIndex(s, pattern): - assert isstring(s) - assert isstring(pattern) + s = fs(s) + pattern = fs(pattern) return s.find(pattern) def llTan(f): - assert isfloat(f) + f = ff(f) if math.isinf(f): return Indet if -9223372036854775808.0 < f < 9223372036854775808.0: @@ -1745,19 +1761,19 @@ def llTan(f): return f def llToLower(s): - assert isstring(s) + s = fs(s) if lslcommon.LSO: return zstr(re.sub(u'[A-Z]', lambda x: x.group().lower(), s)) return zstr(s.lower()) def llToUpper(s): - assert isstring(s) + s = fs(s) if lslcommon.LSO: return zstr(re.sub(u'[a-z]', lambda x: x.group().upper(), s)) return zstr(s.upper()) def llUnescapeURL(s): - assert isstring(s) + s = fs(s) ret = b'' L = len(s) i = 0 @@ -1788,8 +1804,8 @@ def llUnescapeURL(s): return InternalUTF8toString(ret) def llVecDist(v1, v2): - assert isvector(v1) - assert isvector(v2) + v1 = v2f(v1) + v2 = v2f(v2) # For improved accuracy, do the intermediate calcs as doubles vx = v1[0]-v2[0] vy = v1[1]-v2[1] @@ -1797,19 +1813,19 @@ def llVecDist(v1, v2): return F32(math.sqrt(math.fsum((vx*vx, vy*vy, vz*vz)))) def llVecMag(v): - assert isvector(v) + v = v2f(v) return F32(math.sqrt(math.fsum((v[0]*v[0], v[1]*v[1], v[2]*v[2])))) def llVecNorm(v, f32 = True): - assert isvector(v) + v = v2f(v) if v == ZERO_VECTOR: return v f = math.sqrt(math.fsum((v[0]*v[0], v[1]*v[1], v[2]*v[2]))) return F32(Vector((v[0]/f,v[1]/f,v[2]/f)), f32) def llXorBase64(s, xor): - assert isstring(s) - assert isstring(xor) + s = fs(s) + xor = fs(xor) # Xor the underlying bytes. @@ -1854,8 +1870,8 @@ def llXorBase64(s, xor): return b64encode(ret).decode('utf8') def llXorBase64Strings(s, xor): - assert isstring(s) - assert isstring(xor) + s = fs(s) + xor = fs(xor) if not lslcommon.IsCalc: # This function has a delay, therefore it's not safe to compute it @@ -1897,8 +1913,8 @@ def llXorBase64Strings(s, xor): return ret def llXorBase64StringsCorrect(s, xor): - assert isstring(s) - assert isstring(xor) + s = fs(s) + xor = fs(xor) # Xor the underlying bytes but repeating the xor parameter pattern at the first zero (SCR-35). diff --git a/lslopt/lslextrafuncs.py b/lslopt/lslextrafuncs.py index 44e8f01..bcbe3d6 100644 --- a/lslopt/lslextrafuncs.py +++ b/lslopt/lslextrafuncs.py @@ -18,10 +18,10 @@ # Extra functions that have predictable return values for certain arguments. from lslcommon import Key, Vector #, Quaternion -from lslbasefuncs import ELSLCantCompute, isinteger, iskey, islist, \ - isvector, isstring, NULL_KEY, ZERO_VECTOR, ZERO_ROTATION, \ +from lslbasefuncs import ELSLCantCompute, fi,ff,fs,fk,v2f,q2f,fl, \ + NULL_KEY, ZERO_VECTOR, ZERO_ROTATION, \ TOUCH_INVALID_TEXCOORD, cond -#isfloat, isrotation +ff, q2f # keep pyflakes happy as these are not used TouchEvents = ('touch', 'touch_start', 'touch_end') DetectionEvents = ('touch', 'touch_start', 'touch_end', @@ -52,147 +52,147 @@ xp_error_messages = { valid_inventory_kinds = frozenset((0, 1, 3, 5, 6, 7, 10, 13, 20, 21)) def llCloud(v): - assert isvector(v) + v = v2f(v) return 0.0 def llAvatarOnLinkSitTarget(link): - assert isinteger(link) + link = fi(link) if link > 255 or link == -2147483648: return Key(NULL_KEY) raise ELSLCantCompute def llDetectedGrab(idx, event=None): - assert isinteger(idx) + idx = fi(idx) if 0 <= idx <= 15 and (event == 'touch' or event is None): raise ELSLCantCompute return ZERO_VECTOR def llDetectedGroup(idx, event=None): - assert isinteger(idx) + idx = fi(idx) if 0 <= idx <= 15 and (event in DetectionEvents or event is None): raise ELSLCantCompute return 0 def llDetectedKey(idx, event=None): - assert isinteger(idx) + idx = fi(idx) if 0 <= idx <= 15 and (event in DetectionEvents or event is None): raise ELSLCantCompute return Key(NULL_KEY) def llDetectedLinkNumber(idx, event=None): - assert isinteger(idx) + idx = fi(idx) if 0 <= idx <= 15 and (event in DetectionEvents or event is None): raise ELSLCantCompute return 0 def llDetectedName(idx, event=None): - assert isinteger(idx) + idx = fi(idx) if 0 <= idx <= 15 and (event in DetectionEvents or event is None): raise ELSLCantCompute return NULL_KEY def llDetectedOwner(idx, event=None): - assert isinteger(idx) + idx = fi(idx) if 0 <= idx <= 15 and (event in DetectionEvents or event is None): raise ELSLCantCompute return Key(NULL_KEY) def llDetectedPos(idx, event=None): - assert isinteger(idx) + idx = fi(idx) if 0 <= idx <= 15 and (event in DetectionEvents or event is None): raise ELSLCantCompute return ZERO_VECTOR def llDetectedRot(idx, event=None): - assert isinteger(idx) + idx = fi(idx) if 0 <= idx <= 15 and (event in DetectionEvents or event is None): raise ELSLCantCompute return ZERO_ROTATION def llDetectedTouchBinormal(idx, event=None): - assert isinteger(idx) + idx = fi(idx) if 0 <= idx <= 15 and (event in TouchEvents or event is None): raise ELSLCantCompute return ZERO_VECTOR def llDetectedTouchFace(idx, event=None): - assert isinteger(idx) + idx = fi(idx) if 0 <= idx <= 15 and (event in TouchEvents or event is None): raise ELSLCantCompute return -1 if event in DetectionEvents and 0 <= idx <= 15 else 0 def llDetectedTouchNormal(idx, event=None): - assert isinteger(idx) + idx = fi(idx) if 0 <= idx <= 15 and (event in TouchEvents or event is None): raise ELSLCantCompute return ZERO_VECTOR def llDetectedTouchPos(idx, event=None): - assert isinteger(idx) + idx = fi(idx) if 0 <= idx <= 15 and (event in TouchEvents or event is None): raise ELSLCantCompute return ZERO_VECTOR def llDetectedTouchST(idx, event=None): - assert isinteger(idx) + idx = fi(idx) if 0 <= idx <= 15 and (event in TouchEvents or event is None): raise ELSLCantCompute return TOUCH_INVALID_TEXCOORD if event in DetectionEvents \ and 0 <= idx <= 15 else ZERO_VECTOR def llDetectedTouchUV(idx, event=None): - assert isinteger(idx) + idx = fi(idx) if 0 <= idx <= 15 and (event in TouchEvents or event is None): raise ELSLCantCompute return TOUCH_INVALID_TEXCOORD if event in DetectionEvents \ and 0 <= idx <= 15 else ZERO_VECTOR def llDetectedType(idx, event=None): - assert isinteger(idx) + idx = fi(idx) if 0 <= idx <= 15 and (event in DetectionEvents or event is None): raise ELSLCantCompute return 0 def llDetectedVel(idx, event=None): - assert isinteger(idx) + idx = fi(idx) if 0 <= idx <= 15 and (event in DetectionEvents or event is None): raise ELSLCantCompute return ZERO_VECTOR def llEdgeOfWorld(v1, v2): - assert isvector(v1) - assert isvector(v2) + v1 = v2f(v1) + v2 = v2f(v2) if v2[0] == v2[1] == 0: return 1 raise ELSLCantCompute def llGetAgentInfo(id): - assert iskey(id) + id = fk(id) if not cond(id): return 0 raise ELSLCantCompute def llGetAgentLanguage(id): - assert iskey(id) + id = fk(id) if not cond(id): return u'' raise ELSLCantCompute def llGetAgentList(scope, options): - assert isinteger(scope) - assert islist(options) + scope = fi(scope) + options = fl(options) if scope not in (1, 2, 4): return [u'INVALID_SCOPE'] raise ELSLCantCompute def llGetAgentSize(id): - assert iskey(id) + id = fk(id) if not cond(id): return ZERO_VECTOR raise ELSLCantCompute def llGetAlpha(face): - assert isinteger(face) + face = fi(face) if face > 8: return 1.0 # Negative face numbers return (float)llGetNumberOfSides(), which isn't @@ -200,89 +200,89 @@ def llGetAlpha(face): raise ELSLCantCompute def llGetAnimation(id): - assert iskey(id) + id = fk(id) if not cond(id): return u'' raise ELSLCantCompute def llGetAnimationList(id): - assert iskey(id) + id = fk(id) if not cond(id): return [] raise ELSLCantCompute def llGetBoundingBox(id): - assert iskey(id) + id = fk(id) if not cond(id): return [] raise ELSLCantCompute def llGetColor(face): - assert isinteger(face) + face = fi(face) if face > 8: return Vector((1.,1.,1.)) # Returns face 0 when negative (can't be computed) raise ELSLCantCompute def llGetDisplayName(id): - assert iskey(id) + id = fk(id) if not cond(id): return u'' raise ELSLCantCompute def llGetEnv(s): - assert isstring(s) + s = fs(s) if s not in GetEnvSettings: return u"" raise ELSLCantCompute def llGetExperienceErrorMessage(errno): - assert isinteger(errno) + errno = fi(errno) if errno < -1 or errno > 18: errno = -1 return xp_error_messages[errno] def llGetExperienceList(id): - assert iskey(id) + id = fk(id) # This function is not implemented and always returns empty list return [] def llGetHTTPHeader(id, s): - assert iskey(id) - assert isstring(s) + id = fk(id) + s = fs(s) if not cond(id): return u'' raise ELSLCantCompute def llGetInventoryKey(s): - assert isstring(s) + s = fs(s) if s == u'': return Key(NULL_KEY) raise ELSLCantCompute def llGetInventoryName(kind, index): - assert isinteger(kind) - assert isinteger(index) + kind = fi(kind) + index = fi(index) if kind != -1 and kind not in valid_inventory_kinds or index < 0: return u'' raise ELSLCantCompute def llGetInventoryNumber(kind): - assert isinteger(kind) + kind = fi(kind) if kind != -1 and kind not in valid_inventory_kinds: return 0 raise ELSLCantCompute def llGetInventoryPermMask(item, category): - assert isstring(item) - assert isinteger(category) + item = fs(item) + category = fi(category) if category < 0 or category > 4 or item == u'': return 0 raise ELSLCantCompute def llGetOwnerKey(id): - assert iskey(id) + id = fk(id) if not cond(id): return Key(NULL_KEY) raise ELSLCantCompute diff --git a/lslopt/lslfoldconst.py b/lslopt/lslfoldconst.py index fb2af48..5f1ca7b 100644 --- a/lslopt/lslfoldconst.py +++ b/lslopt/lslfoldconst.py @@ -1115,32 +1115,7 @@ class foldconst(object): # Call it fn = sym['Fn'] 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])): - if type(args[argnum][i]) == Quaternion: - args[argnum][i] = lslfuncs.q2f(args[argnum][i]) - elif type(args[argnum][i]) == Vector: - args[argnum][i] = lslfuncs.v2f(args[argnum][i]) - del argtypes + assert len(args) == len(sym['ParamTypes']) try: # May raise ELSLCantCompute if name[:10] == 'llDetected': diff --git a/lslopt/lsljson.py b/lslopt/lsljson.py index ccb3a1b..d656e2a 100644 --- a/lslopt/lsljson.py +++ b/lslopt/lsljson.py @@ -20,7 +20,7 @@ import re import math from lslcommon import * -from lslbasefuncs import llStringTrim, isstring, islist, InternalTypecast +from lslbasefuncs import llStringTrim, fs, fl, InternalTypecast # INCOMPATIBILITY NOTE: The JSON functions in SL have very weird behaviour # in corner cases. Despite our best efforts, that behaviour is not replicated @@ -494,7 +494,7 @@ def InternalJson2Elem(json): return json def llJson2List(json): - assert isstring(json) + json = fs(json) json = llStringTrim(json, 3) # STRING_TRIM if json == u'': @@ -577,8 +577,8 @@ def llJson2List(json): return [InternalJson2Elem(json)] def llJsonGetValue(json, lst): - assert isstring(json) - assert islist(lst) + json = fs(json) + lst = fl(lst) return InternalJsonFindValue(json, lst, ReturnsToken=False) # llJsonSetValue was finally not implemented. This is a failed attempt @@ -613,9 +613,9 @@ def llJsonGetValue(json, lst): def llJsonSetValue(json, lst, val): - assert isstring(json) - assert islist(lst) - assert isstring(val) + json = fs(json) + lst = fl(lst) + val = fs(val) if lst == []: # [] replaces the entire string no matter if it was invalid if val == JSON_DELETE: @@ -631,16 +631,16 @@ def llJsonSetValue(json, lst, val): ''' def llJsonValueType(json, lst): - assert isstring(json) - assert islist(lst) + json = fs(json) + lst = fl(lst) ret = InternalJsonFindValue(json, lst, ReturnsToken=True) if ret == JSON_INVALID: return ret return ret[2] def llList2Json(kind, lst): - assert isstring(kind) - assert islist(lst) + kind = fs(kind) + lst = fl(lst) if kind == JSON_OBJECT: ret = u'{' diff --git a/testfuncs.py b/testfuncs.py index 0fb9990..72fe8b5 100644 --- a/testfuncs.py +++ b/testfuncs.py @@ -1381,9 +1381,10 @@ def do_tests(): test('llList2CSV(llParseStringKeepNulls(u"1abc2ab3abc4",[u"ab"],[u"ab"]))', u'1, c2, 3, c4') test('llParseStringKeepNulls(u"",[],[])', [u""]) - test('llParseStringKeepNulls(u"",[],[""])', [u""]) - test('llParseStringKeepNulls(u"",[""],[])', [u""]) - test('llParseStringKeepNulls(u"",[""],[""])', [u""]) + shouldexcept('llParseStringKeepNulls(u"",[],[""])', ELSLInvalidType) + test('llParseStringKeepNulls(u"",[],[u""])', [u""]) + test('llParseStringKeepNulls(u"",[u""],[])', [u""]) + test('llParseStringKeepNulls(u"",[u""],[u""])', [u""]) test('llParseString2List(u"",[],[])', []) test('llParseString2List(u"",[],[u""])', []) test('llParseString2List(u"",[u""],[])', []) @@ -1592,7 +1593,7 @@ def do_tests(): test(r'llJsonGetValue(u"{\"a\":[1],\"a\":[2],\"a\":[3]}", [u"a",0])', u'3') test(r'llJsonGetValue(u"{\"a\":[2,3,[4]],\"a\":1}", [u"a", 2, 0])', JSON_INVALID) - test(r'llJsonGetValue(u"{\"a\":1,}",["a"])', JSON_INVALID) + test(r'llJsonGetValue(u"{\"a\":1,}",[u"a"])', JSON_INVALID) test(r'llJsonGetValue(u"[3,4,5,[6,7,8],:9,10]", [1])', JSON_INVALID) # *ing crap! ALL entries are tested before the result is returned...