@@ -465,3 +465,338 @@ DataFlow::SourceNode moduleMember(string path, string m) {
465465 result = DataFlow:: ssaDefinitionNode ( ssa )
466466 )
467467}
468+
469+ /**
470+ * The string `method`, `getter`, or `setter`, representing the kind of a function member
471+ * in a class.
472+ *
473+ * Can can be used with `ClassNode.getInstanceMember` to obtain members of a specific kind.
474+ */
475+ class MemberKind extends string {
476+ MemberKind ( ) { this = "method" or this = "getter" or this = "setter" }
477+ }
478+
479+ module MemberKind {
480+ /** The kind of a method, such as `m() {}` */
481+ MemberKind method ( ) { result = "method" }
482+
483+ /** The kind of a getter accessor, such as `get f() {}`. */
484+ MemberKind getter ( ) { result = "getter" }
485+
486+ /** The kind of a setter accessor, such as `set f() {}`. */
487+ MemberKind setter ( ) { result = "setter" }
488+
489+ /** The `getter` and `setter` kinds. */
490+ MemberKind accessor ( ) { result = getter ( ) or result = setter ( ) }
491+
492+ /**
493+ * Gets the member kind of a given syntactic member declaration in a class.
494+ */
495+ MemberKind of ( MemberDeclaration decl ) {
496+ decl instanceof GetterMethodDeclaration and result = getter ( )
497+ or
498+ decl instanceof SetterMethodDeclaration and result = setter ( )
499+ or
500+ decl instanceof MethodDeclaration and
501+ not decl instanceof AccessorMethodDeclaration and
502+ not decl instanceof ConstructorDeclaration and
503+ result = method ( )
504+ }
505+ }
506+
507+ /**
508+ * A data flow node corresponding to a class definition or a function definition
509+ * acting as a class.
510+ *
511+ * The following patterns are recognized as classes and methods:
512+ * ```
513+ * class C {
514+ * method()
515+ * }
516+ *
517+ * function F() {}
518+ *
519+ * F.prototype.method = function() {}
520+ *
521+ * F.prototype = {
522+ * method: function() {}
523+ * }
524+ *
525+ * extend(F.prototype, {
526+ * method: function() {}
527+ * });
528+ * ```
529+ *
530+ * Additional patterns can be recognized as class nodes, by extending `DataFlow::ClassNode::Range`.
531+ */
532+ class ClassNode extends DataFlow:: SourceNode {
533+ ClassNode:: Range impl ;
534+
535+ ClassNode ( ) { this = impl }
536+
537+ /**
538+ * Gets the name of the class, if it has one.
539+ */
540+ string getName ( ) { result = impl .getName ( ) }
541+
542+ /**
543+ * Gets a description of the class.
544+ */
545+ string describe ( ) { result = impl .describe ( ) }
546+
547+ /**
548+ * Gets the constructor function of this class.
549+ */
550+ FunctionNode getConstructor ( ) { result = impl .getConstructor ( ) }
551+
552+ /**
553+ * Gets an instance method declared in this class, with the given name, if any.
554+ *
555+ * Does not include methods from superclasses.
556+ */
557+ FunctionNode getInstanceMethod ( string name ) { result = impl .getInstanceMember ( name , MemberKind:: method ( ) ) }
558+
559+ /**
560+ * Gets an instance method declared in this class.
561+ *
562+ * The constructor is not considered an instance method.
563+ *
564+ * Does not include methods from superclasses.
565+ */
566+ FunctionNode getAnInstanceMethod ( ) { result = impl .getAnInstanceMember ( MemberKind:: method ( ) ) }
567+
568+ /**
569+ * Gets the instance method, getter, or setter with the given name and kind.
570+ *
571+ * Does not include members from superclasses.
572+ */
573+ FunctionNode getInstanceMember ( string name , MemberKind kind ) { result = impl .getInstanceMember ( name , kind ) }
574+
575+ /**
576+ * Gets an instance method, getter, or setter with the given kind.
577+ *
578+ * Does not include members from superclasses.
579+ */
580+ FunctionNode getAnInstanceMember ( MemberKind kind ) { result = impl .getAnInstanceMember ( kind ) }
581+
582+ /**
583+ * Gets an instance method, getter, or setter declared in this class.
584+ *
585+ * Does not include members from superclasses.
586+ */
587+ FunctionNode getAnInstanceMember ( ) { result = impl .getAnInstanceMember ( _) }
588+
589+ /**
590+ * Gets the static method declared in this class with the given name.
591+ */
592+ FunctionNode getStaticMethod ( string name ) { result = impl .getStaticMethod ( name ) }
593+
594+ /**
595+ * Gets a static method declared in this class.
596+ *
597+ * The constructor is not considered a static method.
598+ */
599+ FunctionNode getAStaticMethod ( ) { result = impl .getAStaticMethod ( ) }
600+
601+ /**
602+ * Gets a direct super class of this class.
603+ */
604+ ClassNode getADirectSuperClass ( ) {
605+ result .getConstructor ( ) .getAstNode ( ) = impl
606+ .getASuperClassNode ( )
607+ .analyze ( )
608+ .getAValue ( )
609+ .( AbstractCallable )
610+ .getFunction ( )
611+ }
612+ }
613+
614+ module ClassNode {
615+ /**
616+ * A dataflow node that should be considered a class node.
617+ *
618+ * Subclass this to introduce new kinds of class nodes. If you want to refine
619+ * the definition of existing class nodes, subclass `DataFlow::ClassNode` instead.
620+ */
621+ abstract class Range extends DataFlow:: SourceNode {
622+ /**
623+ * Gets the name of the class, if it has one.
624+ */
625+ abstract string getName ( ) ;
626+
627+ /**
628+ * Gets a description of the class.
629+ */
630+ abstract string describe ( ) ;
631+
632+ /**
633+ * Gets the constructor function of this class.
634+ */
635+ abstract FunctionNode getConstructor ( ) ;
636+
637+ /**
638+ * Gets the instance member with the given name and kind.
639+ */
640+ abstract FunctionNode getInstanceMember ( string name , MemberKind kind ) ;
641+
642+ /**
643+ * Gets an instance member with the given kind.
644+ */
645+ abstract FunctionNode getAnInstanceMember ( MemberKind kind ) ;
646+
647+ /**
648+ * Gets the static method of this class with the given name.
649+ */
650+ abstract FunctionNode getStaticMethod ( string name ) ;
651+
652+ /**
653+ * Gets a static method of this class.
654+ *
655+ * The constructor is not considered a static method.
656+ */
657+ abstract FunctionNode getAStaticMethod ( ) ;
658+
659+ /**
660+ * Gets a dataflow node representing a class to be used as the super-class
661+ * of this node.
662+ */
663+ abstract DataFlow:: Node getASuperClassNode ( ) ;
664+ }
665+
666+ /**
667+ * An ES6 class as a `ClassNode` instance.
668+ */
669+ private class ES6Class extends Range , DataFlow:: ValueNode {
670+ override ClassDefinition astNode ;
671+
672+ override string getName ( ) { result = astNode .getName ( ) }
673+
674+ override string describe ( ) { result = astNode .describe ( ) }
675+
676+ override FunctionNode getConstructor ( ) { result = astNode .getConstructor ( ) .getBody ( ) .flow ( ) }
677+
678+ override FunctionNode getInstanceMember ( string name , MemberKind kind ) {
679+ exists ( MethodDeclaration method |
680+ method = astNode .getMethod ( name ) and
681+ not method .isStatic ( ) and
682+ kind = MemberKind:: of ( method ) and
683+ result = method .getBody ( ) .flow ( )
684+ )
685+ }
686+
687+ override FunctionNode getAnInstanceMember ( MemberKind kind ) {
688+ exists ( MethodDeclaration method |
689+ method = astNode .getAMethod ( ) and
690+ not method .isStatic ( ) and
691+ kind = MemberKind:: of ( method ) and
692+ result = method .getBody ( ) .flow ( )
693+ )
694+ }
695+
696+ override FunctionNode getStaticMethod ( string name ) {
697+ exists ( MethodDeclaration method |
698+ method = astNode .getMethod ( name ) and
699+ method .isStatic ( ) and
700+ result = method .getBody ( ) .flow ( )
701+ )
702+ or
703+ result = getAPropertySource ( name )
704+ }
705+
706+ override FunctionNode getAStaticMethod ( ) {
707+ exists ( MethodDeclaration method |
708+ method = astNode .getAMethod ( ) and
709+ method .isStatic ( ) and
710+ result = method .getBody ( ) .flow ( )
711+ )
712+ }
713+
714+ override DataFlow:: Node getASuperClassNode ( ) { result = astNode .getSuperClass ( ) .flow ( ) }
715+ }
716+
717+ /**
718+ * A function definition with prototype manipulation as a `ClassNode` instance.
719+ */
720+ class FunctionStyleClass extends Range , DataFlow:: ValueNode {
721+ override Function astNode ;
722+
723+ FunctionStyleClass ( ) { exists ( getAPropertyReference ( "prototype" ) ) }
724+
725+ override string getName ( ) { result = astNode .getName ( ) }
726+
727+ override string describe ( ) { result = astNode .describe ( ) }
728+
729+ override FunctionNode getConstructor ( ) { result = this }
730+
731+ private PropertyAccessor getAnAccessor ( MemberKind kind ) {
732+ result .getObjectExpr ( ) = getAPrototypeReference ( ) .asExpr ( ) and
733+ (
734+ kind = MemberKind:: getter ( ) and
735+ result instanceof PropertyGetter
736+ or
737+ kind = MemberKind:: setter ( ) and
738+ result instanceof PropertySetter
739+ )
740+ }
741+
742+ override FunctionNode getInstanceMember ( string name , MemberKind kind ) {
743+ kind = MemberKind:: method ( ) and
744+ result = getAPrototypeReference ( ) .getAPropertySource ( name )
745+ or
746+ exists ( PropertyAccessor accessor |
747+ accessor = getAnAccessor ( kind ) and
748+ accessor .getName ( ) = name and
749+ result = accessor .getInit ( ) .flow ( )
750+ )
751+ }
752+
753+ override FunctionNode getAnInstanceMember ( MemberKind kind ) {
754+ kind = MemberKind:: method ( ) and
755+ result = getAPrototypeReference ( ) .getAPropertyWrite ( ) .getRhs ( ) .getALocalSource ( )
756+ or
757+ exists ( PropertyAccessor accessor |
758+ accessor = getAnAccessor ( kind ) and
759+ result = accessor .getInit ( ) .flow ( )
760+ )
761+ }
762+
763+ override FunctionNode getStaticMethod ( string name ) {
764+ result = getAPropertySource ( name )
765+ }
766+
767+ override FunctionNode getAStaticMethod ( ) {
768+ result = getAPropertyWrite ( ) .getRhs ( ) .getALocalSource ( )
769+ }
770+
771+ /**
772+ * Gets a reference to the prototype of this class.
773+ */
774+ private DataFlow:: SourceNode getAPrototypeReference ( ) {
775+ result = getAPropertyRead ( "prototype" )
776+ or
777+ result = getAPropertySource ( "prototype" )
778+ or
779+ exists ( ExtendCall call |
780+ call .getDestinationOperand ( ) = getAPropertyRead ( "prototype" ) and
781+ result = call .getASourceOperand ( )
782+ )
783+ }
784+
785+ override DataFlow:: Node getASuperClassNode ( ) {
786+ // C.prototype = Object.create(D.prototype)
787+ exists ( DataFlow:: InvokeNode objectCreate , DataFlow:: PropRead superProto |
788+ getAPropertySource ( "prototype" ) = objectCreate and
789+ objectCreate = DataFlow:: globalVarRef ( "Object" ) .getAMemberCall ( "create" ) and
790+ superProto .flowsTo ( objectCreate .getArgument ( 0 ) ) and
791+ superProto .getPropertyName ( ) = "prototype" and
792+ result = superProto .getBase ( )
793+ )
794+ or
795+ // C.prototype = new D()
796+ exists ( DataFlow:: NewNode newCall |
797+ getAPropertySource ( "prototype" ) = newCall and
798+ result = newCall .getCalleeNode ( )
799+ )
800+ }
801+ }
802+ }
0 commit comments