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""" Globals/locals/single arg dir nodes 19 20These nodes give access to variables, highly problematic, because using them, 21the code may change or access anything about them, so nothing can be trusted 22anymore, if we start to not know where their value goes. 23 24The "dir()" call without arguments is reformulated to locals or globals calls. 25""" 26 27from .ConstantRefNodes import makeConstantRefNode 28from .DictionaryNodes import ExpressionKeyValuePair, makeExpressionMakeDict 29from .ExpressionBases import ExpressionBase, ExpressionBuiltinSingleArgBase 30from .VariableRefNodes import ExpressionTempVariableRef, ExpressionVariableRef 31 32 33class ExpressionBuiltinGlobals(ExpressionBase): 34 kind = "EXPRESSION_BUILTIN_GLOBALS" 35 36 def __init__(self, source_ref): 37 ExpressionBase.__init__(self, source_ref=source_ref) 38 39 def finalize(self): 40 del self.parent 41 42 def computeExpressionRaw(self, trace_collection): 43 return self, None, None 44 45 @staticmethod 46 def mayHaveSideEffects(): 47 return False 48 49 @staticmethod 50 def mayRaiseException(exception_type): 51 return False 52 53 54class ExpressionBuiltinLocalsBase(ExpressionBase): 55 # Base classes can be abstract, pylint: disable=abstract-method 56 57 __slots__ = ("variable_traces", "locals_scope") 58 59 def __init__(self, locals_scope, source_ref): 60 ExpressionBase.__init__(self, source_ref=source_ref) 61 62 self.variable_traces = None 63 self.locals_scope = locals_scope 64 65 def finalize(self): 66 del self.locals_scope 67 del self.variable_traces 68 69 def mayHaveSideEffects(self): 70 return False 71 72 def mayRaiseException(self, exception_type): 73 return False 74 75 def getVariableTraces(self): 76 return self.variable_traces 77 78 def getLocalsScope(self): 79 return self.locals_scope 80 81 82class ExpressionBuiltinLocalsUpdated(ExpressionBuiltinLocalsBase): 83 kind = "EXPRESSION_BUILTIN_LOCALS_UPDATED" 84 85 def __init__(self, locals_scope, source_ref): 86 ExpressionBuiltinLocalsBase.__init__( 87 self, locals_scope=locals_scope, source_ref=source_ref 88 ) 89 90 assert locals_scope is not None 91 92 def computeExpressionRaw(self, trace_collection): 93 # Just inform the collection that all escaped. 94 self.variable_traces = trace_collection.onLocalsUsage(self.locals_scope) 95 96 trace_collection.onLocalsDictEscaped(self.locals_scope) 97 98 return self, None, None 99 100 101class ExpressionBuiltinLocalsRef(ExpressionBuiltinLocalsBase): 102 kind = "EXPRESSION_BUILTIN_LOCALS_REF" 103 104 def __init__(self, locals_scope, source_ref): 105 ExpressionBuiltinLocalsBase.__init__( 106 self, locals_scope=locals_scope, source_ref=source_ref 107 ) 108 109 def getLocalsScope(self): 110 return self.locals_scope 111 112 def computeExpressionRaw(self, trace_collection): 113 if self.locals_scope.isMarkedForPropagation(): 114 result = makeExpressionMakeDict( 115 pairs=( 116 ExpressionKeyValuePair( 117 key=makeConstantRefNode( 118 constant=variable_name, source_ref=self.source_ref 119 ), 120 value=ExpressionTempVariableRef( 121 variable=variable, source_ref=self.source_ref 122 ), 123 source_ref=self.source_ref, 124 ) 125 for variable_name, variable in self.locals_scope.getPropagationVariables().items() 126 ), 127 source_ref=self.source_ref, 128 ) 129 130 new_result = result.computeExpressionRaw(trace_collection) 131 132 assert new_result[0] is result 133 134 self.finalize() 135 136 return result, "new_expression", "Propagated locals dictionary reference." 137 138 # Just inform the collection that all escaped unless it is abortative. 139 if not self.getParent().isStatementReturn(): 140 trace_collection.onLocalsUsage(locals_scope=self.locals_scope) 141 142 return self, None, None 143 144 145class ExpressionBuiltinLocalsCopy(ExpressionBuiltinLocalsBase): 146 kind = "EXPRESSION_BUILTIN_LOCALS_COPY" 147 148 def computeExpressionRaw(self, trace_collection): 149 # Just inform the collection that all escaped. 150 151 self.variable_traces = trace_collection.onLocalsUsage( 152 locals_scope=self.locals_scope 153 ) 154 155 for variable, variable_trace in self.variable_traces: 156 if ( 157 not variable_trace.mustHaveValue() 158 and not variable_trace.mustNotHaveValue() 159 ): 160 return self, None, None 161 162 # Other locals elsewhere. 163 if variable_trace.getNameUsageCount() > 1: 164 return self, None, None 165 166 pairs = [] 167 for variable, variable_trace in self.variable_traces: 168 if variable_trace.mustHaveValue(): 169 pairs.append( 170 ExpressionKeyValuePair( 171 key=makeConstantRefNode( 172 constant=variable.getName(), 173 user_provided=True, 174 source_ref=self.source_ref, 175 ), 176 value=ExpressionVariableRef( 177 variable=variable, source_ref=self.source_ref 178 ), 179 source_ref=self.source_ref, 180 ) 181 ) 182 183 # Locals is sorted of course. 184 def _sorted(pairs): 185 names = [ 186 variable.getName() 187 for variable in self.locals_scope.getProvidedVariables() 188 ] 189 190 return tuple( 191 sorted( 192 pairs, 193 key=lambda pair: names.index( 194 pair.subnode_key.getCompileTimeConstant() 195 ), 196 ) 197 ) 198 199 result = makeExpressionMakeDict( 200 pairs=_sorted(pairs), source_ref=self.source_ref 201 ) 202 203 return result, "new_expression", "Statically predicted locals dictionary." 204 205 206class ExpressionBuiltinDir1(ExpressionBuiltinSingleArgBase): 207 kind = "EXPRESSION_BUILTIN_DIR1" 208 209 def computeExpression(self, trace_collection): 210 # TODO: Quite some cases should be possible to predict and this 211 # should be using a slot, with "__dir__" being overloaded or not. 212 213 # Any code could be run, note that. 214 trace_collection.onControlFlowEscape(self) 215 216 # Any exception may be raised. 217 trace_collection.onExceptionRaiseExit(BaseException) 218 219 return self, None, None 220