include testDiscovery in coverage.jar; remove duplicated asm-all

This commit is contained in:
Anna Kozlova
2015-09-15 15:00:18 +03:00
parent 6d50f65056
commit fb7d3b8147
22 changed files with 24 additions and 908 deletions
+1 -11
View File
@@ -17,17 +17,7 @@
<orderEntry type="module" module-name="java-impl" />
<orderEntry type="module" module-name="java-indexing-api" />
<orderEntry type="module" module-name="smRunner" />
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../../../lib/testDiscoveryInstrumenter.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="file://$MODULE_DIR$/testDiscovery" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="library" name="Coverage" level="project" />
</component>
<component name="copyright">
<Base>
@@ -36,6 +36,7 @@ import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.rt.coverage.data.ProjectData;
import com.intellij.util.Alarm;
import com.intellij.util.ArrayUtil;
import com.intellij.util.PathUtil;
@@ -43,7 +44,6 @@ import com.intellij.util.messages.MessageBusConnection;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.testme.instrumentation.ProjectData;
import java.io.File;
import java.io.FilenameFilter;
@@ -1,35 +0,0 @@
<!--
~ Copyright 2000-2014 JetBrains s.r.o.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project name="Test Discovery Agent" default="all">
<dirname property="module.home" file="${ant.file}"/>
<dirname property="idea.home" file="${module.home}/../../../../"/>
<property name="idea.lib.path" value="${idea.home}/community/lib"/>
<property name="instrumenter.jar" value="${idea.lib.path}/testDiscoveryInstrumenter.jar"/>
<property name="build.rt.classes.dir" value="${idea.home}/out/classes/production"/>
<target name="all" >
<delete dir="${instrumenter.jar}"/>
<jar destfile="${instrumenter.jar}">
<manifest>
<attribute name="Premain-Class" value="org.jetbrains.testme.instrumentation.CoveragePremain"/>
</manifest>
<fileset dir="${build.rt.classes.dir}/execution-impl" includes="org/jetbrains/testme/instrumentation/**"/>
</jar>
</target>
</project>
@@ -1,77 +0,0 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.testme.instrumentation;
import java.io.File;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
public class CoveragePremain {
public static void premain(String argsString, Instrumentation instrumentation) throws Exception {
File archiveFile = new File(getArchivePath());
final File lib = archiveFile.getParentFile();
final URL[] urls = new URL[3];
urls[0] = fileToURL(archiveFile);
urls[1] = fileToURL(new File(lib, "asm-all.jar"));
urls[2] = fileToURL(new File(lib, "trove4j.jar"));
final Class instrumentator = Class.forName("org.jetbrains.testme.instrumentation.TestDiscoveryInstrumentator", true, new URLClassLoader(urls) {
protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (this) {
Class result = findLoadedClass(name);
if (result == null) {
try {
result = findClass(name);
} catch (ClassNotFoundException e) {
//ignore, will try to find class in parent
}
}
if (result != null && resolve) {
resolveClass(result);
}
if (result != null) {
return result;
}
}
return getParent().loadClass(name);
}
});
final Method premainMethod = instrumentator.getDeclaredMethod("premain", new Class[]{String.class, Instrumentation.class});
premainMethod.invoke(null, new Object[] {argsString, instrumentation});
}
private static URL fileToURL(final File file) throws MalformedURLException {
return file.getAbsoluteFile().toURI().toURL();
}
private static String getArchivePath() {
final String className = CoveragePremain.class.getName().replace('.', '/') + ".class";
URL resourceURL = CoveragePremain.class.getResource("/" + className);
if (resourceURL == null) {
resourceURL = ClassLoader.getSystemResource(className);
}
return URLsUtil.extractRoot(resourceURL, "/" + className);
}
}
@@ -1,43 +0,0 @@
package org.jetbrains.testme.instrumentation;
import org.jetbrains.org.objectweb.asm.Opcodes;
public class InstrumentedMethodsFilter {
private final String myClassName;
private boolean myEnum;
public InstrumentedMethodsFilter(String className) {
myClassName = className;
}
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
myEnum = (access & Opcodes.ACC_ENUM) != 0;
}
public boolean shouldVisitMethod(final int access,
final String name,
final String desc,
final String signature,
final String[] exceptions) {
if ((access & Opcodes.ACC_BRIDGE) != 0) return false; //try to skip bridge methods
if ((access & Opcodes.ACC_ABSTRACT) != 0) return false; //skip abstracts; do not include interfaces without non-abstract methods in result
if ("<clinit>".equals(name) || //static initializer
((access & Opcodes.ACC_SYNTHETIC) != 0 && name.startsWith("access$")) || // synthetic access method
name.equals("<init>") //&& signature != null && signature.equals("()V") // todo: constructor
) {
// todo skip only trivial default constructor
return false;
}
if (myEnum && isDefaultEnumMethod(name, desc, signature, myClassName)) {
return false;
}
return true;
}
private static boolean isDefaultEnumMethod(String name, String desc, String signature, String className) {
return name.equals("values") && desc.equals("()[L" + className + ";") ||
name.equals("valueOf") && desc.equals("(Ljava/lang/String;)L" + className + ";") ||
name.equals("<init>") && signature != null && signature.equals("()V");
}
}
@@ -1,136 +0,0 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.testme.instrumentation;
import org.jetbrains.org.objectweb.asm.ClassVisitor;
import org.jetbrains.org.objectweb.asm.MethodVisitor;
import org.jetbrains.org.objectweb.asm.Opcodes;
public class Instrumenter extends ClassVisitor {
private static final int ADDED_CODE_STACK_SIZE = 6;
protected final ClassVisitor myClassVisitor;
private final String myClassName;
private final String myInternalClassName;
private final InstrumentedMethodsFilter myMethodFilter;
private final String[] myMethodNames;
private int myCurrentMethodCount;
private boolean myVisitedStaticBlock;
private static final String METHODS_VISITED = "__$methodsVisited$__";
private static final String METHODS_VISITED_CLASS = "[Z";
public Instrumenter(ClassVisitor classVisitor, String className, String[] methodNames) {
super(Opcodes.ASM5, classVisitor);
myClassVisitor = classVisitor;
myMethodFilter = new InstrumentedMethodsFilter(className);
myClassName = className.replace('$', '.'); // for inner classes
myInternalClassName = className.replace('.', '/');
myMethodNames = methodNames;
}
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
myMethodFilter.visit(version, access, name, signature, superName, interfaces);
super.visit(version, access, name, signature, superName, interfaces);
}
public MethodVisitor visitMethod(final int access,
final String name,
final String desc,
final String signature,
final String[] exceptions) {
final MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
if (mv == null) return mv;
if ("<clinit>".equals(name)) {
myVisitedStaticBlock = true;
return new StaticBlockMethodVisitor(mv);
}
if (!myMethodFilter.shouldVisitMethod(access, name, desc, signature, exceptions)) return mv;
assert myCurrentMethodCount < myMethodNames.length;
return new MethodVisitor(Opcodes.ASM5, mv) {
final int myMethodId = myCurrentMethodCount++;
public void visitCode() {
// todo for constructor insert the code after calling 'super'
visitFieldInsn(Opcodes.GETSTATIC, myInternalClassName, METHODS_VISITED, METHODS_VISITED_CLASS);
pushInstruction(this, myMethodId);
visitInsn(Opcodes.ICONST_1);
visitInsn(Opcodes.BASTORE);
super.visitCode();
}
};
}
@Override
public void visitEnd() {
visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_STATIC, METHODS_VISITED,
METHODS_VISITED_CLASS, null, null);
if (!myVisitedStaticBlock) {
MethodVisitor mv = super.visitMethod(Opcodes.ACC_STATIC, "<clinit>", "()V", null, null);
mv = new StaticBlockMethodVisitor(mv);
mv.visitCode();
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(ADDED_CODE_STACK_SIZE, 0);
mv.visitEnd();
}
super.visitEnd();
}
private class StaticBlockMethodVisitor extends MethodVisitor {
public StaticBlockMethodVisitor(MethodVisitor mv) {
super(Opcodes.ASM5, mv);
}
public void visitCode() {
super.visitCode();
visitLdcInsn(myClassName);
pushInstruction(this, myMethodNames.length);
visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_BOOLEAN);
pushInstruction(this, myMethodNames.length);
visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/String");
for(int i = 0; i < myMethodNames.length; ++i) {
visitInsn(Opcodes.DUP);
pushInstruction(this, i);
visitLdcInsn(myMethodNames[i]);
visitInsn(Opcodes.AASTORE);
}
visitMethodInsn(Opcodes.INVOKESTATIC, ProjectData.PROJECT_DATA_OWNER, "trace", "(Ljava/lang/String;[Z[Ljava/lang/String;)[Z", false);
visitFieldInsn(Opcodes.PUTSTATIC, myInternalClassName, METHODS_VISITED, METHODS_VISITED_CLASS);
// no return here
}
public void visitMaxs(int maxStack, int maxLocals) {
final int ourMaxStack = ADDED_CODE_STACK_SIZE;
super.visitMaxs(Math.max(ourMaxStack, maxStack), maxLocals);
}
}
private static void pushInstruction(MethodVisitor mv, int operand) {
if (operand < Byte.MAX_VALUE) mv.visitIntInsn(Opcodes.BIPUSH, operand);
else mv.visitIntInsn(Opcodes.SIPUSH, operand);
}
}
@@ -1,166 +0,0 @@
package org.jetbrains.testme.instrumentation;
import java.io.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class ProjectData {
public static final String PROJECT_DATA_OWNER = "org/jetbrains/testme/instrumentation/ProjectData";
public static final String TRACE_DIR = "org.jetbrains.testme.instrumentation.trace.dir";
protected static final ProjectData ourData = new ProjectData();
private String myTraceDir = System.getProperty(TRACE_DIR, "");
public void setTraceDir(String traceDir) {
myTraceDir = traceDir;
}
private final ConcurrentMap<String, boolean[]> myTrace2 = new ConcurrentHashMap<String, boolean[]>();
private final ConcurrentMap<String, String[]> myTrace3 = new ConcurrentHashMap<String, String[]>();
public static ProjectData getProjectData() {
return ourData;
}
// called from instrumented code during class's static init
public static boolean[] trace(String className, boolean[] methodFlags, String[] methodNames) {
return ourData.traceLines(className, methodFlags, methodNames);
}
private synchronized boolean[] traceLines(String className, boolean[] methodFlags, String[] methodNames) {
//System.out.println("Registering " + className);
assert methodFlags.length == methodNames.length;
final boolean[] previousMethodFlags = myTrace2.putIfAbsent(className, methodFlags);
if (previousMethodFlags != null) {
assert previousMethodFlags.length == methodFlags.length;
final String[] previousMethodNames = myTrace3.get(className);
assert previousMethodNames != null && previousMethodNames.length == methodNames.length;
} else {
myTrace3.put(className, methodNames);
}
return previousMethodFlags != null ? previousMethodFlags : methodFlags;
}
private static final int STRING_LENGTH_THRESHOLD = 255;
private static final int STRING_HEADER_SIZE = 1;
private static void writeUTF(DataOutput storage, byte[] buffer, String value) throws IOException {
int len = value.length();
if (len < STRING_LENGTH_THRESHOLD) {
buffer[0] = (byte)len;
boolean isAscii = true;
for (int i = 0; i < len; i++) {
char c = value.charAt(i);
if (c >= 128) {
isAscii = false;
break;
}
buffer[i + STRING_HEADER_SIZE] = (byte)c;
}
if (isAscii) {
storage.write(buffer, 0, len + STRING_HEADER_SIZE);
return;
}
}
storage.writeByte((byte)0xFF);
storage.writeUTF(value);
}
private static void writeINT(DataOutput record, int val) throws IOException {
if (0 <= val && val < 192) {
record.writeByte(val);
}
else {
record.writeByte(192 + (val & 0x3F));
val >>>= 6;
while (val >= 128) {
record.writeByte((val & 0x7F) | 0x80);
val >>>= 7;
}
record.writeByte(val);
}
}
private static volatile boolean traceDirDumped;
public synchronized void testEnded(final String name) {
//if (myTrace == null) return;
if (!traceDirDumped) {
ClassLoader classLoader = TestDiscoveryInstrumentator.class.getClassLoader();
System.out.println(ourData + "; cl: " + classLoader+ "," + classLoader.getParent());
System.out.println("Trace dir:" + myTraceDir);
traceDirDumped = true;
}
new File(myTraceDir).mkdirs();
final File traceFile = new File(myTraceDir, name + ".tr");
try {
if (!traceFile.exists()) {
traceFile.createNewFile();
}
DataOutputStream os = null;
try {
os = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(traceFile), 64 * 1024));
final byte[] buffer = new byte[STRING_LENGTH_THRESHOLD + STRING_HEADER_SIZE];
//saveOldTrace(os);
Map<String, Integer> classToUsedMethods = new HashMap<String, Integer>();
for(Map.Entry<String, boolean[]> e: myTrace2.entrySet()) {
boolean[] used = e.getValue();
int usedMethodsCount = 0;
for (boolean anUsed : used) {
if (anUsed) ++usedMethodsCount;
}
if (usedMethodsCount > 0) {
classToUsedMethods.put(e.getKey(), usedMethodsCount);
}
}
writeINT(os, classToUsedMethods.size());
for(Map.Entry<String, boolean[]> e: myTrace2.entrySet()) {
final boolean[] used = e.getValue();
final String className = e.getKey();
Integer integer = classToUsedMethods.get(className);
if (integer == null) continue;;
int usedMethodsCount = integer;
writeUTF(os, buffer, className);
writeINT(os, usedMethodsCount);
String[] methodNames = myTrace3.get(className);
for (int i = 0, len = used.length; i < len; ++i) {
// we check usedMethodCount here since used can still be updated by other threads
if (used[i] && usedMethodsCount-- > 0) writeUTF(os, buffer, methodNames[i]);
}
}
}
finally {
if (os != null) {
os.close();
}
}
}
catch (IOException e) {
e.printStackTrace();
}
}
public synchronized void testStarted(final String name) {
//clearOldTrace();
for(Map.Entry<String, boolean[]> e: myTrace2.entrySet()) {
boolean[] used = e.getValue();
for(int i = 0, len = used.length; i < len; ++i) {
if(used[i]) used[i] = false;
}
}
}
}
@@ -1,271 +0,0 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.testme.instrumentation;
import org.jetbrains.org.objectweb.asm.*;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
public class TestDiscoveryInstrumentator {
public static void premain(String argsString, Instrumentation instrumentation) throws Exception {
instrumentation.addTransformer(new ClassFileTransformer() {
private boolean computeFrames = computeFrames();
public byte[] transform(ClassLoader loader,
String className,
Class classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
try {
if (className == null) {
return null;
}
if (loader == null) {
// skip classes loaded by system classloader
//System.out.println("Skipping " + className);
return null;
}
if (className.endsWith(".class")) {
className = className.substring(0, className.length() - 6);
}
className = className.replace('\\', '.').replace('/', '.');
if (className.startsWith("com.intellij.rt.")
|| className.startsWith("com.intellij.util.lang.")
|| className.startsWith("com.intellij.util.containers.")
|| className.startsWith("com.intellij.openapi.util.text.")
|| className.startsWith("com.intellij.openapi.util.io.")
|| className.startsWith("java.")
|| className.startsWith("sun.")
|| className.startsWith("gnu.trove.")
|| className.startsWith("org.jetbrains.org.objectweb.asm.")
|| className.startsWith("org.apache.oro.text.regex.")
|| className.startsWith("org.jetbrains.testme.")
|| className.startsWith("org.apache.log4j.")
|| className.startsWith("org.junit.")
|| className.startsWith("com.sun.")
|| className.startsWith("junit.")
|| className.startsWith("jdk.internal.")
|| className.startsWith("com.intellij.junit3.")
|| className.startsWith("com.intellij.junit4.")) {
return null;
}
//System.out.println(className);
return instrument(classfileBuffer, className, loader, computeFrames);
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
private boolean computeFrames() {
return System.getProperty("idea.coverage.no.frames") == null;
}
});
}
private final static AtomicInteger myInstrumentedClasses = new AtomicInteger();
private final static AtomicInteger myInstrumentedMethods = new AtomicInteger();
private final static AtomicLong myInstrumentedClassesTime = new AtomicLong();
private static byte[] instrument(final byte[] classfileBuffer, final String className, ClassLoader loader, boolean computeFrames) {
long started = System.nanoTime();
final ClassReader cr = new ClassReader(classfileBuffer);
final ClassWriter cw;
if (computeFrames && false) { // frames calculation traverses hierarchy and makes instrumentation longer
final int version = getClassFileVersion(cr);
cw = getClassWriter(version >= Opcodes.V1_6 && version != Opcodes.V1_1 ? ClassWriter.COMPUTE_FRAMES : ClassWriter.COMPUTE_MAXS, loader);
} else {
cw = getClassWriter(ClassWriter.COMPUTE_MAXS, loader);
}
final List<String> instrumentedMethods = new ArrayList<String>();
final ClassVisitor instrumentedMethodCounter = new ClassVisitor(Opcodes.ASM5) {
final InstrumentedMethodsFilter methodsFilter = new InstrumentedMethodsFilter(className);
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
methodsFilter.visit(version, access, name, signature, superName, interfaces);
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
if (methodsFilter.shouldVisitMethod(access, name, desc, signature, exceptions)) {
if ("<init>".equals(name)) {
final int slashPos = className.lastIndexOf('.');
final int $Pos = className.lastIndexOf('$');
name = className.substring(Math.max(slashPos, $Pos) + 1);
}
instrumentedMethods.add(name);
}
return super.visitMethod(access, name, desc, signature, exceptions);
}
};
cr.accept(instrumentedMethodCounter, 0);
// todo there are duplicates in array of instrumented methods
final ClassVisitor cv = new Instrumenter(cw, className, instrumentedMethods.toArray(new String[instrumentedMethods.size()]));
cr.accept(cv, 0);
byte[] bytes = cw.toByteArray();
long time = myInstrumentedClassesTime.addAndGet(System.nanoTime() - started);
int classes = myInstrumentedClasses.incrementAndGet();
int methods = myInstrumentedMethods.addAndGet(instrumentedMethods.size());
//if (classes % 1000 == 0) {
// System.out.println("Done instrumenting " + classes + ", methods:" + methods + " for " + (time / 1000000));
//}
if (false) {
try {
FileOutputStream fileOutputStream = new FileOutputStream("transformed-" + className);
try {
fileOutputStream.write(bytes);
fileOutputStream.close();
} finally {
fileOutputStream.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
return bytes;
}
private static ClassWriter getClassWriter(int flags, final ClassLoader classLoader) {
return new MyClassWriter(flags, classLoader);
}
public static int getClassFileVersion(ClassReader reader) {
final int[] classFileVersion = new int[1];
reader.accept(new ClassVisitor(Opcodes.ASM5) {
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
classFileVersion[0] = version;
}
}, 0);
return classFileVersion[0];
}
private static class MyClassWriter extends ClassWriter {
public static final String JAVA_LANG_OBJECT = "java/lang/Object";
private final ClassLoader classLoader;
public MyClassWriter(int flags, ClassLoader classLoader) {
super(flags);
this.classLoader = classLoader;
}
protected String getCommonSuperClass(String type1, String type2) {
try {
ClassReader info1 = typeInfo(type1);
ClassReader info2 = typeInfo(type2);
String
superType = checkImplementInterface(type1, type2, info1, info2);
if (superType != null) return superType;
superType = checkImplementInterface(type2, type1, info2, info1);
if (superType != null) return superType;
StringBuilder b1 = typeAncestors(type1, info1);
StringBuilder b2 = typeAncestors(type2, info2);
String result = JAVA_LANG_OBJECT;
int end1 = b1.length();
int end2 = b2.length();
while (true) {
int start1 = b1.lastIndexOf(";", end1 - 1);
int start2 = b2.lastIndexOf(";", end2 - 1);
if (start1 != -1 && start2 != -1 && end1 - start1 == end2 - start2) {
String p1 = b1.substring(start1 + 1, end1);
String p2 = b2.substring(start2 + 1, end2);
if (p1.equals(p2)) {
result = p1;
end1 = start1;
end2 = start2;
} else {
return result;
}
} else {
return result;
}
}
} catch (IOException e) {
throw new RuntimeException(e.toString());
}
}
private String checkImplementInterface(String type1, String type2, ClassReader info1, ClassReader info2) throws IOException {
if ((info1.getAccess() & Opcodes.ACC_INTERFACE) != 0) {
if (typeImplements(type2, info2, type1)) {
return type1;
}
return JAVA_LANG_OBJECT;
}
return null;
}
private StringBuilder typeAncestors(String type, ClassReader info) throws IOException {
StringBuilder b = new StringBuilder();
while (!JAVA_LANG_OBJECT.equals(type)) {
b.append(';').append(type);
type = info.getSuperName();
info = typeInfo(type);
}
return b;
}
private boolean typeImplements(String type, ClassReader classReader, String interfaceName) throws IOException {
while (!JAVA_LANG_OBJECT.equals(type)) {
String[] itfs = classReader.getInterfaces();
for (int i = 0; i < itfs.length; ++i) {
if (itfs[i].equals(interfaceName)) {
return true;
}
}
for (int i = 0; i < itfs.length; ++i) {
if (typeImplements(itfs[i], typeInfo(itfs[i]), interfaceName)) {
return true;
}
}
type = classReader.getSuperName();
classReader = typeInfo(type);
}
return false;
}
private ClassReader typeInfo(final String type) throws IOException {
InputStream is = classLoader.getResourceAsStream(type + ".class");
if (is == null) System.out.println(classLoader + "," + type + ".class");
try {
return new ClassReader(is);
} finally {
is.close();
}
}
}
}
@@ -1,137 +0,0 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.testme.instrumentation;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
public class URLsUtil {
public static final String FILE = "file";
public static final String PROTOCOL_DELIMITER = ":";
public static final String JAR_DELIMITER = "!";
public static boolean startsWithChar(CharSequence s, char prefix) {
return s != null && s.length() != 0 && s.charAt(0) == prefix;
}
public static String extractRoot(URL resourceURL, String resourcePath) {
if (!(startsWithChar(resourcePath, '/') || startsWithChar(resourcePath, '\\'))) {
//noinspection HardCodedStringLiteral
System.err.println("precondition failed: "+resourcePath);
return null;
}
String protocol = resourceURL.getProtocol();
String resultPath = null;
if (FILE.equals(protocol)) {
String path = resourceURL.getFile();
final String testPath = path.replace('\\', '/');
final String testResourcePath = resourcePath.replace('\\', '/');
if (endsWithIgnoreCase(testPath, testResourcePath)) {
resultPath = path.substring(0, path.length() - resourcePath.length());
}
}
else if ("jar".equals(protocol)) {
String fullPath = resourceURL.getFile();
int delimiter = fullPath.indexOf(JAR_DELIMITER);
if (delimiter >= 0) {
String archivePath = fullPath.substring(0, delimiter);
if (startsWithConcatenationOf(archivePath, FILE, PROTOCOL_DELIMITER)) {
resultPath = archivePath.substring(FILE.length() + PROTOCOL_DELIMITER.length());
}
}
}
if (resultPath == null) {
//noinspection HardCodedStringLiteral
System.err.println("cannot extract: "+resultPath + " from "+resourceURL);
return null;
}
if (resourcePath.endsWith(File.separator)) {
resultPath = resultPath.substring(0, resultPath.lastIndexOf(File.separator));
}
resultPath = unescapePercentSequences(resultPath);
return resultPath;
}
public static boolean startsWithConcatenationOf(String testee, String firstPrefix, String secondPrefix) {
int l1 = firstPrefix.length();
int l2 = secondPrefix.length();
if (testee.length() < l1 + l2) return false;
return testee.startsWith(firstPrefix) && testee.regionMatches(l1, secondPrefix, 0, l2);
}
public static boolean endsWithIgnoreCase(String str, String suffix) {
final int stringLength = str.length();
final int suffixLength = suffix.length();
return stringLength >= suffixLength && str.regionMatches(true, stringLength - suffixLength, suffix, 0, suffixLength);
}
public static String unescapePercentSequences(String s) {
if (s.indexOf('%') == -1) {
return s;
}
StringBuilder decoded = new StringBuilder();
final int len = s.length();
int i = 0;
while (i < len) {
char c = s.charAt(i);
if (c == '%') {
List bytes = new ArrayList();
while (i + 2 < len && s.charAt(i) == '%') {
final int d1 = decode(s.charAt(i + 1));
final int d2 = decode(s.charAt(i + 2));
if (d1 != -1 && d2 != -1) {
bytes.add(new Integer(((d1 & 0xf) << 4 | d2 & 0xf)));
i += 3;
} else {
break;
}
}
if (!bytes.isEmpty()) {
final byte[] bytesArray = new byte[bytes.size()];
for (int j = 0; j < bytes.size(); j++) {
bytesArray[j] = (byte) ((Integer) bytes.get(j)).intValue();
}
try {
decoded.append(new String(bytesArray, "UTF-8"));
continue;
}
catch (UnsupportedEncodingException ignored) {
}
}
}
decoded.append(c);
i++;
}
return decoded.toString();
}
private static int decode(char c) {
if ((c >= '0') && (c <= '9'))
return c - '0';
if ((c >= 'a') && (c <= 'f'))
return c - 'a' + 10;
if ((c >= 'A') && (c <= 'F'))
return c - 'A' + 10;
return -1;
}
}