/tsjepublisher/static/qooxdoo/tool/pylib/ecmascript/frontend/Scope.py
Python | 330 lines | 283 code | 13 blank | 34 comment | 5 complexity | 8d0a46d9d8335a5b0ea2fcf067b88387 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-3.0, LGPL-2.1
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- ################################################################################
- #
- # qooxdoo - the new era of web development
- #
- # http://qooxdoo.org
- #
- # Copyright:
- # 2006-2010 1&1 Internet AG, Germany, http://www.1und1.de
- #
- # License:
- # LGPL: http://www.gnu.org/licenses/lgpl.html
- # EPL: http://www.eclipse.org/org/documents/epl-v10.php
- # See the LICENSE file in the project's top-level directory for details.
- #
- # Authors:
- # * Fabian Jakobs (fjakobs)
- #
- ################################################################################
- ##
- # Representing scopes of variables.
- ##
- from ecmascript.frontend import treeutil
- from ecmascript.frontend.tree import Node
- ##
- # Class representing a single, flat scope (not including nested scopes), in
- # which variables have a specific binding
- #
- # Each scope has a type, one of ("global", "function" or "exception"), and
- # relates to its top-most AST node. Variables in a scope come in different
- # flavors, depending on the type of scope there are declared ("var") variables,
- # parameters ("arguments") of functions, or arguments of exceptions ("catch"
- # arguments).
- class Scope(object):
- def __init__(self, node, script):
- self.node = node
- self.script = script
- # declare (so __str__ doesn't fail in init)
- self.type = None
- self.variables = []
- self.arguments = []
- self.parentScopeNode = None
- self.uses = []
- # init
- self.type = self._getType()
- if self.type == Scope.EXCEPTION:
- self.variables = self._getExceptionVariables()
- else:
- self.variables = self._getDeclaredVariables()
- self.arguments = self._getArguments()
- self.parentScopeNode = self._getParentScopeNode()
- self.uses = []
- return
- GLOBAL = "global"
- FUNCTION = "function"
- EXCEPTION = "exception"
- def __str__(self):
- arguments = ", ".join([s.__str__() for s in self.arguments])
- variables = ", ".join([s.__str__() for s in self.variables])
- uses = ", ".join([s.__str__() for s in self.uses])
- return """
- Function %s(%s):
- - Defined variables: %s
- - Used Variables: %s""" % (
- treeutil.getFunctionName(self.node),
- arguments, variables, uses
- )
- ##
- # Find all nodes where a variable is referenced/used (rather than
- # declared) in this scope
- def computeVariableUses(self):
- self.uses = []
- if self.type == Scope.GLOBAL:
- startNode = self.node
- elif self.type == Scope.FUNCTION:
- startNode = self.node.getChild("body")
- elif self.type == Scope.EXCEPTION:
- startNode = self.node.getChild("block")
- for (name, node) in Scope.usedVariablesIterator(startNode):
- self.uses.append(VariableUse(name, node, self))
- ##
- # Return the parent in the scope chain, if any.
- def getParentScope(self):
- if self.parentScopeNode:
- return self.script.getScope(self.parentScopeNode)
- else:
- if self.type == Scope.FUNCTION:
- return self.script.getGlobalScope()
- else:
- return None
- ##
- # See if a given variable name is defined in the local scope
- def getLocalDefinition(self, variableName):
- for var in self.variables:
- if var.name == variableName:
- return var
- for arg in self.arguments:
- if arg.name == variableName:
- return arg
- return None
- ##
- # Return the scope type
- def _getType(self):
- if self.node.type == "function":
- return Scope.FUNCTION
- elif self.node.type == "catch":
- return Scope.EXCEPTION
- else:
- return Scope.GLOBAL
- ##
- # Return the parameter ("arguments") of a function scope.
- def _getArguments(self):
- paramsNode = self.node.getChild("params", False)
- if not paramsNode:
- return []
- arguments = []
- if paramsNode.hasChildren():
- for child in paramsNode.children:
- if child.type == "identifier":
- name = child.get("value")
- arguments.append(VariableDefinition(name, child, True, self))
- return arguments
- ##
- # Return the parameters of a "catch" expression.
- def _getExceptionVariables(self):
- assert self.node.type == "catch"
- identifier = treeutil.selectNode(self.node, "params/identifier")
- assert identifier and identifier.type=="identifier", "Unable to retrieve 'catch' parameter"
- return [VariableDefinition(identifier.get("value", ""), identifier, False, self)]
- ##
- # Return the tree node of the parent scope.
- def _getParentScopeNode(self):
- node = self.node
- while node.hasParent():
- node = node.parent
- if node.type in ["function", "catch"]:
- return node
- return None
- ##
- # Create VariableDefinition's for the variable declarations in this scope.
- def _getDeclaredVariables(self):
- variables = {}
- if self.type == Scope.GLOBAL:
- startNode = self.node
- elif self.type == Scope.FUNCTION:
- startNode = self.node.getChild("body")
- for (name, node) in Scope.declaredVariablesIterator(startNode):
- if name in variables:
- variables[name].addDecl(node)
- else:
- variables[name] = VariableDefinition(name, node, False, self)
- return variables.values()
- ##
- # Generator for all nodes in a tree that "var" declare variables
- @staticmethod
- def declaredVariablesIterator(node):
- if node.type == "function":
- name = node.getChild("identifier", False)
- if name:
- yield (name.get("value"), node)
- return
- if node.hasChildren():
- for child in node.children:
- if child.type == "var":
- for definition in child.children:
- if definition.type == "definition":
- definee = definition.getDefinee()
- yield (definee.get("value"), definee)
- for (var, node) in Scope.declaredVariablesIterator(child):
- yield (var, node)
- ##
- # Generate all "identifier" nodes down from this one
- # which are bare identifiers (as in "var foo;" yielding "foo") or head
- # a chain of identifiers (as in "tree.selection.Manager", yielding
- # "tree")
- @staticmethod
- def usedVariablesIterator(node):
- # Switch on node context:
- # "function", "catch":
- if node.type in ["function", "catch"]:
- return
- # "catch": skip the identifier of catch clauses, e.g. the 'e' in 'catch(e)'
- # (it belongs to the catch scope)
- if node.parent and node.parent.type == "catch" and node == node.parent.children[0]:
- return
- # "for-in": treat variables used in for-in loops as used variables (why?)
- # (undeclared variables are handled by the normal "identifier" rule
- # further down)
- if (
- node.type == "var" and
- node.parent.type == "operation" and
- node.parent.get("operator") == "IN"
- ):
- use = node.getChild("definition").getDefinee()
- if use:
- name = use.get("value", False)
- name = None if name == False else name
- yield (name, use)
- return
- # "identifier":
- if node.type == "identifier":
- isFirstChild = False
- isVariableMember = False
- if node.parent.isVar(): # (the old code added "accessor" for the types to check)
- isVariableMember = True
- isFirstChild = treeutil.checkFirstChainChild(node)
- # inside a variable only respect the first member
- if not isVariableMember or isFirstChild:
- name = node.get("value", False)
- name = None if name == False else name
- if name:
- yield (name, node)
- # -- Recurse over children
- if node.children:
- for child in node.children:
- for (name, use) in Scope.usedVariablesIterator(child):
- yield (name, use)
- return
- ##
- # Class representing a defining occurrence of a variable in the code
- # (e.g. as in "var a=3;"); a variable can be defined multiple times
- # within a single scope; each instance links to its scope, and has
- # a list of its VariableUses (see further).
- class VariableDefinition(object):
- def __init__(self, name, node, isArgument, scope):
- self.name = name
- self.nodes = [node]
- self.isArgument = isArgument
- self.scope = scope
- self.uses = []
- def __str__(self):
- return self.name
- def addUse(self, variableUse):
- self.uses.append(variableUse)
- def addDecl(self, node):
- self.nodes.append(node)
- ##
- # Class representing a use occurrence of a variable in the code;
- # VariableUse's maintain their relation the variable's definition
- # at the corresponding VariableDefinition object
- class VariableUse(object):
- def __init__(self, name, node, scope):
- self.name = name
- self.node = node
- self.scope = scope
- self.definition = self.scope.script.getVariableDefinition(name, scope)
- if self.definition:
- self.definition.addUse(self)
- def __str__(self):
- return self.name