Copyright (c) 2015, 2016 Google, Inc and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: Stefan Xenos (Google) - Initial implementation
/******************************************************************************* * Copyright (c) 2015, 2016 Google, Inc and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * Stefan Xenos (Google) - Initial implementation *******************************************************************************/
package org.eclipse.jdt.internal.core.nd.field; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.jdt.internal.core.nd.IDestructable; import org.eclipse.jdt.internal.core.nd.ITypeFactory; import org.eclipse.jdt.internal.core.nd.Nd; import org.eclipse.jdt.internal.core.nd.NdNode; import org.eclipse.jdt.internal.core.nd.db.Database; import org.eclipse.jdt.internal.core.nd.db.ModificationLog; import org.eclipse.jdt.internal.core.nd.db.ModificationLog.Tag; import org.eclipse.jdt.internal.core.nd.util.MathUtils;
Defines a data structure that will appear in the database.

There are three mechanisms for deleting a struct from the database:

  • Explicit deletion. This happens synchronously via manual calls to Nd.delete. Structs intended for manual deletion have refCounted=false and an empty ownerFields.
  • Owner pointers. Such structs have one or more outbound pointers to an "owner" object. They are deleted asynchronously when the last owner pointer is deleted. The structs have refCounted=false and a nonempty ownerFields.
  • Refcounting. Such structs are deleted asynchronously when all elements are removed from all of their ManyToOne relationships which are not marked as incoming owner pointers. Owner relationships need to be excluded from refcounting since they would always create cycles. These structs have refCounted=true.

Structs deleted by refcounting and owner pointers are not intended to inherit from one another, but anything may inherit from a struct that uses manual deletion and anything may inherit from a struct that uses the same deletion mechanism.

/** * Defines a data structure that will appear in the database. * <p> * There are three mechanisms for deleting a struct from the database: * <ul> * <li>Explicit deletion. This happens synchronously via manual calls to Nd.delete. Structs intended for manual * deletion have refCounted=false and an empty ownerFields. * <li>Owner pointers. Such structs have one or more outbound pointers to an "owner" object. They are deleted * asynchronously when the last owner pointer is deleted. The structs have refCounted=false and a nonempty * ownerFields. * <li>Refcounting. Such structs are deleted asynchronously when all elements are removed from all of their ManyToOne * relationships which are not marked as incoming owner pointers. Owner relationships need to be excluded from * refcounting since they would always create cycles. These structs have refCounted=true. * </ul> * <p> * Structs deleted by refcounting and owner pointers are not intended to inherit from one another, but anything may * inherit from a struct that uses manual deletion and anything may inherit from a struct that uses the same deletion * mechanism. */
public final class StructDef<T> { Class<T> clazz; private StructDef<? super T> superClass; private Set<StructDef<?>> dependencies = new HashSet<>(); private List<IField> fields = new ArrayList<>(); private boolean doneCalled; private boolean offsetsComputed; private List<StructDef<? extends T>> dependents = new ArrayList<>(); private int size; List<IDestructableField> destructableFields = new ArrayList<>(); boolean refCounted; private List<IRefCountedField> refCountedFields = new ArrayList<>(); private List<IRefCountedField> ownerFields = new ArrayList<>(); boolean isAbstract; private ITypeFactory<T> factory; protected boolean hasUserDestructor; private DeletionSemantics deletionSemantics; final Tag destructTag; private boolean isNdNode; public static enum DeletionSemantics { EXPLICIT, OWNED, REFCOUNTED } private StructDef(Class<T> clazz) { this(clazz, null); } private StructDef(Class<T> clazz, StructDef<? super T> superClass) { this(clazz, superClass, Modifier.isAbstract(clazz.getModifiers())); } private StructDef(Class<T> clazz, StructDef<? super T> superClass, boolean isAbstract) { this.destructTag = ModificationLog.createTag("Destructing struct " + clazz.getSimpleName()); //$NON-NLS-1$ this.clazz = clazz; this.isNdNode = NdNode.class.isAssignableFrom(clazz); this.superClass = superClass; if (this.superClass != null) { addDependency(this.superClass); } this.isAbstract = isAbstract; final String fullyQualifiedClassName = clazz.getName(); final Constructor<T> constructor; if (!this.isAbstract) { try { constructor = clazz.getConstructor(new Class<?>[] { Nd.class, long.class }); } catch (NoSuchMethodException | SecurityException e) { throw new IllegalArgumentException("The node class " + fullyQualifiedClassName //$NON-NLS-1$ + " does not have an appropriate constructor for it to be used with Nd"); //$NON-NLS-1$ } } else { constructor = null; } this.hasUserDestructor = IDestructable.class.isAssignableFrom(clazz); this.factory = new ITypeFactory<T>() { @Override public T create(Nd dom, long address) { if (StructDef.this.isAbstract) { throw new UnsupportedOperationException( "Attempting to instantiate abstract class" + fullyQualifiedClassName); //$NON-NLS-1$ } try { return constructor.newInstance(dom, address); } catch (InvocationTargetException e) { Throwable target = e.getCause(); if (target instanceof RuntimeException) { throw (RuntimeException) target; } throw new RuntimeException("Error in AutoTypeFactory", e); //$NON-NLS-1$ } catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException("Error in AutoTypeFactory", e); //$NON-NLS-1$ } } @Override public int getRecordSize() { return StructDef.this.size(); } @Override public boolean hasDestructor() { return StructDef.this.hasUserDestructor || hasDestructableFields(); } @Override public Class<?> getElementClass() { return StructDef.this.clazz; } @Override public void destruct(Nd nd, long address) { checkNotMutable(); Database db = nd.getDB(); db.getLog().start(StructDef.this.destructTag); try { if (StructDef.this.hasUserDestructor) { IDestructable destructable = (IDestructable)create(nd, address); destructable.destruct(); } destructFields(nd, address); } finally { db.getLog().end(StructDef.this.destructTag); } } @Override public void destructFields(Nd dom, long address) { StructDef.this.destructFields(dom, address); } @Override public boolean isReadyForDeletion(Nd dom, long address) { return StructDef.this.isReadyForDeletion(dom, address); } @Override public DeletionSemantics getDeletionSemantics() { return StructDef.this.getDeletionSemantics(); } }; } public void addDependency(StructDef<?> newDependency) { if (newDependency.hasIndirectDependent(new HashSet<>(), this)) { throw new IllegalArgumentException("Circular dependency detected. Struct " //$NON-NLS-1$ + getStructName() + " and struct " + newDependency.getStructName() //$NON-NLS-1$ + " both depend on one another"); //$NON-NLS-1$ } if (this.dependencies.add(newDependency)) { this.superClass.dependents.add(this); } } private boolean hasIndirectDependent(Set<StructDef<?>> visited, StructDef<?> structDef) { for (StructDef<?> next : this.dependents) { if (!visited.add(next)) { continue; } if (next.equals(structDef)) { return true; } if (next.hasIndirectDependent(visited, structDef)) { return true; } } return false; } public Class<T> getStructClass() { return this.clazz; } @Override public String toString() { return this.clazz.getName(); } public static <T> StructDef<T> createAbstract(Class<T> clazz) { return new StructDef<T>(clazz, null, true); } public static <T> StructDef<T> createAbstract(Class<T> clazz, StructDef<? super T> superClass) { return new StructDef<T>(clazz, superClass, true); } public static <T> StructDef<T> create(Class<T> clazz) { return new StructDef<T>(clazz); } public static <T> StructDef<T> create(Class<T> clazz, StructDef<? super T> superClass) { return new StructDef<T>(clazz, superClass); } protected boolean isReadyForDeletion(Nd dom, long address) { List<IRefCountedField> toIterate = Collections.EMPTY_LIST; switch (this.deletionSemantics) { case EXPLICIT: return false; case OWNED: toIterate = this.ownerFields; break; case REFCOUNTED: toIterate = this.refCountedFields; break; } for (IRefCountedField next : toIterate) { if (next.hasReferences(dom, address)) { return false; } } final StructDef<? super T> localSuperClass = StructDef.this.superClass; if (localSuperClass != null && localSuperClass.deletionSemantics != DeletionSemantics.EXPLICIT) { return localSuperClass.isReadyForDeletion(dom, address); } return true; } protected boolean hasDestructableFields() { return (!StructDef.this.destructableFields.isEmpty() || (StructDef.this.superClass != null && StructDef.this.superClass.hasDestructableFields())); } public DeletionSemantics getDeletionSemantics() { return this.deletionSemantics; } private boolean areAllDependenciesResolved() { for (StructDef<?> next : this.dependencies) { if (!next.areOffsetsComputed()) { return false; } } return true; }
Call this once all the fields have been added to the struct definition and it is ready to use.
/** * Call this once all the fields have been added to the struct definition and it is * ready to use. */
public void done() { if (this.doneCalled) { throw new IllegalStateException("May not call done() more than once"); //$NON-NLS-1$ } this.doneCalled = true; if (areAllDependenciesResolved()) { computeOffsets(); } } public void add(IField toAdd) { checkMutable(); this.fields.add(toAdd); } public void addDestructableField(IDestructableField field) { checkMutable(); this.destructableFields.add(field); } public StructDef<T> useStandardRefCounting() { checkMutable(); this.refCounted = true; return this; } public void addRefCountedField(IRefCountedField result) { checkMutable(); this.refCountedFields.add(result); } public void addOwnerField(IRefCountedField result) { checkMutable(); this.ownerFields.add(result); } public boolean areOffsetsComputed() { return this.offsetsComputed; } public int size() { checkNotMutable(); return this.size; } void checkNotMutable() { if (!this.offsetsComputed) { throw new IllegalStateException("Must call done() before using the struct"); //$NON-NLS-1$ } } private void checkMutable() { if (this.doneCalled) { throw new IllegalStateException("May not modify a StructDef after done() has been called"); //$NON-NLS-1$ } }
Invoked on all StructDef after both done() has been called on the struct and computeOffsets() has been called on every dependency of this struct.
/** * Invoked on all StructDef after both {@link #done()} has been called on the struct and * {@link #computeOffsets()} has been called on every dependency of this struct. */
private void computeOffsets() { int offset = this.superClass == null ? 0 : this.superClass.size(); for (IField next : this.fields) { offset = MathUtils.roundUpToNearestMultiple(offset, next.getAlignment()); next.setOffset(offset); offset += next.getRecordSize(); } this.size = offset; if (this.refCounted) { this.deletionSemantics = DeletionSemantics.REFCOUNTED; } else { if (!this.ownerFields.isEmpty()) { this.deletionSemantics = DeletionSemantics.OWNED; } else if (this.superClass != null) { this.deletionSemantics = this.superClass.deletionSemantics; } else { this.deletionSemantics = DeletionSemantics.EXPLICIT; } } // Now verify that the deletion semantics of this struct are compatible with the deletion // semantics of its superclass if (this.superClass != null && this.deletionSemantics != this.superClass.deletionSemantics) { if (this.superClass.deletionSemantics != DeletionSemantics.EXPLICIT) { throw new IllegalStateException("A class (" + this.clazz.getName() + ") that uses " //$NON-NLS-1$//$NON-NLS-2$ + this.deletionSemantics.toString() + " deletion semantics may not inherit from a class " //$NON-NLS-1$ + "that uses " + this.superClass.deletionSemantics.toString() + " semantics"); //$NON-NLS-1$//$NON-NLS-2$ } } this.offsetsComputed = true; for (StructDef<? extends T> next : this.dependents) { if (next.doneCalled) { next.computeOffsets(); } } } public FieldPointer addPointer() { FieldPointer result = new FieldPointer(getStructName(), this.fields.size()); add(result); return result; } public FieldShort addShort() { FieldShort result = new FieldShort(getStructName(), this.fields.size()); add(result); return result; } public FieldInt addInt() { FieldInt result = new FieldInt(getStructName(), this.fields.size()); add(result); return result; } public FieldLong addLong() { FieldLong result = new FieldLong(getStructName(), this.fields.size()); add(result); return result; } public FieldString addString() { FieldString result = new FieldString(getStructName(), this.fields.size()); add(result); addDestructableField(result); return result; } public FieldDouble addDouble() { FieldDouble result = new FieldDouble(getStructName(), this.fields.size()); add(result); return result; } public FieldFloat addFloat() { FieldFloat result = new FieldFloat(getStructName(), this.fields.size()); add(result); return result; } public String getStructName() { return this.clazz.getSimpleName(); } public FieldByte addByte() { FieldByte result = new FieldByte(getStructName(), this.fields.size()); add(result); return result; } public FieldChar addChar() { FieldChar result = new FieldChar(getStructName(), this.fields.size()); add(result); return result; } public <F> Field<F> add(ITypeFactory<F> factory1) { Field<F> result = new Field<>(factory1, getStructName(), this.fields.size()); add(result); if (result.factory.hasDestructor()) { this.destructableFields.add(result); } return result; } public ITypeFactory<T> getFactory() { return this.factory; } void destructFields(Nd dom, long address) { for (IDestructableField next : StructDef.this.destructableFields) { next.destruct(dom, address); } if (this.superClass != null) { this.superClass.destructFields(dom, address); } } public boolean isNdNode() { return this.isNdNode; } public int getNumFields() { return this.fields.size(); } }