Skip to content

Commit 524f5f6

Browse files
committed
Optimize UI5 getNode/getModel predicate evaluation
Extract expensive disjuncts from getModel() and getNode() into pragma[nomagic] helper predicates to reduce redundant evaluation: - getDefaultODataModel: isolates FragmentLoad cross-flow analysis, adds early guard restricting to .fragment.xml files - getNonStaticJsonModelNode: separates non-static JsonModel lookup - getExternalModelNode: prevents getModel() inlining into getNode() Achieves ~35% reduction in per-query evaluation time on a representative codebase. All 72 existing unit tests pass with identical results.
1 parent 7ebe6b9 commit 524f5f6

File tree

1 file changed

+56
-30
lines changed
  • javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5

1 file changed

+56
-30
lines changed

javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5View.qll

Lines changed: 56 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -134,27 +134,7 @@ abstract class UI5BindingPath extends BindingPath {
134134
)
135135
or
136136
/* 5. There is no call to `setModel` in the same webapp and a default model exists that is related to the binding path this refers to */
137-
exists(DefaultODataServiceModel defaultModel |
138-
result = defaultModel and
139-
not exists(MethodCallNode viewSetModelCall |
140-
viewSetModelCall.getMethodName() = "setModel" and
141-
inSameWebApp(this.getLocation().getFile(), viewSetModelCall.getFile())
142-
) and
143-
/*
144-
* this binding path can occur in a fragment that is the receiver object for the bindElement model approximation
145-
* i.e. checks that the default model is relevant
146-
*/
147-
148-
exists(FragmentLoad load |
149-
load.getCallbackObjectReference().flowsTo(defaultModel.asBinding().asDataFlowNode()) and
150-
load.getNameArgument()
151-
.getStringValue()
152-
.matches("%" +
153-
this.getLocation().getFile().getBaseName().replaceAll(".fragment.xml", "") + "%") and
154-
// The fragment load call must be in the same webapp as the fragment file
155-
inSameWebApp(this.getLocation().getFile(), load.getFile())
156-
)
157-
)
137+
result = getDefaultODataModel(this)
158138
)
159139
// and
160140
// /* This binding path and the resulting model should live inside the same webapp */
@@ -184,20 +164,66 @@ abstract class UI5BindingPath extends BindingPath {
184164
inSameWebApp(this.getLocation().getFile(), result.getFile())
185165
)
186166
or
187-
exists(JsonModel model, CustomController controller |
188-
not model.contentIsStaticallyVisible() and
189-
result = model and
190-
this.getView() = controller.getAViewReference().getDefinition() and
191-
controller.getModel() = result
192-
)
167+
/* 1-3. Internal (Client-side) model, content not statically visible */
168+
result = getNonStaticJsonModelNode(this)
193169
or
194170
/* 2. External (Server-side) model */
195-
result = this.getModel().(UI5ExternalModel) and
196-
/* Restrict search to inside the same webapp. */
197-
inSameWebApp(this.getLocation().getFile(), result.getFile())
171+
result = getExternalModelNode(this)
198172
}
199173
}
200174

175+
/**
176+
* Gets the `DefaultODataServiceModel` for a binding path in a fragment,
177+
* when no explicit `setModel` call exists in the webapp.
178+
*
179+
* `nomagic` to keep the `FragmentLoad` cross-flow analysis separate from the
180+
* main `getModel()` evaluation.
181+
*/
182+
pragma[nomagic]
183+
private DefaultODataServiceModel getDefaultODataModel(UI5BindingPath bindingPath) {
184+
// Only applies to fragment XML files
185+
bindingPath.getLocation().getFile().getBaseName().matches("%.fragment.xml") and
186+
not exists(MethodCallNode viewSetModelCall |
187+
viewSetModelCall.getMethodName() = "setModel" and
188+
inSameWebApp(bindingPath.getLocation().getFile(), viewSetModelCall.getFile())
189+
) and
190+
exists(FragmentLoad load |
191+
load.getCallbackObjectReference().flowsTo(result.asBinding().asDataFlowNode()) and
192+
load.getNameArgument()
193+
.getStringValue()
194+
.matches("%" +
195+
bindingPath.getLocation().getFile().getBaseName().replaceAll(".fragment.xml", "") + "%") and
196+
inSameWebApp(bindingPath.getLocation().getFile(), load.getFile())
197+
)
198+
}
199+
200+
/**
201+
* Gets the `DataFlow::Node` for a non-statically-visible `JsonModel`.
202+
*
203+
* `nomagic` to avoid re-evaluation of `getModel()` when combined with other
204+
* `getNode()` disjuncts.
205+
*/
206+
pragma[nomagic]
207+
private JsonModel getNonStaticJsonModelNode(UI5BindingPath bindingPath) {
208+
not result.contentIsStaticallyVisible() and
209+
exists(CustomController controller |
210+
bindingPath.getView() = controller.getAViewReference().getDefinition() and
211+
controller.getModel() = result
212+
)
213+
}
214+
215+
/**
216+
* Gets the external (server-side) model node associated with a binding path.
217+
*
218+
* `nomagic` to prevent `getModel()` from being inlined and duplicated across
219+
* the `getNode()` disjuncts.
220+
*/
221+
pragma[nomagic]
222+
private UI5ExternalModel getExternalModelNode(UI5BindingPath bindingPath) {
223+
result = bindingPath.getModel() and
224+
inSameWebApp(bindingPath.getLocation().getFile(), result.getFile())
225+
}
226+
201227
class XmlControlProperty extends XmlAttribute {
202228
XmlControlProperty() { exists(UI5Control control | this.getElement() = control.asXmlControl()) }
203229
}

0 commit comments

Comments
 (0)