SlotMapOwner.java
package org.mozilla.javascript;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.Collections;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Objects;
public abstract class SlotMapOwner {
private static final long serialVersionUID = 1L;
/**
* Maximum size of an {@link EmbeddedSlotMap} before it is promoted to a {@link HashSlotMap}.
* The value must be 3/4 of a power of two, because the embedded slot map's slot size must be a
* power of two, and it is resized when it is 3/4 full.
*/
static final int LARGE_HASH_SIZE = (1 << 10) + (1 << 9);
static final SlotMap EMPTY_SLOT_MAP = new EmptySlotMap();
static final SlotMap THREAD_SAFE_EMPTY_SLOT_MAP = new ThreadSafeEmptySlotMap();
@SuppressWarnings("AndroidJdkLibsChecker")
static final class ThreadedAccess {
private static final VarHandle SLOT_MAP = getSlotMapHandle();
private static VarHandle getSlotMapHandle() {
try {
return MethodHandles.lookup()
.findVarHandle(SlotMapOwner.class, "slotMap", SlotMap.class);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new Error(e);
}
}
static SlotMap checkAndReplaceMap(SlotMapOwner owner, SlotMap oldMap, SlotMap newMap) {
return (SlotMap) SLOT_MAP.compareAndExchange(owner, oldMap, newMap);
}
}
private static class EmptySlotMap implements SlotMap {
@Override
public Iterator<Slot> iterator() {
return Collections.emptyIterator();
}
@Override
public int size() {
return 0;
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public Slot modify(SlotMapOwner owner, Object key, int index, int attributes) {
var newSlot = new Slot(key, index, attributes);
var map = new SingleEntrySlotMap(newSlot);
owner.setMap(map);
return newSlot;
}
@Override
public Slot query(Object key, int index) {
return null;
}
@Override
public void add(SlotMapOwner owner, Slot newSlot) {
if (newSlot != null) {
var map = new SingleEntrySlotMap(newSlot);
owner.setMap(map);
}
}
@Override
public <S extends Slot> S compute(
SlotMapOwner owner, Object key, int index, SlotComputer<S> c) {
var newSlot = c.compute(key, index, null);
if (newSlot != null) {
var map = new SingleEntrySlotMap(newSlot);
owner.setMap(map);
}
return newSlot;
}
}
private static final class ThreadSafeEmptySlotMap extends EmptySlotMap {
@Override
public Slot modify(SlotMapOwner owner, Object key, int index, int attributes) {
var newSlot = new Slot(key, index, attributes);
var currentMap = replaceMapAndAddSlot(owner, newSlot);
if (currentMap != this) {
return currentMap.modify(owner, key, index, attributes);
}
return newSlot;
}
@Override
public void add(SlotMapOwner owner, Slot newSlot) {
if (newSlot != null) {
var currentMap = replaceMapAndAddSlot(owner, newSlot);
if (currentMap != this) {
currentMap.add(owner, newSlot);
}
return;
}
}
@Override
public <S extends Slot> S compute(
SlotMapOwner owner, Object key, int index, SlotComputer<S> c) {
var newSlot = c.compute(key, index, null);
if (newSlot != null) {
var currentMap = replaceMapAndAddSlot(owner, newSlot);
if (currentMap != this) {
return currentMap.compute(owner, key, index, c);
}
}
return newSlot;
}
private SlotMap replaceMapAndAddSlot(SlotMapOwner owner, Slot newSlot) {
var map = new ThreadSafeSingleEntrySlotMap(newSlot);
return ThreadedAccess.checkAndReplaceMap(owner, this, map);
}
}
private static final class Iter implements Iterator<Slot> {
private Slot next;
Iter(Slot slot) {
next = slot;
}
@Override
public boolean hasNext() {
return next != null;
}
@Override
public Slot next() {
Slot ret = next;
if (ret == null) {
throw new NoSuchElementException();
}
next = next.orderedNext;
return ret;
}
}
static class SingleEntrySlotMap implements SlotMap {
SingleEntrySlotMap(Slot slot) {
assert (slot != null);
this.slot = slot;
}
protected final Slot slot;
@Override
public Iterator<Slot> iterator() {
return new Iter(slot);
}
@Override
public int size() {
return 1;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public Slot modify(SlotMapOwner owner, Object key, int index, int attributes) {
final int indexOrHash = (key != null ? key.hashCode() : index);
if (indexOrHash == slot.indexOrHash && Objects.equals(slot.name, key)) {
return slot;
}
Slot newSlot = new Slot(key, index, attributes);
add(owner, newSlot);
return newSlot;
}
@Override
public Slot query(Object key, int index) {
final int indexOrHash = (key != null ? key.hashCode() : index);
if (indexOrHash == slot.indexOrHash && Objects.equals(slot.name, key)) {
return slot;
}
return null;
}
@Override
public void add(SlotMapOwner owner, Slot newSlot) {
if (owner == null) {
throw new IllegalStateException();
} else {
var newMap = new EmbeddedSlotMap();
owner.setMap(newMap);
newMap.add(owner, slot);
newMap.add(owner, newSlot);
}
}
@Override
public <S extends Slot> S compute(
SlotMapOwner owner, Object key, int index, SlotComputer<S> c) {
var newMap = new EmbeddedSlotMap();
owner.setMap(newMap);
newMap.add(owner, slot);
return newMap.compute(owner, key, index, c);
}
}
static final class ThreadSafeSingleEntrySlotMap extends SingleEntrySlotMap {
ThreadSafeSingleEntrySlotMap(Slot slot) {
super(slot);
}
@Override
public void add(SlotMapOwner owner, Slot newSlot) {
if (owner == null) {
throw new IllegalStateException();
} else {
var newMap = new ThreadSafeEmbeddedSlotMap(2);
newMap.add(null, slot);
var currentMap = ThreadedAccess.checkAndReplaceMap(owner, this, newMap);
if (currentMap == this) {
newMap.add(owner, newSlot);
} else {
currentMap.add(owner, newSlot);
}
}
}
@Override
public <S extends Slot> S compute(
SlotMapOwner owner, Object key, int index, SlotComputer<S> c) {
var newMap = new ThreadSafeEmbeddedSlotMap(2);
newMap.add(null, slot);
var currentMap = ThreadedAccess.checkAndReplaceMap(owner, this, newMap);
if (currentMap == this) {
return newMap.compute(owner, key, index, c);
} else {
return currentMap.compute(owner, key, index, c);
}
}
}
/**
* This holds all the slots. It may or may not be thread-safe, and may expand itself to a
* different data structure depending on the size of the object.
*/
private SlotMap slotMap;
protected SlotMapOwner() {
slotMap = createSlotMap(0);
}
protected SlotMapOwner(int capacity) {
slotMap = createSlotMap(capacity);
}
protected SlotMapOwner(SlotMap map) {
slotMap = map;
}
protected static SlotMap createSlotMap(int initialSize) {
Context cx = Context.getCurrentContext();
if ((cx != null) && cx.hasFeature(Context.FEATURE_THREAD_SAFE_OBJECTS)) {
if (initialSize == 0) {
return THREAD_SAFE_EMPTY_SLOT_MAP;
} else if (initialSize > LARGE_HASH_SIZE) {
return new ThreadSafeHashSlotMap(initialSize);
} else {
return new ThreadSafeEmbeddedSlotMap();
}
} else if (initialSize == 0) {
return EMPTY_SLOT_MAP;
} else if (initialSize > LARGE_HASH_SIZE) {
return new HashSlotMap();
} else {
return new EmbeddedSlotMap();
}
}
final SlotMap getMap() {
return slotMap;
}
final void setMap(SlotMap newMap) {
slotMap = newMap;
}
}