diff options
| author | Glenn Glazer <coyot@lindenlab.com> | 2016-07-21 16:51:47 -0700 | 
|---|---|---|
| committer | Glenn Glazer <coyot@lindenlab.com> | 2016-07-21 16:51:47 -0700 | 
| commit | 609595e9384c86f78f5eb5471c0b7cac4b7fef9e (patch) | |
| tree | f87de87a8cf6e9cc44cf2461350d88771128788a | |
| parent | 5ab6b73d57818c7aaf9a4bcd0b302854166fd93a (diff) | |
MAINT-6585: put back indra/ipc files needed for scripts/template_verifier.py
| -rwxr-xr-x | indra/lib/python/indra/__init__.py | 25 | ||||
| -rwxr-xr-x | indra/lib/python/indra/ipc/__init__.py | 27 | ||||
| -rwxr-xr-x | indra/lib/python/indra/ipc/compatibility.py | 123 | ||||
| -rwxr-xr-x | indra/lib/python/indra/ipc/llmessage.py | 372 | ||||
| -rwxr-xr-x | indra/lib/python/indra/ipc/tokenstream.py | 154 | 
5 files changed, 701 insertions, 0 deletions
| diff --git a/indra/lib/python/indra/__init__.py b/indra/lib/python/indra/__init__.py new file mode 100755 index 0000000000..0c5053cf49 --- /dev/null +++ b/indra/lib/python/indra/__init__.py @@ -0,0 +1,25 @@ +"""\ +@file __init__.py +@brief Initialization file for the indra module. + +$LicenseInfo:firstyear=2006&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2006-2010, Linden Research, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; +version 2.1 of the License only. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + +Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA +$/LicenseInfo$ +""" diff --git a/indra/lib/python/indra/ipc/__init__.py b/indra/lib/python/indra/ipc/__init__.py new file mode 100755 index 0000000000..302bbf4a03 --- /dev/null +++ b/indra/lib/python/indra/ipc/__init__.py @@ -0,0 +1,27 @@ +"""\ +@file __init__.py +@brief Initialization file for the indra ipc module. + +$LicenseInfo:firstyear=2006&license=mit$ + +Copyright (c) 2006-2009, Linden Research, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +$/LicenseInfo$ +""" diff --git a/indra/lib/python/indra/ipc/compatibility.py b/indra/lib/python/indra/ipc/compatibility.py new file mode 100755 index 0000000000..b9045c22f3 --- /dev/null +++ b/indra/lib/python/indra/ipc/compatibility.py @@ -0,0 +1,123 @@ +"""\ +@file compatibility.py +@brief Classes that manage compatibility states. + +$LicenseInfo:firstyear=2007&license=mit$ + +Copyright (c) 2007-2009, Linden Research, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +$/LicenseInfo$ +""" + + +"""Compatibility combination table: + +    I   M   O   N   S +    --  --  --  --  -- +I:  I   I   I   I   I +M:  I   M   M   M   M    +O:  I   M   O   M   O +N:  I   M   M   N   N +S:  I   M   O   N   S + +""" + +class _Compatibility(object): +    def __init__(self, reason): +        self.reasons = [ ] +        if reason: +            self.reasons.append(reason) +         +    def combine(self, other): +        if self._level() <= other._level(): +            return self._buildclone(other) +        else: +            return other._buildclone(self) +     +    def prefix(self, leadin): +        self.reasons = [ leadin + r for r in self.reasons ]  +     +    def same(self):         return self._level() >=  1 +    def deployable(self):   return self._level() >   0 +    def resolved(self):     return self._level() >  -1 +    def compatible(self):   return self._level() >  -2 +     +    def explain(self): +        return self.__class__.__name__ + "\n" + "\n".join(self.reasons) + "\n" +         +    def _buildclone(self, other=None): +        c = self._buildinstance() +        c.reasons = self.reasons +        if other: +            c.reasons = c.reasons + other.reasons +        return c +         +    def _buildinstance(self): +        return self.__class__(None) + +#    def _level(self): +#        raise RuntimeError('implement in subclass') + + +class Incompatible(_Compatibility): +    def _level(self): +        return -2 + +class Mixed(_Compatibility): +    def __init__(self, *inputs): +        _Compatibility.__init__(self, None) +        for i in inputs: +            self.reasons += i.reasons +                     +    def _buildinstance(self): +        return self.__class__() +         +    def _level(self): +        return -1 + +class _Aged(_Compatibility): +    def combine(self, other): +        if self._level() == other._level(): +            return self._buildclone(other) +        if int(self._level()) == int(other._level()): +            return Mixed(self, other) +        return _Compatibility.combine(self, other) + +class Older(_Aged): +    def _level(self): +        return -0.25 +     +class Newer(_Aged): +    def _level(self): +        return 0.25 + +class Same(_Compatibility): +    def __init__(self): +        _Compatibility.__init__(self, None) +     +    def _buildinstance(self): +        return self.__class__() +         +    def _level(self): +        return 1 + + + + diff --git a/indra/lib/python/indra/ipc/llmessage.py b/indra/lib/python/indra/ipc/llmessage.py new file mode 100755 index 0000000000..91fb36b72c --- /dev/null +++ b/indra/lib/python/indra/ipc/llmessage.py @@ -0,0 +1,372 @@ +"""\ +@file llmessage.py +@brief Message template parsing and compatiblity + +$LicenseInfo:firstyear=2007&license=mit$ + +Copyright (c) 2007-2009, Linden Research, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +$/LicenseInfo$ +""" + +from compatibility import Incompatible, Older, Newer, Same +from tokenstream import TokenStream + +### +### Message Template +### + +class Template: +    def __init__(self): +        self.messages = { } +     +    def addMessage(self, m): +        self.messages[m.name] = m +     +    def compatibleWithBase(self, base): +        messagenames = ( +              frozenset(self.messages.keys()) +            | frozenset(base.messages.keys()) +            ) +             +        compatibility = Same() +        for name in messagenames: +            selfmessage = self.messages.get(name, None) +            basemessage = base.messages.get(name, None) +             +            if not selfmessage: +                c = Older("missing message %s, did you mean to deprecate?" % name) +            elif not basemessage: +                c = Newer("added message %s" % name) +            else: +                c = selfmessage.compatibleWithBase(basemessage) +                c.prefix("in message %s: " % name) +             +            compatibility = compatibility.combine(c) +         +        return compatibility + + + +class Message: +    HIGH = "High" +    MEDIUM = "Medium" +    LOW = "Low" +    FIXED = "Fixed" +    priorities = [ HIGH, MEDIUM, LOW, FIXED ] +    prioritieswithnumber = [ FIXED ] +     +    TRUSTED = "Trusted" +    NOTTRUSTED = "NotTrusted" +    trusts = [ TRUSTED, NOTTRUSTED ] +     +    UNENCODED = "Unencoded" +    ZEROCODED = "Zerocoded" +    encodings = [ UNENCODED, ZEROCODED ] + +    NOTDEPRECATED = "NotDeprecated" +    DEPRECATED = "Deprecated" +    UDPDEPRECATED = "UDPDeprecated" +    UDPBLACKLISTED = "UDPBlackListed" +    deprecations = [ NOTDEPRECATED, UDPDEPRECATED, UDPBLACKLISTED, DEPRECATED ] +    # in order of increasing deprecation +     +    def __init__(self, name, number, priority, trust, coding): +        self.name = name +        self.number = number +        self.priority = priority +        self.trust = trust +        self.coding = coding +        self.deprecateLevel = 0 +        self.blocks = [ ] + +    def deprecated(self): +        return self.deprecateLevel != 0 + +    def deprecate(self, deprecation):  +        self.deprecateLevel = self.deprecations.index(deprecation) + +    def addBlock(self, block): +        self.blocks.append(block) +         +    def compatibleWithBase(self, base): +        if self.name != base.name: +            # this should never happen in real life because of the +            # way Template matches up messages by name +            return Incompatible("has different name: %s vs. %s in base" +                               % (self.name, base.name))  +        if self.priority != base.priority: +            return Incompatible("has different priority: %s vs. %s in base" +                                % (self.priority, base.priority))  +        if self.trust != base.trust: +            return Incompatible("has different trust: %s vs. %s in base" +                                % (self.trust, base.trust))  +        if self.coding != base.coding: +            return Incompatible("has different coding: %s vs. %s in base" +                                % (self.coding, base.coding))  +        if self.number != base.number: +            return Incompatible("has different number: %s vs. %s in base" +                                % (self.number, base.number)) +         +        compatibility = Same() + +        if self.deprecateLevel != base.deprecateLevel: +            if self.deprecateLevel < base.deprecateLevel: +                c = Older("is less deprecated: %s vs. %s in base" % ( +                    self.deprecations[self.deprecateLevel], +                    self.deprecations[base.deprecateLevel])) +            else: +                c = Newer("is more deprecated: %s vs. %s in base" % ( +                    self.deprecations[self.deprecateLevel], +                    self.deprecations[base.deprecateLevel])) +            compatibility = compatibility.combine(c) +         +        selflen = len(self.blocks) +        baselen = len(base.blocks) +        samelen = min(selflen, baselen) +             +        for i in xrange(0, samelen): +            selfblock = self.blocks[i] +            baseblock = base.blocks[i] +             +            c = selfblock.compatibleWithBase(baseblock) +            if not c.same(): +                c = Incompatible("block %d isn't identical" % i) +            compatibility = compatibility.combine(c) +         +        if selflen > baselen: +            c = Newer("has %d extra blocks" % (selflen - baselen)) +        elif selflen < baselen: +            c = Older("missing %d extra blocks" % (baselen - selflen)) +        else: +            c = Same() +         +        compatibility = compatibility.combine(c) +        return compatibility + + + +class Block(object): +    SINGLE = "Single" +    MULTIPLE = "Multiple" +    VARIABLE = "Variable" +    repeats = [ SINGLE, MULTIPLE, VARIABLE ] +    repeatswithcount = [ MULTIPLE ] +     +    def __init__(self, name, repeat, count=None): +        self.name = name +        self.repeat = repeat +        self.count = count +        self.variables = [ ] + +    def addVariable(self, variable): +        self.variables.append(variable) +         +    def compatibleWithBase(self, base): +        if self.name != base.name: +            return Incompatible("has different name: %s vs. %s in base" +                                % (self.name, base.name)) +        if self.repeat != base.repeat: +            return Incompatible("has different repeat: %s vs. %s in base" +                                % (self.repeat, base.repeat)) +        if self.repeat in Block.repeatswithcount: +            if self.count != base.count: +                return Incompatible("has different count: %s vs. %s in base" +                                    % (self.count, base.count))  + +        compatibility = Same() +         +        selflen = len(self.variables) +        baselen = len(base.variables) +         +        for i in xrange(0, min(selflen, baselen)): +            selfvar = self.variables[i] +            basevar = base.variables[i] +             +            c = selfvar.compatibleWithBase(basevar) +            if not c.same(): +                c = Incompatible("variable %d isn't identical" % i) +            compatibility = compatibility.combine(c) + +        if selflen > baselen: +            c = Newer("has %d extra variables" % (selflen - baselen)) +        elif selflen < baselen: +            c = Older("missing %d extra variables" % (baselen - selflen)) +        else: +            c = Same() + +        compatibility = compatibility.combine(c) +        return compatibility + + + +class Variable: +    U8 = "U8"; U16 = "U16"; U32 = "U32"; U64 = "U64" +    S8 = "S8"; S16 = "S16"; S32 = "S32"; S64 = "S64" +    F32 = "F32"; F64 = "F64" +    LLVECTOR3 = "LLVector3"; LLVECTOR3D = "LLVector3d"; LLVECTOR4 = "LLVector4" +    LLQUATERNION = "LLQuaternion" +    LLUUID = "LLUUID" +    BOOL = "BOOL" +    IPADDR = "IPADDR"; IPPORT = "IPPORT" +    FIXED = "Fixed" +    VARIABLE = "Variable" +    types = [ U8, U16, U32, U64, S8, S16, S32, S64, F32, F64, +                LLVECTOR3, LLVECTOR3D, LLVECTOR4, LLQUATERNION, +                LLUUID, BOOL, IPADDR, IPPORT, FIXED, VARIABLE ] +    typeswithsize = [ FIXED, VARIABLE ] +     +    def __init__(self, name, type, size): +        self.name = name +        self.type = type +        self.size = size +         +    def compatibleWithBase(self, base): +        if self.name != base.name: +            return Incompatible("has different name: %s vs. %s in base" +                                % (self.name, base.name)) +        if self.type != base.type: +            return Incompatible("has different type: %s vs. %s in base" +                                % (self.type, base.type)) +        if self.type in Variable.typeswithsize: +            if self.size != base.size: +                return Incompatible("has different size: %s vs. %s in base" +                                    % (self.size, base.size))  +        return Same() + + + +### +### Parsing Message Templates +### + +class TemplateParser: +    def __init__(self, tokens): +        self._tokens = tokens +        self._version = 0 +        self._numbers = { } +        for p in Message.priorities: +            self._numbers[p] = 0 + +    def parseTemplate(self): +        tokens = self._tokens +        t = Template() +        while True: +            if tokens.want("version"): +                v = float(tokens.require(tokens.wantFloat())) +                self._version = v +                t.version = v +                continue +     +            m = self.parseMessage() +            if m: +                t.addMessage(m) +                continue +             +            if self._version >= 2.0: +                tokens.require(tokens.wantEOF()) +                break +            else: +                if tokens.wantEOF(): +                    break +             +                tokens.consume() +                    # just assume (gulp) that this is a comment +                    # line 468: "sim -> dataserver" +        return t                 + + +    def parseMessage(self): +        tokens = self._tokens +        if not tokens.want("{"): +            return None +         +        name     = tokens.require(tokens.wantSymbol()) +        priority = tokens.require(tokens.wantOneOf(Message.priorities)) +         +        if self._version >= 2.0  or  priority in Message.prioritieswithnumber: +            number = int("+" + tokens.require(tokens.wantInteger()), 0) +        else: +            self._numbers[priority] += 1 +            number = self._numbers[priority] +         +        trust    = tokens.require(tokens.wantOneOf(Message.trusts)) +        coding   = tokens.require(tokens.wantOneOf(Message.encodings)) +     +        m = Message(name, number, priority, trust, coding) +         +        if self._version >= 2.0: +            d = tokens.wantOneOf(Message.deprecations) +            if d: +                m.deprecate(d) +                 +        while True: +            b = self.parseBlock() +            if not b: +                break +            m.addBlock(b) +             +        tokens.require(tokens.want("}")) +         +        return m +     +     +    def parseBlock(self): +        tokens = self._tokens +        if not tokens.want("{"): +            return None +        name = tokens.require(tokens.wantSymbol()) +        repeat = tokens.require(tokens.wantOneOf(Block.repeats)) +        if repeat in Block.repeatswithcount: +            count = int(tokens.require(tokens.wantInteger())) +        else: +            count = None +     +        b = Block(name, repeat, count) +     +        while True: +            v = self.parseVariable() +            if not v: +                break +            b.addVariable(v) +             +        tokens.require(tokens.want("}")) +        return b +             +     +    def parseVariable(self): +        tokens = self._tokens +        if not tokens.want("{"): +            return None +        name = tokens.require(tokens.wantSymbol()) +        type = tokens.require(tokens.wantOneOf(Variable.types)) +        if type in Variable.typeswithsize: +            size = tokens.require(tokens.wantInteger()) +        else: +            tokens.wantInteger() # in LandStatRequest: "{ ParcelLocalID S32 1 }"  +            size = None +        tokens.require(tokens.want("}")) +        return Variable(name, type, size) +         +def parseTemplateString(s): +    return TemplateParser(TokenStream().fromString(s)).parseTemplate() + +def parseTemplateFile(f): +    return TemplateParser(TokenStream().fromFile(f)).parseTemplate() diff --git a/indra/lib/python/indra/ipc/tokenstream.py b/indra/lib/python/indra/ipc/tokenstream.py new file mode 100755 index 0000000000..b96f26d3ff --- /dev/null +++ b/indra/lib/python/indra/ipc/tokenstream.py @@ -0,0 +1,154 @@ +"""\ +@file tokenstream.py +@brief Message template parsing utility class + +$LicenseInfo:firstyear=2007&license=mit$ + +Copyright (c) 2007-2009, Linden Research, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +$/LicenseInfo$ +""" + +import re + +class _EOF(object): +    pass + +EOF = _EOF() + +class _LineMarker(int): +    pass +     +_commentRE = re.compile(r'//.*') +_symbolRE = re.compile(r'[a-zA-Z_][a-zA-Z_0-9]*') +_integerRE = re.compile(r'(0x[0-9A-Fa-f]+|0\d*|[1-9]\d*)') +_floatRE = re.compile(r'\d+(\.\d*)?') + + +class ParseError(Exception): +    def __init__(self, stream, reason): +        self.line = stream.line +        self.context = stream._context() +        self.reason = reason + +    def _contextString(self):     +        c = [ ] +        for t in self.context: +            if isinstance(t, _LineMarker): +                break +            c.append(t) +        return " ".join(c) + +    def __str__(self): +        return "line %d: %s @ ... %s" % ( +            self.line, self.reason, self._contextString()) + +    def __nonzero__(self): +        return False + + +def _optionText(options): +    n = len(options) +    if n == 1: +        return '"%s"' % options[0] +    return '"' + '", "'.join(options[0:(n-1)]) + '" or "' + options[-1] + '"' + + +class TokenStream(object): +    def __init__(self): +        self.line = 0 +        self.tokens = [ ] +     +    def fromString(self, string): +        return self.fromLines(string.split('\n')) +         +    def fromFile(self, file): +        return self.fromLines(file) + +    def fromLines(self, lines): +        i = 0 +        for line in lines: +            i += 1 +            self.tokens.append(_LineMarker(i)) +            self.tokens.extend(_commentRE.sub(" ", line).split()) +        self._consumeLines() +        return self +     +    def consume(self): +        if not self.tokens: +            return EOF +        t = self.tokens.pop(0) +        self._consumeLines() +        return t +     +    def _consumeLines(self): +        while self.tokens and isinstance(self.tokens[0], _LineMarker): +            self.line = self.tokens.pop(0) +     +    def peek(self): +        if not self.tokens: +            return EOF +        return self.tokens[0] +             +    def want(self, t): +        if t == self.peek(): +            return self.consume() +        return ParseError(self, 'expected "%s"' % t) + +    def wantOneOf(self, options): +        assert len(options) +        if self.peek() in options: +            return self.consume() +        return ParseError(self, 'expected one of %s' % _optionText(options)) + +    def wantEOF(self): +        return self.want(EOF) +         +    def wantRE(self, re, message=None): +        t = self.peek() +        if t != EOF: +            m = re.match(t) +            if m and m.end() == len(t): +                return self.consume() +        if not message: +            message = "expected match for r'%s'" % re.pattern +        return ParseError(self, message) +     +    def wantSymbol(self): +        return self.wantRE(_symbolRE, "expected symbol") +     +    def wantInteger(self): +        return self.wantRE(_integerRE, "expected integer") +     +    def wantFloat(self): +        return self.wantRE(_floatRE, "expected float") +     +    def _context(self): +        n = min(5, len(self.tokens)) +        return self.tokens[0:n] + +    def require(self, t): +        if t: +            return t +        if isinstance(t, ParseError): +            raise t +        else: +            raise ParseError(self, "unmet requirement") + | 
