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 unary operations. 19 20Some of these come from built-ins, e.g. abs, some from syntax, and repr from both. 21""" 22from nuitka import PythonOperators 23 24from .ConstantRefNodes import makeConstantRefNode 25from .ExpressionBases import ExpressionChildHavingBase 26from .shapes.BuiltinTypeShapes import tshape_bool, tshape_str 27 28 29class ExpressionOperationUnaryBase(ExpressionChildHavingBase): 30 named_child = "operand" 31 32 __slots__ = ("operator", "simulator") 33 34 def __init__(self, operand, source_ref): 35 ExpressionChildHavingBase.__init__(self, value=operand, source_ref=source_ref) 36 37 def getOperator(self): 38 return self.operator 39 40 def computeExpression(self, trace_collection): 41 operator = self.getOperator() 42 operand = self.subnode_operand 43 44 if operand.isCompileTimeConstant(): 45 operand_value = operand.getCompileTimeConstant() 46 47 return trace_collection.getCompileTimeComputationResult( 48 node=self, 49 computation=lambda: self.simulator(operand_value), 50 description="Operator '%s' with constant argument." % operator, 51 ) 52 else: 53 # TODO: May go down to MemoryError for compile time constant overflow 54 # ones. 55 trace_collection.onExceptionRaiseExit(BaseException) 56 57 # The value of that node escapes and could change its contents. 58 # TODO: The unary operations don't do much to an operator, add 59 # methods, that don't do stuff on common types though. 60 # trace_collection.onValueEscapeSomeUnaryOperator(operand) 61 62 # Any code could be run, note that. 63 trace_collection.onControlFlowEscape(self) 64 65 return self, None, None 66 67 @staticmethod 68 def isExpressionOperationUnary(): 69 return True 70 71 72class ExpressionOperationUnaryRepr(ExpressionOperationUnaryBase): 73 """Python unary operator `x` and repr built-in.""" 74 75 kind = "EXPRESSION_OPERATION_UNARY_REPR" 76 77 operator = "Repr" 78 79 __slots__ = ("escape_desc",) 80 81 def __init__(self, operand, source_ref): 82 assert operand.isExpression(), operand 83 84 ExpressionOperationUnaryBase.__init__( 85 self, operand=operand, source_ref=source_ref 86 ) 87 88 self.escape_desc = None 89 90 def computeExpression(self, trace_collection): 91 result, self.escape_desc = self.subnode_operand.computeExpressionOperationRepr( 92 repr_node=self, trace_collection=trace_collection 93 ) 94 95 return result 96 97 def mayRaiseException(self, exception_type): 98 # TODO: Match getExceptionExit() more precisely against exception type given 99 return ( 100 self.escape_desc is None 101 or self.escape_desc.getExceptionExit() is not None 102 or self.subnode_operand.mayRaiseException(exception_type) 103 ) 104 105 def mayRaiseExceptionBool(self, exception_type): 106 return False 107 108 def mayHaveSideEffects(self): 109 operand = self.subnode_operand 110 111 if operand.mayHaveSideEffects(): 112 return True 113 114 return self.escape_desc is None or self.escape_desc.isControlFlowEscape() 115 116 @staticmethod 117 def getTypeShape(): 118 # Even unicode gets decoded in Python2 119 return tshape_str 120 121 122class ExpressionOperationUnarySub(ExpressionOperationUnaryBase): 123 """Python unary operator -""" 124 125 kind = "EXPRESSION_OPERATION_UNARY_SUB" 126 127 operator = "USub" 128 simulator = PythonOperators.unary_operator_functions[operator] 129 130 def __init__(self, operand, source_ref): 131 assert operand.isExpression(), operand 132 133 ExpressionOperationUnaryBase.__init__( 134 self, operand=operand, source_ref=source_ref 135 ) 136 137 138class ExpressionOperationUnaryAdd(ExpressionOperationUnaryBase): 139 """Python unary operator +""" 140 141 kind = "EXPRESSION_OPERATION_UNARY_ADD" 142 143 operator = "UAdd" 144 simulator = PythonOperators.unary_operator_functions[operator] 145 146 def __init__(self, operand, source_ref): 147 assert operand.isExpression(), operand 148 149 ExpressionOperationUnaryBase.__init__( 150 self, operand=operand, source_ref=source_ref 151 ) 152 153 154class ExpressionOperationUnaryInvert(ExpressionOperationUnaryBase): 155 """Python unary operator ~""" 156 157 kind = "EXPRESSION_OPERATION_UNARY_INVERT" 158 159 operator = "Invert" 160 simulator = PythonOperators.unary_operator_functions[operator] 161 162 def __init__(self, operand, source_ref): 163 assert operand.isExpression(), operand 164 165 ExpressionOperationUnaryBase.__init__( 166 self, operand=operand, source_ref=source_ref 167 ) 168 169 170class ExpressionOperationNot(ExpressionOperationUnaryBase): 171 kind = "EXPRESSION_OPERATION_NOT" 172 173 operator = "Not" 174 simulator = PythonOperators.unary_operator_functions[operator] 175 176 def __init__(self, operand, source_ref): 177 ExpressionOperationUnaryBase.__init__( 178 self, operand=operand, source_ref=source_ref 179 ) 180 181 @staticmethod 182 def getTypeShape(): 183 return tshape_bool 184 185 def computeExpression(self, trace_collection): 186 return self.subnode_operand.computeExpressionOperationNot( 187 not_node=self, trace_collection=trace_collection 188 ) 189 190 def mayRaiseException(self, exception_type): 191 return self.subnode_operand.mayRaiseException( 192 exception_type 193 ) or self.subnode_operand.mayRaiseExceptionBool(exception_type) 194 195 def mayRaiseExceptionBool(self, exception_type): 196 return self.subnode_operand.mayRaiseExceptionBool(exception_type) 197 198 def getTruthValue(self): 199 result = self.subnode_operand.getTruthValue() 200 201 # Need to invert the truth value of operand of course here. 202 return None if result is None else not result 203 204 def mayHaveSideEffects(self): 205 operand = self.subnode_operand 206 207 if operand.mayHaveSideEffects(): 208 return True 209 210 return operand.mayHaveSideEffectsBool() 211 212 def mayHaveSideEffectsBool(self): 213 return self.subnode_operand.mayHaveSideEffectsBool() 214 215 def extractSideEffects(self): 216 operand = self.subnode_operand 217 218 # TODO: Find the common ground of these, and make it an expression 219 # method. 220 if operand.isExpressionMakeSequence(): 221 return operand.extractSideEffects() 222 223 if operand.isExpressionMakeDict(): 224 return operand.extractSideEffects() 225 226 return (self,) 227 228 229class ExpressionOperationUnaryAbs(ExpressionOperationUnaryBase): 230 kind = "EXPRESSION_OPERATION_UNARY_ABS" 231 232 operator = "Abs" 233 simulator = PythonOperators.unary_operator_functions[operator] 234 235 def __init__(self, operand, source_ref): 236 ExpressionOperationUnaryBase.__init__( 237 self, operand=operand, source_ref=source_ref 238 ) 239 240 def computeExpression(self, trace_collection): 241 return self.subnode_operand.computeExpressionAbs( 242 abs_node=self, trace_collection=trace_collection 243 ) 244 245 def mayRaiseException(self, exception_type): 246 operand = self.subnode_operand 247 248 if operand.mayRaiseException(exception_type): 249 return True 250 251 return operand.mayRaiseExceptionAbs(exception_type) 252 253 def mayHaveSideEffects(self): 254 operand = self.subnode_operand 255 256 if operand.mayHaveSideEffects(): 257 return True 258 259 return operand.mayHaveSideEffectsAbs() 260 261 262def makeExpressionOperationUnary(operator, operand, source_ref): 263 if operator == "Repr": 264 unary_class = ExpressionOperationUnaryRepr 265 elif operator == "USub": 266 unary_class = ExpressionOperationUnarySub 267 elif operator == "UAdd": 268 unary_class = ExpressionOperationUnaryAdd 269 elif operator == "Invert": 270 unary_class = ExpressionOperationUnaryInvert 271 else: 272 assert False, operand 273 274 # Shortcut these unary operations, avoiding "-1", etc. to ever become one. 275 if operand.isCompileTimeConstant(): 276 try: 277 constant = unary_class.simulator(operand.getCompileTimeConstant()) 278 except Exception: # Catch all the things, pylint: disable=broad-except 279 # Compile time detectable error, postpone these, so they get traced. 280 pass 281 else: 282 return makeConstantRefNode( 283 constant=constant, 284 source_ref=source_ref, 285 user_provided=getattr(operand, "user_provided", False), 286 ) 287 288 return unary_class(operand=operand, source_ref=source_ref) 289