@@ -230,6 +230,72 @@ module API {
230230 result = this .getASuccessor ( Label:: promisedError ( ) )
231231 }
232232
233+ /**
234+ * Gets any class that has this value as a decorator.
235+ *
236+ * For example:
237+ * ```js
238+ * import { D } from "foo";
239+ *
240+ * // moduleImport("foo").getMember("D").getADecoratedClass()
241+ * @D
242+ * class C1 {}
243+ *
244+ * // moduleImport("foo").getMember("D").getReturn().getADecoratedClass()
245+ * @D()
246+ * class C2 {}
247+ * ```
248+ */
249+ cached
250+ Node getADecoratedClass ( ) { result = this .getASuccessor ( Label:: decoratedClass ( ) ) }
251+
252+ /**
253+ * Gets any method, field, or accessor that has this value as a decorator.
254+ *
255+ * In the case of an accessor, this gets the return value of a getter, or argument to a setter.
256+ *
257+ * For example:
258+ * ```js
259+ * import { D } from "foo";
260+ *
261+ * class C {
262+ * // moduleImport("foo").getMember("D").getADecoratedMember()
263+ * @D m1() {}
264+ * @D f;
265+ * @D get g() { return this.x; }
266+ *
267+ * // moduleImport("foo").getMember("D").getReturn().getADecoratedMember()
268+ * @D() m2() {}
269+ * @D() f2;
270+ * @D() get g2() { return this.x; }
271+ * }
272+ * ```
273+ */
274+ cached
275+ Node getADecoratedMember ( ) { result = this .getASuccessor ( Label:: decoratedMember ( ) ) }
276+
277+ /**
278+ * Gets any parameter that has this value as a decorator.
279+ *
280+ * For example:
281+ * ```js
282+ * import { D } from "foo";
283+ *
284+ * class C {
285+ * method(
286+ * // moduleImport("foo").getMember("D").getADecoratedParameter()
287+ * @D
288+ * param1,
289+ * // moduleImport("foo").getMember("D").getReturn().getADecoratedParameter()
290+ * @D()
291+ * param2
292+ * ) {}
293+ * }
294+ * ```
295+ */
296+ cached
297+ Node getADecoratedParameter ( ) { result = this .getASuccessor ( Label:: decoratedParameter ( ) ) }
298+
233299 /**
234300 * Gets a string representation of the lexicographically least among all shortest access paths
235301 * from the root to this node.
@@ -570,6 +636,15 @@ module API {
570636 lbl = Label:: memberFromRef ( pw )
571637 )
572638 )
639+ or
640+ decoratorDualEdge ( base , lbl , rhs )
641+ or
642+ decoratorRhsEdge ( base , lbl , rhs )
643+ or
644+ exists ( DataFlow:: PropWrite write |
645+ decoratorPropEdge ( base , lbl , write ) and
646+ rhs = write .getRhs ( )
647+ )
573648 }
574649
575650 /**
@@ -699,6 +774,98 @@ module API {
699774 lbl = Label:: parameter ( 1 ) and
700775 ref = awaited ( call )
701776 )
777+ or
778+ decoratorDualEdge ( base , lbl , ref )
779+ or
780+ decoratorUseEdge ( base , lbl , ref )
781+ or
782+ // for fields and accessors, mark the reads as use-nodes
783+ decoratorPropEdge ( base , lbl , ref .( DataFlow:: PropRead ) )
784+ )
785+ }
786+
787+ /** Holds if `base` is a use-node that flows to the decorator expression of the given decorator. */
788+ pragma [ nomagic]
789+ private predicate useNodeFlowsToDecorator ( TApiNode base , Decorator decorator ) {
790+ exists ( DataFlow:: SourceNode decoratorSrc |
791+ use ( base , decoratorSrc ) and
792+ trackUseNode ( decoratorSrc ) .flowsToExpr ( decorator .getExpression ( ) )
793+ )
794+ }
795+
796+ /**
797+ * Holds if `ref` corresponds to both a use and def-node that should have an incoming edge from `base` labelled `lbl`.
798+ *
799+ * This happens because the decorated value escapes into the decorator function, and is then replaced
800+ * by the function's return value. In the JS analysis we generally assume decorators return their input,
801+ * but library models may want to find the return value.
802+ */
803+ private predicate decoratorDualEdge ( TApiNode base , Label:: ApiLabel lbl , DataFlow:: Node ref ) {
804+ exists ( ClassDefinition cls |
805+ useNodeFlowsToDecorator ( base , cls .getADecorator ( ) ) and
806+ lbl = Label:: decoratedClass ( ) and
807+ ref = DataFlow:: valueNode ( cls )
808+ )
809+ or
810+ exists ( MethodDefinition method |
811+ useNodeFlowsToDecorator ( base , method .getADecorator ( ) ) and
812+ not method instanceof AccessorMethodDefinition and
813+ lbl = Label:: decoratedMember ( ) and
814+ ref = DataFlow:: valueNode ( method .getBody ( ) )
815+ )
816+ }
817+
818+ /** Holds if `ref` is a use that should have an incoming edge from `base` labelled `lbl`, induced by a decorator. */
819+ private predicate decoratorUseEdge ( TApiNode base , Label:: ApiLabel lbl , DataFlow:: Node ref ) {
820+ exists ( SetterMethodDefinition accessor |
821+ useNodeFlowsToDecorator ( base ,
822+ [ accessor .getADecorator ( ) , accessor .getCorrespondingGetter ( ) .getADecorator ( ) ] ) and
823+ lbl = Label:: decoratedMember ( ) and
824+ ref = DataFlow:: parameterNode ( accessor .getBody ( ) .getParameter ( 0 ) )
825+ )
826+ or
827+ exists ( Parameter param |
828+ useNodeFlowsToDecorator ( base , param .getADecorator ( ) ) and
829+ lbl = Label:: decoratedParameter ( ) and
830+ ref = DataFlow:: parameterNode ( param )
831+ )
832+ }
833+
834+ /** Holds if `rhs` is a def node that should have an incoming edge from `base` labelled `lbl`, induced by a decorator. */
835+ private predicate decoratorRhsEdge ( TApiNode base , Label:: ApiLabel lbl , DataFlow:: Node rhs ) {
836+ exists ( GetterMethodDefinition accessor |
837+ useNodeFlowsToDecorator ( base ,
838+ [ accessor .getADecorator ( ) , accessor .getCorrespondingSetter ( ) .getADecorator ( ) ] ) and
839+ lbl = Label:: decoratedMember ( ) and
840+ rhs = DataFlow:: valueNode ( accessor .getBody ( ) .getAReturnedExpr ( ) )
841+ )
842+ }
843+
844+ /**
845+ * Holds if `ref` is a reference to a field/accessor that should have en incoming edge from base labelled `lbl`.
846+ *
847+ * Since fields do not have their own data-flow nodes, we generate a node for each read or write.
848+ * For property writes, the right-hand side becomes a def-node and property reads become use-nodes.
849+ *
850+ * For accessors this predicate computes each use of the accessor.
851+ * The return value inside the accessor is computed by the `decoratorRhsEdge` predicate.
852+ */
853+ private predicate decoratorPropEdge ( TApiNode base , Label:: ApiLabel lbl , DataFlow:: PropRef ref ) {
854+ exists ( MemberDefinition fieldLike , DataFlow:: ClassNode cls |
855+ fieldLike instanceof FieldDefinition
856+ or
857+ fieldLike instanceof AccessorMethodDefinition
858+ |
859+ useNodeFlowsToDecorator ( base , fieldLike .getADecorator ( ) ) and
860+ lbl = Label:: decoratedMember ( ) and
861+ cls = fieldLike .getDeclaringClass ( ) .flow ( ) and
862+ (
863+ fieldLike .isStatic ( ) and
864+ ref = cls .getAClassReference ( ) .getAPropertyReference ( fieldLike .getName ( ) )
865+ or
866+ not fieldLike .isStatic ( ) and
867+ ref = cls .getAnInstanceReference ( ) .getAPropertyReference ( fieldLike .getName ( ) )
868+ )
702869 )
703870 }
704871
@@ -1106,6 +1273,15 @@ module API {
11061273 /** Gets the `promisedError` edge label connecting a promise to its rejected value. */
11071274 LabelPromisedError promisedError ( ) { any ( ) }
11081275
1276+ /** Gets the label for an edge leading from a value `D` to any class that has `D` as a decorator. */
1277+ LabelDecoratedClass decoratedClass ( ) { any ( ) }
1278+
1279+ /** Gets the label for an edge leading from a value `D` to any method, field, or accessor that has `D` as a decorator. */
1280+ LabelDecoratedMethod decoratedMember ( ) { any ( ) }
1281+
1282+ /** Gets the label for an edge leading from a value `D` to any parameter that has `D` as a decorator. */
1283+ LabelDecoratedParameter decoratedParameter ( ) { any ( ) }
1284+
11091285 /** Gets an entry-point label for the entry-point `e`. */
11101286 LabelEntryPoint entryPoint ( API:: EntryPoint e ) { result .getEntryPoint ( ) = e }
11111287
@@ -1140,6 +1316,9 @@ module API {
11401316 MkLabelReturn ( ) or
11411317 MkLabelPromised ( ) or
11421318 MkLabelPromisedError ( ) or
1319+ MkLabelDecoratedClass ( ) or
1320+ MkLabelDecoratedMember ( ) or
1321+ MkLabelDecoratedParameter ( ) or
11431322 MkLabelEntryPoint ( API:: EntryPoint e )
11441323
11451324 /** A label for an entry-point. */
@@ -1229,6 +1408,21 @@ module API {
12291408 class LabelReceiver extends ApiLabel , MkLabelReceiver {
12301409 override string toString ( ) { result = "receiver" }
12311410 }
1411+
1412+ /** A label for a class decorated by the current value. */
1413+ class LabelDecoratedClass extends ApiLabel , MkLabelDecoratedClass {
1414+ override string toString ( ) { result = "decorated-class" }
1415+ }
1416+
1417+ /** A label for a method, field, or accessor decorated by the current value. */
1418+ class LabelDecoratedMethod extends ApiLabel , MkLabelDecoratedMember {
1419+ override string toString ( ) { result = "decorated-member" }
1420+ }
1421+
1422+ /** A label for a parameter decorated by the current value. */
1423+ class LabelDecoratedParameter extends ApiLabel , MkLabelDecoratedParameter {
1424+ override string toString ( ) { result = "decorated-parameter" }
1425+ }
12321426 }
12331427 }
12341428}
0 commit comments