@@ -230,6 +230,72 @@ module API {
230
230
result = this .getASuccessor ( Label:: promisedError ( ) )
231
231
}
232
232
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
+
233
299
/**
234
300
* Gets a string representation of the lexicographically least among all shortest access paths
235
301
* from the root to this node.
@@ -570,6 +636,15 @@ module API {
570
636
lbl = Label:: memberFromRef ( pw )
571
637
)
572
638
)
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
+ )
573
648
}
574
649
575
650
/**
@@ -699,6 +774,98 @@ module API {
699
774
lbl = Label:: parameter ( 1 ) and
700
775
ref = awaited ( call )
701
776
)
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
+ )
702
869
)
703
870
}
704
871
@@ -1106,6 +1273,15 @@ module API {
1106
1273
/** Gets the `promisedError` edge label connecting a promise to its rejected value. */
1107
1274
LabelPromisedError promisedError ( ) { any ( ) }
1108
1275
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
+
1109
1285
/** Gets an entry-point label for the entry-point `e`. */
1110
1286
LabelEntryPoint entryPoint ( API:: EntryPoint e ) { result .getEntryPoint ( ) = e }
1111
1287
@@ -1140,6 +1316,9 @@ module API {
1140
1316
MkLabelReturn ( ) or
1141
1317
MkLabelPromised ( ) or
1142
1318
MkLabelPromisedError ( ) or
1319
+ MkLabelDecoratedClass ( ) or
1320
+ MkLabelDecoratedMember ( ) or
1321
+ MkLabelDecoratedParameter ( ) or
1143
1322
MkLabelEntryPoint ( API:: EntryPoint e )
1144
1323
1145
1324
/** A label for an entry-point. */
@@ -1229,6 +1408,21 @@ module API {
1229
1408
class LabelReceiver extends ApiLabel , MkLabelReceiver {
1230
1409
override string toString ( ) { result = "receiver" }
1231
1410
}
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
+ }
1232
1426
}
1233
1427
}
1234
1428
}
0 commit comments