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 concern with exec and eval builtins. 19 20These are the dynamic codes, and as such rather difficult. We would like 21to eliminate or limit their impact as much as possible, but it's difficult 22to do. 23""" 24 25from nuitka.PythonVersions import python_version 26 27from .ExpressionBases import ExpressionChildrenHavingBase 28from .NodeBases import StatementChildHavingBase, StatementChildrenHavingBase 29from .NodeMakingHelpers import ( 30 convertNoneConstantToNone, 31 makeStatementOnlyNodesFromExpressions, 32) 33 34 35class ExpressionBuiltinEval(ExpressionChildrenHavingBase): 36 kind = "EXPRESSION_BUILTIN_EVAL" 37 38 named_children = ("source", "globals_arg", "locals_arg") 39 40 def __init__(self, source_code, globals_arg, locals_arg, source_ref): 41 ExpressionChildrenHavingBase.__init__( 42 self, 43 values={ 44 "source": source_code, 45 "globals_arg": globals_arg, 46 "locals_arg": locals_arg, 47 }, 48 source_ref=source_ref, 49 ) 50 51 def computeExpression(self, trace_collection): 52 # TODO: Attempt for constant values to do it. 53 return self, None, None 54 55 56# Note: Python3 only so far. 57if python_version >= 0x300: 58 59 class ExpressionBuiltinExec(ExpressionBuiltinEval): 60 kind = "EXPRESSION_BUILTIN_EXEC" 61 62 def __init__(self, source_code, globals_arg, locals_arg, source_ref): 63 ExpressionBuiltinEval.__init__( 64 self, 65 source_code=source_code, 66 globals_arg=globals_arg, 67 locals_arg=locals_arg, 68 source_ref=source_ref, 69 ) 70 71 def computeExpression(self, trace_collection): 72 # TODO: Attempt for constant values to do it. 73 return self, None, None 74 75 def computeExpressionDrop(self, statement, trace_collection): 76 if self.getParentVariableProvider().isEarlyClosure(): 77 result = StatementExec( 78 source_code=self.subnode_source, 79 globals_arg=self.subnode_globals_arg, 80 locals_arg=self.subnode_locals_arg, 81 source_ref=self.source_ref, 82 ) 83 84 del self.parent 85 86 return ( 87 result, 88 "new_statements", 89 """\ 90Replaced built-in exec call to exec statement in early closure context.""", 91 ) 92 else: 93 return statement, None, None 94 95 96# Note: Python2 only 97if python_version < 0x300: 98 99 class ExpressionBuiltinExecfile(ExpressionBuiltinEval): 100 kind = "EXPRESSION_BUILTIN_EXECFILE" 101 102 named_children = ("source", "globals_arg", "locals_arg") 103 104 def __init__(self, source_code, globals_arg, locals_arg, source_ref): 105 ExpressionBuiltinEval.__init__( 106 self, 107 source_code=source_code, 108 globals_arg=globals_arg, 109 locals_arg=locals_arg, 110 source_ref=source_ref, 111 ) 112 113 def computeExpressionDrop(self, statement, trace_collection): 114 # In this case, the copy-back must be done and will only be done 115 # correctly by the code for exec statements. 116 provider = self.getParentVariableProvider() 117 118 if provider.isExpressionClassBody(): 119 result = StatementExec( 120 source_code=self.subnode_source, 121 globals_arg=self.subnode_globals_arg, 122 locals_arg=self.subnode_locals_arg, 123 source_ref=self.source_ref, 124 ) 125 126 del self.parent 127 128 return ( 129 result, 130 "new_statements", 131 """\ 132Changed 'execfile' with unused result to 'exec' on class level.""", 133 ) 134 else: 135 return statement, None, None 136 137 138class StatementExec(StatementChildrenHavingBase): 139 kind = "STATEMENT_EXEC" 140 141 named_children = ("source", "globals_arg", "locals_arg") 142 143 def __init__(self, source_code, globals_arg, locals_arg, source_ref): 144 StatementChildrenHavingBase.__init__( 145 self, 146 values={ 147 "globals_arg": globals_arg, 148 "locals_arg": locals_arg, 149 "source": source_code, 150 }, 151 source_ref=source_ref, 152 ) 153 154 def setChild(self, name, value): 155 if name in ("globals_arg", "locals_arg"): 156 value = convertNoneConstantToNone(value) 157 158 return StatementChildrenHavingBase.setChild(self, name, value) 159 160 def computeStatement(self, trace_collection): 161 source_code = trace_collection.onExpression(expression=self.subnode_source) 162 163 if source_code.mayRaiseException(BaseException): 164 trace_collection.onExceptionRaiseExit(BaseException) 165 166 if source_code.willRaiseException(BaseException): 167 result = source_code 168 169 return ( 170 result, 171 "new_raise", 172 """\ 173Exec statement raises implicitly when determining source code argument.""", 174 ) 175 176 globals_arg = trace_collection.onExpression( 177 expression=self.subnode_globals_arg, allow_none=True 178 ) 179 180 if globals_arg is not None and globals_arg.mayRaiseException(BaseException): 181 trace_collection.onExceptionRaiseExit(BaseException) 182 183 if globals_arg is not None and globals_arg.willRaiseException(BaseException): 184 result = makeStatementOnlyNodesFromExpressions( 185 expressions=(source_code, globals_arg) 186 ) 187 188 return ( 189 result, 190 "new_raise", 191 """\ 192Exec statement raises implicitly when determining globals argument.""", 193 ) 194 195 locals_arg = trace_collection.onExpression( 196 expression=self.subnode_locals_arg, allow_none=True 197 ) 198 199 if locals_arg is not None and locals_arg.mayRaiseException(BaseException): 200 trace_collection.onExceptionRaiseExit(BaseException) 201 202 if locals_arg is not None and locals_arg.willRaiseException(BaseException): 203 result = makeStatementOnlyNodesFromExpressions( 204 expressions=(source_code, globals_arg, locals_arg) 205 ) 206 207 return ( 208 result, 209 "new_raise", 210 """\ 211Exec statement raises implicitly when determining locals argument.""", 212 ) 213 214 trace_collection.onExceptionRaiseExit(BaseException) 215 216 str_value = self.subnode_source.getStrValue() 217 218 if str_value is not None: 219 # TODO: This needs to be re-done. 220 # TODO: Don't forget to consider side effects of source code, 221 # locals_arg, and globals_arg. 222 return self, None, None 223 224 # exec_body = ... 225 # return (exec_body, "new_statements", "In-lined constant exec statement.") 226 227 return self, None, None 228 229 230class StatementLocalsDictSync(StatementChildHavingBase): 231 kind = "STATEMENT_LOCALS_DICT_SYNC" 232 233 named_child = "locals_arg" 234 235 __slots__ = ("locals_scope", "previous_traces", "variable_traces") 236 237 def __init__(self, locals_scope, locals_arg, source_ref): 238 StatementChildHavingBase.__init__(self, value=locals_arg, source_ref=source_ref) 239 240 self.previous_traces = None 241 self.variable_traces = None 242 243 self.locals_scope = locals_scope 244 245 def getDetails(self): 246 return {"locals_scope": self.locals_scope} 247 248 def getPreviousVariablesTraces(self): 249 return self.previous_traces 250 251 def computeStatement(self, trace_collection): 252 result, change_tags, change_desc = self.computeStatementSubExpressions( 253 trace_collection=trace_collection 254 ) 255 256 if result is not self: 257 return result, change_tags, change_desc 258 259 provider = self.getParentVariableProvider() 260 if provider.isCompiledPythonModule(): 261 return None, "new_statements", "Removed sync back to locals without locals." 262 263 self.previous_traces = trace_collection.onLocalsUsage(self.locals_scope) 264 if not self.previous_traces: 265 return None, "new_statements", "Removed sync back to locals without locals." 266 267 trace_collection.removeAllKnowledge() 268 self.variable_traces = trace_collection.onLocalsUsage(self.locals_scope) 269 270 return self, None, None 271 272 @staticmethod 273 def mayRaiseException(exception_type): 274 return False 275 276 277class ExpressionBuiltinCompile(ExpressionChildrenHavingBase): 278 kind = "EXPRESSION_BUILTIN_COMPILE" 279 280 named_children = ("source", "filename", "mode", "flags", "dont_inherit", "optimize") 281 282 def __init__( 283 self, source_code, filename, mode, flags, dont_inherit, optimize, source_ref 284 ): 285 ExpressionChildrenHavingBase.__init__( 286 self, 287 values={ 288 "source": source_code, 289 "filename": filename, 290 "mode": mode, 291 "flags": flags, 292 "dont_inherit": dont_inherit, 293 "optimize": optimize, 294 }, 295 source_ref=source_ref, 296 ) 297 298 def computeExpression(self, trace_collection): 299 trace_collection.onExceptionRaiseExit(BaseException) 300 301 # TODO: Attempt for constant values to do it. 302 return self, None, None 303