/*
* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.max.vm.classfile.constant;
import static com.sun.max.vm.classfile.constant.ConstantPool.Tag.*;
import java.io.*;
import com.sun.max.collect.*;
/**
* A mechanism for looking up and adding new entries to a constant pool. To ensure that
* modifications of a constant pool that are performed in a thread safe manner,
* {@linkplain ConstantPoolEditorClient an interface} is provided for use in conjunction with
* {@link ConstantPool#edit(ConstantPoolEditorClient, boolean)}.
*/
public final class ConstantPoolEditor {
private ConstantPool pool;
private Mapping<PoolConstantKey, Integer> constantsToIndices;
private final Thread owner;
private final boolean allowAppending;
private int acquistionCount;
/**
* The amount by which the pool expands if extra capacity is needed when {@linkplain ConstantPoolEditor#append(PoolConstant) appending} new entries to the pool.
*/
private static final int EXPANSION_AMOUNT = 10;
/**
* This must only be called from {@link ConstantPool#edit()}.
*/
ConstantPoolEditor(ConstantPool pool, boolean allowAppending) {
this.pool = pool;
this.owner = Thread.currentThread();
pool.editor = this;
this.allowAppending = allowAppending;
acquire();
}
/**
* Gets the mapping from pool constants to indexes.
* <p>
* By constructing this data structure lazily, the cost of acquiring a ConstantPoolEditor is minimized - only upon
* the first modification or search of the pool is a significant cost paid.
*/
private Mapping<PoolConstantKey, Integer> constantsToIndices() {
if (constantsToIndices == null) {
constantsToIndices = new HashEntryChainedHashMapping<PoolConstantKey, Integer>(pool.constants().length);
// The pool is traversed in reverse so that the canonical index for a duplicated
// constant is the lowest one. Most constant pools do not contain duplicate
// entries but it is not illegal for them to do so. Not surprisingly, at least
// one of the JCK tests contains such a constant pool.
for (int index = pool.length - 1; index >= 1; --index) {
final PoolConstant constant = pool.constants()[index];
if (constant.tag() != INVALID) {
final PoolConstantKey key = constant.key(pool);
constantsToIndices.put(key, index);
}
}
}
return constantsToIndices;
}
/**
* Creates and returns an editor on a copy of the pool being edited by this editor. This is typically used by a
* process that will add extra entries to the pool for the purpose of writing a valid class file. Using a copy
* prevents extra clutter from being added to the runtime version of a pool.
*/
public ConstantPoolEditor copy() {
final ConstantPool poolCopy = new ConstantPool(pool.classLoader(), pool.constants().clone(), pool.length);
poolCopy.setHolder(poolCopy.holder());
return new ConstantPoolEditor(poolCopy, true);
}
public ConstantPool pool() {
return pool;
}
void acquire() {
assert Thread.currentThread() == owner;
++acquistionCount;
//if (_pool.holder() != null) System.err.printAddress(_owner + ": " + _acquistionCount + " acquired " + _pool);
}
public void release() {
assert Thread.currentThread() == owner;
assert pool != null && pool.editor == this;
//if (_pool.holder() != null) System.err.printAddress(_owner + ": " + _acquistionCount + " releasing " + _pool);
if (--acquistionCount <= 0) {
synchronized (pool) {
pool.notifyAll();
pool.editor = null;
pool = null;
}
}
}
public Thread owner() {
return owner;
}
public int append(PoolConstant constant) {
if (!allowAppending) {
throw new IllegalStateException("Attempting to add an entry to " + pool());
}
if (pool.length == pool.constants().length) {
final int newCapacity = pool.constants().length + ConstantPoolEditor.EXPANSION_AMOUNT;
final PoolConstant[] newConstants = new PoolConstant[newCapacity];
System.arraycopy(pool.constants(), 0, newConstants, 0, pool.length);
pool.setConstants(newConstants);
}
final int index = pool.length;
assert pool.constants()[index] == null;
pool.setConstant(index, constant);
++pool.length;
return index;
}
/**
* Gets the index of a given constant in the constant pool. If {@code appendIfAbsent == true} and the constant
* does not already exist in the pool, it's appended at the end.
*
* @return the index of {@code constant} in the pool or -1 if it's not in the pool and {@code appendIfAbsent == false}
*/
public int indexOf(PoolConstant constant, boolean appendIfAbsent) {
final PoolConstantKey key = constant instanceof PoolConstantKey ? (PoolConstantKey) constant : constant.key(pool);
return find(key, constant, appendIfAbsent);
}
/**
* Gets the index of a given constant in the constant pool. If the constant does not already exist in the pool,
* it's appended at the end.
*/
public int indexOf(PoolConstant constant) {
return indexOf(constant, true);
}
private int find(PoolConstantKey key, PoolConstant constant, boolean appendIfAbsent) {
final Mapping<PoolConstantKey, Integer> map = constantsToIndices();
Integer index = map.get(key);
if (index == null) {
if (!appendIfAbsent) {
return -1;
}
index = append(constant);
map.put(key, index);
}
return index.intValue();
}
/**
* Writes the contents of the constant pool to a classfile stream.
* <p>
* This may cause extra entries to be added to the pool.
*/
public void write(DataOutputStream stream) throws IOException {
stream.writeShort(pool.numberOfConstants());
for (int index = 1; index != pool.numberOfConstants(); ++index) {
final PoolConstant constant = pool.at(index);
constant.writeOn(stream, this, index);
}
}
// Checkstyle: stop
/**
* It's illegal to have a MethodRef in a class file's constant pool referring to
* method named "<clinit>". As such, any the entry in an invocation stub for
* a class iniitalizer is rewritten to refer to a method named "$clinit$" instead.
* This does not break the stub but makes it possible for the reconstituted
* class file to be loaded (in the Inspector for example).
*/
public static final Utf8Constant $CLINIT$ = SymbolTable.makeSymbol("$clinit$");
}