@@ -0,0 +1,28 @@ | |||
# This workflow will build a Java project with Maven | |||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven | |||
name: Java CI with Maven | |||
on: | |||
push: | |||
branches: [ master ] | |||
pull_request: | |||
branches: [ master ] | |||
jobs: | |||
build: | |||
runs-on: ubuntu-latest | |||
strategy: | |||
matrix: | |||
os: [ ubuntu-latest ] | |||
java-version: [ 11.0.3, 11 ] | |||
steps: | |||
- uses: actions/checkout@v2 | |||
- name: Set up JDK ${{ matrix.java-version }} | |||
uses: actions/setup-java@v2 | |||
with: | |||
java-version: ${{ matrix.java-version }} | |||
distribution: 'zulu' | |||
- name: Build with Maven | |||
run: mvn -B package --file pom.xml |
@@ -1,7 +1,9 @@ | |||
[![Java CI with Maven](https://github.com/jboss-javassist/javassist/actions/workflows/maven.yml/badge.svg)](https://github.com/jboss-javassist/javassist/actions/workflows/maven.yml) | |||
Java bytecode engineering toolkit | |||
### [Javassist version 3](http://www.javassist.org) | |||
Copyright (C) 1999-2019 by Shigeru Chiba, All rights reserved. | |||
Copyright (C) 1999-2021 by Shigeru Chiba, All rights reserved. | |||
Javassist (JAVA programming ASSISTant) makes Java bytecode manipulation | |||
simple. It is a class library for editing bytecodes in Java; it enables Java |
@@ -7,7 +7,7 @@ | |||
<h1>Javassist version 3</h1> | |||
<h3>Copyright (C) 1999-2019 by Shigeru Chiba, All rights reserved.</h3> | |||
<h3>Copyright (C) 1999-2021 by Shigeru Chiba, All rights reserved.</h3> | |||
<p><br></p> | |||
@@ -277,11 +277,31 @@ To run, first type the following commands: | |||
<p>Javassist provides a class file viewer for debugging. For more details, | |||
see javassist.Dump. | |||
<p><br> | |||
<h2>Release</h2> | |||
<p></p>When you want to upload a new release to Sonatype, do the following steps:</p> | |||
<ul> | |||
<li>1. Switch to jdk11 in bash shell</li> | |||
<li>2. pull latest from git master branch</li> | |||
<li>3. <code>mvn -P centralRelease javadoc:jar deploy</code></li> | |||
<li>4. close and release via <a href="https://oss.sonatype.org/index.html#stagingRepositories">https://oss.sonatype.org/index.html#stagingRepositories</a></li> | |||
</ul> | |||
<p><br/></p> | |||
<h2>Changes</h2> | |||
<p>-version 3.27 | |||
<p>-version 3.29 | |||
<ul> | |||
<li>GitHub Issue #378. | |||
</li> | |||
<p>-version 3.28 on May 8, 2021 | |||
<ul> | |||
<li>GitHub Issue #305, #328, #339, #350, #357, and PR #363. | |||
</ul> | |||
<p>-version 3.27 on March 19, 2020 | |||
<ul> | |||
<li>GitHub Issue #271 (PR #279), #280 (PR #281), #282, and PR #294. | |||
</ul> | |||
@@ -300,7 +320,7 @@ see javassist.Dump. | |||
<p>-version 3.24.1 on December 9, 2018 | |||
<ul> | |||
<li>GitHub Issue #228, #229</li> | |||
<ul> | |||
</ul> | |||
</p> | |||
<p>-version 3.24 on November 1, 2018 | |||
@@ -889,7 +909,6 @@ and all other contributors for their contributions. | |||
<hr> | |||
<a href="http://www.javassist.org">Shigeru Chiba</a> | |||
(Email: <tt>chiba@javassist.org</tt>) | |||
</body> | |||
</html> |
@@ -6,7 +6,7 @@ | |||
<project name="javassist" default="jar" basedir="."> | |||
<property name="dist-version" value="javassist-3.26.0-GA"/> | |||
<property name="dist-version" value="javassist-3.28.0-GA"/> | |||
<property environment="env"/> | |||
<property name="target.jar" value="javassist.jar"/> | |||
@@ -102,6 +102,19 @@ | |||
</target> | |||
<target name="runtest" depends="jar,test-compile"> | |||
<copy file="${test.lib.dir}/empty.jar" | |||
tofile="${test.lib.dir}/emptyorig.jar" | |||
preservelastmodified="true" /> | |||
<junit fork="true" printsummary="true" dir="${test.run.dir}"> | |||
<classpath refid="test.classpath"/> | |||
<formatter type="xml" extension=".xml"/> | |||
<test name="javassist.JvstTest" outfile="TestLog" /> | |||
</junit> | |||
<move file="${test.lib.dir}/emptyorig.jar" | |||
tofile="${test.lib.dir}/empty.jar" /> | |||
</target> | |||
<target name="runtest8" depends="jar,test-compile"> | |||
<copy file="${test.lib.dir}/empty.jar" | |||
tofile="${test.lib.dir}/emptyorig.jar" | |||
preservelastmodified="true" /> |
@@ -7,7 +7,7 @@ | |||
Javassist (JAVA programming ASSISTant) makes Java bytecode manipulation | |||
simple. It is a class library for editing bytecodes in Java. | |||
</description> | |||
<version>3.26.0-GA</version> | |||
<version>3.28.0-GA</version> | |||
<name>Javassist</name> | |||
<url>http://www.javassist.org/</url> | |||
@@ -40,7 +40,7 @@ | |||
--> | |||
<license> | |||
<name>Apache License 2.0</name> | |||
<url>http://www.apache.org/licenses/</url> | |||
<url>https://www.apache.org/licenses/LICENSE-2.0</url> | |||
</license> | |||
</licenses> | |||
@@ -202,7 +202,7 @@ | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-javadoc-plugin</artifactId> | |||
<version>3.0.1</version> | |||
<version>3.2.0</version> | |||
<configuration> | |||
<attach>true</attach> | |||
<excludePackageNames>javassist.compiler:javassist.convert:javassist.scopedpool:javassist.bytecode.stackmap</excludePackageNames> | |||
@@ -211,6 +211,7 @@ Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.</i>]]></bottom> | |||
<show>public</show> | |||
<nohelp>true</nohelp> | |||
<doclint>none</doclint> | |||
<source>8</source> | |||
</configuration> | |||
</plugin> | |||
<plugin> | |||
@@ -320,7 +321,7 @@ Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.</i>]]></bottom> | |||
<dependency> | |||
<groupId>junit</groupId> | |||
<artifactId>junit</artifactId> | |||
<version>4.12</version> | |||
<version>[4.13.1,)</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> |
@@ -1,5 +1,5 @@ | |||
Specification-Title: Javassist | |||
Specification-Vendor: Shigeru Chiba, www.javassist.org | |||
Specification-Version: 3.26.0-GA | |||
Specification-Version: 3.29.0-SNAPSHOT | |||
Main-Class: javassist.CtClass | |||
Automatic-Module-Name: org.javassist |
@@ -69,7 +69,7 @@ public abstract class CtClass { | |||
/** | |||
* The version number of this release. | |||
*/ | |||
public static final String version = "3.26.0-GA"; | |||
public static final String version = "3.29.0-SNAPSHOT"; | |||
/** | |||
* Prints the version number and the copyright notice. | |||
@@ -80,7 +80,7 @@ public abstract class CtClass { | |||
*/ | |||
public static void main(String[] args) { | |||
System.out.println("Javassist version " + CtClass.version); | |||
System.out.println("Copyright (C) 1999-2019 Shigeru Chiba." | |||
System.out.println("Copyright (C) 1999-2021 Shigeru Chiba." | |||
+ " All Rights Reserved."); | |||
} | |||
@@ -1423,7 +1423,7 @@ public abstract class CtClass { | |||
public void detach() { | |||
ClassPool cp = getClassPool(); | |||
CtClass obj = cp.removeCached(getName()); | |||
if (obj != this) | |||
if (obj != null && obj != this) | |||
cp.cacheCtClass(getName(), obj, false); | |||
} | |||
@@ -179,6 +179,7 @@ class CtClassType extends CtClass { | |||
} | |||
public ClassFile getClassFile3(boolean doCompress) { | |||
// quick path - no locking | |||
ClassFile cfile = classfile; | |||
if (cfile != null) | |||
return cfile; | |||
@@ -186,17 +187,29 @@ class CtClassType extends CtClass { | |||
if (doCompress) | |||
classPool.compress(); | |||
if (rawClassfile != null) { | |||
byte[] rcfile; | |||
synchronized (this) { | |||
// repeat under lock to make sure we get a consistent result (classfile might have been set by another thread) | |||
cfile = classfile; | |||
if (cfile != null) | |||
return cfile; | |||
rcfile = rawClassfile; | |||
} | |||
if (rcfile != null) { | |||
final ClassFile cf; | |||
try { | |||
ClassFile cf = new ClassFile(new DataInputStream( | |||
new ByteArrayInputStream(rawClassfile))); | |||
rawClassfile = null; | |||
getCount = GET_THRESHOLD; | |||
return setClassFile(cf); | |||
cf = new ClassFile(new DataInputStream(new ByteArrayInputStream(rcfile))); | |||
} | |||
catch (IOException e) { | |||
throw new RuntimeException(e.toString(), e); | |||
} | |||
getCount = GET_THRESHOLD; | |||
synchronized (this) { | |||
rawClassfile = null; | |||
return setClassFile(cf); | |||
} | |||
} | |||
InputStream fin = null; |
@@ -444,6 +444,7 @@ public class Loader extends ClassLoader { | |||
if (doDelegation) | |||
if (name.startsWith("java.") | |||
|| name.startsWith("javax.") | |||
|| name.startsWith("jdk.internal.") | |||
|| name.startsWith("sun.") | |||
|| name.startsWith("com.sun.") | |||
|| name.startsWith("org.w3c.") |
@@ -117,6 +117,28 @@ public class AnnotationDefaultAttribute extends AttributeInfo { | |||
} | |||
} | |||
@Override | |||
void renameClass(String oldname, String newname) { | |||
try { | |||
MemberValue defaultValue = getDefaultValue(); | |||
defaultValue.renameClass(oldname, newname); | |||
setDefaultValue(defaultValue); | |||
} catch (Exception e) { | |||
// ignore | |||
} | |||
} | |||
@Override | |||
void renameClass(Map<String, String> classnames) { | |||
try { | |||
MemberValue defaultValue = getDefaultValue(); | |||
defaultValue.renameClass(classnames); | |||
setDefaultValue(defaultValue); | |||
} catch (Exception e) { | |||
// ignore | |||
} | |||
} | |||
/** | |||
* Obtains the default value represented by this attribute. | |||
*/ |
@@ -254,7 +254,7 @@ public class AttributeInfo { | |||
/* The following two methods are used to implement | |||
* ClassFile.renameClass(). | |||
* Only CodeAttribute, LocalVariableAttribute, | |||
* AnnotationsAttribute, and SignatureAttribute | |||
* AnnotationDefaultAttribute, AnnotationsAttribute, and SignatureAttribute | |||
* override these methods. | |||
*/ | |||
void renameClass(String oldname, String newname) {} |
@@ -217,6 +217,22 @@ public class LocalVariableAttribute extends AttributeInfo { | |||
return getConstPool().getUtf8Info(nameIndex(i)); | |||
} | |||
/** | |||
* Returns the name of the local variable with given index. | |||
* If you want to get the parameter name of method with correct order, | |||
* you should using this method. | |||
* | |||
* @param index the index of the local variable. | |||
*/ | |||
public String variableNameByIndex(int index) { | |||
for (int i = 0; i < tableLength(); i++) { | |||
if (index(i) == index) { | |||
return variableName(i); | |||
} | |||
} | |||
throw new ArrayIndexOutOfBoundsException(); | |||
} | |||
/** | |||
* Returns the value of | |||
* <code>local_variable_table[i].descriptor_index</code>. |
@@ -56,6 +56,14 @@ public class MethodParametersAttribute extends AttributeInfo { | |||
return ByteArray.readU16bit(info, i * 4 + 1); | |||
} | |||
/** | |||
* Returns the name of the i-th element of <code>parameters</code>. | |||
* @param i the position of the parameter. | |||
*/ | |||
public String parameterName(int i) { | |||
return getConstPool().getUtf8Info(name(i)); | |||
} | |||
/** | |||
* Returns the value of <code>access_flags</code> of the i-th element of <code>parameters</code>. | |||
* |
@@ -150,8 +150,9 @@ public class SignatureAttribute extends AttributeInfo { | |||
} | |||
} | |||
catch (IndexOutOfBoundsException e) { break; } | |||
nameBufs.add(nameBuf); | |||
genericParamBufs.add(genericParamBuf); | |||
nameBufs.add(nameBuf); | |||
genericParamBufs.add(genericParamBuf); | |||
i = k + 1; | |||
String name = String.join("$", nameBufs.toArray(new StringBuilder[0])); |
@@ -204,8 +204,11 @@ public class StackMapTable extends AttributeInfo { | |||
} | |||
else if (type < 128) | |||
pos = sameLocals(pos, type); | |||
else if (type < 247) | |||
throw new BadBytecode("bad frame_type in StackMapTable"); | |||
else if (type < 247) { | |||
throw new BadBytecode( | |||
"bad frame_type " + type + " in StackMapTable (pos: " | |||
+ pos + ", frame no.:" + nth + ")"); | |||
} | |||
else if (type == 247) // SAME_LOCALS_1_STACK_ITEM_EXTENDED | |||
pos = sameLocals(pos, type); | |||
else if (type < 251) { | |||
@@ -881,16 +884,21 @@ public class StackMapTable extends AttributeInfo { | |||
position = oldPos + offsetDelta + (oldPos == 0 ? 0 : 1); | |||
boolean match; | |||
if (exclusive) | |||
match = oldPos < where && where <= position; | |||
// We optimize this expression by hand: | |||
// match = (oldPos == 0 && where == 0 && (0 < position || 0 == position)) | |||
// || oldPos < where && where <= position; | |||
match = (oldPos == 0 && where == 0) | |||
|| oldPos < where && where <= position; | |||
else | |||
match = oldPos <= where && where < position; | |||
if (match) { | |||
int current = info[pos] & 0xff; | |||
int newDelta = offsetDelta + gap; | |||
position += gap; | |||
if (newDelta < 64) | |||
info[pos] = (byte)(newDelta + base); | |||
else if (offsetDelta < 64) { | |||
else if (offsetDelta < 64 && current != entry) { | |||
byte[] newinfo = insertGap(info, pos, 2); | |||
newinfo[pos] = (byte)entry; | |||
ByteArray.write16bit(newDelta, newinfo, pos + 1); | |||
@@ -931,7 +939,8 @@ public class StackMapTable extends AttributeInfo { | |||
position = oldPos + offsetDelta + (oldPos == 0 ? 0 : 1); | |||
boolean match; | |||
if (exclusive) | |||
match = oldPos < where && where <= position; | |||
match = (oldPos == 0 && where == 0) | |||
|| oldPos < where && where <= position; | |||
else | |||
match = oldPos <= where && where < position; | |||
@@ -15,9 +15,7 @@ | |||
*/ | |||
package javassist.bytecode.analysis; | |||
import java.util.HashMap; | |||
import java.util.IdentityHashMap; | |||
import java.util.Map; | |||
import java.util.*; | |||
import javassist.ClassPool; | |||
import javassist.CtClass; | |||
@@ -496,24 +494,26 @@ public class Type { | |||
if (typeMap == null||typeMap.isEmpty()) | |||
alterMap.clear(); | |||
for (String name:alterMap.keySet()) | |||
Iterator<String> it = alterMap.keySet().iterator(); | |||
while (it.hasNext()) { | |||
String name = it.next(); | |||
if (!typeMap.containsKey(name)) | |||
alterMap.remove(name); | |||
it.remove(); | |||
} | |||
// Reduce to subinterfaces | |||
// This does not need to be recursive since we make a copy, | |||
// and that copy contains all super types for the whole hierarchy | |||
for (CtClass intf:alterMap.values()) { | |||
CtClass[] interfaces; | |||
Collection<CtClass> interfaces = new ArrayList<>(); | |||
for (CtClass intf : alterMap.values()) { | |||
try { | |||
interfaces = intf.getInterfaces(); | |||
interfaces.addAll(Arrays.asList(intf.getInterfaces())); | |||
} catch (NotFoundException e) { | |||
throw new RuntimeException(e); | |||
} | |||
for (CtClass c:interfaces) | |||
alterMap.remove(c.getName()); | |||
} | |||
for (CtClass c : interfaces) | |||
alterMap.remove(c.getName()); | |||
return alterMap; | |||
} |
@@ -18,6 +18,7 @@ package javassist.bytecode.annotation; | |||
import java.io.IOException; | |||
import java.lang.reflect.Array; | |||
import java.lang.reflect.Method; | |||
import java.util.Map; | |||
import javassist.ClassPool; | |||
import javassist.bytecode.ConstPool; | |||
@@ -87,6 +88,30 @@ public class ArrayMemberValue extends MemberValue { | |||
return a.getClass(); | |||
} | |||
@Override | |||
public void renameClass(String oldname, String newname) { | |||
if (type != null) { | |||
type.renameClass(oldname, newname); | |||
} | |||
if (values != null) { | |||
for (MemberValue value : values) { | |||
value.renameClass(oldname, newname); | |||
} | |||
} | |||
} | |||
@Override | |||
public void renameClass(Map<String, String> classnames) { | |||
if (type != null) { | |||
type.renameClass(classnames); | |||
} | |||
if (values != null) { | |||
for (MemberValue value : values) { | |||
value.renameClass(classnames); | |||
} | |||
} | |||
} | |||
/** | |||
* Obtains the type of the elements. | |||
* |
@@ -18,6 +18,7 @@ package javassist.bytecode.annotation; | |||
import java.io.IOException; | |||
import java.lang.reflect.Method; | |||
import java.util.Map; | |||
import javassist.ClassPool; | |||
import javassist.bytecode.BadBytecode; | |||
@@ -95,6 +96,20 @@ public class ClassMemberValue extends MemberValue { | |||
return loadClass(cl, "java.lang.Class"); | |||
} | |||
@Override | |||
public void renameClass(String oldname, String newname) { | |||
String value = cp.getUtf8Info(valueIndex); | |||
String newValue = Descriptor.rename(value, oldname, newname); | |||
setValue(Descriptor.toClassName(newValue)); | |||
} | |||
@Override | |||
public void renameClass(Map<String, String> classnames) { | |||
String value = cp.getUtf8Info(valueIndex); | |||
String newValue = Descriptor.rename(value, classnames); | |||
setValue(Descriptor.toClassName(newValue)); | |||
} | |||
/** | |||
* Obtains the value of the member. | |||
* |
@@ -18,6 +18,7 @@ package javassist.bytecode.annotation; | |||
import java.io.IOException; | |||
import java.lang.reflect.Method; | |||
import java.util.Map; | |||
import javassist.ClassPool; | |||
import javassist.bytecode.ConstPool; | |||
@@ -76,6 +77,20 @@ public class EnumMemberValue extends MemberValue { | |||
return loadClass(cl, getType()); | |||
} | |||
@Override | |||
public void renameClass(String oldname, String newname) { | |||
String type = cp.getUtf8Info(typeIndex); | |||
String newType = Descriptor.rename(type, oldname, newname); | |||
setType(Descriptor.toClassName(newType)); | |||
} | |||
@Override | |||
public void renameClass(Map<String, String> classnames) { | |||
String type = cp.getUtf8Info(typeIndex); | |||
String newType = Descriptor.rename(type, classnames); | |||
setType(Descriptor.toClassName(newType)); | |||
} | |||
/** | |||
* Obtains the enum type name. | |||
* |
@@ -18,6 +18,7 @@ package javassist.bytecode.annotation; | |||
import java.io.IOException; | |||
import java.lang.reflect.Method; | |||
import java.util.Map; | |||
import javassist.ClassPool; | |||
import javassist.bytecode.ConstPool; | |||
@@ -74,6 +75,14 @@ public abstract class MemberValue { | |||
return classname; | |||
} | |||
/* The following two methods are used to implement | |||
* ClassFile.renameClass(). | |||
* Only ArrayMemberValue, ClassMemberValue, EnumMemberValue | |||
* override these methods. | |||
*/ | |||
public void renameClass(String oldname, String newname) {} | |||
public void renameClass(Map<String, String> classnames) {} | |||
/** | |||
* Accepts a visitor. | |||
*/ |
@@ -206,7 +206,12 @@ public abstract class TypeData { | |||
} | |||
@Override | |||
public boolean eq(TypeData d) { return getName().equals(d.getName()); } | |||
public boolean eq(TypeData d) { | |||
if (d.isUninit()) | |||
return d.eq(this); | |||
else | |||
return getName().equals(d.getName()); | |||
} | |||
} | |||
/* a type variable representing a class type or a basic type. | |||
@@ -853,7 +858,12 @@ public abstract class TypeData { | |||
} | |||
@Override | |||
public boolean eq(TypeData d) { return name.equals(d.getName()); } | |||
public boolean eq(TypeData d) { | |||
if (d.isUninit()) | |||
return d.eq(this); | |||
else | |||
return name.equals(d.getName()); | |||
} | |||
@Override | |||
public void setType(String typeName, ClassPool cp) throws BadBytecode {} |
@@ -648,7 +648,7 @@ public class MemberCodeGen extends CodeGen { | |||
throw new CompileError("no such constructor: " + targetClass.getName()); | |||
if (declClass != thisClass && AccessFlag.isPrivate(acc)) { | |||
if (declClass.getClassFile().getMajorVersion() < ClassFile.JAVA_11 | |||
if (declClass.getClassFile().getMajorVersion() < ClassFile.JAVA_8 | |||
|| !isFromSameDeclaringClass(declClass, thisClass)) { | |||
desc = getAccessibleConstructor(desc, declClass, minfo); | |||
bytecode.addOpcode(Opcode.ACONST_NULL); // the last parameter |
@@ -933,7 +933,7 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { | |||
} | |||
} | |||
throw new CompileError("bad filed access"); | |||
throw new CompileError("bad field access"); | |||
} | |||
private CtField fieldAccess2(Expr e, String jvmClassName) throws CompileError { |
@@ -213,7 +213,6 @@ class SecurityActions extends SecurityManager | |||
if (e.getCause() instanceof NoSuchFieldException) | |||
throw new ClassNotFoundException("No such instance.", e.getCause()); | |||
if (e.getCause() instanceof IllegalAccessException | |||
|| e.getCause() instanceof IllegalAccessException | |||
|| e.getCause() instanceof SecurityException) | |||
throw new ClassNotFoundException("Security denied access.", e.getCause()); | |||
throw new RuntimeException(e.getCause()); |
@@ -1179,6 +1179,7 @@ public class JvstTest extends JvstTestRoot { | |||
suite.addTestSuite(test.javassist.proxy.ProxySimpleTest.class); | |||
suite.addTestSuite(test.javassist.bytecode.analysis.AnalyzerTest.class); | |||
suite.addTestSuite(test.javassist.convert.ArrayAccessReplaceTest.class); | |||
suite.addTestSuite(test.javassist.convert.ArrayAccessReplaceTest2.class); | |||
suite.addTestSuite(test.javassist.bytecode.analysis.DomTreeTest.class); | |||
return suite; | |||
} |
@@ -275,10 +275,8 @@ public class JvstTest2 extends JvstTestRoot { | |||
String src = | |||
"public void sampleMethod() throws Exception {" | |||
+ "java.util.Properties props = new java.util.Properties();" | |||
+ "java.rmi.activation.ActivationGroupDesc.CommandEnvironment ace " | |||
+ " = null;" | |||
+ "java.rmi.activation.ActivationGroupDesc agd " | |||
+ " = new java.rmi.activation.ActivationGroupDesc(props,ace);}"; | |||
+ "test2.Inner2.Child ace = null;" | |||
+ "test2.Inner2 agd = new test2.Inner2(props, ace);}"; | |||
CtMethod newmethod = CtNewMethod.make(src, target); | |||
target.addMethod(newmethod); | |||
@@ -1019,11 +1019,15 @@ public class JvstTest4 extends JvstTestRoot { | |||
assertEquals(2, attr.size()); | |||
assertEquals("i", cp.getUtf8Info(attr.name(0))); | |||
assertEquals("s", cp.getUtf8Info(attr.name(1))); | |||
assertEquals("i", attr.parameterName(0)); | |||
assertEquals("s", attr.parameterName(1)); | |||
attr = (MethodParametersAttribute)attr.copy(cp, null); | |||
assertEquals(2, attr.size()); | |||
assertEquals("i", cp.getUtf8Info(attr.name(0))); | |||
assertEquals("s", cp.getUtf8Info(attr.name(1))); | |||
assertEquals("i", attr.parameterName(0)); | |||
assertEquals("s", attr.parameterName(1)); | |||
} | |||
// JIRA JASSIST-220 |
@@ -354,6 +354,9 @@ public class BytecodeTest extends TestCase { | |||
assertEquals("I", ainfo2.descriptor(i)); | |||
} | |||
print("**** end ***"); | |||
assertEquals("this", ainfo2.variableNameByIndex(0)); | |||
assertEquals("i", ainfo2.variableNameByIndex(1)); | |||
} | |||
public void testAnnotations() throws Exception { | |||
@@ -472,10 +475,14 @@ public class BytecodeTest extends TestCase { | |||
changeMsig2("<S:Ljava/lang/Object;>(TS;[TS;)Ljava/lang/Objec;", "java/lang/Object", | |||
"<S:Ljava/lang/Object2;>(TS;[TS;)Ljava/lang/Objec;", "java/lang/Object2"); | |||
String sig = "<T:Ljava/lang/Exception;>LPoi$Foo<Ljava/lang/String;>;LBar;LBar2;"; | |||
//String res = "<T:Ljava/lang/Exception;>LPoi$Foo<Ljava/lang/String2;>;LBar;LBar2;"; | |||
changeMsig(sig, "java/lang/String", sig, "java/lang/String2"); | |||
changeMsig2(sig, "java/lang/String", sig, "java/lang/String2"); | |||
changeMsig("Ltest<TE;>.List;", "ist", "Ltest<TE;>.List;", "IST"); | |||
String res = "<T:Ljava/lang/Exception;>LPoi$Foo<Ljava/lang/String2;>;LBar;LBar2;"; | |||
changeMsig(sig, "java/lang/String", res, "java/lang/String2"); | |||
changeMsig2(sig, "java/lang/String", res, "java/lang/String2"); | |||
//changeMsig("Ltest<TE;>.List;", "ist", "Ltest<TE;>.List;", "IST"); | |||
changeMsig("Ljava/lang/String<Ljava/lang/Object;>;", "java/lang/String", | |||
"Ljava/lang/String2<Ljava/lang/Object;>;", "java/lang/String2"); | |||
changeMsig2("Ljava/lang/String<Ljava/lang/Object;>;", "java/lang/String", | |||
"Ljava/lang/String2<Ljava/lang/Object;>;", "java/lang/String2"); | |||
} | |||
private void changeMsig(String old, String oldname, String result, String newname) { |
@@ -12,6 +12,7 @@ import java.lang.reflect.Method; | |||
import javassist.ClassPool; | |||
import javassist.CodeConverter; | |||
import javassist.CtClass; | |||
import javassist.CtConstructor; | |||
import javassist.CtMethod; | |||
import javassist.CtNewConstructor; | |||
import javassist.CtNewMethod; | |||
@@ -830,4 +831,173 @@ public class StackMapTest extends TestCase { | |||
return null; | |||
} | |||
public static class C7 { | |||
public int value; | |||
public static int value2; | |||
public C7() { this(3); } | |||
public C7(int i) { | |||
value = i; | |||
} | |||
} | |||
public void testIssue328() throws Exception { | |||
CtClass cc = loader.get("javassist.bytecode.StackMapTest$C7"); | |||
CtConstructor cons = cc.getDeclaredConstructor(new CtClass[] { CtClass.intType }); | |||
cons.insertBefore("if ($1 < 0) { super(); if (value2 > 0) { value2++; } return; }"); | |||
cc.writeFile(); | |||
Object t1 = make(cc.getName()); | |||
} | |||
public static class C8 { | |||
int value = 0; | |||
int loop = 0; | |||
int loop2 = 1; | |||
public void foo(int i) { value += i; } | |||
public void bar(int i) { value += i; } | |||
public void foo2(int i) { value += i; } | |||
public void bar2(int i) { value += i; } | |||
} | |||
public static class C9 extends C8 { | |||
public void foo(int i) { | |||
while(true) { | |||
loop--; | |||
if (loop < 0) | |||
break; | |||
} | |||
value += i; | |||
} | |||
public void bar(int i) { | |||
value += i; | |||
for (int k = 0; i < 10; k++) | |||
loop += k; | |||
} | |||
public void foo2(int i) { | |||
while(true) { | |||
loop2--; | |||
if (loop2 < 0) | |||
break; | |||
} | |||
value += i; | |||
for (int k = 0; k < 3; k++) | |||
loop2--; | |||
} | |||
public void bar2(int i) { | |||
value += i; | |||
for (int k = 0; i < 10; k++) | |||
loop += k; | |||
} | |||
public int run() { | |||
foo(1); | |||
bar(10); | |||
foo2(100); | |||
bar2(1000); | |||
return value; | |||
} | |||
} | |||
public void testIssue339() throws Exception { | |||
CtClass cc0 = loader.get("javassist.bytecode.StackMapTest$C8"); | |||
CtClass cc = loader.get("javassist.bytecode.StackMapTest$C9"); | |||
testIssue339b(cc, cc0, "foo", true); | |||
testIssue339b(cc, cc0, "bar", true); | |||
testIssue339b(cc, cc0, "foo2", false); | |||
testIssue339b(cc, cc0, "bar2", false); | |||
cc.writeFile(); | |||
Object t1 = make(cc.getName()); | |||
assertEquals(2322, invoke(t1, "run")); | |||
} | |||
public void testIssue339b(CtClass cc, CtClass cc0, String name, boolean exclusive) throws Exception { | |||
Bytecode newCode = new Bytecode(cc.getClassFile().constPool); | |||
newCode.addAload(0); // Loads 'this' | |||
newCode.addIload(1); // Loads method param 1 (int) | |||
newCode.addInvokespecial(cc0.getName(), name, "(I)V"); | |||
CodeAttribute ca = cc.getDeclaredMethod(name).getMethodInfo().getCodeAttribute(); | |||
CodeIterator ci = ca.iterator(); | |||
if (exclusive) | |||
ci.insertEx(newCode.get()); | |||
else | |||
ci.insert(newCode.get()); | |||
} | |||
public void testIssue350() throws Exception { | |||
byte sameLocals1StackItemFrameExtended = 247 - 256; | |||
byte sameFrameExtended = 251 - 256; | |||
byte appendFrame = 252 - 256; | |||
ConstPool cp = new ConstPool("Test"); | |||
StackMapTable stmt; | |||
int originalLength; | |||
stmt = new StackMapTable(cp, new byte[] { | |||
0, 1, | |||
sameLocals1StackItemFrameExtended, 0, 63, 1 | |||
}); | |||
originalLength = stmt.info.length; | |||
assertEquals(63, stmt.info[4]); | |||
stmt.shiftPc(0, 2, false); | |||
assertEquals(originalLength, stmt.info.length); | |||
assertEquals(65, stmt.info[4]); | |||
stmt = new StackMapTable(cp, new byte[] { | |||
0, 1, | |||
sameFrameExtended, 0, 63 | |||
}); | |||
originalLength = stmt.info.length; | |||
assertEquals(63, stmt.info[4]); | |||
stmt.shiftPc(0, 2, false); | |||
assertEquals(originalLength, stmt.info.length); | |||
assertEquals(65, stmt.info[4]); | |||
stmt = new StackMapTable(cp, new byte[] { | |||
0, 2, | |||
sameLocals1StackItemFrameExtended, 0, 63, 1, | |||
sameFrameExtended, 0, 63 | |||
}); | |||
originalLength = stmt.info.length; | |||
assertEquals(63, stmt.info[4]); | |||
assertEquals(63, stmt.info[8]); | |||
stmt.shiftPc(0, 2, false); | |||
assertEquals(originalLength, stmt.info.length); | |||
assertEquals(65, stmt.info[4]); | |||
assertEquals(63, stmt.info[8]); | |||
stmt.shiftPc(100, 2, false); | |||
assertEquals(65, stmt.info[4]); | |||
assertEquals(65, stmt.info[8]); | |||
// Actual StackMapTable reported in https://github.com/jboss-javassist/javassist/issues/350. | |||
stmt = new StackMapTable(cp, new byte[] { | |||
0, 7, // size | |||
sameLocals1StackItemFrameExtended, 0, 76, 7, 2, 206 - 256, | |||
sameLocals1StackItemFrameExtended, 0, 63, 7, 2, 221 - 256, | |||
appendFrame, 0, 63, 7, 0, 14, | |||
appendFrame, 0, 43, 7, 2, 225 - 256, 1, | |||
74, 7, 0, 19, // same_locals_1_stack_item_frame (not extended) | |||
appendFrame, 0, 23, 7, 0, 19, | |||
66, 7, 2, 225 - 256 // same_locals_1_stack_item_frame (not extended) | |||
}); | |||
assertEquals(63, stmt.info[10]); | |||
originalLength = stmt.info.length; | |||
stmt.shiftPc(100, 2, false); | |||
assertEquals(originalLength, stmt.info.length); | |||
assertEquals(65, stmt.info[10]); | |||
} | |||
public static void dump(byte[] content) { | |||
final int bytesPerLine = 16; | |||
for (int i = 0; i < content.length; i += bytesPerLine) { | |||
for (int j = 0; j < bytesPerLine && i + j < content.length; j++) { | |||
int unsignedByte = content[i + j]; | |||
if (unsignedByte < 0) { | |||
unsignedByte = 256 + unsignedByte; | |||
} | |||
System.out.print(unsignedByte + ", "); | |||
} | |||
System.out.println(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,21 @@ | |||
package test.javassist.convert; | |||
import javassist.ClassPool; | |||
import javassist.CodeConverter; | |||
import javassist.CtClass; | |||
import junit.framework.TestCase; | |||
public class ArrayAccessReplaceTest2 extends TestCase { | |||
public void testAdvancedInstrumentation() throws Exception { | |||
ClassPool pool = new ClassPool(true); | |||
CtClass monitoringClass = pool.get(ArrayAccessReplaceTest2.class.getName()); | |||
CtClass targetClass = pool.get(InstrumentationTarget.class.getName()); | |||
CodeConverter converter = new CodeConverter(); | |||
// we just test if the instrumentation works, the monitoring class does not need to actually contain the replacer methods | |||
// what is only relevant when code gets executed | |||
converter.replaceArrayAccess(monitoringClass, new CodeConverter.DefaultArrayAccessReplacementMethodNames()); | |||
targetClass.instrument(converter); | |||
} | |||
} |
@@ -0,0 +1,761 @@ | |||
package test.javassist.convert; | |||
import java.util.AbstractQueue; | |||
import java.util.Collection; | |||
import java.util.Iterator; | |||
import java.util.NoSuchElementException; | |||
import java.util.concurrent.BlockingQueue; | |||
import java.util.concurrent.TimeUnit; | |||
import java.util.concurrent.locks.Condition; | |||
import java.util.concurrent.locks.ReentrantLock; | |||
/** | |||
* A clone of java.util.concurrent.ArrayBlockingQueue as target for testing | |||
*/ | |||
public class InstrumentationTarget<E> extends AbstractQueue<E> | |||
implements BlockingQueue<E>, java.io.Serializable { | |||
/** | |||
* Serialization ID. This class relies on default serialization | |||
* even for the items array, which is default-serialized, even if | |||
* it is empty. Otherwise it could not be declared final, which is | |||
* necessary here. | |||
*/ | |||
private static final long serialVersionUID = -817911632652898426L; | |||
/** | |||
* The queued items | |||
*/ | |||
final Object[] items; | |||
/** | |||
* items index for next take, poll, peek or remove | |||
*/ | |||
int takeIndex; | |||
/** | |||
* items index for next put, offer, or add | |||
*/ | |||
int putIndex; | |||
/** | |||
* Number of elements in the queue | |||
*/ | |||
int count; | |||
/* | |||
* Concurrency control uses the classic two-condition algorithm | |||
* found in any textbook. | |||
*/ | |||
/** | |||
* Main lock guarding all access | |||
*/ | |||
final ReentrantLock lock; | |||
/** | |||
* Condition for waiting takes | |||
*/ | |||
private final Condition notEmpty; | |||
/** | |||
* Condition for waiting puts | |||
*/ | |||
private final Condition notFull; | |||
// Internal helper methods | |||
/** | |||
* Circularly increment i. | |||
*/ | |||
final int inc(int i) { | |||
return (++i == items.length) ? 0 : i; | |||
} | |||
/** | |||
* Circularly decrement i. | |||
*/ | |||
final int dec(int i) { | |||
return ((i == 0) ? items.length : i) - 1; | |||
} | |||
@SuppressWarnings("unchecked") | |||
static <E> E cast(Object item) { | |||
return (E) item; | |||
} | |||
/** | |||
* Returns item at index i. | |||
*/ | |||
final E itemAt(int i) { | |||
return this.<E>cast(items[i]); | |||
} | |||
/** | |||
* Throws NullPointerException if argument is null. | |||
* | |||
* @param v the element | |||
*/ | |||
private static void checkNotNull(Object v) { | |||
if (v == null) | |||
throw new NullPointerException(); | |||
} | |||
/** | |||
* Inserts element at current put position, advances, and signals. | |||
* Call only when holding lock. | |||
*/ | |||
private void insert(E x) { | |||
items[putIndex] = x; | |||
putIndex = inc(putIndex); | |||
++count; | |||
notEmpty.signal(); | |||
} | |||
/** | |||
* Extracts element at current take position, advances, and signals. | |||
* Call only when holding lock. | |||
*/ | |||
private E extract() { | |||
final Object[] items = this.items; | |||
E x = this.<E>cast(items[takeIndex]); | |||
items[takeIndex] = null; | |||
takeIndex = inc(takeIndex); | |||
--count; | |||
notFull.signal(); | |||
return x; | |||
} | |||
/** | |||
* Deletes item at position i. | |||
* Utility for remove and iterator.remove. | |||
* Call only when holding lock. | |||
*/ | |||
void removeAt(int i) { | |||
final Object[] items = this.items; | |||
// if removing front item, just advance | |||
if (i == takeIndex) { | |||
items[takeIndex] = null; | |||
takeIndex = inc(takeIndex); | |||
} else { | |||
// slide over all others up through putIndex. | |||
for (; ; ) { | |||
int nexti = inc(i); | |||
if (nexti != putIndex) { | |||
items[i] = items[nexti]; | |||
i = nexti; | |||
} else { | |||
items[i] = null; | |||
putIndex = i; | |||
break; | |||
} | |||
} | |||
} | |||
--count; | |||
notFull.signal(); | |||
} | |||
/** | |||
* Creates an {@code Target} with the given (fixed) | |||
* capacity and default access policy. | |||
* | |||
* @param capacity the capacity of this queue | |||
* @throws IllegalArgumentException if {@code capacity < 1} | |||
*/ | |||
public InstrumentationTarget(int capacity) { | |||
this(capacity, false); | |||
} | |||
/** | |||
* Creates an {@code Target} with the given (fixed) | |||
* capacity and the specified access policy. | |||
* | |||
* @param capacity the capacity of this queue | |||
* @param fair if {@code true} then queue accesses for threads blocked | |||
* on insertion or removal, are processed in FIFO order; | |||
* if {@code false} the access order is unspecified. | |||
* @throws IllegalArgumentException if {@code capacity < 1} | |||
*/ | |||
public InstrumentationTarget(int capacity, boolean fair) { | |||
if (capacity <= 0) | |||
throw new IllegalArgumentException(); | |||
this.items = new Object[capacity]; | |||
lock = new ReentrantLock(fair); | |||
notEmpty = lock.newCondition(); | |||
notFull = lock.newCondition(); | |||
} | |||
/** | |||
* Creates an {@code Target} with the given (fixed) | |||
* capacity, the specified access policy and initially containing the | |||
* elements of the given collection, | |||
* added in traversal order of the collection's iterator. | |||
* | |||
* @param capacity the capacity of this queue | |||
* @param fair if {@code true} then queue accesses for threads blocked | |||
* on insertion or removal, are processed in FIFO order; | |||
* if {@code false} the access order is unspecified. | |||
* @param c the collection of elements to initially contain | |||
* @throws IllegalArgumentException if {@code capacity} is less than | |||
* {@code c.size()}, or less than 1. | |||
* @throws NullPointerException if the specified collection or any | |||
* of its elements are null | |||
*/ | |||
public InstrumentationTarget(int capacity, boolean fair, | |||
Collection<? extends E> c) { | |||
this(capacity, fair); | |||
final ReentrantLock lock = this.lock; | |||
lock.lock(); // Lock only for visibility, not mutual exclusion | |||
try { | |||
int i = 0; | |||
try { | |||
for (E e : c) { | |||
checkNotNull(e); | |||
items[i++] = e; | |||
} | |||
} catch (ArrayIndexOutOfBoundsException ex) { | |||
throw new IllegalArgumentException(); | |||
} | |||
count = i; | |||
putIndex = (i == capacity) ? 0 : i; | |||
} finally { | |||
lock.unlock(); | |||
} | |||
} | |||
/** | |||
* Inserts the specified element at the tail of this queue if it is | |||
* possible to do so immediately without exceeding the queue's capacity, | |||
* returning {@code true} upon success and throwing an | |||
* {@code IllegalStateException} if this queue is full. | |||
* | |||
* @param e the element to add | |||
* @return {@code true} (as specified by {@link Collection#add}) | |||
* @throws IllegalStateException if this queue is full | |||
* @throws NullPointerException if the specified element is null | |||
*/ | |||
public boolean add(E e) { | |||
return super.add(e); | |||
} | |||
/** | |||
* Inserts the specified element at the tail of this queue if it is | |||
* possible to do so immediately without exceeding the queue's capacity, | |||
* returning {@code true} upon success and {@code false} if this queue | |||
* is full. This method is generally preferable to method {@link #add}, | |||
* which can fail to insert an element only by throwing an exception. | |||
* | |||
* @throws NullPointerException if the specified element is null | |||
*/ | |||
public boolean offer(E e) { | |||
checkNotNull(e); | |||
final ReentrantLock lock = this.lock; | |||
lock.lock(); | |||
try { | |||
if (count == items.length) | |||
return false; | |||
else { | |||
insert(e); | |||
return true; | |||
} | |||
} finally { | |||
lock.unlock(); | |||
} | |||
} | |||
/** | |||
* Inserts the specified element at the tail of this queue, waiting | |||
* for space to become available if the queue is full. | |||
* | |||
* @throws InterruptedException {@inheritDoc} | |||
* @throws NullPointerException {@inheritDoc} | |||
*/ | |||
public void put(E e) throws InterruptedException { | |||
checkNotNull(e); | |||
final ReentrantLock lock = this.lock; | |||
lock.lockInterruptibly(); | |||
try { | |||
while (count == items.length) | |||
notFull.await(); | |||
insert(e); | |||
} finally { | |||
lock.unlock(); | |||
} | |||
} | |||
/** | |||
* Inserts the specified element at the tail of this queue, waiting | |||
* up to the specified wait time for space to become available if | |||
* the queue is full. | |||
* | |||
* @throws InterruptedException {@inheritDoc} | |||
* @throws NullPointerException {@inheritDoc} | |||
*/ | |||
public boolean offer(E e, long timeout, TimeUnit unit) | |||
throws InterruptedException { | |||
checkNotNull(e); | |||
long nanos = unit.toNanos(timeout); | |||
final ReentrantLock lock = this.lock; | |||
lock.lockInterruptibly(); | |||
try { | |||
while (count == items.length) { | |||
if (nanos <= 0) | |||
return false; | |||
nanos = notFull.awaitNanos(nanos); | |||
} | |||
insert(e); | |||
return true; | |||
} finally { | |||
lock.unlock(); | |||
} | |||
} | |||
public E poll() { | |||
final ReentrantLock lock = this.lock; | |||
lock.lock(); | |||
try { | |||
return (count == 0) ? null : extract(); | |||
} finally { | |||
lock.unlock(); | |||
} | |||
} | |||
public E take() throws InterruptedException { | |||
final ReentrantLock lock = this.lock; | |||
lock.lockInterruptibly(); | |||
try { | |||
while (count == 0) | |||
notEmpty.await(); | |||
return extract(); | |||
} finally { | |||
lock.unlock(); | |||
} | |||
} | |||
public E poll(long timeout, TimeUnit unit) throws InterruptedException { | |||
long nanos = unit.toNanos(timeout); | |||
final ReentrantLock lock = this.lock; | |||
lock.lockInterruptibly(); | |||
try { | |||
while (count == 0) { | |||
if (nanos <= 0) | |||
return null; | |||
nanos = notEmpty.awaitNanos(nanos); | |||
} | |||
return extract(); | |||
} finally { | |||
lock.unlock(); | |||
} | |||
} | |||
public E peek() { | |||
final ReentrantLock lock = this.lock; | |||
lock.lock(); | |||
try { | |||
return (count == 0) ? null : itemAt(takeIndex); | |||
} finally { | |||
lock.unlock(); | |||
} | |||
} | |||
// this doc comment is overridden to remove the reference to collections | |||
// greater in size than Integer.MAX_VALUE | |||
/** | |||
* Returns the number of elements in this queue. | |||
* | |||
* @return the number of elements in this queue | |||
*/ | |||
public int size() { | |||
final ReentrantLock lock = this.lock; | |||
lock.lock(); | |||
try { | |||
return count; | |||
} finally { | |||
lock.unlock(); | |||
} | |||
} | |||
// this doc comment is a modified copy of the inherited doc comment, | |||
// without the reference to unlimited queues. | |||
/** | |||
* Returns the number of additional elements that this queue can ideally | |||
* (in the absence of memory or resource constraints) accept without | |||
* blocking. This is always equal to the initial capacity of this queue | |||
* less the current {@code size} of this queue. | |||
* | |||
* <p>Note that you <em>cannot</em> always tell if an attempt to insert | |||
* an element will succeed by inspecting {@code remainingCapacity} | |||
* because it may be the case that another thread is about to | |||
* insert or remove an element. | |||
*/ | |||
public int remainingCapacity() { | |||
final ReentrantLock lock = this.lock; | |||
lock.lock(); | |||
try { | |||
return items.length - count; | |||
} finally { | |||
lock.unlock(); | |||
} | |||
} | |||
/** | |||
* Removes a single instance of the specified element from this queue, | |||
* if it is present. More formally, removes an element {@code e} such | |||
* that {@code o.equals(e)}, if this queue contains one or more such | |||
* elements. | |||
* Returns {@code true} if this queue contained the specified element | |||
* (or equivalently, if this queue changed as a result of the call). | |||
* | |||
* <p>Removal of interior elements in circular array based queues | |||
* is an intrinsically slow and disruptive operation, so should | |||
* be undertaken only in exceptional circumstances, ideally | |||
* only when the queue is known not to be accessible by other | |||
* threads. | |||
* | |||
* @param o element to be removed from this queue, if present | |||
* @return {@code true} if this queue changed as a result of the call | |||
*/ | |||
public boolean remove(Object o) { | |||
if (o == null) return false; | |||
final Object[] items = this.items; | |||
final ReentrantLock lock = this.lock; | |||
lock.lock(); | |||
try { | |||
for (int i = takeIndex, k = count; k > 0; i = inc(i), k--) { | |||
if (o.equals(items[i])) { | |||
removeAt(i); | |||
return true; | |||
} | |||
} | |||
return false; | |||
} finally { | |||
lock.unlock(); | |||
} | |||
} | |||
/** | |||
* Returns {@code true} if this queue contains the specified element. | |||
* More formally, returns {@code true} if and only if this queue contains | |||
* at least one element {@code e} such that {@code o.equals(e)}. | |||
* | |||
* @param o object to be checked for containment in this queue | |||
* @return {@code true} if this queue contains the specified element | |||
*/ | |||
public boolean contains(Object o) { | |||
if (o == null) return false; | |||
final Object[] items = this.items; | |||
final ReentrantLock lock = this.lock; | |||
lock.lock(); | |||
try { | |||
for (int i = takeIndex, k = count; k > 0; i = inc(i), k--) | |||
if (o.equals(items[i])) | |||
return true; | |||
return false; | |||
} finally { | |||
lock.unlock(); | |||
} | |||
} | |||
/** | |||
* Returns an array containing all of the elements in this queue, in | |||
* proper sequence. | |||
* | |||
* <p>The returned array will be "safe" in that no references to it are | |||
* maintained by this queue. (In other words, this method must allocate | |||
* a new array). The caller is thus free to modify the returned array. | |||
* | |||
* <p>This method acts as bridge between array-based and collection-based | |||
* APIs. | |||
* | |||
* @return an array containing all of the elements in this queue | |||
*/ | |||
public Object[] toArray() { | |||
final Object[] items = this.items; | |||
final ReentrantLock lock = this.lock; | |||
lock.lock(); | |||
try { | |||
final int count = this.count; | |||
Object[] a = new Object[count]; | |||
for (int i = takeIndex, k = 0; k < count; i = inc(i), k++) | |||
a[k] = items[i]; | |||
return a; | |||
} finally { | |||
lock.unlock(); | |||
} | |||
} | |||
/** | |||
* Returns an array containing all of the elements in this queue, in | |||
* proper sequence; the runtime type of the returned array is that of | |||
* the specified array. If the queue fits in the specified array, it | |||
* is returned therein. Otherwise, a new array is allocated with the | |||
* runtime type of the specified array and the size of this queue. | |||
* | |||
* <p>If this queue fits in the specified array with room to spare | |||
* (i.e., the array has more elements than this queue), the element in | |||
* the array immediately following the end of the queue is set to | |||
* {@code null}. | |||
* | |||
* <p>Like the {@link #toArray()} method, this method acts as bridge between | |||
* array-based and collection-based APIs. Further, this method allows | |||
* precise control over the runtime type of the output array, and may, | |||
* under certain circumstances, be used to save allocation costs. | |||
* | |||
* <p>Suppose {@code x} is a queue known to contain only strings. | |||
* The following code can be used to dump the queue into a newly | |||
* allocated array of {@code String}: | |||
* | |||
* <pre> | |||
* String[] y = x.toArray(new String[0]);</pre> | |||
* <p> | |||
* Note that {@code toArray(new Object[0])} is identical in function to | |||
* {@code toArray()}. | |||
* | |||
* @param a the array into which the elements of the queue are to | |||
* be stored, if it is big enough; otherwise, a new array of the | |||
* same runtime type is allocated for this purpose | |||
* @return an array containing all of the elements in this queue | |||
* @throws ArrayStoreException if the runtime type of the specified array | |||
* is not a supertype of the runtime type of every element in | |||
* this queue | |||
* @throws NullPointerException if the specified array is null | |||
*/ | |||
@SuppressWarnings("unchecked") | |||
public <T> T[] toArray(T[] a) { | |||
final Object[] items = this.items; | |||
final ReentrantLock lock = this.lock; | |||
lock.lock(); | |||
try { | |||
final int count = this.count; | |||
final int len = a.length; | |||
if (len < count) | |||
a = (T[]) java.lang.reflect.Array.newInstance( | |||
a.getClass().getComponentType(), count); | |||
for (int i = takeIndex, k = 0; k < count; i = inc(i), k++) | |||
a[k] = (T) items[i]; | |||
if (len > count) | |||
a[count] = null; | |||
return a; | |||
} finally { | |||
lock.unlock(); | |||
} | |||
} | |||
public String toString() { | |||
final ReentrantLock lock = this.lock; | |||
lock.lock(); | |||
try { | |||
int k = count; | |||
if (k == 0) | |||
return "[]"; | |||
StringBuilder sb = new StringBuilder(); | |||
sb.append('['); | |||
for (int i = takeIndex; ; i = inc(i)) { | |||
Object e = items[i]; | |||
sb.append(e == this ? "(this Collection)" : e); | |||
if (--k == 0) | |||
return sb.append(']').toString(); | |||
sb.append(',').append(' '); | |||
} | |||
} finally { | |||
lock.unlock(); | |||
} | |||
} | |||
/** | |||
* Atomically removes all of the elements from this queue. | |||
* The queue will be empty after this call returns. | |||
*/ | |||
public void clear() { | |||
final Object[] items = this.items; | |||
final ReentrantLock lock = this.lock; | |||
lock.lock(); | |||
try { | |||
for (int i = takeIndex, k = count; k > 0; i = inc(i), k--) | |||
items[i] = null; | |||
count = 0; | |||
putIndex = 0; | |||
takeIndex = 0; | |||
notFull.signalAll(); | |||
} finally { | |||
lock.unlock(); | |||
} | |||
} | |||
/** | |||
* @throws UnsupportedOperationException {@inheritDoc} | |||
* @throws ClassCastException {@inheritDoc} | |||
* @throws NullPointerException {@inheritDoc} | |||
* @throws IllegalArgumentException {@inheritDoc} | |||
*/ | |||
public int drainTo(Collection<? super E> c) { | |||
checkNotNull(c); | |||
if (c == this) | |||
throw new IllegalArgumentException(); | |||
final Object[] items = this.items; | |||
final ReentrantLock lock = this.lock; | |||
lock.lock(); | |||
try { | |||
int i = takeIndex; | |||
int n = 0; | |||
int max = count; | |||
while (n < max) { | |||
c.add(this.<E>cast(items[i])); | |||
items[i] = null; | |||
i = inc(i); | |||
++n; | |||
} | |||
if (n > 0) { | |||
count = 0; | |||
putIndex = 0; | |||
takeIndex = 0; | |||
notFull.signalAll(); | |||
} | |||
return n; | |||
} finally { | |||
lock.unlock(); | |||
} | |||
} | |||
/** | |||
* @throws UnsupportedOperationException {@inheritDoc} | |||
* @throws ClassCastException {@inheritDoc} | |||
* @throws NullPointerException {@inheritDoc} | |||
* @throws IllegalArgumentException {@inheritDoc} | |||
*/ | |||
public int drainTo(Collection<? super E> c, int maxElements) { | |||
checkNotNull(c); | |||
if (c == this) | |||
throw new IllegalArgumentException(); | |||
if (maxElements <= 0) | |||
return 0; | |||
final Object[] items = this.items; | |||
final ReentrantLock lock = this.lock; | |||
lock.lock(); | |||
try { | |||
int i = takeIndex; | |||
int n = 0; | |||
int max = (maxElements < count) ? maxElements : count; | |||
while (n < max) { | |||
c.add(this.<E>cast(items[i])); | |||
items[i] = null; | |||
i = inc(i); | |||
++n; | |||
} | |||
if (n > 0) { | |||
count -= n; | |||
takeIndex = i; | |||
notFull.signalAll(); | |||
} | |||
return n; | |||
} finally { | |||
lock.unlock(); | |||
} | |||
} | |||
/** | |||
* Returns an iterator over the elements in this queue in proper sequence. | |||
* The elements will be returned in order from first (head) to last (tail). | |||
* | |||
* <p>The returned {@code Iterator} is a "weakly consistent" iterator that | |||
* will never throw {@link java.util.ConcurrentModificationException | |||
* ConcurrentModificationException}, | |||
* and guarantees to traverse elements as they existed upon | |||
* construction of the iterator, and may (but is not guaranteed to) | |||
* reflect any modifications subsequent to construction. | |||
* | |||
* @return an iterator over the elements in this queue in proper sequence | |||
*/ | |||
public Iterator<E> iterator() { | |||
return new Itr(); | |||
} | |||
/** | |||
* Iterator for Target. To maintain weak consistency | |||
* with respect to puts and takes, we (1) read ahead one slot, so | |||
* as to not report hasNext true but then not have an element to | |||
* return -- however we later recheck this slot to use the most | |||
* current value; (2) ensure that each array slot is traversed at | |||
* most once (by tracking "remaining" elements); (3) skip over | |||
* null slots, which can occur if takes race ahead of iterators. | |||
* However, for circular array-based queues, we cannot rely on any | |||
* well established definition of what it means to be weakly | |||
* consistent with respect to interior removes since these may | |||
* require slot overwrites in the process of sliding elements to | |||
* cover gaps. So we settle for resiliency, operating on | |||
* established apparent nexts, which may miss some elements that | |||
* have moved between calls to next. | |||
*/ | |||
private class Itr implements Iterator<E> { | |||
private int remaining; // Number of elements yet to be returned | |||
private int nextIndex; // Index of element to be returned by next | |||
private E nextItem; // Element to be returned by next call to next | |||
private E lastItem; // Element returned by last call to next | |||
private int lastRet; // Index of last element returned, or -1 if none | |||
Itr() { | |||
final ReentrantLock lock = InstrumentationTarget.this.lock; | |||
lock.lock(); | |||
try { | |||
lastRet = -1; | |||
if ((remaining = count) > 0) | |||
nextItem = itemAt(nextIndex = takeIndex); | |||
} finally { | |||
lock.unlock(); | |||
} | |||
} | |||
public boolean hasNext() { | |||
return remaining > 0; | |||
} | |||
public E next() { | |||
final ReentrantLock lock = InstrumentationTarget.this.lock; | |||
lock.lock(); | |||
try { | |||
if (remaining <= 0) | |||
throw new NoSuchElementException(); | |||
lastRet = nextIndex; | |||
E x = itemAt(nextIndex); // check for fresher value | |||
if (x == null) { | |||
x = nextItem; // we are forced to report old value | |||
lastItem = null; // but ensure remove fails | |||
} else | |||
lastItem = x; | |||
while (--remaining > 0 && // skip over nulls | |||
(nextItem = itemAt(nextIndex = inc(nextIndex))) == null) | |||
; | |||
return x; | |||
} finally { | |||
lock.unlock(); | |||
} | |||
} | |||
public void remove() { | |||
final ReentrantLock lock = InstrumentationTarget.this.lock; | |||
lock.lock(); | |||
try { | |||
int i = lastRet; | |||
if (i == -1) | |||
throw new IllegalStateException(); | |||
lastRet = -1; | |||
E x = lastItem; | |||
lastItem = null; | |||
// only remove if item still at index | |||
if (x != null && x == items[i]) { | |||
boolean removingHead = (i == takeIndex); | |||
removeAt(i); | |||
if (!removingHead) | |||
nextIndex = dec(nextIndex); | |||
} | |||
} finally { | |||
lock.unlock(); | |||
} | |||
} | |||
} | |||
} | |||
@@ -4,9 +4,8 @@ package test2; | |||
public class Inner { | |||
public void sample() throws Exception { | |||
java.util.Properties props = new java.util.Properties(); | |||
java.rmi.activation.ActivationGroupDesc.CommandEnvironment ace = null; | |||
java.rmi.activation.ActivationGroupDesc agd = new | |||
java.rmi.activation.ActivationGroupDesc(props,ace); | |||
test2.Inner2.Child ace = null; | |||
test2.Inner2 agd = new test2.Inner2(props, ace); | |||
} | |||
public static void main(String args[]) { | |||
System.out.println("Inner"); |
@@ -0,0 +1,23 @@ | |||
package test2; | |||
/** | |||
* Used by test2.Inner | |||
*/ | |||
public class Inner2 { | |||
public static class Child { | |||
public int value; | |||
} | |||
private java.util.Properties p; | |||
private Child c; | |||
public Inner2(java.util.Properties props, Child child) { | |||
p = props; | |||
c = child; | |||
} | |||
public void print() { | |||
System.out.println(p); | |||
System.out.println(c); | |||
} | |||
} |