/nuitka/codegen/Identifiers.py
Python | 340 lines | 312 code | 4 blank | 24 comment | 2 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
20These are generally the means to effectively hide the reference count. The best part
21is where getCheapRefCount tries to not allocate references not needed.
22"""
23
24# The method signatures do not always require usage of self, sometimes can be decided
25# based on class. pylint: disable=R0201
26
27from nuitka import Utils
28
29def encodeNonAscii( var_name ):
30 if Utils.python_version < 300:
31 return var_name
32 else:
33 var_name = var_name.encode( "ascii", "xmlcharrefreplace" )
34
35 return var_name.decode( "ascii" ).replace( "&#", "$$" ).replace( ";", "" )
36
37class Identifier:
38 def __init__( self, code, ref_count ):
39 self.code = code
40 self.ref_count = ref_count
41
42 def getRefCount( self ):
43 return self.ref_count
44
45 def getCheapRefCount( self ):
46 return self.ref_count
47
48 def getCode( self ):
49 return self.code
50
51 def getCodeObject( self ):
52 return self.code
53
54 def getCodeExportRef( self ):
55 if self.getRefCount():
56 return self.getCodeObject()
57 else:
58 return "INCREASE_REFCOUNT( %s )" % self.getCodeObject()
59
60 def getCodeTemporaryRef( self ):
61 if self.getRefCount():
62 return "PyObjectTemporary( %s ).asObject()" % self.getCodeObject()
63 else:
64 return self.getCodeObject()
65
66 def getCodeDropRef( self ):
67 if self.ref_count == 0:
68 return self.getCodeTemporaryRef()
69 else:
70 return "DECREASE_REFCOUNT( %s )" % self.getCodeObject()
71
72 def __repr__( self ):
73 return "<Identifier %s (%d)>" % ( self.code, self.ref_count )
74
75 def isConstantIdentifier( self ):
76 return False
77
78
79class ConstantIdentifier( Identifier ):
80 def __init__( self, constant_code, constant_value ):
81 Identifier.__init__( self, constant_code, 0 )
82
83 self.constant_value = constant_value
84
85 def __repr__( self ):
86 return "<ConstantIdentifier %s>" % self.code
87
88 def isConstantIdentifier( self ):
89 return True
90
91 def getCheapRefCount( self ):
92 return 0
93
94 def getConstant( self ):
95 return self.constant_value
96
97
98class SpecialConstantIdentifier( ConstantIdentifier ):
99 def __init__( self, constant_value ):
100 if constant_value is None:
101 ConstantIdentifier.__init__( self, "Py_None", None )
102 elif constant_value is True:
103 ConstantIdentifier.__init__( self, "Py_True", True )
104 elif constant_value is False:
105 ConstantIdentifier.__init__( self, "Py_False", False )
106 elif constant_value is Ellipsis:
107 ConstantIdentifier.__init__( self, "Py_Ellipsis", Ellipsis )
108 else:
109 assert False, constant_value
110
111
112class EmptyDictIdentifier( Identifier ):
113 def __init__( self ):
114 Identifier.__init__( self, "PyDict_New()", 1 )
115
116 def getCheapRefCount( self ):
117 return 1
118
119 def isConstantIdentifier( self ):
120 return True
121
122 def getConstant( self ):
123 return {}
124
125
126class ModuleVariableIdentifier:
127 def __init__( self, var_name, module_code_name ):
128 self.var_name = var_name
129 self.module_code_name = module_code_name
130
131 def isConstantIdentifier( self ):
132 return False
133
134 def __repr__( self ):
135 return "<ModuleVariableIdentifier %s>" % self.var_name
136
137 def getRefCount( self ):
138 return 0
139
140 def getCheapRefCount( self ):
141 # The asObject0 is the fastest way, stealing a reference directly from the module
142 # dictionary if possible.
143 return 0
144
145 def getCodeTemporaryRef( self ):
146 return "_mvar_%s_%s.asObject0()" % ( self.module_code_name, self.var_name )
147
148 def getCodeExportRef( self ):
149 return "_mvar_%s_%s.asObject()" % ( self.module_code_name, self.var_name )
150
151 def getCodeDropRef( self ):
152 return self.getCodeTemporaryRef()
153
154 def getCode( self ):
155 return "_mvar_%s_%s" % ( self.module_code_name, encodeNonAscii( self.var_name ) )
156
157
158class MaybeModuleVariableIdentifier( Identifier ):
159 def __init__( self, var_name, module_code_name ):
160 Identifier.__init__(
161 self,
162 "_mvar_%s_%s.asObject0( locals_dict.asObject() )" % (
163 module_code_name,
164 var_name
165 ),
166 0
167 )
168
169
170class LocalVariableIdentifier:
171 def __init__( self, var_name, from_context = False ):
172 assert type( var_name ) == str
173
174 self.from_context = from_context
175 self.var_name = var_name
176
177 def isConstantIdentifier( self ):
178 return False
179
180 def __repr__( self ):
181 return "<LocalVariableIdentifier %s>" % self.var_name
182
183 def getCode( self ):
184 if not self.from_context:
185 return "_python_var_" + encodeNonAscii( self.var_name )
186 else:
187 return "_python_context->python_var_" + encodeNonAscii( self.var_name )
188
189 def getRefCount( self ):
190 return 0
191
192 def getCheapRefCount( self ):
193 return 0
194
195 def getCodeObject( self ):
196 return "%s.asObject()" % self.getCode()
197
198 def getCodeTemporaryRef( self ):
199 return "%s.asObject()" % self.getCode()
200
201 def getCodeExportRef( self ):
202 return "%s.asObject1()" % self.getCode()
203
204 def getCodeDropRef( self ):
205 return self.getCodeTemporaryRef()
206
207
208class TempVariableIdentifier( Identifier ):
209 def __init__( self, var_name ):
210 self.tempvar_name = var_name
211
212 Identifier.__init__( self, "_python_tmp_" + var_name, 0 )
213
214 def __repr__( self ):
215 return "<TempVariableIdentifier %s >" % self.tempvar_name
216
217 def getCheapRefCount( self ):
218 return 0
219
220 def getCodeObject( self ):
221 return "%s.asObject()" % self.getCode()
222
223 def getClass( self ):
224 return "PyObjectTemporary"
225
226
227class TempObjectIdentifier( Identifier ):
228 def __init__( self, var_name ):
229 self.tempvar_name = var_name
230
231 Identifier.__init__( self, "_python_tmp_" + var_name, 0 )
232
233 def getCodeTemporaryRef( self ):
234 return self.code
235
236
237class ClosureVariableIdentifier( Identifier ):
238 def __init__( self, var_name, from_context ):
239 assert type( from_context ) is str
240
241 self.var_name = var_name
242 self.from_context = from_context
243
244 if self.from_context:
245 Identifier.__init__( self, self.from_context + "python_closure_" + encodeNonAscii( self.var_name ), 0 )
246 else:
247 # TODO: Use a variable object to decide naming policy
248
249 Identifier.__init__( self, "python_closure_" + encodeNonAscii( self.var_name ), 0 )
250
251 def __repr__( self ):
252 return "<ClosureVariableIdentifier %s >" % self.var_name
253
254 def getCheapRefCount( self ):
255 return 0
256
257 def getCodeObject( self ):
258 return self.getCode() + ".asObject()"
259
260
261class DefaultValueIdentifier( Identifier ):
262 def __init__( self, count ):
263 Identifier.__init__(
264 self,
265 code = "PyTuple_GET_ITEM( self->m_defaults, %d )" % count,
266 ref_count = 0
267 )
268
269 def getCheapRefCount( self ):
270 return 0
271
272
273class NullIdentifier( Identifier ):
274 def __init__( self ):
275 Identifier.__init__(
276 self,
277 code = "NULL",
278 ref_count = 0
279 )
280
281 def getCodeExportRef( self ):
282 return "NULL"
283
284
285class ThrowingIdentifier( Identifier ):
286 def __init__( self, code ):
287 Identifier.__init__(
288 self,
289 code = code,
290 ref_count = 0
291 )
292
293 def getCodeExportRef( self ):
294 return self.getCodeObject()
295
296 def getCodeTemporaryRef( self ):
297 return self.getCodeObject()
298
299 def getCheapRefCount( self ):
300 return 0
301
302
303class CallIdentifier( Identifier ):
304 def __init__( self, called, args ):
305 Identifier.__init__(
306 self,
307 code = "%s( %s )" % (
308 called,
309 ", ".join( args )
310 ),
311 ref_count = 1
312 )
313
314
315class HelperCallIdentifier( CallIdentifier ):
316 def __init__( self, helper, *args ):
317 CallIdentifier.__init__(
318 self,
319 called = helper,
320 args = [
321 arg.getCodeTemporaryRef() if arg is not None else "NULL"
322 for arg in
323 args
324 ]
325 )
326
327
328def getCodeTemporaryRefs( identifiers ):
329 """ Helper to create temporary reference code of many identifiers at once.
330
331 """
332
333 return [ identifier.getCodeTemporaryRef() for identifier in identifiers ]
334
335def getCodeExportRefs( identifiers ):
336 """ Helper to create export reference code of many identifiers at once.
337
338 """
339
340 return [ identifier.getCodeExportRef() for identifier in identifiers ]