Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -134,45 +134,66 @@ public boolean forNullFieldValue(JavaConstant receiver, AnalysisField field, Sca
}

@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public boolean forNonNullFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) {
if (field.isStatic()) {
TypeData typeData = field.getDeclaringClass().getOrComputeData();
AnalysisFuture<JavaConstant> fieldValueTask = typeData.getFieldTask(field);
if (fieldValueTask.isDone()) {
JavaConstant fieldSnapshot = fieldValueTask.guardedGet();
if (!Objects.equals(fieldSnapshot, fieldValue)) {
Consumer<ScanReason> onAnalysisModified = (deepReason) -> onStaticFieldMismatch(field, fieldSnapshot, fieldValue, deepReason);
scanner.patchStaticField(typeData, field, fieldValue, reason, onAnalysisModified).ensureDone();
heapPatched = true;
Object fieldValueTask = typeData.getFieldValue(field);
if (fieldValueTask instanceof JavaConstant) {
JavaConstant fieldSnapshot = (JavaConstant) fieldValueTask;
verifyStaticFieldValue(typeData, field, fieldSnapshot, fieldValue, reason);
} else if (fieldValueTask instanceof AnalysisFuture<?>) {
AnalysisFuture<JavaConstant> future = (AnalysisFuture<JavaConstant>) fieldValueTask;
if (future.isDone()) {
JavaConstant fieldSnapshot = future.guardedGet();
verifyStaticFieldValue(typeData, field, fieldSnapshot, fieldValue, reason);
} else {
onStaticFieldNotComputed(field, fieldValue, reason);
}
} else {
onStaticFieldNotComputed(field, fieldValue, reason);
}
} else {
ImageHeapInstance receiverObject = (ImageHeapInstance) getReceiverObject(receiver, reason);
AnalysisFuture<JavaConstant> fieldValueTask = receiverObject.getFieldTask(field);
if (fieldValueTask.isDone()) {
JavaConstant fieldSnapshot = fieldValueTask.guardedGet();
if (!Objects.equals(fieldSnapshot, fieldValue)) {
Consumer<ScanReason> onAnalysisModified = (deepReason) -> onInstanceFieldMismatch(receiverObject, field, fieldSnapshot, fieldValue, deepReason);
Object fieldValueTask = receiverObject.getFieldValue(field);
if (fieldValueTask instanceof JavaConstant) {
JavaConstant fieldSnapshot = (JavaConstant) fieldValueTask;
verifyInstanceFieldValue(field, receiverObject, fieldSnapshot, fieldValue, reason);
} else if (fieldValueTask instanceof AnalysisFuture<?>) {
AnalysisFuture<JavaConstant> future = (AnalysisFuture<JavaConstant>) fieldValueTask;
if (future.isDone()) {
JavaConstant fieldSnapshot = future.guardedGet();
verifyInstanceFieldValue(field, receiverObject, fieldSnapshot, fieldValue, reason);
} else {
/*
* There may be some instance fields not yet computed because the verifier
* can insert new objects for annotation proxy implementations when scanning
* types. The annotations are set lazily, based on reachability, since we
* only want annotations in the heap that are otherwise marked as used.
*/
Consumer<ScanReason> onAnalysisModified = (deepReason) -> onInstanceFieldNotComputed(receiverObject, field, fieldValue, deepReason);
scanner.patchInstanceField(receiverObject, field, fieldValue, reason, onAnalysisModified).ensureDone();
heapPatched = true;
}
} else {
/*
* There may be some instance fields not yet computed because the verifier can
* insert new objects for annotation proxy implementations when scanning types.
* The annotations are set lazily, based on reachability, since we only want
* annotations in the heap that are otherwise marked as used.
*/
Consumer<ScanReason> onAnalysisModified = (deepReason) -> onInstanceFieldNotComputed(receiverObject, field, fieldValue, deepReason);
scanner.patchInstanceField(receiverObject, field, fieldValue, reason, onAnalysisModified).ensureDone();
heapPatched = true;
}
}
return false;
}

private void verifyStaticFieldValue(TypeData typeData, AnalysisField field, JavaConstant fieldSnapshot, JavaConstant fieldValue, ScanReason reason) {
if (!Objects.equals(fieldSnapshot, fieldValue)) {
Consumer<ScanReason> onAnalysisModified = (deepReason) -> onStaticFieldMismatch(field, fieldSnapshot, fieldValue, deepReason);
scanner.patchStaticField(typeData, field, fieldValue, reason, onAnalysisModified).ensureDone();
heapPatched = true;
}
}

private void verifyInstanceFieldValue(AnalysisField field, ImageHeapInstance receiverObject, JavaConstant fieldSnapshot, JavaConstant fieldValue, ScanReason reason) {
if (!Objects.equals(fieldSnapshot, fieldValue)) {
Consumer<ScanReason> onAnalysisModified = (deepReason) -> onInstanceFieldMismatch(receiverObject, field, fieldSnapshot, fieldValue, deepReason);
scanner.patchInstanceField(receiverObject, field, fieldValue, reason, onAnalysisModified).ensureDone();
heapPatched = true;
}
}

@Override
public boolean forNullArrayElement(JavaConstant array, AnalysisType arrayType, int elementIndex, ScanReason reason) {
boolean result = scanner.getScanningObserver().forNullArrayElement(array, arrayType, elementIndex, reason);
Expand All @@ -194,26 +215,47 @@ public boolean forNonNullArrayElement(JavaConstant array, AnalysisType arrayType
return false;
}

@SuppressWarnings({"unchecked", "rawtypes"})
private ImageHeapObject getReceiverObject(JavaConstant constant, ScanReason reason) {
AnalysisFuture<ImageHeapObject> task = imageHeap.getTask(constant);
if (task == null || !task.isDone()) {
throw error(reason, "Task %s for constant %s.", (task == null ? "is null" : "not yet executed"), constant);
Object task = imageHeap.getTask(constant);
if (task == null) {
throw error(reason, "Task is null for constant %s.", constant);
} else if (task instanceof ImageHeapObject) {
return (ImageHeapObject) task;
} else {
assert task instanceof AnalysisFuture;
AnalysisFuture<ImageHeapObject> future = ((AnalysisFuture<ImageHeapObject>) task);
if (future.isDone()) {
return future.guardedGet();
} else {
throw error(reason, "Task not yet executed for constant %s.", constant);
}
}
return task.guardedGet();
}

@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public void forEmbeddedRoot(JavaConstant root, ScanReason reason) {
AnalysisFuture<ImageHeapObject> rootTask = imageHeap.getTask(root);
Object rootTask = imageHeap.getTask(root);
if (rootTask == null) {
throw error(reason, "No snapshot task found for embedded root %s %n", root);
} else if (rootTask.isDone()) {
JavaConstant rootSnapshot = rootTask.guardedGet().getObject();
if (!Objects.equals(rootSnapshot, root)) {
throw error(reason, "Value mismatch for embedded root %n snapshot: %s %n new value: %s %n", rootSnapshot, root);
}
} else if (rootTask instanceof ImageHeapObject) {
JavaConstant snapshot = ((ImageHeapObject) rootTask).getObject();
verifyEmbeddedRoot(snapshot, root, reason);
} else {
throw error(reason, "Snapshot not yet computed for embedded root %n new value: %s %n", root);
AnalysisFuture<ImageHeapObject> future = ((AnalysisFuture<ImageHeapObject>) rootTask);
if (future.isDone()) {
JavaConstant snapshot = future.guardedGet().getObject();
verifyEmbeddedRoot(snapshot, root, reason);
} else {
throw error(reason, "Snapshot not yet computed for embedded root %n new value: %s %n", root);
}
}
}

private void verifyEmbeddedRoot(JavaConstant rootSnapshot, JavaConstant root, ScanReason reason) {
if (!Objects.equals(rootSnapshot, root)) {
throw error(reason, "Value mismatch for embedded root %n snapshot: %s %n new value: %s %n", rootSnapshot, root);
}
}

Expand Down Expand Up @@ -241,26 +283,37 @@ private void ensureTypeScanned(JavaConstant typeConstant, AnalysisType type, Sca
ensureTypeScanned(null, typeConstant, type, reason);
}

private void ensureTypeScanned(JavaConstant object, JavaConstant typeConstant, AnalysisType type, ScanReason reason) {
@SuppressWarnings({"unchecked", "rawtypes"})
private void ensureTypeScanned(JavaConstant value, JavaConstant typeConstant, AnalysisType type, ScanReason reason) {
AnalysisError.guarantee(type.isReachable(), "The heap snapshot verifier discovered a type not marked as reachable " + type.toJavaName());
AnalysisFuture<ImageHeapObject> task = imageHeap.getTask(typeConstant);
Object task = imageHeap.getTask(typeConstant);
/* Make sure the DynamicHub value is scanned. */
if (task == null) {
onNoTaskForClassConstant(type, reason);
scanner.toImageHeapObject(typeConstant, reason, null);
heapPatched = true;
} else if (task instanceof ImageHeapObject) {
JavaConstant snapshot = ((ImageHeapObject) task).getObject();
verifyTypeConstant(snapshot, typeConstant, reason);
} else {
if (task.isDone()) {
JavaConstant snapshot = task.guardedGet().getObject();
if (!Objects.equals(snapshot, typeConstant)) {
throw error(reason, "Value mismatch for class constant snapshot: %s %n new value: %s %n", snapshot, typeConstant);
}
assert task instanceof AnalysisFuture;
AnalysisFuture<ImageHeapObject> future = ((AnalysisFuture<ImageHeapObject>) task);
if (future.isDone()) {
JavaConstant snapshot = future.guardedGet().getObject();
verifyTypeConstant(snapshot, typeConstant, reason);
} else {
onTaskForClassConstantNotDone(object, type, reason);
task.ensureDone();
onTaskForClassConstantNotDone(value, type, reason);
future.ensureDone();
}
}
}

private void verifyTypeConstant(JavaConstant snapshot, JavaConstant typeConstant, ScanReason reason) {
if (!Objects.equals(snapshot, typeConstant)) {
throw error(reason, "Value mismatch for class constant snapshot: %s %n new value: %s %n", snapshot, typeConstant);
}
}

}

private void onNoTaskForClassConstant(AnalysisType type, ScanReason reason) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,24 +34,59 @@

import jdk.vm.ci.meta.JavaConstant;

/**
* The heap snapshot. It stores all object snapshots and provides methods to access and update them.
*/
public class ImageHeap {

/** Map the original object *and* the replaced object to the HeapObject snapshot. */
protected ConcurrentHashMap<JavaConstant, AnalysisFuture<ImageHeapObject>> heapObjects;
/** Store a mapping from types to all scanned objects. */
protected Map<AnalysisType, Set<ImageHeapObject>> typesToObjects;
/**
* Map the original object *and* the replaced object to the same snapshot. The value is either a
* not-yet-executed {@link AnalysisFuture} of {@link ImageHeapObject} or its results, an
* {@link ImageHeapObject}.
*/
private final ConcurrentHashMap<JavaConstant, /* ImageHeapObject */ Object> heapObjects;
/** Store a mapping from types to object snapshots. */
private final Map<AnalysisType, Set<ImageHeapObject>> typesToObjects;

/*
* Note on the idea of merging the heapObjects and typesToObjects maps:
*
* - heapObjects maps both the original and the replaced JavaConstant objects to the
* corresponding ImageHeapObject (which also wraps the replaced JavaConstant).
*
* - typesToObjects maps the AnalysisType of the replaced objects to the collection of
* corresponding ImageHeapObject
*
* - If we were to combine the two into a Map<AnalysisType, Map<JavaConstant, Object>> which
* type do we use as a key? That would be the type of the JavaConstant object, but that doesn't
* always match with the type of the ImageHeapObject in case of object replacers that change
* type. This can lead to issues when trying to iterate objects of a specific type, e.g., to
* walk its fields. We could use the type of the replaced object wrapped in the ImageHeapObject,
* but then we wouldn't have a direct way to get from the original JavaConstant to the
* corresponding ImageHeapObject. Which means that we would need to run the replacers *before*
* checking if we already have a registered ImageHeapObject task (in
* ImageHeapScanner.getOrCreateConstantReachableTask()), so may end up running the replacers
* more often, otherwise we can end up with duplicated ImageHeapObject snapshots.
*/

public ImageHeap() {
heapObjects = new ConcurrentHashMap<>();
typesToObjects = new ConcurrentHashMap<>();
}

public AnalysisFuture<ImageHeapObject> getTask(JavaConstant constant) {
/** Record the future computing the snapshot or its result. */
public Object getTask(JavaConstant constant) {
return heapObjects.get(constant);
}

public AnalysisFuture<ImageHeapObject> addTask(JavaConstant constant, AnalysisFuture<ImageHeapObject> object) {
return heapObjects.putIfAbsent(constant, object);
/** Record the future computing the snapshot in the heap. */
public Object setTask(JavaConstant constant, AnalysisFuture<ImageHeapObject> task) {
return heapObjects.putIfAbsent(constant, task);
}

/** Record the snapshot in the heap. */
public void setValue(JavaConstant constant, ImageHeapObject value) {
heapObjects.put(constant, value);
}

public Set<ImageHeapObject> getObjects(AnalysisType type) {
Expand Down
Loading