From b6ab1a16e7e48d2d5ba23020c2c7466b72b516fa Mon Sep 17 00:00:00 2001 From: Patrick Ziegler Date: Fri, 12 Dec 2025 16:36:57 +0100 Subject: [PATCH] Fix InterceptJSInvokeTypeFlow encountering primitive flow states The InterceptJSInvokeTypeFlow is created with only the type flows for non-primitive arguments. However, there was a bug in onObservedSaturated that when the method was called with `observed==null` would add primitive typeflow states to the arguments array again. --- .../pointsto/flow/InterceptJSInvokeTypeFlow.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/pointsto/flow/InterceptJSInvokeTypeFlow.java b/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/pointsto/flow/InterceptJSInvokeTypeFlow.java index 8628b31f4121..c2d9aef8e817 100644 --- a/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/pointsto/flow/InterceptJSInvokeTypeFlow.java +++ b/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/pointsto/flow/InterceptJSInvokeTypeFlow.java @@ -43,6 +43,8 @@ */ public class InterceptJSInvokeTypeFlow extends TypeFlow { private final AnalysisMethod targetMethod; + /// One [TypeFlow] per method argument (including receiver for non-static methods at index 0). + /// Contains `null` for primitive arguments, in which case that entry should be ignored. private final TypeFlow[] arguments; public InterceptJSInvokeTypeFlow(BytecodePosition source, AnalysisMethod targetMethod, TypeFlow[] arguments) { @@ -99,7 +101,16 @@ public void onObservedSaturated(PointsToAnalysis bb, TypeFlow observed) { // If the input type is saturated, then it must be replaced with the // all-instantiated-type-flow of the corresponding type parameter. for (int i = 0; i < this.arguments.length; i++) { - if (this.arguments[i] == observed) { + TypeFlow argument = this.arguments[i]; + if (argument == null) { + // Ignore arguments without typeflows (they're primitive type arguments) + continue; + } + + // observed == null is used when this method call is deferred (because this flow wasn't + // enabled yet when the observed flow was saturated). In that case, we need to + // conservatively treat all argument flows as saturated. + if (observed == null || argument == observed) { TypeFlow paramTypeFlow; if (targetMethod.isStatic()) { paramTypeFlow = targetMethod.getSignature().getParameterType(i).getTypeFlow(bb, true); @@ -110,6 +121,9 @@ public void onObservedSaturated(PointsToAnalysis bb, TypeFlow observed) { paramTypeFlow = targetMethod.getSignature().getParameterType(i - 1).getTypeFlow(bb, true); } } + + assert !paramTypeFlow.getState().isPrimitive() : "Found primitive type state for argument " + i + " in method " + this.targetMethod; + this.arguments[i] = paramTypeFlow; this.arguments[i].addUse(bb, this); this.arguments[i].addObserver(bb, this);