1# Copyright 2021, 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""" Nodes that build containers. 19 20""" 21 22import functools 23from abc import abstractmethod 24 25from nuitka.PythonVersions import needsSetLiteralReverseInsertion 26 27from .ConstantRefNodes import ( 28 ExpressionConstantListEmptyRef, 29 ExpressionConstantSetEmptyRef, 30 ExpressionConstantTupleEmptyRef, 31 makeConstantRefNode, 32) 33from .ExpressionBases import ExpressionChildHavingBase 34from .IterationHandles import ListAndTupleContainerMakingIterationHandle 35from .NodeBases import SideEffectsFromChildrenMixin 36from .NodeMakingHelpers import ( 37 getComputationResult, 38 makeStatementOnlyNodesFromExpressions, 39 wrapExpressionWithSideEffects, 40) 41from .shapes.BuiltinTypeShapes import tshape_list, tshape_set, tshape_tuple 42 43 44class ExpressionMakeSequenceBase( 45 SideEffectsFromChildrenMixin, ExpressionChildHavingBase 46): 47 __slots__ = ("sequence_kind",) 48 49 named_child = "elements" 50 51 def __init__(self, sequence_kind, elements, source_ref): 52 assert elements 53 54 for element in elements: 55 assert element.isExpression(), element 56 57 self.sequence_kind = sequence_kind.lower() 58 59 ExpressionChildHavingBase.__init__( 60 self, value=tuple(elements), source_ref=source_ref 61 ) 62 63 @staticmethod 64 def isExpressionMakeSequence(): 65 return True 66 67 @abstractmethod 68 def getSimulator(self): 69 """The simulator for the container making, for overload.""" 70 71 def computeExpression(self, trace_collection): 72 elements = self.subnode_elements 73 74 for count, element in enumerate(elements): 75 if element.willRaiseException(BaseException): 76 result = wrapExpressionWithSideEffects( 77 side_effects=elements[:count], new_node=element, old_node=self 78 ) 79 80 return result, "new_raise", "Sequence creation raises exception" 81 82 for element in elements: 83 if not element.isCompileTimeConstant(): 84 return self, None, None 85 86 simulator = self.getSimulator() 87 assert simulator is not None 88 89 return getComputationResult( 90 node=self, 91 computation=lambda: simulator( 92 element.getCompileTimeConstant() for element in elements 93 ), 94 description="%s with constant arguments." % simulator.__name__.title(), 95 user_provided=True, 96 ) 97 98 @staticmethod 99 def mayHaveSideEffectsBool(): 100 return False 101 102 def isKnownToBeIterable(self, count): 103 return count is None or count == len(self.subnode_elements) 104 105 def isKnownToBeIterableAtMin(self, count): 106 return count <= len(self.subnode_elements) 107 108 def getIterationValue(self, count): 109 return self.subnode_elements[count] 110 111 def getIterationValueRange(self, start, stop): 112 return self.subnode_elements[start:stop] 113 114 @staticmethod 115 def canPredictIterationValues(): 116 return True 117 118 def getIterationValues(self): 119 return self.subnode_elements 120 121 def getIterationHandle(self): 122 return ListAndTupleContainerMakingIterationHandle(self.subnode_elements) 123 124 @staticmethod 125 def getTruthValue(): 126 return True 127 128 def mayRaiseException(self, exception_type): 129 for element in self.subnode_elements: 130 if element.mayRaiseException(exception_type): 131 return True 132 133 return False 134 135 def computeExpressionDrop(self, statement, trace_collection): 136 result = makeStatementOnlyNodesFromExpressions( 137 expressions=self.subnode_elements 138 ) 139 140 del self.parent 141 142 return ( 143 result, 144 "new_statements", 145 """\ 146Removed sequence creation for unused sequence.""", 147 ) 148 149 def onContentEscapes(self, trace_collection): 150 for element in self.subnode_elements: 151 element.onContentEscapes(trace_collection) 152 153 154def makeExpressionMakeTuple(elements, source_ref): 155 if elements: 156 return ExpressionMakeTuple(elements, source_ref) 157 else: 158 # TODO: Get rid of user provided for empty tuple refs, makes no sense. 159 return ExpressionConstantTupleEmptyRef( 160 user_provided=False, source_ref=source_ref 161 ) 162 163 164def makeExpressionMakeTupleOrConstant(elements, user_provided, source_ref): 165 for element in elements: 166 # TODO: Compile time constant ought to be the criterion. 167 if not element.isExpressionConstantRef(): 168 result = makeExpressionMakeTuple(elements, source_ref) 169 break 170 else: 171 result = makeConstantRefNode( 172 constant=tuple(element.getCompileTimeConstant() for element in elements), 173 user_provided=user_provided, 174 source_ref=source_ref, 175 ) 176 177 if elements: 178 result.setCompatibleSourceReference( 179 source_ref=elements[-1].getCompatibleSourceReference() 180 ) 181 182 return result 183 184 185class ExpressionMakeTuple(ExpressionMakeSequenceBase): 186 kind = "EXPRESSION_MAKE_TUPLE" 187 188 def __init__(self, elements, source_ref): 189 ExpressionMakeSequenceBase.__init__( 190 self, sequence_kind="TUPLE", elements=elements, source_ref=source_ref 191 ) 192 193 @staticmethod 194 def getTypeShape(): 195 return tshape_tuple 196 197 @staticmethod 198 def getSimulator(): 199 return tuple 200 201 def getIterationLength(self): 202 return len(self.subnode_elements) 203 204 205def makeExpressionMakeList(elements, source_ref): 206 if elements: 207 return ExpressionMakeList(elements, source_ref) 208 else: 209 # TODO: Get rid of user provided for empty list refs, makes no sense. 210 return ExpressionConstantListEmptyRef( 211 user_provided=False, source_ref=source_ref 212 ) 213 214 215def makeExpressionMakeListOrConstant(elements, user_provided, source_ref): 216 assert type(elements) is list 217 218 for element in elements: 219 # TODO: Compile time constant ought to be the criterion. 220 if not element.isExpressionConstantRef(): 221 result = makeExpressionMakeList(elements, source_ref) 222 break 223 else: 224 result = makeConstantRefNode( 225 constant=[element.getCompileTimeConstant() for element in elements], 226 user_provided=user_provided, 227 source_ref=source_ref, 228 ) 229 230 if elements: 231 result.setCompatibleSourceReference( 232 source_ref=elements[-1].getCompatibleSourceReference() 233 ) 234 235 return result 236 237 238class ExpressionMakeList(ExpressionMakeSequenceBase): 239 kind = "EXPRESSION_MAKE_LIST" 240 241 def __init__(self, elements, source_ref): 242 ExpressionMakeSequenceBase.__init__( 243 self, sequence_kind="LIST", elements=elements, source_ref=source_ref 244 ) 245 246 @staticmethod 247 def getTypeShape(): 248 return tshape_list 249 250 @staticmethod 251 def getSimulator(): 252 return list 253 254 def getIterationLength(self): 255 return len(self.subnode_elements) 256 257 def computeExpressionIter1(self, iter_node, trace_collection): 258 result = ExpressionMakeTuple( 259 elements=self.subnode_elements, source_ref=self.source_ref 260 ) 261 262 self.parent.replaceChild(self, result) 263 del self.parent 264 265 return ( 266 iter_node, 267 "new_expression", 268 """\ 269Iteration over list lowered to iteration over tuple.""", 270 ) 271 272 273class ExpressionMakeSet(ExpressionMakeSequenceBase): 274 kind = "EXPRESSION_MAKE_SET" 275 276 def __init__(self, elements, source_ref): 277 ExpressionMakeSequenceBase.__init__( 278 self, sequence_kind="SET", elements=elements, source_ref=source_ref 279 ) 280 281 @staticmethod 282 def getTypeShape(): 283 return tshape_set 284 285 @staticmethod 286 def getSimulator(): 287 return set 288 289 def getIterationLength(self): 290 element_count = len(self.subnode_elements) 291 292 # Hashing and equality may consume elements of the produced set. 293 if element_count >= 2: 294 return None 295 else: 296 return element_count 297 298 @staticmethod 299 def getIterationMinLength(): 300 # Hashing and equality may consume elements of the produced set. 301 return 1 302 303 def mayRaiseException(self, exception_type): 304 for element in self.subnode_elements: 305 if not element.isKnownToBeHashable(): 306 return True 307 308 if element.mayRaiseException(exception_type): 309 return True 310 311 return False 312 313 def computeExpressionIter1(self, iter_node, trace_collection): 314 result = ExpressionMakeTuple( 315 elements=self.subnode_elements, source_ref=self.source_ref 316 ) 317 318 self.parent.replaceChild(self, result) 319 del self.parent 320 321 return ( 322 iter_node, 323 "new_expression", 324 """\ 325Iteration over set lowered to iteration over tuple.""", 326 ) 327 328 329needs_set_literal_reverse = needsSetLiteralReverseInsertion() 330 331 332def makeExpressionMakeSetLiteral(elements, source_ref): 333 if elements: 334 if needs_set_literal_reverse: 335 return ExpressionMakeSetLiteral(elements, source_ref) 336 else: 337 return ExpressionMakeSet(elements, source_ref) 338 else: 339 # TODO: Get rid of user provided for empty set refs, makes no sense. 340 return ExpressionConstantSetEmptyRef(user_provided=False, source_ref=source_ref) 341 342 343@functools.wraps(set) 344def reversed_set(value): 345 return set(reversed(tuple(value))) 346 347 348def makeExpressionMakeSetLiteralOrConstant(elements, user_provided, source_ref): 349 for element in elements: 350 # TODO: Compile time constant ought to be the criterion. 351 if not element.isExpressionConstantRef(): 352 result = makeExpressionMakeSetLiteral(elements, source_ref) 353 break 354 else: 355 # Need to reverse now if needed. 356 if needs_set_literal_reverse: 357 elements = tuple(reversed(elements)) 358 359 result = makeConstantRefNode( 360 constant=set(element.getCompileTimeConstant() for element in elements), 361 user_provided=user_provided, 362 source_ref=source_ref, 363 ) 364 365 if elements: 366 result.setCompatibleSourceReference( 367 source_ref=elements[-1].getCompatibleSourceReference() 368 ) 369 370 return result 371 372 373class ExpressionMakeSetLiteral(ExpressionMakeSet): 374 kind = "EXPRESSION_MAKE_SET_LITERAL" 375 376 @staticmethod 377 def getSimulator(): 378 return reversed_set 379