Skip to content

Commit

Permalink
Merge pull request #6691 from sdedic/gradle/project-loading-recursion
Browse files Browse the repository at this point in the history
Prevent endless recursion, limit traversal depth.
  • Loading branch information
sdedic authored Dec 4, 2023
2 parents b9cadbc + d726811 commit ca7b58b
Show file tree
Hide file tree
Showing 20 changed files with 429 additions and 57 deletions.
13 changes: 13 additions & 0 deletions extide/gradle/apichanges.xml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,19 @@ is the proper place.
<!-- ACTUAL CHANGES BEGIN HERE: -->

<changes>
<change id="gradle-report-severity">
<api name="general"/>
<summary>Gradle project problems have severity and stacktraces</summary>
<version major="2" minor="38"/>
<date day="13" month="11" year="2023"/>
<author login="sdedic"/>
<compatibility semantic="compatible"/>
<description>
<p>
Reports from the NB tooling gradle plugin are now annotated by severity.
</p>
</description>
</change>
<change id="gradle-setting-deprecation">
<api name="general"/>
<summary>Some Gradle Settings Removed from Use</summary>
Expand Down
2 changes: 1 addition & 1 deletion extide/gradle/manifest.mf
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ AutoUpdate-Show-In-Client: true
OpenIDE-Module: org.netbeans.modules.gradle/2
OpenIDE-Module-Layer: org/netbeans/modules/gradle/layer.xml
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/gradle/Bundle.properties
OpenIDE-Module-Specification-Version: 2.37
OpenIDE-Module-Specification-Version: 2.38
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
import org.gradle.plugin.use.PluginId;
import org.gradle.util.GradleVersion;
import org.netbeans.modules.gradle.tooling.internal.NbProjectInfo;
import org.netbeans.modules.gradle.tooling.internal.NbProjectInfo.Report;

/**
*
Expand All @@ -125,6 +126,18 @@ class NbProjectInfoBuilder {
* project loader is enabled to FINER level.
*/
private static final Logger LOG = Logging.getLogger(NbProjectInfoBuilder.class);

/**
* Maximum recursion depth into structures, arrays and maps. If this depth is reached, the
* builder will record a warning and stop descending.
*/
private static final int MAX_INTROSPECTION_DEPTH = 25;

/**
* If a warning is recorded during introspection, further warnings will
* be suppressed until the introspector goes up to this level.
*/
private static final int INTROSPECTION_RESET_DEPTH_WARNING = 5;

/**
* Name of the default extensibility point for various domain objects defined by Gradle.
Expand Down Expand Up @@ -213,6 +226,7 @@ public ValueAndType(Class type) {

public NbProjectInfo buildAll() {
adapter.setModel(model);

runAndRegisterPerf(model, "meta", this::detectProjectMetadata);
detectProps(model);
detectLicense(model);
Expand All @@ -226,15 +240,13 @@ public NbProjectInfo buildAll() {
// introspection is only allowed for gradle 7.4 and above.
// TODO: investigate if some of the instrospection could be done for earlier Gradles.
sinceGradle("7.0", () -> {
initIntrospection();

runAndRegisterPerf(model, "detectExtensions", this::detectExtensions);
});
sinceGradle("7.0", () -> {
runAndRegisterPerf(model, "detectPlugins2", this::detectAdditionalPlugins);
});
sinceGradle("7.0", () -> {
runAndRegisterPerf(model, "taskDependencies", this::detectTaskDependencies);
runAndRegisterPerf(model, "taskProperties", this::detectTaskProperties);
});
runAndRegisterPerf(model, "taskProperties", this::detectTaskProperties);
runAndRegisterPerf(model, "artifacts", this::detectConfigurationArtifacts);
storeGlobalTypes(model);
return model;
Expand Down Expand Up @@ -305,7 +317,7 @@ private void detectTaskProperties(NbProjectInfoModel model) {
Class nonDecorated = findNonDecoratedClass(taskClass);

taskPropertyTypes.put(task.getName(), nonDecorated.getName());
inspectObjectAndValues(taskClass, task, task.getName() + ".", globalTypes, taskPropertyTypes, taskProperties, EXCLUDE_TASK_PROPERTIES, true); // NOI18N
startInspectObjectAndValues(taskClass, task, task.getName() + ".", globalTypes, taskPropertyTypes, taskProperties, EXCLUDE_TASK_PROPERTIES, true); // NOI18N
}

model.getInfo().put("tasks.propertyValues", taskProperties); // NOI18N
Expand Down Expand Up @@ -433,7 +445,7 @@ private void storeGlobalTypes(NbProjectInfoModel model) {
model.getInfo().put("extensions.globalTypes", globalTypes); // NOI18N
}

private void detectExtensions(NbProjectInfoModel model) {
private void initIntrospection() {
StringBuilder sb = new StringBuilder();
for (String s : IGNORED_SYSTEM_CLASSES_REGEXP) {
if (sb.length() > 0) {
Expand All @@ -442,7 +454,9 @@ private void detectExtensions(NbProjectInfoModel model) {
sb.append(s);
}
ignoreClassesPattern = Pattern.compile(sb.toString());

}

private void detectExtensions(NbProjectInfoModel model) {
inspectExtensions("", project.getExtensions());
model.getInfo().put("extensions.propertyTypes", propertyTypes); // NOI18N
model.getInfo().put("extensions.propertyValues", values); // NOI18N
Expand Down Expand Up @@ -539,19 +553,53 @@ private static boolean isPrimitiveOrString(Class c) {
* @param propertyTypes
* @param defaultValues
*/
private void startInspectObjectAndValues(Class clazz, Object object, String prefix, Map<String, Map<String, String>> globalTypes, Map<String, String> propertyTypes, Map<String, Object> defaultValues) {
depth = 0;
inspectObjectAndValues(clazz, object, prefix, globalTypes, propertyTypes, defaultValues, null, true);
}

private void startInspectObjectAndValues(Class clazz, Object object, String prefix, Map<String, Map<String, String>> globalTypes, Map<String, String> propertyTypes, Map<String, Object> defaultValues, Set<String> excludes, boolean type) {
depth = 0;
inspectObjectAndValues(clazz, object, prefix, globalTypes, propertyTypes, defaultValues, excludes, type);
}

private void inspectObjectAndValues(Class clazz, Object object, String prefix, Map<String, Map<String, String>> globalTypes, Map<String, String> propertyTypes, Map<String, Object> defaultValues) {
inspectObjectAndValues(clazz, object, prefix, globalTypes, propertyTypes, defaultValues, null, true);
}
/**
* The current introspection depth
*/
private int depth;

/**
* If true, depth exceeded warnings will not be logged.
*/
private boolean suppressDepthWarning;

private void inspectObjectAndValues(Class clazz, Object object, String prefix, Map<String, Map<String, String>> globalTypes, Map<String, String> propertyTypes, Map<String, Object> defaultValues, Set<String> excludes, boolean type) {
if (valueIdentities.put(object, Boolean.TRUE) == Boolean.TRUE) {
return;
}
try {
if (depth++ >= MAX_INTROSPECTION_DEPTH) {
if (!suppressDepthWarning) {
LOG.warn("Too deep structure, truncating");
model.noteProblem(Report.Severity.WARNING,
String.format("Object structure too deep encountered in class %s", clazz),
String.format("Object structure is too deep for the project model builder. This is unlikely to affect basic project operations. "
+ "Check logs and report this issue. The path for the object: %s, current value: %s", prefix, object));
suppressDepthWarning = true;
}
return;
} else if (depth <= INTROSPECTION_RESET_DEPTH_WARNING) {
suppressDepthWarning = false;
}
inspectObjectAndValues0(clazz, object, prefix, globalTypes, propertyTypes, defaultValues, excludes, type);
} catch (RuntimeException ex) {
LOG.warn("Error during inspection of {}, value {}, prefix {}", clazz, object, prefix);
model.noteProblem(ex, true);
} finally {
depth--;
valueIdentities.remove(object);
}
}
Expand Down Expand Up @@ -753,7 +801,7 @@ private void dumpContainerProperties(Map<String, ?> m, String prefix, Map<String
if (v == null) {
defaultValues.put(prefix + "." + k, null); // NOI18N
} else {
defaultValues.put(prefix + "." + k, Objects.toString(v)); // NOI18N
defaultValues.put(prefix + "." + k, objectToString(v)); // NOI18N
inspectObjectAndValues(v.getClass(), v, newPrefix, globalTypes, propertyTypes, defaultValues, null, false);
}
}
Expand Down Expand Up @@ -789,8 +837,8 @@ private boolean dumpValue(Object value, String prefix, Map<String, String> prope
Object v = m.get(k);
if (v == null) {
defaultValues.put(prefix + "." + k, null); // NOI18N
} else {
defaultValues.put(prefix + "." + k, Objects.toString(v)); // NOI18N
} else if (!v.equals(value)) {
defaultValues.put(prefix + "." + k, objectToString(v)); // NOI18N
inspectObjectAndValues(v.getClass(), v, newPrefix, globalTypes, propertyTypes, defaultValues, null, false);
}
}
Expand Down Expand Up @@ -821,7 +869,7 @@ private boolean dumpValue(Object value, String prefix, Map<String, String> prope
String newPrefix = prefix + "[" + index + "]."; // NOI18N
if (o == null) {
defaultValues.put(prefix + "[" + index + "]", null); //NOI18N
} else {
} else if (!o.equals(value)) {
defaultValues.put(prefix + "[" + index + "]", Objects.toString(o)); //NOI18N
inspectObjectAndValues(o.getClass(), o, newPrefix, globalTypes, propertyTypes, defaultValues, null, false);
}
Expand Down Expand Up @@ -869,8 +917,8 @@ private boolean dumpValue(Object value, String prefix, Map<String, String> prope
Object v = mvalue.get(o);
if (v == null) {
defaultValues.put(newPrefix, null); // NOI18N
} else {
defaultValues.put(newPrefix, Objects.toString(v)); // NOI18N
} else if (!v.equals(value)) {
defaultValues.put(newPrefix, objectToString(v)); // NOI18N
inspectObjectAndValues(v.getClass(), v, newPrefix + ".", globalTypes, propertyTypes, defaultValues, null, itemClass == null);
}
}
Expand All @@ -880,6 +928,17 @@ private boolean dumpValue(Object value, String prefix, Map<String, String> prope
return dumped;
}

private static String objectToString(Object o) {
if (o instanceof Map || o instanceof Iterable) {
return o.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(o));
}
try {
return Objects.toString(o);
} catch (RuntimeException ex) {
return o.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(o));
}
}

private static Class findNonDecoratedClass(Class clazz) {
while (clazz != Object.class && (clazz.getModifiers() & 0x1000 /* Modifiers.SYNTHETIC */) > 0) {
clazz = clazz.getSuperclass();
Expand Down Expand Up @@ -930,7 +989,7 @@ private void inspectExtensions(String prefix, ExtensionContainer container) {

Class c = findNonDecoratedClass(ext.getClass());
propertyTypes.put(prefix + extName, c.getName());
inspectObjectAndValues(ext.getClass(), ext, prefix + extName + ".", globalTypes, propertyTypes, values);
startInspectObjectAndValues(ext.getClass(), ext, prefix + extName + ".", globalTypes, propertyTypes, values);
if (ext instanceof ExtensionAware) {
inspectExtensions(prefix + extName + ".", ((ExtensionAware)ext).getExtensions()); // NOI18N
}
Expand Down Expand Up @@ -986,7 +1045,8 @@ private void detectProjectMetadata(NbProjectInfoModel model) {
try {
model.getInfo().put("buildClassPath", storeSet(project.getBuildscript().getConfigurations().getByName("classpath").getFiles()));
} catch (RuntimeException e) {
model.noteProblem(e);
// unexpected exception, build classpath should be available.
model.noteProblem(e, true);
}
Set<String[]> tasks = new HashSet<>();
for (org.gradle.api.Task t : project.getTasks()) {
Expand Down Expand Up @@ -1206,15 +1266,15 @@ private void detectSources(NbProjectInfoModel model) {
} catch(Exception e) {
convertOfflineException(e);
// will not be reached
model.noteProblem(e);
model.noteProblem(e, false);
}
sinceGradle("4.6", () -> {
try {
model.getInfo().put(propBase + "classpath_annotation", storeSet(getProperty(sourceSet, "annotationProcessorPath", "files")));
} catch(Exception e) {
convertOfflineException(e);
// will not be reached
model.noteProblem(e);
model.noteProblem(e, false);
}
model.getInfo().put(propBase + "configuration_annotation", getProperty(sourceSet, "annotationProcessorConfigurationName"));
});
Expand All @@ -1230,6 +1290,7 @@ private void detectSources(NbProjectInfoModel model) {
}
} else {
model.getInfo().put("sourcesets", Collections.emptySet());
// TODO: should be this converted to Problem, severity INFO ?
model.noteProblem("No sourceSets found on this project. This project mightbe a Model/Rule based one which is not supported at the moment.");
}
}
Expand All @@ -1246,12 +1307,14 @@ private void detectArtifacts(NbProjectInfoModel model) {
try {
model.getInfo().put("exploded_war_dir", getProperty(project, "explodedWar", "destinationDir"));
} catch(Exception e) {
model.noteProblem(e);
// probably not an internal error, but misconfiguration, omit trace
model.noteProblem(e, false);
}
try {
model.getInfo().put("web_classpath", getProperty(project, "war", "classpath", "files"));
} catch(Exception e) {
model.noteProblem(e);
// probably not an internal error, but misconfiguration, omit trace
model.noteProblem(e, false);
}
}
Map<String, Object> archives = new HashMap<>();
Expand Down Expand Up @@ -1540,7 +1603,7 @@ private void detectDependencies(NbProjectInfoModel model) {

walker.walkResolutionResult(it.getIncoming().getResolutionResult().getRoot());
} catch (ResolveException ex) {
model.noteProblem(ex);
model.noteProblem(ex, false);
}
} else {
unresolvedIds.addAll(componentIds);
Expand Down
Loading

0 comments on commit ca7b58b

Please sign in to comment.