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 for statements. 19 20""" 21 22from .NodeBases import StatementBase, StatementChildHavingBase 23 24 25def checkStatements(value): 26 """Check that statements list value property. 27 28 Must not be None, must not contain None, and of course only statements, 29 may be empty. 30 """ 31 32 assert value is not None 33 assert None not in value 34 35 for statement in value: 36 assert ( 37 statement.isStatement() or statement.isStatementsFrame() 38 ), statement.asXmlText() 39 40 return tuple(value) 41 42 43class StatementsSequence(StatementChildHavingBase): 44 kind = "STATEMENTS_SEQUENCE" 45 46 named_child = "statements" 47 48 checker = checkStatements 49 50 def __init__(self, statements, source_ref): 51 StatementChildHavingBase.__init__( 52 self, value=tuple(statements), source_ref=source_ref 53 ) 54 55 def finalize(self): 56 del self.parent 57 58 for s in self.subnode_statements: 59 s.finalize() 60 61 # Overloading name based automatic check, so that derived ones know it too. 62 def isStatementsSequence(self): 63 # Virtual method, pylint: disable=no-self-use 64 65 return True 66 67 def trimStatements(self, statement): 68 assert statement.parent is self 69 70 old_statements = list(self.subnode_statements) 71 assert statement in old_statements, (statement, self) 72 73 new_statements = old_statements[: old_statements.index(statement) + 1] 74 75 self.setChild("statements", new_statements) 76 77 def removeStatement(self, statement): 78 assert statement.parent is self 79 80 statements = list(self.subnode_statements) 81 statements.remove(statement) 82 self.setChild("statements", statements) 83 84 if statements: 85 return self 86 else: 87 return None 88 89 def replaceStatement(self, statement, statements): 90 old_statements = list(self.subnode_statements) 91 92 merge_index = old_statements.index(statement) 93 94 new_statements = ( 95 tuple(old_statements[:merge_index]) 96 + tuple(statements) 97 + tuple(old_statements[merge_index + 1 :]) 98 ) 99 100 self.setChild("statements", new_statements) 101 102 def mayHaveSideEffects(self): 103 # Statement sequences have a side effect if one of the statements does. 104 for statement in self.subnode_statements: 105 if statement.mayHaveSideEffects(): 106 return True 107 return False 108 109 def mayRaiseException(self, exception_type): 110 for statement in self.subnode_statements: 111 if statement.mayRaiseException(exception_type): 112 return True 113 return False 114 115 def needsFrame(self): 116 for statement in self.subnode_statements: 117 if statement.needsFrame(): 118 return True 119 return False 120 121 def mayReturn(self): 122 for statement in self.subnode_statements: 123 if statement.mayReturn(): 124 return True 125 return False 126 127 def mayBreak(self): 128 for statement in self.subnode_statements: 129 if statement.mayBreak(): 130 return True 131 return False 132 133 def mayContinue(self): 134 for statement in self.subnode_statements: 135 if statement.mayContinue(): 136 return True 137 return False 138 139 def mayRaiseExceptionOrAbort(self, exception_type): 140 return ( 141 self.mayRaiseException(exception_type) 142 or self.mayReturn() 143 or self.mayBreak() 144 or self.mayContinue() 145 ) 146 147 def isStatementAborting(self): 148 return self.subnode_statements[-1].isStatementAborting() 149 150 def computeStatement(self, trace_collection): 151 # Don't want to be called like this. 152 assert False, self 153 154 def computeStatementsSequence(self, trace_collection): 155 new_statements = [] 156 157 statements = self.subnode_statements 158 assert statements, self 159 160 for count, statement in enumerate(statements): 161 # May be frames embedded. 162 if statement.isStatementsFrame(): 163 new_statement = statement.computeStatementsSequence(trace_collection) 164 else: 165 new_statement = trace_collection.onStatement(statement=statement) 166 167 if new_statement is not None: 168 if ( 169 new_statement.isStatementsSequence() 170 and not new_statement.isStatementsFrame() 171 ): 172 new_statements.extend(new_statement.subnode_statements) 173 else: 174 new_statements.append(new_statement) 175 176 if ( 177 statement is not statements[-1] 178 and new_statement.isStatementAborting() 179 ): 180 trace_collection.signalChange( 181 "new_statements", 182 statements[count + 1].getSourceReference(), 183 "Removed dead statements.", 184 ) 185 186 for s in statements[statements.index(statement) + 1 :]: 187 s.finalize() 188 189 break 190 191 if statements != new_statements: 192 if new_statements: 193 self.setChild("statements", new_statements) 194 195 return self 196 else: 197 return None 198 else: 199 return self 200 201 @staticmethod 202 def getStatementNiceName(): 203 return "statements sequence" 204 205 206class StatementExpressionOnly(StatementChildHavingBase): 207 kind = "STATEMENT_EXPRESSION_ONLY" 208 209 named_child = "expression" 210 211 def __init__(self, expression, source_ref): 212 assert expression.isExpression() 213 214 StatementChildHavingBase.__init__(self, value=expression, source_ref=source_ref) 215 216 def mayHaveSideEffects(self): 217 return self.subnode_expression.mayHaveSideEffects() 218 219 def mayRaiseException(self, exception_type): 220 return self.subnode_expression.mayRaiseException(exception_type) 221 222 def computeStatement(self, trace_collection): 223 expression = trace_collection.onExpression(expression=self.subnode_expression) 224 225 if expression.mayRaiseException(BaseException): 226 trace_collection.onExceptionRaiseExit(BaseException) 227 228 result, change_tags, change_desc = expression.computeExpressionDrop( 229 statement=self, trace_collection=trace_collection 230 ) 231 232 if result is not self: 233 return result, change_tags, change_desc 234 235 return self, None, None 236 237 @staticmethod 238 def getStatementNiceName(): 239 return "expression only statement" 240 241 def getDetailsForDisplay(self): 242 return {"expression": self.subnode_expression.kind} 243 244 245class StatementPreserveFrameException(StatementBase): 246 kind = "STATEMENT_PRESERVE_FRAME_EXCEPTION" 247 248 __slots__ = ("preserver_id",) 249 250 def __init__(self, preserver_id, source_ref): 251 StatementBase.__init__(self, source_ref=source_ref) 252 253 self.preserver_id = preserver_id 254 255 def finalize(self): 256 del self.parent 257 258 def getDetails(self): 259 return {"preserver_id": self.preserver_id} 260 261 def getPreserverId(self): 262 return self.preserver_id 263 264 def computeStatement(self, trace_collection): 265 # For Python2 generators, it's not necessary to preserve, the frame 266 # decides it. TODO: This check makes only sense once. 267 268 if self.getParentStatementsFrame().needsExceptionFramePreservation(): 269 return self, None, None 270 else: 271 return ( 272 None, 273 "new_statements", 274 "Removed frame preservation for generators.", 275 ) 276 277 @staticmethod 278 def mayRaiseException(exception_type): 279 return False 280 281 @staticmethod 282 def needsFrame(): 283 return True 284 285 286class StatementRestoreFrameException(StatementBase): 287 kind = "STATEMENT_RESTORE_FRAME_EXCEPTION" 288 289 __slots__ = ("preserver_id",) 290 291 def __init__(self, preserver_id, source_ref): 292 StatementBase.__init__(self, source_ref=source_ref) 293 294 self.preserver_id = preserver_id 295 296 def finalize(self): 297 del self.parent 298 299 def getDetails(self): 300 return {"preserver_id": self.preserver_id} 301 302 def getPreserverId(self): 303 return self.preserver_id 304 305 def computeStatement(self, trace_collection): 306 return self, None, None 307 308 @staticmethod 309 def mayRaiseException(exception_type): 310 return False 311 312 313class StatementPublishException(StatementBase): 314 kind = "STATEMENT_PUBLISH_EXCEPTION" 315 316 def __init__(self, source_ref): 317 StatementBase.__init__(self, source_ref=source_ref) 318 319 def finalize(self): 320 del self.parent 321 322 def computeStatement(self, trace_collection): 323 # TODO: Determine the need for it. 324 return self, None, None 325 326 @staticmethod 327 def mayRaiseException(exception_type): 328 return False 329