Ejemplo n.º 1
0
 def FUNCTION_build(self, tokenizer):
     node = Node(tokenizer)
     if node.type != "function":
         if tokenizer.token.value == "get":
             node.type = "getter"
         else:
             node.type = "setter"
             
     return node
Ejemplo n.º 2
0
    def FUNCTION_build(self, tokenizer):
        node = Node(tokenizer)
        if node.type != "function":
            if tokenizer.token.value == "get":
                node.type = "getter"
            else:
                node.type = "setter"

        return node
Ejemplo n.º 3
0
def combineExpressions(condition, thenExpression, elseExpression):
    """ Combines then and else expression using a hook statement. """
    
    hook = createHook(condition, thenExpression, elseExpression)
    semicolon = Node(condition.tokenizer, "semicolon")
    semicolon.append(hook, "expression")
    
    fixParens(condition)
    fixParens(thenExpression)
    fixParens(elseExpression)
    
    return semicolon
Ejemplo n.º 4
0
def compactIf(node, thenPart, condition):
    """
    Reduces the size of a if statement (without elsePart) using boolean operators
    instead of the typical keywords e.g. 
    "if(something)make()" is translated to "something&&make()"
    which is two characters shorter. This however only works when the
    thenPart is only based on expressions and does not contain other 
    statements.
    """
    
    thenExpression = getattr(thenPart, "expression", None)
    if not thenExpression:
        # Empty semicolon statement => translate if into semicolon statement
        node.remove(condition)
        node.remove(node.thenPart)
        node.append(condition, "expression")
        node.type = "semicolon"

    else:
        # Has expression => Translate IF using a AND or OR operator
        if condition.type == "not":
            replacement = Node(thenPart.tokenizer, "or")
            condition = condition[0]
        else:
            replacement = Node(thenPart.tokenizer, "and")

        replacement.append(condition)
        replacement.append(thenExpression)

        thenPart.append(replacement, "expression")

        fixParens(thenExpression)
        fixParens(condition)
        
        node.parent.replace(node, thenPart)
Ejemplo n.º 5
0
def combineToCommaExpression(node):
    """
    This method tries to combine a block with multiple statements into
    one semicolon statement with a comma expression containing all expressions
    from the previous block. This only works when the block exclusively consists
    of expressions as this do not work with other statements. Still this conversion
    reduces the size impact of many blocks and leads to the removal of a lot of 
    curly braces in the result code.
    
    Example: {x++;y+=3} => x++,x+=3
    """
    
    if node == None or node.type != "block":
        return node
        
    for child in node:
        if child.type != "semicolon":
            return node
    
    comma = Node(node.tokenizer, "comma")
    
    for child in list(node):
        # Ignore empty semicolons
        if hasattr(child, "expression"):
            comma.append(child.expression)
            
    semicolon = Node(node.tokenizer, "semicolon")
    semicolon.append(comma, "expression")
    
    parent = node.parent
    parent.replace(node, semicolon)
    
    return semicolon
Ejemplo n.º 6
0
def __combineVarStatements(node):
    """Top level method called to optimize a script node"""
    firstVar = __findFirstVarStatement(node)
    
    # Special case, when a node has variables, but no valid "var" block to hold them
    # This happens in cases where there is a for-loop which contains a "var", but
    # there are no other variable declarations anywhere. In this case we are not able
    # to optimize the code further and just exit at this point
    
    # Only size-saving when there are multiple for-in loops, but no other var statement
    if not firstVar and len(node.stats.declared) > 1:
        firstVar = Node(None, "var")
        node.append(firstVar)
    
    if firstVar:
        __patchVarStatements(node, firstVar)
        __cleanFirst(firstVar)
        
        if len(firstVar) == 0:
            firstVar.parent.remove(firstVar)

        else:
            # When there is a classical for loop immediately after our 
            # first var statement, then we try to move the var declaration
            # into there as a setup expression
        
            firstVarParent = firstVar.parent
            firstVarPos = firstVarParent.index(firstVar)
            if len(firstVarParent) > firstVarPos+1:
                possibleForStatement = firstVarParent[firstVarPos+1]
                if possibleForStatement.type == "for" and not hasattr(possibleForStatement, "setup"):
                    possibleForStatement.append(firstVar, "setup")
Ejemplo n.º 7
0
    def UNARY_build(self, tokenizer):
        # NB: tokenizer.token.type must be "delete", "void", "typeof", "not", "bitwise_not",
        # "unary_plus", "unary_minus", "increment", or "decrement".
        if tokenizer.token.type == "plus":
            tokenizer.token.type = "unary_plus"
        elif tokenizer.token.type == "minus":
            tokenizer.token.type = "unary_minus"

        return Node(tokenizer)
Ejemplo n.º 8
0
def __createSimpleAssignment(identifier, valueNode):
    assignNode = Node(None, "assign")
    identNode = Node(None, "identifier")
    identNode.value = identifier
    assignNode.append(identNode)
    assignNode.append(valueNode)

    return assignNode
Ejemplo n.º 9
0
def reworkElse(node, elsePart):
    """ 
    If an if ends with a return/throw we are able to inline the content 
    of the else to the same parent as the if resides into. This method
    deals with all the nasty details of this operation.
    """
    
    target = node.parent
    targetIndex = target.index(node)+1

    # A workaround for compact if-else blocks
    # We are a elsePart of the if where we want to move our
    # content to. This cannot work. So we need to wrap ourself
    # into a block and move the else statements to this newly
    # established block
    if not target.type in ("block","script"):
        newBlock = Node(None, "block")
        newBlock.wrapped = True
        
        # Replace node with newly created block and put ourself into it
        node.parent.replace(node, newBlock)
        newBlock.append(node)
        
        # Update the target and the index
        target = newBlock
        targetIndex = 1
        
    if not target.type in ("block","script"):
        print("No possible target found/created")
        return elsePart
        
    if elsePart.type == "block":
        for child in reversed(elsePart):
            target.insert(targetIndex, child)

        # Remove else block from if statement
        node.remove(elsePart)
            
    else:
        target.insert(targetIndex, elsePart)
        
    return  
Ejemplo n.º 10
0
    def __rebuildAsSplitted(self, value, mapper):
        """ The real splitter engine. Creates plus Node instances and cascade them automatically """
        
        result = []
        splits = self.__replacer.split(value)
        if len(splits) == 1:
            return None
        
        pair = Node(None, "plus")

        for entry in splits:
            if entry == "":
                continue
                
            if len(pair) == 2:
                newPair = Node(None, "plus")
                newPair.append(pair)
                pair = newPair

            if self.__replacer.match(entry):
                pos = int(entry[1]) - 1
                
                # Items might be added multiple times. Copy to protect original.
                try:
                    repl = mapper[pos]
                except KeyError:
                    raise TranslationError("Invalid positional value: %s in %s" % (entry, value))
                
                copied = copy.deepcopy(mapper[pos])
                if copied.type not in ("identifier", "call"):
                    copied.parenthesized = True
                pair.append(copied)
                
            else:
                child = Node(None, "string")
                child.value = entry
                pair.append(child)
                
        return pair
Ejemplo n.º 11
0
def createHook(condition, thenPart, elsePart):
    """ Creates a hook expression with the given then/else parts """
    
    hook = Node(condition.tokenizer, "hook")
    hook.append(condition, "condition")
    hook.append(thenPart, "thenPart")
    hook.append(elsePart, "elsePart")
    return hook
    
Ejemplo n.º 12
0
    def PRIMARY_build(self, tokenizer, tokenType):
        # NB: tokenizer.token.type must be "null", "this", "true", "false", "identifier", "number", "string", or "regexp".
        node = Node(tokenizer, tokenType)
        if tokenType in ("identifier", "string", "regexp"):
            node.value = tokenizer.token.value

        if tokenType == "number":
            value = tokenizer.token.value
            if type(value) != str:
                node.value = value
            else:
                # Check whether Python and JavaScript number created is the same
                try:
                    conv = float(value)
                    if str(conv) == value:
                        node.value = conv
                    else:
                        node.value = value
                except ValueError:
                    # Required for preventing issues with 0xF0 like values
                    node.value = value

        return node
Ejemplo n.º 13
0
 def PRIMARY_build(self, tokenizer, tokenType):
     # NB: tokenizer.token.type must be "null", "this", "true", "false", "identifier", "number", "string", or "regexp".
     node = Node(tokenizer, tokenType)
     if tokenType in ("identifier", "string", "regexp"):
         node.value = tokenizer.token.value
         
     if tokenType == "number":
         value = tokenizer.token.value
         if type(value) != str:
             node.value = value
         else:
             # Check whether Python and JavaScript number created is the same
             try:
                 conv = float(value)
                 if str(conv) == value:
                     node.value = conv
                 else:
                     node.value = value
             except ValueError:
                 # Required for preventing issues with 0xF0 like values
                 node.value = value
         
     return node
Ejemplo n.º 14
0
 def WHILE_build(self, tokenizer):
     node = Node(tokenizer, "while")
     node.isLoop = True
     return node
Ejemplo n.º 15
0
    def __rebuildAsSplitted(self, value, mapper):
        """ The real splitter engine. Creates plus Node instances and cascade them automatically """

        result = []
        splits = self.__replacer.split(value)
        if len(splits) == 1:
            return None

        pair = Node(None, "plus")

        for entry in splits:
            if entry == "":
                continue

            if len(pair) == 2:
                newPair = Node(None, "plus")
                newPair.append(pair)
                pair = newPair

            if self.__replacer.match(entry):
                pos = int(entry[1]) - 1

                # Items might be added multiple times. Copy to protect original.
                try:
                    repl = mapper[pos]
                except KeyError:
                    raise TranslationError(
                        "Invalid positional value: %s in %s" % (entry, value))

                copied = copy.deepcopy(mapper[pos])
                if copied.type not in ("identifier", "call"):
                    copied.parenthesized = True
                pair.append(copied)

            else:
                child = Node(None, "string")
                child.value = entry
                pair.append(child)

        return pair
Ejemplo n.º 16
0
 def DEFAULT_build(self, tokenizer):
     return Node(tokenizer, "default")
Ejemplo n.º 17
0
 def PROPERTYINIT_build(self, tokenizer):
     return Node(tokenizer, "property_init")
Ejemplo n.º 18
0
 def SWITCH_build(self, tokenizer):
     node = Node(tokenizer, "switch")
     node.defaultIndex = -1
     return node
Ejemplo n.º 19
0
 def MEMBER_build(self, tokenizer, tokenType=None):
     node = Node(tokenizer, tokenType)
     if node.type == "identifier":
         node.value = tokenizer.token.value
     return node
Ejemplo n.º 20
0
 def ARRAYINIT_build(self, tokenizer):
     return Node(tokenizer, "array_init")
Ejemplo n.º 21
0
 def LIST_build(self, tokenizer):
     return Node(tokenizer, "list")
Ejemplo n.º 22
0
 def ARRAYCOMP_build(self, tokenizer):
     return Node(tokenizer, "array_comp")
Ejemplo n.º 23
0
def optimize(node):
    # Process from inside to outside
    for child in list(node):
        # None children are allowed sometimes e.g. during array_init like [1,2,,,7,8]
        if child != None:
            optimize(child)
    
    
    # Cleans up empty semicolon statements (or pseudo-empty)
    if node.type == "semicolon" and node.parent.type in ("block", "script"):
        expr = getattr(node, "expression", None)
        if not expr or expr.type in ("null", "this", "true", "false", "identifier", "number", "string", "regexp"):
            node.parent.remove(node)
            return


    # Remove unneeded parens
    if getattr(node, "parenthesized", False):
        cleanParens(node)
    
    
    # Pre-compute numeric expressions where it makes sense
    if node.type in ("plus","minus","mul","div","mod") and node[0].type == "number" and node[1].type == "number":
        firstNumber = node[0]
        secondNumber = node[1]
        operator = node.type

        # Only do for real numeric values and not for protected strings (float differences between Python and JS)
        if type(firstNumber.value) == str or type(secondNumber.value) == str:
            pass
        elif operator == "plus":
            firstNumber.value += secondNumber.value
            node.parent.replace(node, firstNumber)
        elif operator == "minus":
            firstNumber.value -= secondNumber.value
            node.parent.replace(node, firstNumber)
        else:
            if operator == "mul":
                result = firstNumber.value * secondNumber.value
            elif operator == "div" and secondNumber.value is not 0:
                result = firstNumber.value / secondNumber.value
            elif operator == "mod":
                result = firstNumber.value % secondNumber.value
            else:
                result = None
            
            if result is not None and len(str(result)) < len(compress(node)):
                firstNumber.value = result
                node.parent.replace(node, firstNumber)


    # Pre-combine strings (even supports mixed string + number concats)
    elif node.type == "plus" and node[0].type in ("number", "string") and node[1].type in ("number", "string"):
        node[0].value = "%s%s" % (node[0].value, node[1].value)
        node[0].type = "string"
        node.parent.replace(node, node[0])


    # Unwrap blocks
    if node.type == "block":
        if node.parent.type in ("try", "catch", "finally"):
            pass
        elif len(node) == 0:
            repl =Node(node.tokenizer, "semicolon")
            node.parent.replace(node, repl)
            node = repl
        elif len(node) == 1:
            if node.parent.type == "if" and node.rel == "thenPart" and hasattr(node.parent, "elsePart") and containsIf(node):
                # if with else where the thenBlock contains another if
                pass
            elif node.parent.type == "if" and node.rel == "thenPart" and containsIfElse(node):
                # if without else where the thenBlock contains a if-else
                pass
            elif node.parent.type in ("case", "default"):
                # virtual blocks inside case/default statements
                pass
            else:
                node.parent.replace(node, node[0])
                node = node[0]
        else:
            node = combineToCommaExpression(node)
        
        
    # Remove "empty" semicolons who are inside a block/script parent
    if node.type == "semicolon":
        if not hasattr(node, "expression"):
            if node.parent.type in ("block", "script"):
                node.parent.remove(node)
            elif node.parent.type == "if":
                rel = getattr(node, "rel", None)
                if rel == "elsePart":
                    node.parent.remove(node)
            
            
    # Process all if-statements
    if node.type == "if":
        condition = node.condition
        thenPart = node.thenPart
        elsePart = getattr(node, "elsePart", None)
        
        # Optimize for empty thenPart if elsePart is available
        if thenPart.type == "semicolon" and not hasattr(thenPart, "expression") and elsePart:
            if condition.type == "not":
                node.replace(condition, condition[0])
                condition = condition[0]
            else:
                repl = Node(None, "not")
                node.replace(condition, repl)
                repl.append(condition)
                condition = repl
            
            node.replace(thenPart, elsePart)
            thenPart = elsePart
            elsePart = None
        
        # Optimize using hook operator
        if elsePart and thenPart.type == "return" and elsePart.type == "return":
            # Combine return statement
            replacement = createReturn(createHook(condition, thenPart.value, elsePart.value))
            node.parent.replace(node, replacement)
            return

        # Check whether if-part ends with a return statement. Then
        # We do not need a else statement here and just can wrap the whole content
        # of the else block inside the parent
        if elsePart and endsWithReturnOrThrow(thenPart):
            reworkElse(node, elsePart)
            elsePart = None

        # Optimize using "AND" or "OR" operators
        # Combine multiple semicolon statements into one semicolon statement using an "comma" expression
        thenPart = combineToCommaExpression(thenPart)
        elsePart = combineToCommaExpression(elsePart)
        
        # Optimize remaining if or if-else constructs
        if elsePart:
            mergeParts(node, thenPart, elsePart, condition)
        elif thenPart.type == "semicolon":
            compactIf(node, thenPart, condition)
Ejemplo n.º 24
0
 def FOR_build(self, tokenizer):
     node = Node(tokenizer, "for")
     node.isLoop = True
     node.isEach = False
     return node
Ejemplo n.º 25
0
 def DEFAULT_initializeStatements(self, node, tokenizer):
     node.append(Node(tokenizer, "block"), "statements")
Ejemplo n.º 26
0
 def DO_build(self, tokenizer):
     node = Node(tokenizer, "do")
     node.isLoop = True
     return node
Ejemplo n.º 27
0
 def COMMA_build(self, tokenizer):
     return Node(tokenizer, "comma")
Ejemplo n.º 28
0
 def CATCH_wrapException(self, tokenizer):
     node = Node(tokenizer, "exception")
     node.value = tokenizer.token.value
     return node
Ejemplo n.º 29
0
def __createDeclaration(name):
    declNode = Node(None, "declaration")
    declNode.name = name
    declNode.readOnly = False
    return declNode
Ejemplo n.º 30
0
 def FUNCTION_wrapParam(self, tokenizer):
     param = Node(tokenizer)
     param.value = tokenizer.token.value
     return param
Ejemplo n.º 31
0
def __rebuildAsAssignment(node, firstVarStatement):
    """Rebuilds the items of a var statement into a assignment list and moves declarations to the given var statement"""
    assignment = Node(node.tokenizer, "semicolon")
    assignmentList = Node(node.tokenizer, "comma")
    assignment.append(assignmentList, "expression")

    # Casting to list() creates a copy during the process (keeps loop stable)
    for child in list(node):
        if hasattr(child, "name"):
            # Cleanup initializer and move to assignment
            if hasattr(child, "initializer"):
                assign = __createSimpleAssignment(child.name, child.initializer)
                assignmentList.append(assign)
                
            firstVarStatement.append(child)
        
        else:
            # JS 1.7 Destructing Expression
            for identifier in child.names:
                firstVarStatement.append(__createDeclaration(identifier.value))

            if hasattr(child, "initializer"):
                assign = __createMultiAssignment(child.names, child.initializer)
                assignmentList.append(assign)
                
            node.remove(child)
                        
    # Patch parent node to contain assignment instead of declaration
    if len(assignmentList) > 0:
        node.parent.replace(node, assignment)
    
    # Special process for "for-in" loops
    # It is OK to be second because of assignments are not allowed at
    # all in for-in loops and so the first if basically does nothing
    # for these kind of statements.
    elif getattr(node, "rel", None) == "iterator":
        if hasattr(child, "name"):
            node.parent.replace(node, __createIdentifier(child.name))
        else:
            # JS 1.7 Destructing Expressions
            node.parent.replace(node, child.names)
    
    # Edge case. Not yet found if this happen realistically
    else:
        if hasattr(node, "rel"):
            logging.warn("Remove related node (%s) from parent: %s" % (node.rel, node))
            
        node.parent.remove(node)
        
    # Minor post-cleanup. Remove useless comma statement when only one expression is the result
    if len(assignmentList) == 1:
        assignment.replace(assignmentList, assignmentList[0])
Ejemplo n.º 32
0
 def MEMBER_build(self, tokenizer, tokenType=None):
     node = Node(tokenizer, tokenType)
     if node.type == "identifier":
         node.value = tokenizer.token.value
     return node
Ejemplo n.º 33
0
 def OBJECTINIT_build(self, tokenizer):
     return Node(tokenizer, "object_init")
Ejemplo n.º 34
0
 def FOR_build(self, tokenizer):
     node = Node(tokenizer, "for")
     node.isLoop = True
     node.isEach = False
     return node
Ejemplo n.º 35
0
    def __recurser(self, node):
        if node.type == "call":
            funcName = None

            if node[0].type == "identifier":
                funcName = node[0].value
            elif node[0].type == "dot" and node[0][1].type == "identifier":
                funcName = node[0][1].value

            if funcName in methods:
                params = node[1]
                table = self.__table

                # Verify param types
                if params[0].type != "string":
                    logging.warn(
                        "Expecting translation string to be type string: %s at line %s"
                        % (params[0].type, params[0].line))

                if (funcName == "trn"
                        or funcName == "trc") and params[1].type != "string":
                    logging.warn(
                        "Expecting translation string to be type string: %s at line %s"
                        % (params[1].type, params[1].line))

                # Signature tr(msg, arg1, arg2, ...)
                if funcName == "tr":
                    key = params[0].value
                    if key in table:
                        params[0].value = table[key]

                    if len(params) == 1:
                        node.parent.replace(node, params[0])
                    else:
                        self.__splitTemplate(node, params[0], params[1:])

                # Signature trc(hint, msg, arg1, arg2, ...)
                elif funcName == "trc":
                    key = params[0].value
                    if key in table:
                        params[1].value = table[key]

                    if len(params) == 2:
                        node.parent.replace(node, params[1])
                    else:
                        self.__splitTemplate(node, params[1], params[2:])

                # Signature trn(msg, msg2, [...], int, arg1, arg2, ...)
                elif funcName == "trn":
                    keySingular = params[0].value
                    if keySingular in table:
                        params[0].value = table[keySingular]

                    keyPlural = params[1].value
                    if keyPlural in table:
                        params[1].value = table[keyPlural]

                    # TODO: Multi plural support

                    # Patch strings with dynamic values
                    if len(params) >= 3:
                        self.__splitTemplate(params[0], params[0], params[3:])
                        self.__splitTemplate(params[1], params[1], params[3:])

                    # Replace the whole call with: int < 2 ? singularMessage : pluralMessage
                    hook = Node(None, "hook")
                    hook.parenthesized = True
                    condition = Node(None, "le")
                    condition.append(params[2])
                    number = Node(None, "number")
                    number.value = 1
                    condition.append(number)

                    hook.append(condition, "condition")
                    hook.append(params[1], "elsePart")
                    hook.append(params[0], "thenPart")

                    node.parent.replace(node, hook)

        # Process children
        for child in node:
            if child != None:
                self.__recurser(child)
Ejemplo n.º 36
0
def __createMultiAssignment(names, valueNode):
    assignNode = Node(None, "assign")
    assignNode.append(names)
    assignNode.append(valueNode)

    return assignNode    
Ejemplo n.º 37
0
 def CASE_build(self, tokenizer):
     return Node(tokenizer, "case")
Ejemplo n.º 38
0
def __createIdentifier(value):
    identifier = Node(None, "identifier")
    identifier.value = value
    return identifier    
Ejemplo n.º 39
0
    def __recurser(self, node):
        if node.type == "call":
            funcName = None
            
            if node[0].type == "identifier":
                funcName = node[0].value
            elif node[0].type == "dot" and node[0][1].type == "identifier":
                funcName = node[0][1].value
            
            if funcName in methods:
                params = node[1]
                table = self.__table

                # Verify param types
                if params[0].type != "string":
                    logging.warn("Expecting translation string to be type string: %s at line %s" % (params[0].type, params[0].line))
                    
                if (funcName == "trn" or funcName == "trc") and params[1].type != "string":
                    logging.warn("Expecting translation string to be type string: %s at line %s" % (params[1].type, params[1].line))


                # Signature tr(msg, arg1, arg2, ...)
                if funcName == "tr":
                    key = params[0].value
                    if key in table:
                        params[0].value = table[key]
                        
                    if len(params) == 1:
                        node.parent.replace(node, params[0])
                    else:
                        self.__splitTemplate(node, params[0], params[1:])
                        
                        
                # Signature trc(hint, msg, arg1, arg2, ...)
                elif funcName == "trc":
                    key = params[0].value
                    if key in table:
                        params[1].value = table[key]

                    if len(params) == 2:
                        node.parent.replace(node, params[1])
                    else:
                        self.__splitTemplate(node, params[1], params[2:])
                        
                        
                # Signature trn(msg, msg2, [...], int, arg1, arg2, ...)
                elif funcName == "trn":
                    keySingular = params[0].value
                    if keySingular in table:
                        params[0].value = table[keySingular]

                    keyPlural = params[1].value
                    if keyPlural in table:
                        params[1].value = table[keyPlural]
                        
                    # TODO: Multi plural support
                    
                    # Patch strings with dynamic values
                    if len(params) >= 3:
                        self.__splitTemplate(params[0], params[0], params[3:])
                        self.__splitTemplate(params[1], params[1], params[3:])
                    
                    
                    # Replace the whole call with: int < 2 ? singularMessage : pluralMessage
                    hook = Node(None, "hook")
                    hook.parenthesized = True
                    condition = Node(None, "le")
                    condition.append(params[2])
                    number = Node(None, "number")
                    number.value = 1
                    condition.append(number)
                    
                    hook.append(condition, "condition")
                    hook.append(params[1], "elsePart")
                    hook.append(params[0], "thenPart")
                    
                    node.parent.replace(node, hook)



        # Process children
        for child in node:
            if child != None:
                self.__recurser(child)
                
Ejemplo n.º 40
0
def createReturn(value):
    """ Creates a return statement with the given value """
    
    ret = Node(value.tokenizer, "return")
    ret.append(value, "value")
    return ret
Ejemplo n.º 41
0
 def COMPTAIL_build(self, tokenizer):
     return Node(tokenizer, "comp_tail")