1 #!/usr/bin/env python 2 3 """ 4 Inspect source files, obtaining details of classes and attributes. 5 6 Copyright (C) 2007, 2008 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 21 -------- 22 23 The results of inspecting a module are as follows: 24 25 Constants 26 --------- 27 28 All constants identified within the code shall be registered. 29 30 Classes 31 ------- 32 33 All global classes shall be registered; local classes (within functions) or 34 nested classes (within classes) are not currently registered. 35 36 Base classes must be detected and constant. 37 38 All classes without bases are made to inherit from __builtins__.object in order 39 to support some standard methods. 40 41 Functions 42 --------- 43 44 All functions and lambda definitions shall be registered. 45 46 Namespaces 47 ---------- 48 49 Modules define their own "global" namespace, within which classes, functions 50 and lambda definitions establish a hierarchy of namespaces. 51 52 Only local, global and built-in namespaces are recognised; closures are not 53 supported. 54 55 Assignments 56 ----------- 57 58 Name assignment and attribute assignment involving modules and classes cause 59 names to be associated with values within namespaces. 60 61 Any assignments within loops are considered to cause the targets of such 62 assignments to provide non-constant values. 63 64 Assignments to names are only really considered to cause the targets of such 65 assignments to provide constant values if the targets reside in the module 66 namespace or in class namespaces, subject to the above conditions. 67 68 Assignments to names within functions are not generally considered to cause the 69 targets of such assignments to provide constant values since functions can be 70 invoked many times with different inputs. However, there may be benefits in 71 considering a local to be constant within a single invocation. 72 """ 73 74 from micropython.common import * 75 from micropython.data import * 76 import compiler.ast 77 from compiler.visitor import ASTVisitor 78 79 # Program visitors. 80 81 class InspectedModule(ASTVisitor, Module): 82 83 """ 84 An inspected module, providing core details via the Module superclass, but 85 capable of being used as an AST visitor. 86 """ 87 88 def __init__(self, name, importer): 89 90 """ 91 Initialise this visitor with a module 'name' and an 'importer' which is 92 used to provide access to other modules when required. 93 """ 94 95 ASTVisitor.__init__(self) 96 Module.__init__(self, name) 97 self.visitor = self 98 99 # Import machinery links. 100 101 self.importer = importer 102 self.optimisations = importer.optimisations 103 self.builtins = self.importer.modules.get("__builtins__") 104 self.loaded = 0 105 106 # Current expression state. 107 108 self.expr = None 109 110 # Namespace state. 111 112 self.in_init = 0 # Find instance attributes in __init__ methods. 113 self.in_method = 0 # Find instance attributes in all methods. 114 self.in_loop = 0 # Note loop "membership", affecting assignments. 115 self.namespaces = [] 116 self.module = None 117 118 def parse(self, filename): 119 120 "Parse the file having the given 'filename'." 121 122 module = compiler.parseFile(filename) 123 self.process(module) 124 125 def process(self, module): 126 127 "Process the given 'module'." 128 129 self.astnode = self.module = module 130 processed = self.dispatch(module) 131 if self.has_key("__all__"): 132 all = self["__all__"] 133 if isinstance(all, compiler.ast.List): 134 for n in all.nodes: 135 self.store(n.value, self.importer.add_module(self.name + "." + n.value)) 136 return processed 137 138 def vacuum(self): 139 140 "Vacuum the module namespace, removing unloaded module references." 141 142 for name, value in self.items(): 143 144 if isinstance(value, Attr): 145 attr_value = value.value 146 147 # Remove non-loaded modules. 148 149 if isinstance(attr_value, Module) and not attr_value.loaded: 150 del self[name] 151 152 # Remove unreferenced names. 153 # This, due to the nature of the referenced attribute, assumes 154 # that only explicitly mentioned classes and functions are 155 # employed in the final program. 156 157 elif self.should_optimise_unused_objects(): 158 159 # Only remove entries for classes and functions, not methods. 160 161 for attr_value in value.assignment_values: 162 if (isinstance(attr_value, Function) and not attr_value.is_method() or 163 isinstance(attr_value, Class)) and not attr_value.referenced: 164 pass 165 else: 166 break 167 else: 168 del self[name] 169 170 # Complain about globals not initialised at the module level. 171 172 elif isinstance(value, Global): 173 print "Warning: global %r in module %r not initialised at the module level." % (name, self.name) 174 175 # Remove unreferenced objects. 176 177 if self.should_optimise_unused_objects(): 178 179 all_objects = list(self.all_objects) 180 181 for obj in all_objects: 182 if (isinstance(obj, Function) and not obj.is_method() or 183 isinstance(obj, Class)) and not obj.referenced: 184 self.all_objects.remove(obj) 185 186 def add_object(self, obj, any_scope=0): 187 188 """ 189 Record 'obj' if non-local or if the optional 'any_scope' is set to a 190 true value. 191 """ 192 193 if any_scope or not (self.namespaces and isinstance(self.namespaces[-1], Function)): 194 self.all_objects.add(obj) 195 196 # Optimisation tests. 197 198 def should_optimise_unused_objects(self): 199 return "unused_objects" in self.optimisations 200 201 # Namespace methods. 202 203 def store(self, name, obj): 204 205 "Record attribute or local 'name', storing 'obj'." 206 207 if not self.namespaces: 208 self.set(name, obj, not self.in_loop) 209 else: 210 self.namespaces[-1].set(name, obj, not self.in_loop) 211 212 def store_lambda(self, obj): 213 214 "Store a lambda function 'obj'." 215 216 self.add_object(obj) 217 218 def store_module_attr(self, name, module): 219 220 """ 221 Record module attribute 'name' in the given 'module' using the current 222 expression. 223 """ 224 225 if isinstance(self.expr, Attr): 226 assigned_value = self.expr.value 227 else: 228 assigned_value = self.expr 229 230 module.set(name, assigned_value, 0) 231 232 def store_class_attr(self, name): 233 234 """ 235 Record class attribute 'name' in the current class using the current 236 expression. 237 """ 238 239 if self.in_method and self.namespaces[-2].has_key(name): 240 241 if isinstance(self.expr, Attr): 242 assigned_value = self.expr.value 243 else: 244 assigned_value = self.expr 245 246 self.namespaces[-2].set(name, assigned_value, 0) 247 return 1 248 249 return 0 250 251 def store_instance_attr(self, name): 252 253 "Record instance attribute 'name' in the current class." 254 255 if self.in_method: 256 257 # Current namespace is the function. 258 # Previous namespace is the class. 259 260 self.namespaces[-2].add_instance_attribute(name) 261 262 def get_parent(self): 263 264 "Return the parent (or most recent) namespace currently exposed." 265 266 return (self.namespaces[-1:] or [self])[0] 267 268 # Visitor methods. 269 270 def default(self, node, *args): 271 raise InspectError(self.full_name(), node, "Node class %r is not supported." % node.__class__) 272 273 def dispatch(self, node, *args): 274 return ASTVisitor.dispatch(self, node, *args) 275 276 def NOP(self, node): 277 for n in node.getChildNodes(): 278 self.dispatch(n) 279 return None 280 281 def OP(self, node): 282 for n in node.getChildNodes(): 283 self.dispatch(n) 284 return Instance() 285 286 def _visitFunction(self, node, name): 287 288 """ 289 Return a function object for the function defined by 'node' with the 290 given 'name'. If a lambda expression is being visited, 'name' should be 291 None. 292 """ 293 294 function = Function( 295 name, 296 self.get_parent(), 297 node.argnames, 298 node.defaults, 299 (node.flags & 4 != 0), 300 (node.flags & 8 != 0), 301 self, 302 node 303 ) 304 305 # Make a back reference from the node for code generation. 306 307 node.unit = function 308 309 # Process the defaults. 310 311 for n in node.defaults: 312 self.expr = self.dispatch(n) 313 if isinstance(self.expr, Attr): 314 function.store_default(self.expr.value) 315 else: 316 function.store_default(self.expr) 317 318 # Enter the function. 319 320 self.namespaces.append(function) 321 322 # Current namespace is the function. 323 # Previous namespace is the class. 324 325 if len(self.namespaces) > 1 and isinstance(self.namespaces[-2], Class): 326 if name == "__init__": 327 self.in_init = 1 328 self.in_method = 1 329 330 self.dispatch(node.code) 331 self.in_init = 0 332 self.in_method = 0 333 self.namespaces.pop() 334 335 if name is not None: 336 self.store(name, function) 337 else: 338 self.store_lambda(function) 339 340 # Lambda functions are always assumed to be referenced. This is 341 # because other means of discovering the referencing of objects rely 342 # on the original names inherent in the definition of those objects. 343 344 function.set_referenced() 345 346 # Where defaults exist, an instance needs creating. Thus, it makes 347 # no sense to return a reference to the function here, since the 348 # recipient will not be referencing the function itself. 349 350 if node.defaults: 351 return Instance() # indicates no known target 352 353 self.add_object(function, any_scope=1) 354 return function 355 356 visitAdd = OP 357 358 visitAnd = OP 359 360 visitAssert = NOP 361 362 def visitAssign(self, node): 363 self.expr = self.dispatch(node.expr) 364 for n in node.nodes: 365 self.dispatch(n) 366 return None 367 368 def visitAssAttr(self, node): 369 expr = self.dispatch(node.expr) 370 if isinstance(expr, Attr): 371 if expr.name == "self": 372 if not self.store_class_attr(node.attrname): 373 self.store_instance_attr(node.attrname) 374 elif isinstance(expr.value, Module): 375 self.store_module_attr(node.attrname, expr.value) 376 print "Warning: attribute %r of module %r set outside the module." % (node.attrname, expr.value.name) 377 return None 378 379 def visitAssList(self, node): 380 for i, n in enumerate(node.nodes): 381 self.dispatch(n) 382 self.importer.make_constant(i) # for __getitem__(i) at run-time 383 return None 384 385 def visitAssName(self, node): 386 self.store(node.name, self.expr) 387 return None 388 389 visitAssTuple = visitAssList 390 391 visitAugAssign = OP 392 393 visitBackquote = OP 394 395 visitBitand = OP 396 397 visitBitor = OP 398 399 visitBitxor = OP 400 401 visitBreak = NOP 402 403 visitCallFunc = OP 404 405 def visitClass(self, node): 406 407 """ 408 Register the class at the given 'node' subject to the restrictions 409 mentioned in the module docstring. 410 """ 411 412 if self.namespaces: 413 print "Class %r in %r is not global: ignored." % (node.name, self.namespaces[-1].full_name()) 414 return None 415 else: 416 cls = Class(node.name, self.get_parent(), self, node) 417 418 # Visit the base class expressions, attempting to find concrete 419 # definitions of classes. 420 421 for base in node.bases: 422 expr = self.dispatch(base) 423 if isinstance(expr, Attr): 424 if expr.assignments != 1: 425 raise InspectError(self.full_name(), node, 426 "Base class %r for %r is not constant." % (base, cls.full_name())) 427 else: 428 cls.add_base(expr.value) 429 else: # if expr is None: 430 raise InspectError(self.full_name(), node, 431 "Base class %r for %r is not found: it may be hidden in some way." % (base, cls.full_name())) 432 433 # NOTE: Potentially dubious measure to permit __init__ availability. 434 # If no bases exist, adopt the 'object' class. 435 436 if not node.bases and not (self.name == "__builtins__" and node.name == "object") : 437 expr = self.dispatch(compiler.ast.Name("object")) 438 cls.add_base(expr.value) 439 440 # Make a back reference from the node for code generation. 441 442 node.unit = cls 443 444 # Make an entry for the class. 445 446 self.store(node.name, cls) 447 self.add_object(cls) 448 449 # Process the class body. 450 451 self.namespaces.append(cls) 452 self.dispatch(node.code) 453 self.namespaces.pop() 454 455 return cls 456 457 visitCompare = OP 458 459 def visitConst(self, node): 460 return self.importer.make_constant(node.value) 461 462 visitContinue = NOP 463 464 visitDecorators = NOP 465 466 visitDict = OP 467 468 visitDiscard = NOP 469 470 visitDiv = OP 471 472 visitEllipsis = NOP 473 474 visitExec = NOP 475 476 visitExpression = OP 477 478 visitFloorDiv = OP 479 480 def visitFor(self, node): 481 self.in_loop = 1 482 self.NOP(node) 483 self.in_loop = 0 484 return None 485 486 def visitFrom(self, node): 487 module = self.importer.load(node.modname, 1) 488 489 if module is not None: 490 module.set_referenced() 491 492 #if module is None: 493 # print "Warning:", node.modname, "not imported." 494 495 for name, alias in node.names: 496 if name != "*": 497 if module is not None and module.namespace.has_key(name): 498 attr = module[name] 499 self.store(alias or name, attr.value) 500 if isinstance(attr.value, Module) and not attr.value.loaded: 501 self.importer.load(attr.value.name) 502 503 # Support the import of names from missing modules. 504 505 else: 506 self.store(alias or name, UnresolvedName(name, node.modname, self)) 507 else: 508 if module is not None: 509 for n in module.namespace.keys(): 510 attr = module[n] 511 self.store(n, attr.value) 512 if isinstance(attr.value, Module) and not attr.value.loaded: 513 self.importer.load(attr.value.name) 514 515 return None 516 517 def visitFunction(self, node): 518 return self._visitFunction(node, node.name) 519 520 visitGenExpr = OP 521 522 visitGenExprFor = NOP 523 524 visitGenExprIf = NOP 525 526 visitGenExprInner = NOP 527 528 def visitGetattr(self, node): 529 expr = self.dispatch(node.expr) 530 attrname = node.attrname 531 532 if isinstance(expr, Attr): 533 value = expr.value 534 if isinstance(value, (Class, Module)): 535 attr = value.namespace.get(attrname) 536 elif isinstance(value, UnresolvedName): 537 attr = UnresolvedName(attrname, value.full_name(), self) 538 else: 539 attr = None 540 elif self.builtins is not None: 541 attr = self.builtins.get(attrname) 542 else: 543 attr = UnresolvedName(attrname, value.full_name(), self) 544 545 # Accounting. 546 547 if attr is not None: 548 attr.set_referenced() 549 550 self.importer.use_name(attrname) 551 552 return attr 553 554 def visitGlobal(self, node): 555 if self.namespaces: 556 for name in node.names: 557 self.namespaces[-1].make_global(name) 558 559 # Record a global entry for the name in the module. 560 561 if not self.has_key(name): 562 self[name] = Global() 563 564 def visitIf(self, node): 565 for test, body in node.tests: 566 self.dispatch(body) 567 if node.else_ is not None: 568 self.dispatch(node.else_) 569 return None 570 571 visitIfExp = NOP 572 573 def visitImport(self, node): 574 for name, alias in node.names: 575 if alias is not None: 576 module = self.importer.load(name, 1) or UnresolvedName(None, name, self) 577 self.store(alias, module) 578 else: 579 module = self.importer.load(name) or UnresolvedName(None, name.split(".")[0], self) 580 self.store(name.split(".")[0], module) 581 module.set_referenced() 582 583 return None 584 585 visitInvert = OP 586 587 def visitKeyword(self, node): 588 self.dispatch(node.expr) 589 self.importer.make_constant(node.name) 590 self.keyword_names.add(node.name) 591 return None 592 593 def visitLambda(self, node): 594 return self._visitFunction(node, None) 595 596 visitLeftShift = OP 597 598 visitList = OP 599 600 visitListComp = OP 601 602 visitListCompFor = NOP 603 604 visitListCompIf = NOP 605 606 visitMod = OP 607 608 def visitModule(self, node): 609 610 # Make a back reference from the node for code generation. 611 612 node.unit = self 613 return self.dispatch(node.node) 614 615 visitMul = OP 616 617 def visitName(self, node): 618 name = node.name 619 620 if self.importer.predefined_constants.has_key(name): 621 attr = self.importer.get_predefined_constant(name) 622 elif self.namespaces and self.namespaces[-1].has_key(name): 623 attr = self.namespaces[-1][name] 624 elif self.has_key(name): 625 attr = self[name] 626 elif self.builtins is not None and self.builtins.has_key(name): 627 attr = self.builtins[name] 628 else: 629 attr = None 630 631 # Accounting. 632 633 if attr is not None: 634 attr.set_referenced() 635 636 self.importer.use_name(name) 637 638 return attr 639 640 visitNot = OP 641 642 visitOr = OP 643 644 visitPass = NOP 645 646 visitPower = OP 647 648 visitPrint = NOP 649 650 visitPrintnl = NOP 651 652 visitRaise = NOP 653 654 visitReturn = NOP 655 656 visitRightShift = OP 657 658 visitSlice = OP 659 660 visitSliceobj = OP 661 662 def visitStmt(self, node): 663 for n in node.nodes: 664 self.dispatch(n) 665 return None 666 667 visitSub = OP 668 669 visitSubscript = OP 670 671 def visitTryExcept(self, node): 672 self.dispatch(node.body) 673 for name, var, n in node.handlers: 674 675 # Establish the local for the handler. 676 677 if var is not None: 678 self.dispatch(var) 679 if n is not None: 680 self.dispatch(n) 681 if node.else_ is not None: 682 self.dispatch(node.else_) 683 return None 684 685 visitTryFinally = NOP 686 687 visitTuple = OP 688 689 visitUnaryAdd = OP 690 691 visitUnarySub = OP 692 693 def visitWhile(self, node): 694 self.in_loop = 1 695 self.NOP(node) 696 self.in_loop = 0 697 return None 698 699 visitWith = NOP 700 701 visitYield = NOP 702 703 class Global: 704 705 """ 706 A reference to an object assigned to a global from outside the module 707 top-level. 708 """ 709 710 pass 711 712 # vim: tabstop=4 expandtab shiftwidth=4