PageRenderTime 28ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/tsjepublisher/static/qooxdoo/tool/pylib/ecmascript/frontend/Scope.py

http://hackathon.codeplex.com
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
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. ################################################################################
  4. #
  5. # qooxdoo - the new era of web development
  6. #
  7. # http://qooxdoo.org
  8. #
  9. # Copyright:
  10. # 2006-2010 1&1 Internet AG, Germany, http://www.1und1.de
  11. #
  12. # License:
  13. # LGPL: http://www.gnu.org/licenses/lgpl.html
  14. # EPL: http://www.eclipse.org/org/documents/epl-v10.php
  15. # See the LICENSE file in the project's top-level directory for details.
  16. #
  17. # Authors:
  18. # * Fabian Jakobs (fjakobs)
  19. #
  20. ################################################################################
  21. ##
  22. # Representing scopes of variables.
  23. ##
  24. from ecmascript.frontend import treeutil
  25. from ecmascript.frontend.tree import Node
  26. ##
  27. # Class representing a single, flat scope (not including nested scopes), in
  28. # which variables have a specific binding
  29. #
  30. # Each scope has a type, one of ("global", "function" or "exception"), and
  31. # relates to its top-most AST node. Variables in a scope come in different
  32. # flavors, depending on the type of scope there are declared ("var") variables,
  33. # parameters ("arguments") of functions, or arguments of exceptions ("catch"
  34. # arguments).
  35. class Scope(object):
  36. def __init__(self, node, script):
  37. self.node = node
  38. self.script = script
  39. # declare (so __str__ doesn't fail in init)
  40. self.type = None
  41. self.variables = []
  42. self.arguments = []
  43. self.parentScopeNode = None
  44. self.uses = []
  45. # init
  46. self.type = self._getType()
  47. if self.type == Scope.EXCEPTION:
  48. self.variables = self._getExceptionVariables()
  49. else:
  50. self.variables = self._getDeclaredVariables()
  51. self.arguments = self._getArguments()
  52. self.parentScopeNode = self._getParentScopeNode()
  53. self.uses = []
  54. return
  55. GLOBAL = "global"
  56. FUNCTION = "function"
  57. EXCEPTION = "exception"
  58. def __str__(self):
  59. arguments = ", ".join([s.__str__() for s in self.arguments])
  60. variables = ", ".join([s.__str__() for s in self.variables])
  61. uses = ", ".join([s.__str__() for s in self.uses])
  62. return """
  63. Function %s(%s):
  64. - Defined variables: %s
  65. - Used Variables: %s""" % (
  66. treeutil.getFunctionName(self.node),
  67. arguments, variables, uses
  68. )
  69. ##
  70. # Find all nodes where a variable is referenced/used (rather than
  71. # declared) in this scope
  72. def computeVariableUses(self):
  73. self.uses = []
  74. if self.type == Scope.GLOBAL:
  75. startNode = self.node
  76. elif self.type == Scope.FUNCTION:
  77. startNode = self.node.getChild("body")
  78. elif self.type == Scope.EXCEPTION:
  79. startNode = self.node.getChild("block")
  80. for (name, node) in Scope.usedVariablesIterator(startNode):
  81. self.uses.append(VariableUse(name, node, self))
  82. ##
  83. # Return the parent in the scope chain, if any.
  84. def getParentScope(self):
  85. if self.parentScopeNode:
  86. return self.script.getScope(self.parentScopeNode)
  87. else:
  88. if self.type == Scope.FUNCTION:
  89. return self.script.getGlobalScope()
  90. else:
  91. return None
  92. ##
  93. # See if a given variable name is defined in the local scope
  94. def getLocalDefinition(self, variableName):
  95. for var in self.variables:
  96. if var.name == variableName:
  97. return var
  98. for arg in self.arguments:
  99. if arg.name == variableName:
  100. return arg
  101. return None
  102. ##
  103. # Return the scope type
  104. def _getType(self):
  105. if self.node.type == "function":
  106. return Scope.FUNCTION
  107. elif self.node.type == "catch":
  108. return Scope.EXCEPTION
  109. else:
  110. return Scope.GLOBAL
  111. ##
  112. # Return the parameter ("arguments") of a function scope.
  113. def _getArguments(self):
  114. paramsNode = self.node.getChild("params", False)
  115. if not paramsNode:
  116. return []
  117. arguments = []
  118. if paramsNode.hasChildren():
  119. for child in paramsNode.children:
  120. if child.type == "identifier":
  121. name = child.get("value")
  122. arguments.append(VariableDefinition(name, child, True, self))
  123. return arguments
  124. ##
  125. # Return the parameters of a "catch" expression.
  126. def _getExceptionVariables(self):
  127. assert self.node.type == "catch"
  128. identifier = treeutil.selectNode(self.node, "params/identifier")
  129. assert identifier and identifier.type=="identifier", "Unable to retrieve 'catch' parameter"
  130. return [VariableDefinition(identifier.get("value", ""), identifier, False, self)]
  131. ##
  132. # Return the tree node of the parent scope.
  133. def _getParentScopeNode(self):
  134. node = self.node
  135. while node.hasParent():
  136. node = node.parent
  137. if node.type in ["function", "catch"]:
  138. return node
  139. return None
  140. ##
  141. # Create VariableDefinition's for the variable declarations in this scope.
  142. def _getDeclaredVariables(self):
  143. variables = {}
  144. if self.type == Scope.GLOBAL:
  145. startNode = self.node
  146. elif self.type == Scope.FUNCTION:
  147. startNode = self.node.getChild("body")
  148. for (name, node) in Scope.declaredVariablesIterator(startNode):
  149. if name in variables:
  150. variables[name].addDecl(node)
  151. else:
  152. variables[name] = VariableDefinition(name, node, False, self)
  153. return variables.values()
  154. ##
  155. # Generator for all nodes in a tree that "var" declare variables
  156. @staticmethod
  157. def declaredVariablesIterator(node):
  158. if node.type == "function":
  159. name = node.getChild("identifier", False)
  160. if name:
  161. yield (name.get("value"), node)
  162. return
  163. if node.hasChildren():
  164. for child in node.children:
  165. if child.type == "var":
  166. for definition in child.children:
  167. if definition.type == "definition":
  168. definee = definition.getDefinee()
  169. yield (definee.get("value"), definee)
  170. for (var, node) in Scope.declaredVariablesIterator(child):
  171. yield (var, node)
  172. ##
  173. # Generate all "identifier" nodes down from this one
  174. # which are bare identifiers (as in "var foo;" yielding "foo") or head
  175. # a chain of identifiers (as in "tree.selection.Manager", yielding
  176. # "tree")
  177. @staticmethod
  178. def usedVariablesIterator(node):
  179. # Switch on node context:
  180. # "function", "catch":
  181. if node.type in ["function", "catch"]:
  182. return
  183. # "catch": skip the identifier of catch clauses, e.g. the 'e' in 'catch(e)'
  184. # (it belongs to the catch scope)
  185. if node.parent and node.parent.type == "catch" and node == node.parent.children[0]:
  186. return
  187. # "for-in": treat variables used in for-in loops as used variables (why?)
  188. # (undeclared variables are handled by the normal "identifier" rule
  189. # further down)
  190. if (
  191. node.type == "var" and
  192. node.parent.type == "operation" and
  193. node.parent.get("operator") == "IN"
  194. ):
  195. use = node.getChild("definition").getDefinee()
  196. if use:
  197. name = use.get("value", False)
  198. name = None if name == False else name
  199. yield (name, use)
  200. return
  201. # "identifier":
  202. if node.type == "identifier":
  203. isFirstChild = False
  204. isVariableMember = False
  205. if node.parent.isVar(): # (the old code added "accessor" for the types to check)
  206. isVariableMember = True
  207. isFirstChild = treeutil.checkFirstChainChild(node)
  208. # inside a variable only respect the first member
  209. if not isVariableMember or isFirstChild:
  210. name = node.get("value", False)
  211. name = None if name == False else name
  212. if name:
  213. yield (name, node)
  214. # -- Recurse over children
  215. if node.children:
  216. for child in node.children:
  217. for (name, use) in Scope.usedVariablesIterator(child):
  218. yield (name, use)
  219. return
  220. ##
  221. # Class representing a defining occurrence of a variable in the code
  222. # (e.g. as in "var a=3;"); a variable can be defined multiple times
  223. # within a single scope; each instance links to its scope, and has
  224. # a list of its VariableUses (see further).
  225. class VariableDefinition(object):
  226. def __init__(self, name, node, isArgument, scope):
  227. self.name = name
  228. self.nodes = [node]
  229. self.isArgument = isArgument
  230. self.scope = scope
  231. self.uses = []
  232. def __str__(self):
  233. return self.name
  234. def addUse(self, variableUse):
  235. self.uses.append(variableUse)
  236. def addDecl(self, node):
  237. self.nodes.append(node)
  238. ##
  239. # Class representing a use occurrence of a variable in the code;
  240. # VariableUse's maintain their relation the variable's definition
  241. # at the corresponding VariableDefinition object
  242. class VariableUse(object):
  243. def __init__(self, name, node, scope):
  244. self.name = name
  245. self.node = node
  246. self.scope = scope
  247. self.definition = self.scope.script.getVariableDefinition(name, scope)
  248. if self.definition:
  249. self.definition.addUse(self)
  250. def __str__(self):
  251. return self.name