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 functions and their creations. 19 20Lambdas are functions too. The functions are at the core of the language and 21have their complexities. 22 23Creating a CPython function object is an optional thing. Some things might 24only be used to be called directly, while knowing exactly what it is. So 25the "ExpressionFunctionCreation" might be used to provide that kind of 26CPython reference, and may escape. 27 28Coroutines and generators live in their dedicated module and share base 29classes. 30""" 31 32import inspect 33 34from nuitka import Options, Variables 35from nuitka.Constants import isMutable 36from nuitka.optimizations.TraceCollections import ( 37 TraceCollectionPureFunction, 38 withChangeIndicationsTo, 39) 40from nuitka.PythonVersions import python_version 41from nuitka.specs.ParameterSpecs import ( 42 ParameterSpec, 43 TooManyArguments, 44 matchCall, 45) 46from nuitka.Tracing import optimization_logger 47from nuitka.tree.Extractions import updateVariableUsage 48from nuitka.tree.TreeHelpers import makeDictCreationOrConstant2 49 50from .Checkers import checkStatementsSequenceOrNone 51from .CodeObjectSpecs import CodeObjectSpec 52from .ExpressionBases import ( 53 ExpressionBase, 54 ExpressionChildHavingBase, 55 ExpressionChildrenHavingBase, 56) 57from .FutureSpecs import fromFlags 58from .IndicatorMixins import ( 59 EntryPointMixin, 60 MarkUnoptimizedFunctionIndicatorMixin, 61) 62from .LocalsScopes import getLocalsDictHandle 63from .NodeBases import ( 64 ClosureGiverNodeMixin, 65 ClosureTakerMixin, 66 SideEffectsFromChildrenMixin, 67) 68from .NodeMakingHelpers import ( 69 makeRaiseExceptionReplacementExpressionFromInstance, 70 wrapExpressionWithSideEffects, 71) 72from .shapes.BuiltinTypeShapes import tshape_function 73 74_is_verbose = Options.isVerbose() 75 76 77class MaybeLocalVariableUsage(Exception): 78 pass 79 80 81class ExpressionFunctionBodyBase( 82 ClosureTakerMixin, ClosureGiverNodeMixin, ExpressionChildHavingBase 83): 84 # TODO: The code_prefix should be a class attribute instead. 85 __slots__ = ( 86 "provider", 87 "taken", 88 "name", 89 "code_prefix", 90 "code_name", 91 "uids", 92 "temp_variables", 93 "temp_scopes", 94 "preserver_id", 95 "flags", 96 ) 97 98 if python_version >= 0x340: 99 __slots__ += ("qualname_provider",) 100 101 if python_version >= 0x300: 102 __slots__ += ("non_local_declarations",) 103 104 named_child = "body" 105 106 checker = checkStatementsSequenceOrNone 107 108 def __init__(self, provider, name, body, code_prefix, flags, source_ref): 109 while provider.isExpressionOutlineBody(): 110 provider = provider.getParentVariableProvider() 111 112 ExpressionChildHavingBase.__init__( 113 self, 114 value=body, # Might be None initially in some cases. 115 source_ref=source_ref, 116 ) 117 118 ClosureTakerMixin.__init__(self, provider=provider) 119 120 ClosureGiverNodeMixin.__init__(self, name=name, code_prefix=code_prefix) 121 122 # Special things, "has_super" indicates presence of "super" in variable 123 # usage, which modifies some behaviors. 124 self.flags = flags or None 125 126 # Hack: This allows some APIs to work although this is not yet 127 # officially a child yet. Important during building. 128 self.parent = provider 129 130 # Python3.4: Might be overridden by global statement on the class name. 131 # TODO: Make this class only code. 132 if python_version >= 0x340: 133 self.qualname_provider = provider 134 135 # Non-local declarations. 136 if python_version >= 0x300: 137 self.non_local_declarations = None 138 139 @staticmethod 140 def isExpressionFunctionBodyBase(): 141 return True 142 143 def getEntryPoint(self): 144 """Entry point for code. 145 146 Normally ourselves. Only outlines will refer to their parent which 147 technically owns them. 148 149 """ 150 151 return self 152 153 def getContainingClassDictCreation(self): 154 current = self 155 156 while not current.isCompiledPythonModule(): 157 if current.isExpressionClassBody(): 158 return current 159 160 current = current.getParentVariableProvider() 161 162 return None 163 164 def hasFlag(self, flag): 165 return self.flags is not None and flag in self.flags 166 167 def discardFlag(self, flag): 168 if self.flags is not None: 169 self.flags.discard(flag) 170 171 @staticmethod 172 def isEarlyClosure(): 173 """Early closure taking means immediate binding of references. 174 175 Normally it's good to lookup name references immediately, but not for 176 functions. In case of a function body it is not allowed to do that, 177 because a later assignment needs to be queried first. Nodes need to 178 indicate via this if they would like to resolve references at the same 179 time as assignments. 180 """ 181 182 return False 183 184 def getLocalsScope(self): 185 return self.locals_scope 186 187 # TODO: Dubious function doing to distinct things, should be moved to users. 188 def hasVariableName(self, variable_name): 189 return ( 190 self.locals_scope.hasProvidedVariable(variable_name) 191 or variable_name in self.temp_variables 192 ) 193 194 def getProvidedVariables(self): 195 if self.locals_scope is not None: 196 return self.locals_scope.getProvidedVariables() 197 else: 198 return () 199 200 def getLocalVariables(self): 201 return [ 202 variable 203 for variable in self.getProvidedVariables() 204 if variable.isLocalVariable() 205 ] 206 207 def getUserLocalVariables(self): 208 return [ 209 variable 210 for variable in self.getProvidedVariables() 211 if variable.isLocalVariable() and not variable.isParameterVariable() 212 if variable.getOwner() is self 213 ] 214 215 def getOutlineLocalVariables(self): 216 result = [] 217 218 outlines = self.getTraceCollection().getOutlineFunctions() 219 220 if outlines is None: 221 return result 222 223 for outline in outlines: 224 result.extend(outline.getUserLocalVariables()) 225 226 return result 227 228 def removeClosureVariable(self, variable): 229 # Do not remove parameter variables of ours. 230 assert not variable.isParameterVariable() or variable.getOwner() is not self 231 232 self.locals_scope.unregisterClosureVariable(variable) 233 234 self.taken.remove(variable) 235 236 self.code_object.removeFreeVarname(variable.getName()) 237 238 def demoteClosureVariable(self, variable): 239 assert variable.isLocalVariable() 240 241 self.taken.remove(variable) 242 243 assert variable.getOwner() is not self 244 245 new_variable = Variables.LocalVariable( 246 owner=self, variable_name=variable.getName() 247 ) 248 for variable_trace in variable.traces: 249 if variable_trace.getOwner() is self: 250 new_variable.addTrace(variable_trace) 251 new_variable.updateUsageState() 252 253 self.locals_scope.unregisterClosureVariable(variable) 254 self.locals_scope.registerProvidedVariable(new_variable) 255 256 updateVariableUsage( 257 provider=self, old_variable=variable, new_variable=new_variable 258 ) 259 260 def hasClosureVariable(self, variable): 261 return variable in self.taken 262 263 def getVariableForAssignment(self, variable_name): 264 # print("ASS func", self, variable_name) 265 266 if self.hasTakenVariable(variable_name): 267 result = self.getTakenVariable(variable_name) 268 else: 269 result = self.getProvidedVariable(variable_name) 270 271 return result 272 273 def getVariableForReference(self, variable_name): 274 # print( "REF func", self, variable_name ) 275 276 if self.hasProvidedVariable(variable_name): 277 result = self.getProvidedVariable(variable_name) 278 else: 279 result = self.getClosureVariable(variable_name=variable_name) 280 281 # Remember that we need that closure variable for something, so 282 # we don't create it again all the time. 283 if not result.isModuleVariable(): 284 self.locals_scope.registerClosureVariable(result) 285 286 entry_point = self.getEntryPoint() 287 288 # For "exec" containing/star import containing, we raise this exception to indicate 289 # that instead of merely a variable, to be assigned, we need to replace with locals 290 # dict access. 291 if ( 292 python_version < 0x300 293 and not entry_point.isExpressionClassBody() 294 and not entry_point.isPythonMainModule() 295 and result.isModuleVariable() 296 and entry_point.isUnoptimized() 297 ): 298 raise MaybeLocalVariableUsage 299 300 return result 301 302 def getVariableForClosure(self, variable_name): 303 # print( "getVariableForClosure", self.getCodeName(), variable_name, self.isUnoptimized() ) 304 305 if self.hasProvidedVariable(variable_name): 306 return self.getProvidedVariable(variable_name) 307 308 return self.takeVariableForClosure(variable_name) 309 310 def takeVariableForClosure(self, variable_name): 311 result = self.provider.getVariableForClosure(variable_name) 312 self.taken.add(result) 313 return result 314 315 def createProvidedVariable(self, variable_name): 316 # print("createProvidedVariable", self, variable_name) 317 318 assert self.locals_scope, self 319 320 return self.locals_scope.getLocalVariable( 321 variable_name=variable_name, owner=self 322 ) 323 324 def addNonlocalsDeclaration(self, names, user_provided, source_ref): 325 """Add a nonlocal declared name. 326 327 This happens during tree building, and is a Python3 only 328 feature. We remember the names for later use through the 329 function @consumeNonlocalDeclarations 330 """ 331 if self.non_local_declarations is None: 332 self.non_local_declarations = [] 333 334 self.non_local_declarations.append((names, user_provided, source_ref)) 335 336 def consumeNonlocalDeclarations(self): 337 """Return the nonlocal declared names for this function. 338 339 There may not be any, which is why we assigned it to 340 None originally and now check and return empty tuple 341 in that case. 342 """ 343 344 result = self.non_local_declarations or () 345 self.non_local_declarations = None 346 return result 347 348 def getFunctionName(self): 349 return self.name 350 351 def getFunctionQualname(self): 352 """Function __qualname__ new in CPython3.3 353 354 Should contain some kind of full name descriptions for the closure to 355 recognize and will be used for outputs. 356 """ 357 358 function_name = self.getFunctionName() 359 360 if python_version < 0x340: 361 provider = self.getParentVariableProvider() 362 else: 363 provider = self.qualname_provider 364 365 if provider.isCompiledPythonModule(): 366 return function_name 367 elif provider.isExpressionClassBody(): 368 return provider.getFunctionQualname() + "." + function_name 369 else: 370 return provider.getFunctionQualname() + ".<locals>." + function_name 371 372 def computeExpression(self, trace_collection): 373 assert False 374 375 # Function body is quite irreplaceable. 376 return self, None, None 377 378 def mayRaiseException(self, exception_type): 379 body = self.subnode_body 380 381 if body is None: 382 return False 383 else: 384 return self.subnode_body.mayRaiseException(exception_type) 385 386 def getFunctionInlineCost(self, values): 387 """Cost of inlining this function with given arguments 388 389 Returns: None or integer values, None means don't do it. 390 """ 391 392 # For overload, pylint: disable=no-self-use,unused-argument 393 return None 394 395 def optimizeUnusedClosureVariables(self): 396 """Gets called once module is complete, to consider giving up on closure variables.""" 397 398 changed = False 399 400 for closure_variable in self.getClosureVariables(): 401 402 # Need to take closure of those either way 403 if ( 404 closure_variable.isParameterVariable() 405 and self.isExpressionGeneratorObjectBody() 406 ): 407 continue 408 409 empty = self.trace_collection.hasEmptyTraces(closure_variable) 410 411 if empty: 412 changed = True 413 414 self.trace_collection.signalChange( 415 "var_usage", 416 self.source_ref, 417 message="Remove unused closure variable '%s'." 418 % closure_variable.getName(), 419 ) 420 421 self.removeClosureVariable(closure_variable) 422 423 return changed 424 425 def optimizeVariableReleases(self): 426 for parameter_variable in self.getParameterVariablesWithManualRelease(): 427 read_only = self.trace_collection.hasReadOnlyTraces(parameter_variable) 428 429 if read_only: 430 self.trace_collection.signalChange( 431 "var_usage", 432 self.source_ref, 433 message="Schedule removal releases of unassigned parameter variable '%s'." 434 % parameter_variable.getName(), 435 ) 436 437 self.removeVariableReleases(parameter_variable) 438 439 440class ExpressionFunctionEntryPointBase(EntryPointMixin, ExpressionFunctionBodyBase): 441 __slots__ = ("trace_collection", "code_object", "locals_scope", "auto_release") 442 443 def __init__( 444 self, provider, name, code_object, code_prefix, flags, auto_release, source_ref 445 ): 446 ExpressionFunctionBodyBase.__init__( 447 self, 448 provider=provider, 449 name=name, 450 code_prefix=code_prefix, 451 flags=flags, 452 body=None, 453 source_ref=source_ref, 454 ) 455 456 EntryPointMixin.__init__(self) 457 458 self.code_object = code_object 459 460 provider.getParentModule().addFunction(self) 461 462 if flags is not None and "has_exec" in flags: 463 locals_kind = "python2_function_exec" 464 else: 465 locals_kind = "python_function" 466 467 self.locals_scope = getLocalsDictHandle( 468 "locals_%s" % self.getCodeName(), locals_kind, self 469 ) 470 471 # Automatic parameter variable releases. 472 self.auto_release = auto_release or None 473 474 def getDetails(self): 475 result = ExpressionFunctionBodyBase.getDetails(self) 476 477 result["auto_release"] = tuple(sorted(self.auto_release or ())) 478 479 return result 480 481 def getCodeObject(self): 482 return self.code_object 483 484 def computeFunctionRaw(self, trace_collection): 485 from nuitka.optimizations.TraceCollections import ( 486 TraceCollectionFunction, 487 ) 488 489 trace_collection = TraceCollectionFunction( 490 parent=trace_collection, function_body=self 491 ) 492 old_collection = self.setTraceCollection(trace_collection) 493 494 self.computeFunction(trace_collection) 495 496 trace_collection.updateVariablesFromCollection(old_collection, self.source_ref) 497 498 def computeFunction(self, trace_collection): 499 statements_sequence = self.subnode_body 500 501 # TODO: Lift this restriction to only functions here and it code generation. 502 if statements_sequence is not None and self.isExpressionFunctionBody(): 503 if statements_sequence.subnode_statements[0].isStatementReturnNone(): 504 self.clearChild("body") 505 statements_sequence.finalize() 506 statements_sequence = None 507 508 if statements_sequence is not None: 509 result = statements_sequence.computeStatementsSequence( 510 trace_collection=trace_collection 511 ) 512 513 if result is not statements_sequence: 514 self.setChild("body", result) 515 516 def removeVariableReleases(self, variable): 517 assert variable in self.locals_scope.providing.values(), (self, variable) 518 519 if self.auto_release is None: 520 self.auto_release = set() 521 522 self.auto_release.add(variable) 523 524 def getParameterVariablesWithManualRelease(self): 525 """Return the list of parameter variables that have release statements. 526 527 These are for consideration if these can be dropped, and if so, they 528 are releases automatically by function code. 529 """ 530 return tuple( 531 variable 532 for variable in self.locals_scope.getProvidedVariables() 533 if not self.auto_release or variable not in self.auto_release 534 if variable.isParameterVariable() 535 if variable.getOwner() is self 536 ) 537 538 def isAutoReleaseVariable(self, variable): 539 """Is this variable to be automatically released.""" 540 return self.auto_release is not None and variable in self.auto_release 541 542 def getFunctionVariablesWithAutoReleases(self): 543 """Return the list of function variables that should be released at exit.""" 544 if self.auto_release is None: 545 return () 546 547 return tuple( 548 variable 549 for variable in self.locals_scope.getProvidedVariables() 550 if variable in self.auto_release 551 ) 552 553 @staticmethod 554 def getConstantReturnValue(): 555 """Special function that checks if code generation allows to use common C code. 556 557 Notes: 558 This is only done for standard functions. 559 560 """ 561 return False, False 562 563 564class ExpressionFunctionBody( 565 MarkUnoptimizedFunctionIndicatorMixin, ExpressionFunctionEntryPointBase 566): 567 kind = "EXPRESSION_FUNCTION_BODY" 568 569 # TODO: These should be more special than the general type in order to not cover exec ones. 570 __slots__ = ( 571 "unoptimized_locals", 572 "unqualified_exec", 573 "doc", 574 "return_exception", 575 "needs_creation", 576 "needs_direct", 577 "cross_module_use", 578 "parameters", 579 ) 580 581 if python_version >= 0x340: 582 __slots__ += ("qualname_setup",) 583 584 checkers = { 585 # TODO: Is "None" really an allowed value. 586 "body": checkStatementsSequenceOrNone 587 } 588 589 def __init__( 590 self, 591 provider, 592 name, 593 code_object, 594 doc, 595 parameters, 596 flags, 597 auto_release, 598 source_ref, 599 ): 600 ExpressionFunctionEntryPointBase.__init__( 601 self, 602 provider=provider, 603 name=name, 604 code_object=code_object, 605 code_prefix="function", 606 flags=flags, 607 auto_release=auto_release, 608 source_ref=source_ref, 609 ) 610 611 MarkUnoptimizedFunctionIndicatorMixin.__init__(self, flags) 612 613 self.doc = doc 614 615 # Indicator if the return value exception might be required. 616 self.return_exception = False 617 618 # Indicator if the function needs to be created as a function object. 619 self.needs_creation = False 620 621 # Indicator if the function is called directly. 622 self.needs_direct = False 623 624 # Indicator if the function is used outside of where it's defined. 625 self.cross_module_use = False 626 627 if python_version >= 0x340: 628 self.qualname_setup = None 629 630 self.parameters = parameters 631 self.parameters.setOwner(self) 632 633 for variable in self.parameters.getAllVariables(): 634 self.locals_scope.registerProvidedVariable(variable) 635 636 def getDetails(self): 637 return { 638 "name": self.getFunctionName(), 639 "ref_name": self.getCodeName(), 640 "parameters": self.getParameters(), 641 "code_object": self.code_object, 642 "provider": self.provider.getCodeName(), 643 "doc": self.doc, 644 "flags": self.flags, 645 } 646 647 def getDetailsForDisplay(self): 648 result = { 649 "name": self.getFunctionName(), 650 "provider": self.provider.getCodeName(), 651 "flags": self.flags, 652 } 653 654 result.update(self.parameters.getDetails()) 655 656 if self.code_object: 657 result.update(self.code_object.getDetails()) 658 659 if self.doc is not None: 660 result["doc"] = self.doc 661 662 return result 663 664 @classmethod 665 def fromXML(cls, provider, source_ref, **args): 666 assert provider is not None 667 668 parameter_spec_args = {} 669 code_object_args = {} 670 other_args = {} 671 672 for key, value in args.items(): 673 if key.startswith("ps_"): 674 parameter_spec_args[key] = value 675 elif key.startswith("co_"): 676 code_object_args[key] = value 677 elif key == "code_flags": 678 code_object_args["future_spec"] = fromFlags(args["code_flags"]) 679 else: 680 other_args[key] = value 681 682 parameters = ParameterSpec(**parameter_spec_args) 683 code_object = CodeObjectSpec(**code_object_args) 684 685 # The empty doc string and no doc string are distinguished by presence. The 686 # most common case is going to be not present. 687 if "doc" not in other_args: 688 other_args["doc"] = None 689 690 return cls( 691 provider=provider, 692 parameters=parameters, 693 code_object=code_object, 694 source_ref=source_ref, 695 **other_args 696 ) 697 698 @staticmethod 699 def isExpressionFunctionBody(): 700 return True 701 702 def getParent(self): 703 assert False 704 705 def getDoc(self): 706 return self.doc 707 708 def getParameters(self): 709 return self.parameters 710 711 def needsCreation(self): 712 return self.needs_creation 713 714 def markAsNeedsCreation(self): 715 self.needs_creation = True 716 717 def needsDirectCall(self): 718 return self.needs_direct 719 720 def markAsDirectlyCalled(self): 721 self.needs_direct = True 722 723 def isCrossModuleUsed(self): 724 return self.cross_module_use 725 726 def markAsCrossModuleUsed(self): 727 self.cross_module_use = True 728 729 def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection): 730 # TODO: Until we have something to re-order the arguments, we need to 731 # skip this. For the immediate need, we avoid this complexity, as a 732 # re-ordering will be needed. 733 734 assert False, self 735 736 @staticmethod 737 def isCompileTimeConstant(): 738 # TODO: It's actually pretty much compile time accessible maybe, but that 739 # would require extra effort. 740 return False 741 742 @staticmethod 743 def mayHaveSideEffects(): 744 # The function definition has no side effects, calculating the defaults 745 # would be, but that is done outside of this. 746 return False 747 748 def mayRaiseException(self, exception_type): 749 body = self.subnode_body 750 751 return body is not None and body.mayRaiseException(exception_type) 752 753 def markAsExceptionReturnValue(self): 754 self.return_exception = True 755 756 def needsExceptionReturnValue(self): 757 return self.return_exception 758 759 def getConstantReturnValue(self): 760 """Special function that checks if code generation allows to use common C code.""" 761 body = self.subnode_body 762 763 if body is None: 764 return True, None 765 766 first_statement = body.subnode_statements[0] 767 768 if first_statement.isStatementReturnConstant(): 769 constant_value = first_statement.getConstant() 770 771 # TODO: For mutable constants, we could also have something, but it would require an indicator 772 # flag to make a deep copy. 773 if not isMutable(constant_value): 774 return True, constant_value 775 else: 776 return False, False 777 else: 778 return False, False 779 780 781class ExpressionFunctionPureBody(ExpressionFunctionBody): 782 kind = "EXPRESSION_FUNCTION_PURE_BODY" 783 784 __slots__ = ( 785 # These need only one optimization ever. 786 "optimization_done", 787 ) 788 789 def __init__( 790 self, 791 provider, 792 name, 793 code_object, 794 doc, 795 parameters, 796 flags, 797 auto_release, 798 source_ref, 799 ): 800 ExpressionFunctionBody.__init__( 801 self, 802 provider=provider, 803 name=name, 804 code_object=code_object, 805 doc=doc, 806 parameters=parameters, 807 flags=flags, 808 auto_release=auto_release, 809 source_ref=source_ref, 810 ) 811 812 self.optimization_done = False 813 814 def computeFunctionRaw(self, trace_collection): 815 if self.optimization_done: 816 for function_body in self.trace_collection.getUsedFunctions(): 817 trace_collection.onUsedFunction(function_body) 818 819 def mySignal(tag, source_ref, change_desc): 820 if _is_verbose: 821 optimization_logger.info( 822 "{source_ref} : {tags} : {message}".format( 823 source_ref=source_ref.getAsString(), 824 tags=tags, 825 message=change_desc() 826 if inspect.isfunction(change_desc) 827 else change_desc, 828 ) 829 ) 830 831 tags.add(tag) 832 833 tags = set() 834 835 while 1: 836 trace_collection = TraceCollectionPureFunction(function_body=self) 837 old_collection = self.setTraceCollection(trace_collection) 838 839 with withChangeIndicationsTo(mySignal): 840 self.computeFunction(trace_collection) 841 842 trace_collection.updateVariablesFromCollection( 843 old_collection, self.source_ref 844 ) 845 846 if tags: 847 tags.clear() 848 else: 849 break 850 851 self.optimization_done = True 852 853 854class ExpressionFunctionPureInlineConstBody(ExpressionFunctionBody): 855 kind = "EXPRESSION_FUNCTION_PURE_INLINE_CONST_BODY" 856 857 def getFunctionInlineCost(self, values): 858 return 0 859 860 861def _convertNoneConstantOrEmptyDictToNone(node): 862 if node is None: 863 return None 864 elif node.isExpressionConstantNoneRef(): 865 return None 866 elif node.isExpressionConstantDictEmptyRef(): 867 return None 868 else: 869 return node 870 871 872# TODO: Function direct call node ought to be here too. 873 874 875class ExpressionFunctionCreation( 876 SideEffectsFromChildrenMixin, ExpressionChildrenHavingBase 877): 878 kind = "EXPRESSION_FUNCTION_CREATION" 879 880 __slots__ = ("variable_closure_traces",) 881 882 # Note: The order of evaluation for these is a bit unexpected, but 883 # true. Keyword defaults go first, then normal defaults, and annotations of 884 # all kinds go last. 885 886 # A bug of CPython3.x not fixed before version 3.4, see bugs.python.org/issue16967 887 kw_defaults_before_defaults = python_version < 0x340 888 889 if kw_defaults_before_defaults: 890 named_children = ("kw_defaults", "defaults", "annotations", "function_ref") 891 else: 892 named_children = ("defaults", "kw_defaults", "annotations", "function_ref") 893 894 checkers = {"kw_defaults": _convertNoneConstantOrEmptyDictToNone} 895 896 def __init__(self, function_ref, defaults, kw_defaults, annotations, source_ref): 897 assert kw_defaults is None or kw_defaults.isExpression() 898 assert annotations is None or annotations.isExpression() 899 assert function_ref.isExpressionFunctionRef() 900 901 ExpressionChildrenHavingBase.__init__( 902 self, 903 values={ 904 "function_ref": function_ref, 905 "defaults": tuple(defaults), 906 "kw_defaults": kw_defaults, 907 "annotations": annotations, 908 }, 909 source_ref=source_ref, 910 ) 911 912 self.variable_closure_traces = None 913 914 def getName(self): 915 return self.subnode_function_ref.getName() 916 917 @staticmethod 918 def getTypeShape(): 919 return tshape_function 920 921 def computeExpression(self, trace_collection): 922 self.variable_closure_traces = [] 923 924 for ( 925 closure_variable 926 ) in self.subnode_function_ref.getFunctionBody().getClosureVariables(): 927 trace = trace_collection.getVariableCurrentTrace(closure_variable) 928 trace.addNameUsage() 929 930 self.variable_closure_traces.append((closure_variable, trace)) 931 932 kw_defaults = self.subnode_kw_defaults 933 if kw_defaults is not None: 934 kw_defaults.onContentEscapes(trace_collection) 935 936 for default in self.subnode_defaults: 937 default.onContentEscapes(trace_collection) 938 939 # TODO: Function body may know something too. 940 return self, None, None 941 942 def mayRaiseException(self, exception_type): 943 for default in self.subnode_defaults: 944 if default.mayRaiseException(exception_type): 945 return True 946 947 kw_defaults = self.subnode_kw_defaults 948 949 if kw_defaults is not None and kw_defaults.mayRaiseException(exception_type): 950 return True 951 952 annotations = self.subnode_annotations 953 954 if annotations is not None and annotations.mayRaiseException(exception_type): 955 return True 956 957 return False 958 959 def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection): 960 961 trace_collection.onExceptionRaiseExit(BaseException) 962 963 # TODO: Until we have something to re-order the keyword arguments, we 964 # need to skip this. For the immediate need, we avoid this complexity, 965 # as a re-ordering will be needed. 966 if call_kw is not None and not call_kw.isExpressionConstantDictEmptyRef(): 967 return call_node, None, None 968 969 if call_args is None: 970 args_tuple = () 971 else: 972 assert ( 973 call_args.isExpressionConstantTupleRef() 974 or call_args.isExpressionMakeTuple() 975 ) 976 977 args_tuple = call_args.getIterationValues() 978 979 function_body = self.subnode_function_ref.getFunctionBody() 980 981 # TODO: Actually the above disables it entirely, as it is at least 982 # the empty dictionary node in any case. We will need some enhanced 983 # interfaces for "matchCall" to work on. 984 985 call_spec = function_body.getParameters() 986 987 try: 988 args_dict = matchCall( 989 func_name=self.getName(), 990 args=call_spec.getArgumentNames(), 991 kw_only_args=call_spec.getKwOnlyParameterNames(), 992 star_list_arg=call_spec.getStarListArgumentName(), 993 star_dict_arg=call_spec.getStarDictArgumentName(), 994 num_defaults=call_spec.getDefaultCount(), 995 num_posonly=call_spec.getPosOnlyParameterCount(), 996 positional=args_tuple, 997 pairs=(), 998 ) 999 1000 values = [args_dict[name] for name in call_spec.getParameterNames()] 1001 1002 # TODO: Not handling default values either yet. 1003 if None in values: 1004 return call_node, None, None 1005 1006 # TODO: This is probably something that the matchCall ought to do 1007 # for us, but that will need cleanups. Also these functions and 1008 # nodes ought to work with # ordered dictionaries maybe. 1009 if call_spec.getStarDictArgumentName(): 1010 values[-1] = makeDictCreationOrConstant2( 1011 keys=[value[0] for value in values[-1]], 1012 values=[value[1] for value in values[-1]], 1013 source_ref=call_node.source_ref, 1014 ) 1015 1016 result = ExpressionFunctionCall( 1017 function=self, values=values, source_ref=call_node.source_ref 1018 ) 1019 1020 return ( 1021 result, 1022 "new_statements", # TODO: More appropriate tag maybe. 1023 """\ 1024Replaced call to created function body '%s' with direct \ 1025function call.""" 1026 % self.getName(), 1027 ) 1028 1029 except TooManyArguments as e: 1030 result = wrapExpressionWithSideEffects( 1031 new_node=makeRaiseExceptionReplacementExpressionFromInstance( 1032 expression=call_node, exception=e.getRealException() 1033 ), 1034 old_node=call_node, 1035 side_effects=call_node.extractSideEffectsPreCall(), 1036 ) 1037 1038 return ( 1039 result, 1040 "new_raise", # TODO: More appropriate tag maybe. 1041 """Replaced call to created function body '%s' to argument \ 1042error""" 1043 % self.getName(), 1044 ) 1045 1046 def getClosureVariableVersions(self): 1047 return self.variable_closure_traces 1048 1049 1050class ExpressionFunctionRef(ExpressionBase): 1051 kind = "EXPRESSION_FUNCTION_REF" 1052 1053 __slots__ = "function_body", "code_name" 1054 1055 def __init__(self, source_ref, function_body=None, code_name=None): 1056 assert function_body is not None or code_name is not None 1057 assert code_name != "None" 1058 1059 ExpressionBase.__init__(self, source_ref=source_ref) 1060 1061 self.function_body = function_body 1062 self.code_name = code_name 1063 1064 def finalize(self): 1065 del self.parent 1066 del self.function_body 1067 1068 def getName(self): 1069 return self.function_body.getName() 1070 1071 def getDetails(self): 1072 return {"function_body": self.function_body} 1073 1074 def getDetailsForDisplay(self): 1075 return {"code_name": self.getFunctionBody().getCodeName()} 1076 1077 def getFunctionBody(self): 1078 if self.function_body is None: 1079 module_code_name, _ = self.code_name.split("$$$", 1) 1080 1081 from nuitka.ModuleRegistry import getModuleFromCodeName 1082 1083 module = getModuleFromCodeName(module_code_name) 1084 1085 self.function_body = module.getFunctionFromCodeName(self.code_name) 1086 1087 return self.function_body 1088 1089 def computeExpressionRaw(self, trace_collection): 1090 trace_collection.onUsedFunction(self.getFunctionBody()) 1091 1092 # TODO: Function after collection may now know something. 1093 return self, None, None 1094 1095 @staticmethod 1096 def mayHaveSideEffects(): 1097 # Using a function has no side effects, the use might, but this is not it. 1098 return False 1099 1100 @staticmethod 1101 def mayRaiseException(exception_type): 1102 return False 1103 1104 1105class ExpressionFunctionCall(ExpressionChildrenHavingBase): 1106 """Shared function call. 1107 1108 This is for calling created function bodies with multiple users. Not 1109 clear if such a thing should exist. But what this will do is to have 1110 respect for the fact that there are multiple such calls. 1111 """ 1112 1113 kind = "EXPRESSION_FUNCTION_CALL" 1114 1115 __slots__ = ("variable_closure_traces",) 1116 1117 named_children = ("function", "values") 1118 1119 def __init__(self, function, values, source_ref): 1120 assert function.isExpressionFunctionCreation() 1121 1122 ExpressionChildrenHavingBase.__init__( 1123 self, 1124 values={"function": function, "values": tuple(values)}, 1125 source_ref=source_ref, 1126 ) 1127 1128 self.variable_closure_traces = None 1129 1130 def computeExpression(self, trace_collection): 1131 function = self.subnode_function 1132 function_body = function.subnode_function_ref.getFunctionBody() 1133 1134 if function_body.mayRaiseException(BaseException): 1135 trace_collection.onExceptionRaiseExit(BaseException) 1136 1137 values = self.subnode_values 1138 1139 # Ask for function for its cost. 1140 cost = function_body.getFunctionInlineCost(values) 1141 1142 if cost is not None and cost < 50: 1143 from nuitka.optimizations.FunctionInlining import ( 1144 convertFunctionCallToOutline, 1145 ) 1146 1147 result = convertFunctionCallToOutline( 1148 provider=self.getParentVariableProvider(), 1149 function_body=function_body, 1150 values=values, 1151 call_source_ref=self.source_ref, 1152 ) 1153 1154 return ( 1155 result, 1156 "new_statements", 1157 lambda: "Function call to '%s' in-lined." % function_body.getCodeName(), 1158 ) 1159 1160 self.variable_closure_traces = [] 1161 1162 for closure_variable in function_body.getClosureVariables(): 1163 trace = trace_collection.getVariableCurrentTrace(closure_variable) 1164 trace.addNameUsage() 1165 1166 self.variable_closure_traces.append((closure_variable, trace)) 1167 1168 return self, None, None 1169 1170 def mayRaiseException(self, exception_type): 1171 function = self.subnode_function 1172 1173 if function.subnode_function_ref.getFunctionBody().mayRaiseException( 1174 exception_type 1175 ): 1176 return True 1177 1178 values = self.subnode_values 1179 1180 for value in values: 1181 if value.mayRaiseException(exception_type): 1182 return True 1183 1184 return False 1185 1186 def getClosureVariableVersions(self): 1187 return self.variable_closure_traces 1188