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""" Slice nodes. 19 20Slices are important when working with lists. Tracking them can allow to 21achieve more compact code, or predict results at compile time. 22 23There will be a method "computeExpressionSlice" to aid predicting them. 24""" 25 26from nuitka.PythonVersions import python_version 27from nuitka.specs import BuiltinParameterSpecs 28 29from .ConstantRefNodes import ExpressionConstantNoneRef, makeConstantRefNode 30from .ExpressionBases import ( 31 ExpressionChildHavingBase, 32 ExpressionChildrenHavingBase, 33 ExpressionSpecBasedComputationNoRaiseMixin, 34) 35from .NodeBases import ( 36 SideEffectsFromChildrenMixin, 37 StatementChildrenHavingBase, 38) 39from .NodeMakingHelpers import ( 40 convertNoneConstantToNone, 41 makeStatementExpressionOnlyReplacementNode, 42 makeStatementOnlyNodesFromExpressions, 43 wrapExpressionWithSideEffects, 44) 45from .shapes.BuiltinTypeShapes import tshape_slice 46 47 48class StatementAssignmentSlice(StatementChildrenHavingBase): 49 kind = "STATEMENT_ASSIGNMENT_SLICE" 50 51 named_children = ("source", "expression", "lower", "upper") 52 53 def __init__(self, expression, lower, upper, source, source_ref): 54 assert python_version < 0x300 55 56 StatementChildrenHavingBase.__init__( 57 self, 58 values={ 59 "source": source, 60 "expression": expression, 61 "lower": lower, 62 "upper": upper, 63 }, 64 source_ref=source_ref, 65 ) 66 67 def computeStatement(self, trace_collection): 68 source = trace_collection.onExpression(self.subnode_source) 69 70 # No assignment will occur, if the assignment source raises, so strip it 71 # away. 72 if source.willRaiseException(BaseException): 73 result = makeStatementExpressionOnlyReplacementNode( 74 expression=source, node=self 75 ) 76 77 return ( 78 result, 79 "new_raise", 80 """\ 81Slice assignment raises exception in assigned value, removed assignment.""", 82 ) 83 84 lookup_source = trace_collection.onExpression(self.subnode_expression) 85 86 if lookup_source.willRaiseException(BaseException): 87 result = makeStatementOnlyNodesFromExpressions( 88 expressions=(source, lookup_source) 89 ) 90 91 return ( 92 result, 93 "new_raise", 94 """\ 95Slice assignment raises exception in sliced value, removed assignment.""", 96 ) 97 98 lower = trace_collection.onExpression(self.subnode_lower, allow_none=True) 99 100 if lower is not None and lower.willRaiseException(BaseException): 101 result = makeStatementOnlyNodesFromExpressions( 102 expressions=(source, lookup_source, lower) 103 ) 104 105 return ( 106 result, 107 "new_raise", 108 """\ 109Slice assignment raises exception in lower slice boundary value, removed \ 110assignment.""", 111 ) 112 113 upper = trace_collection.onExpression(self.subnode_upper, allow_none=True) 114 115 if upper is not None and upper.willRaiseException(BaseException): 116 result = makeStatementOnlyNodesFromExpressions( 117 expressions=(source, lookup_source, lower, upper) 118 ) 119 120 return ( 121 result, 122 "new_raise", 123 """\ 124Slice assignment raises exception in upper slice boundary value, removed \ 125assignment.""", 126 ) 127 128 return lookup_source.computeExpressionSetSlice( 129 set_node=self, 130 lower=lower, 131 upper=upper, 132 value_node=source, 133 trace_collection=trace_collection, 134 ) 135 136 137class StatementDelSlice(StatementChildrenHavingBase): 138 kind = "STATEMENT_DEL_SLICE" 139 140 named_children = ("expression", "lower", "upper") 141 142 def __init__(self, expression, lower, upper, source_ref): 143 StatementChildrenHavingBase.__init__( 144 self, 145 values={"expression": expression, "lower": lower, "upper": upper}, 146 source_ref=source_ref, 147 ) 148 149 def computeStatement(self, trace_collection): 150 lookup_source = trace_collection.onExpression(self.subnode_expression) 151 152 if lookup_source.willRaiseException(BaseException): 153 result = makeStatementExpressionOnlyReplacementNode( 154 expression=lookup_source, node=self 155 ) 156 157 return ( 158 result, 159 "new_raise", 160 """\ 161Slice del raises exception in sliced value, removed del""", 162 ) 163 164 lower = trace_collection.onExpression(self.subnode_lower, allow_none=True) 165 166 if lower is not None and lower.willRaiseException(BaseException): 167 result = makeStatementOnlyNodesFromExpressions( 168 expressions=(lookup_source, lower) 169 ) 170 171 return ( 172 result, 173 "new_raise", 174 """ 175Slice del raises exception in lower slice boundary value, removed del""", 176 ) 177 178 trace_collection.onExpression(self.subnode_upper, allow_none=True) 179 upper = self.subnode_upper 180 181 if upper is not None and upper.willRaiseException(BaseException): 182 result = makeStatementOnlyNodesFromExpressions( 183 expressions=(lookup_source, lower, upper) 184 ) 185 186 return ( 187 result, 188 "new_raise", 189 """ 190Slice del raises exception in upper slice boundary value, removed del""", 191 ) 192 193 return lookup_source.computeExpressionDelSlice( 194 set_node=self, lower=lower, upper=upper, trace_collection=trace_collection 195 ) 196 197 198class ExpressionSliceLookup(ExpressionChildrenHavingBase): 199 kind = "EXPRESSION_SLICE_LOOKUP" 200 201 named_children = ("expression", "lower", "upper") 202 203 checkers = {"upper": convertNoneConstantToNone, "lower": convertNoneConstantToNone} 204 205 def __init__(self, expression, lower, upper, source_ref): 206 assert python_version < 0x300 207 208 ExpressionChildrenHavingBase.__init__( 209 self, 210 values={"expression": expression, "upper": upper, "lower": lower}, 211 source_ref=source_ref, 212 ) 213 214 def computeExpression(self, trace_collection): 215 lookup_source = self.subnode_expression 216 217 return lookup_source.computeExpressionSlice( 218 lookup_node=self, 219 lower=self.subnode_lower, 220 upper=self.subnode_upper, 221 trace_collection=trace_collection, 222 ) 223 224 @staticmethod 225 def isKnownToBeIterable(count): 226 # TODO: Should ask SliceRegistry 227 return None 228 229 230def makeExpressionBuiltinSlice(start, stop, step, source_ref): 231 if ( 232 (start is None or start.isCompileTimeConstant()) 233 and (stop is None or stop.isCompileTimeConstant()) 234 and (step is None or step.isCompileTimeConstant()) 235 ): 236 # Avoid going slices for what is effectively constant. 237 238 start_value = None if start is None else start.getCompileTimeConstant() 239 stop_value = None if stop is None else stop.getCompileTimeConstant() 240 step_value = None if step is None else step.getCompileTimeConstant() 241 242 return makeConstantRefNode( 243 constant=slice(start_value, stop_value, step_value), source_ref=source_ref 244 ) 245 246 if start is None and step is None: 247 return ExpressionBuiltinSlice1(stop=stop, source_ref=source_ref) 248 249 if start is None: 250 start = ExpressionConstantNoneRef(source_ref=source_ref) 251 if stop is None: 252 stop = ExpressionConstantNoneRef(source_ref=source_ref) 253 254 if step is None: 255 return ExpressionBuiltinSlice2(start=start, stop=stop, source_ref=source_ref) 256 257 return ExpressionBuiltinSlice3( 258 start=start, stop=stop, step=step, source_ref=source_ref 259 ) 260 261 262class ExpressionBuiltinSliceMixin(SideEffectsFromChildrenMixin): 263 # Mixins are required to slots 264 __slots__ = () 265 266 builtin_spec = BuiltinParameterSpecs.builtin_slice_spec 267 268 @staticmethod 269 def getTypeShape(): 270 return tshape_slice 271 272 @staticmethod 273 def isKnownToBeIterable(count): 274 # Virtual method provided by mixin, pylint: disable=unused-argument 275 276 # Definitely not iterable at all 277 return False 278 279 def mayHaveSideEffects(self): 280 return self.mayRaiseException(BaseException) 281 282 283class ExpressionBuiltinSlice3( 284 ExpressionBuiltinSliceMixin, 285 ExpressionSpecBasedComputationNoRaiseMixin, 286 ExpressionChildrenHavingBase, 287): 288 kind = "EXPRESSION_BUILTIN_SLICE3" 289 290 named_children = ("start", "stop", "step") 291 292 def __init__(self, start, stop, step, source_ref): 293 ExpressionChildrenHavingBase.__init__( 294 self, 295 values={"start": start, "stop": stop, "step": step}, 296 source_ref=source_ref, 297 ) 298 299 def computeExpression(self, trace_collection): 300 if ( 301 self.subnode_step.isExpressionConstantNoneRef() 302 or self.subnode_step.getIndexValue() == 1 303 ): 304 return trace_collection.computedExpressionResult( 305 wrapExpressionWithSideEffects( 306 old_node=self, 307 new_node=ExpressionBuiltinSlice2( 308 start=self.subnode_start, 309 stop=self.subnode_stop, 310 source_ref=self.source_ref, 311 ), 312 side_effects=self.subnode_step.extractSideEffects(), 313 ), 314 "new_expression", 315 "Reduce 3 argument slice object creation to two argument form.", 316 ) 317 318 return self.computeBuiltinSpec( 319 trace_collection=trace_collection, 320 given_values=(self.subnode_start, self.subnode_stop, self.subnode_step), 321 ) 322 323 def mayRaiseException(self, exception_type): 324 return ( 325 self.subnode_start.mayRaiseException(exception_type) 326 or self.subnode_stop.mayRaiseException(exception_type) 327 or self.subnode_step.mayRaiseException(exception_type) 328 ) 329 330 331class ExpressionBuiltinSlice2( 332 ExpressionBuiltinSliceMixin, 333 ExpressionSpecBasedComputationNoRaiseMixin, 334 ExpressionChildrenHavingBase, 335): 336 kind = "EXPRESSION_BUILTIN_SLICE2" 337 338 named_children = ("start", "stop") 339 340 def __init__(self, start, stop, source_ref): 341 ExpressionChildrenHavingBase.__init__( 342 self, 343 values={"start": start, "stop": stop}, 344 source_ref=source_ref, 345 ) 346 347 def computeExpression(self, trace_collection): 348 if self.subnode_start.isExpressionConstantNoneRef(): 349 return trace_collection.computedExpressionResult( 350 wrapExpressionWithSideEffects( 351 old_node=self, 352 new_node=ExpressionBuiltinSlice1( 353 stop=self.subnode_stop, source_ref=self.source_ref 354 ), 355 side_effects=self.subnode_start.extractSideEffects(), 356 ), 357 "new_expression", 358 "Reduce 2 argument slice object creation to single argument form.", 359 ) 360 361 return self.computeBuiltinSpec( 362 trace_collection=trace_collection, 363 given_values=(self.subnode_start, self.subnode_stop), 364 ) 365 366 def mayRaiseException(self, exception_type): 367 return self.subnode_start.mayRaiseException( 368 exception_type 369 ) or self.subnode_stop.mayRaiseException(exception_type) 370 371 372class ExpressionBuiltinSlice1( 373 ExpressionBuiltinSliceMixin, 374 ExpressionSpecBasedComputationNoRaiseMixin, 375 ExpressionChildHavingBase, 376): 377 kind = "EXPRESSION_BUILTIN_SLICE1" 378 379 named_child = "stop" 380 381 def __init__(self, stop, source_ref): 382 ExpressionChildHavingBase.__init__( 383 self, 384 value=stop, 385 source_ref=source_ref, 386 ) 387 388 def computeExpression(self, trace_collection): 389 return self.computeBuiltinSpec( 390 trace_collection=trace_collection, 391 given_values=(self.subnode_stop,), 392 ) 393 394 def mayRaiseException(self, exception_type): 395 return self.subnode_stop.mayRaiseException(exception_type) 396