/nuitka/codegen/Identifiers.py

https://bitbucket.org/pombredanne/nuitka · Python · 340 lines · 216 code · 91 blank · 33 comment · 22 complexity · edf329be77a4767c7681575d956c7a49 MD5 · raw file

  1. # Copyright 2013, Kay Hayen, mailto:kay.hayen@gmail.com
  2. #
  3. # Part of "Nuitka", an optimizing Python compiler that is compatible and
  4. # integrates with CPython, but also works on its own.
  5. #
  6. # Licensed under the Apache License, Version 2.0 (the "License");
  7. # you may not use this file except in compliance with the License.
  8. # You may obtain a copy of the License at
  9. #
  10. # http://www.apache.org/licenses/LICENSE-2.0
  11. #
  12. # Unless required by applicable law or agreed to in writing, software
  13. # distributed under the License is distributed on an "AS IS" BASIS,
  14. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. # See the License for the specific language governing permissions and
  16. # limitations under the License.
  17. #
  18. """ Identifiers hold code references.
  19. These are generally the means to effectively hide the reference count. The best part
  20. is where getCheapRefCount tries to not allocate references not needed.
  21. """
  22. # The method signatures do not always require usage of self, sometimes can be decided
  23. # based on class. pylint: disable=R0201
  24. from nuitka import Utils
  25. def encodeNonAscii( var_name ):
  26. if Utils.python_version < 300:
  27. return var_name
  28. else:
  29. var_name = var_name.encode( "ascii", "xmlcharrefreplace" )
  30. return var_name.decode( "ascii" ).replace( "&#", "$$" ).replace( ";", "" )
  31. class Identifier:
  32. def __init__( self, code, ref_count ):
  33. self.code = code
  34. self.ref_count = ref_count
  35. def getRefCount( self ):
  36. return self.ref_count
  37. def getCheapRefCount( self ):
  38. return self.ref_count
  39. def getCode( self ):
  40. return self.code
  41. def getCodeObject( self ):
  42. return self.code
  43. def getCodeExportRef( self ):
  44. if self.getRefCount():
  45. return self.getCodeObject()
  46. else:
  47. return "INCREASE_REFCOUNT( %s )" % self.getCodeObject()
  48. def getCodeTemporaryRef( self ):
  49. if self.getRefCount():
  50. return "PyObjectTemporary( %s ).asObject()" % self.getCodeObject()
  51. else:
  52. return self.getCodeObject()
  53. def getCodeDropRef( self ):
  54. if self.ref_count == 0:
  55. return self.getCodeTemporaryRef()
  56. else:
  57. return "DECREASE_REFCOUNT( %s )" % self.getCodeObject()
  58. def __repr__( self ):
  59. return "<Identifier %s (%d)>" % ( self.code, self.ref_count )
  60. def isConstantIdentifier( self ):
  61. return False
  62. class ConstantIdentifier( Identifier ):
  63. def __init__( self, constant_code, constant_value ):
  64. Identifier.__init__( self, constant_code, 0 )
  65. self.constant_value = constant_value
  66. def __repr__( self ):
  67. return "<ConstantIdentifier %s>" % self.code
  68. def isConstantIdentifier( self ):
  69. return True
  70. def getCheapRefCount( self ):
  71. return 0
  72. def getConstant( self ):
  73. return self.constant_value
  74. class SpecialConstantIdentifier( ConstantIdentifier ):
  75. def __init__( self, constant_value ):
  76. if constant_value is None:
  77. ConstantIdentifier.__init__( self, "Py_None", None )
  78. elif constant_value is True:
  79. ConstantIdentifier.__init__( self, "Py_True", True )
  80. elif constant_value is False:
  81. ConstantIdentifier.__init__( self, "Py_False", False )
  82. elif constant_value is Ellipsis:
  83. ConstantIdentifier.__init__( self, "Py_Ellipsis", Ellipsis )
  84. else:
  85. assert False, constant_value
  86. class EmptyDictIdentifier( Identifier ):
  87. def __init__( self ):
  88. Identifier.__init__( self, "PyDict_New()", 1 )
  89. def getCheapRefCount( self ):
  90. return 1
  91. def isConstantIdentifier( self ):
  92. return True
  93. def getConstant( self ):
  94. return {}
  95. class ModuleVariableIdentifier:
  96. def __init__( self, var_name, module_code_name ):
  97. self.var_name = var_name
  98. self.module_code_name = module_code_name
  99. def isConstantIdentifier( self ):
  100. return False
  101. def __repr__( self ):
  102. return "<ModuleVariableIdentifier %s>" % self.var_name
  103. def getRefCount( self ):
  104. return 0
  105. def getCheapRefCount( self ):
  106. # The asObject0 is the fastest way, stealing a reference directly from the module
  107. # dictionary if possible.
  108. return 0
  109. def getCodeTemporaryRef( self ):
  110. return "_mvar_%s_%s.asObject0()" % ( self.module_code_name, self.var_name )
  111. def getCodeExportRef( self ):
  112. return "_mvar_%s_%s.asObject()" % ( self.module_code_name, self.var_name )
  113. def getCodeDropRef( self ):
  114. return self.getCodeTemporaryRef()
  115. def getCode( self ):
  116. return "_mvar_%s_%s" % ( self.module_code_name, encodeNonAscii( self.var_name ) )
  117. class MaybeModuleVariableIdentifier( Identifier ):
  118. def __init__( self, var_name, module_code_name ):
  119. Identifier.__init__(
  120. self,
  121. "_mvar_%s_%s.asObject0( locals_dict.asObject() )" % (
  122. module_code_name,
  123. var_name
  124. ),
  125. 0
  126. )
  127. class LocalVariableIdentifier:
  128. def __init__( self, var_name, from_context = False ):
  129. assert type( var_name ) == str
  130. self.from_context = from_context
  131. self.var_name = var_name
  132. def isConstantIdentifier( self ):
  133. return False
  134. def __repr__( self ):
  135. return "<LocalVariableIdentifier %s>" % self.var_name
  136. def getCode( self ):
  137. if not self.from_context:
  138. return "_python_var_" + encodeNonAscii( self.var_name )
  139. else:
  140. return "_python_context->python_var_" + encodeNonAscii( self.var_name )
  141. def getRefCount( self ):
  142. return 0
  143. def getCheapRefCount( self ):
  144. return 0
  145. def getCodeObject( self ):
  146. return "%s.asObject()" % self.getCode()
  147. def getCodeTemporaryRef( self ):
  148. return "%s.asObject()" % self.getCode()
  149. def getCodeExportRef( self ):
  150. return "%s.asObject1()" % self.getCode()
  151. def getCodeDropRef( self ):
  152. return self.getCodeTemporaryRef()
  153. class TempVariableIdentifier( Identifier ):
  154. def __init__( self, var_name ):
  155. self.tempvar_name = var_name
  156. Identifier.__init__( self, "_python_tmp_" + var_name, 0 )
  157. def __repr__( self ):
  158. return "<TempVariableIdentifier %s >" % self.tempvar_name
  159. def getCheapRefCount( self ):
  160. return 0
  161. def getCodeObject( self ):
  162. return "%s.asObject()" % self.getCode()
  163. def getClass( self ):
  164. return "PyObjectTemporary"
  165. class TempObjectIdentifier( Identifier ):
  166. def __init__( self, var_name ):
  167. self.tempvar_name = var_name
  168. Identifier.__init__( self, "_python_tmp_" + var_name, 0 )
  169. def getCodeTemporaryRef( self ):
  170. return self.code
  171. class ClosureVariableIdentifier( Identifier ):
  172. def __init__( self, var_name, from_context ):
  173. assert type( from_context ) is str
  174. self.var_name = var_name
  175. self.from_context = from_context
  176. if self.from_context:
  177. Identifier.__init__( self, self.from_context + "python_closure_" + encodeNonAscii( self.var_name ), 0 )
  178. else:
  179. # TODO: Use a variable object to decide naming policy
  180. Identifier.__init__( self, "python_closure_" + encodeNonAscii( self.var_name ), 0 )
  181. def __repr__( self ):
  182. return "<ClosureVariableIdentifier %s >" % self.var_name
  183. def getCheapRefCount( self ):
  184. return 0
  185. def getCodeObject( self ):
  186. return self.getCode() + ".asObject()"
  187. class DefaultValueIdentifier( Identifier ):
  188. def __init__( self, count ):
  189. Identifier.__init__(
  190. self,
  191. code = "PyTuple_GET_ITEM( self->m_defaults, %d )" % count,
  192. ref_count = 0
  193. )
  194. def getCheapRefCount( self ):
  195. return 0
  196. class NullIdentifier( Identifier ):
  197. def __init__( self ):
  198. Identifier.__init__(
  199. self,
  200. code = "NULL",
  201. ref_count = 0
  202. )
  203. def getCodeExportRef( self ):
  204. return "NULL"
  205. class ThrowingIdentifier( Identifier ):
  206. def __init__( self, code ):
  207. Identifier.__init__(
  208. self,
  209. code = code,
  210. ref_count = 0
  211. )
  212. def getCodeExportRef( self ):
  213. return self.getCodeObject()
  214. def getCodeTemporaryRef( self ):
  215. return self.getCodeObject()
  216. def getCheapRefCount( self ):
  217. return 0
  218. class CallIdentifier( Identifier ):
  219. def __init__( self, called, args ):
  220. Identifier.__init__(
  221. self,
  222. code = "%s( %s )" % (
  223. called,
  224. ", ".join( args )
  225. ),
  226. ref_count = 1
  227. )
  228. class HelperCallIdentifier( CallIdentifier ):
  229. def __init__( self, helper, *args ):
  230. CallIdentifier.__init__(
  231. self,
  232. called = helper,
  233. args = [
  234. arg.getCodeTemporaryRef() if arg is not None else "NULL"
  235. for arg in
  236. args
  237. ]
  238. )
  239. def getCodeTemporaryRefs( identifiers ):
  240. """ Helper to create temporary reference code of many identifiers at once.
  241. """
  242. return [ identifier.getCodeTemporaryRef() for identifier in identifiers ]
  243. def getCodeExportRefs( identifiers ):
  244. """ Helper to create export reference code of many identifiers at once.
  245. """
  246. return [ identifier.getCodeExportRef() for identifier in identifiers ]