@SystemIndependent instrumentation removed (will be in a separate plugin)

This commit is contained in:
Pavel Fatin
2017-07-03 17:32:49 +02:00
parent 15236b0324
commit 1d2b1ec0f2
8 changed files with 14 additions and 366 deletions
@@ -194,11 +194,6 @@ public class InstrumentationClassFinder {
return is;
}
public boolean isAvaiable(String resourceName) {
return myPlatformClasspath.getResource(resourceName) != null ||
myClasspath.getResource(resourceName) != null;
}
public InputStream getResourceAsStream(String resourceName) throws IOException {
InputStream is = null;
@@ -23,7 +23,6 @@ import org.jetbrains.jps.builders.java.ResourcesTargetType;
import org.jetbrains.jps.incremental.dependencies.DependencyResolvingBuilder;
import org.jetbrains.jps.incremental.instrumentation.NotNullInstrumentingBuilder;
import org.jetbrains.jps.incremental.instrumentation.RmiStubsGenerator;
import org.jetbrains.jps.incremental.instrumentation.internal.SystemIndependentInstrumentingBuilder;
import org.jetbrains.jps.incremental.java.JavaBuilder;
import org.jetbrains.jps.incremental.resources.ResourcesBuilder;
import org.jetbrains.jps.service.SharedThreadPool;
@@ -52,7 +51,6 @@ public class JavaBuilderService extends BuilderService {
return Arrays.asList(
new JavaBuilder(SharedThreadPool.getInstance()),
new NotNullInstrumentingBuilder(),
new SystemIndependentInstrumentingBuilder(),
new RmiStubsGenerator(),
new DependencyResolvingBuilder(),
new BackwardReferenceIndexBuilder()
@@ -58,10 +58,6 @@ public abstract class ClassProcessingBuilder extends ModuleLevelBuilder {
protected abstract boolean isEnabled(CompileContext context, ModuleChunk chunk);
protected boolean isEnabled(CompileContext context, InstrumentationClassFinder finder) throws IOException {
return true;
}
protected abstract String getProgressMessage();
@Override
@@ -79,35 +75,33 @@ public abstract class ClassProcessingBuilder extends ModuleLevelBuilder {
return ExitCode.NOTHING_DONE;
}
InstrumentationClassFinder finder = CLASS_FINDER.get(context); // try using shared finder
if (finder == null) {
final Collection<File> platformCp = ProjectPaths.getPlatformCompilationClasspath(chunk, false);
final Collection<File> classpath = new ArrayList<>();
classpath.addAll(ProjectPaths.getCompilationClasspath(chunk, false));
classpath.addAll(ProjectPaths.getSourceRootsWithDependents(chunk).keySet());
final JpsSdk<JpsDummyElement> sdk = chunk.representativeTarget().getModule().getSdk(JpsJavaSdkType.INSTANCE);
finder = createInstrumentationClassFinder(sdk, platformCp, classpath, outputConsumer);
CLASS_FINDER.set(context, finder);
}
if (!isEnabled(context, finder)) {
return ExitCode.NOTHING_DONE;
}
final String progress = getProgressMessage();
final boolean shouldShowProgress = !StringUtil.isEmptyOrSpaces(progress);
if (shouldShowProgress) {
context.processMessage(new ProgressMessage(progress + " [" + chunk.getPresentableShortName() + "]"));
}
ExitCode exitCode = ExitCode.NOTHING_DONE;
try {
return performBuild(context, chunk, finder, outputConsumer);
InstrumentationClassFinder finder = CLASS_FINDER.get(context); // try using shared finder
if (finder == null) {
final Collection<File> platformCp = ProjectPaths.getPlatformCompilationClasspath(chunk, false);
final Collection<File> classpath = new ArrayList<>();
classpath.addAll(ProjectPaths.getCompilationClasspath(chunk, false));
classpath.addAll(ProjectPaths.getSourceRootsWithDependents(chunk).keySet());
final JpsSdk<JpsDummyElement> sdk = chunk.representativeTarget().getModule().getSdk(JpsJavaSdkType.INSTANCE);
finder = createInstrumentationClassFinder(sdk, platformCp, classpath, outputConsumer);
CLASS_FINDER.set(context, finder);
}
exitCode = performBuild(context, chunk, finder, outputConsumer);
}
finally {
if (shouldShowProgress) {
context.processMessage(new ProgressMessage("")); // cleanup progress
}
}
return exitCode;
}
@Override
@@ -63,10 +63,6 @@ public class NotNullInstrumentingBuilder extends BaseInstrumentingBuilder{
@Override
protected boolean isEnabled(CompileContext context, ModuleChunk chunk) {
return isEnabledIn(context);
}
public static boolean isEnabledIn(CompileContext context) {
final ProjectDescriptor pd = context.getProjectDescriptor();
return JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(pd.getProject()).isAddNotNullAssertions();
}
@@ -1,38 +0,0 @@
/*
* Copyright 2000-2017 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.jps.incremental.instrumentation.internal;
import com.intellij.openapi.diagnostic.Logger;
/*
A template for the synthetic method embedded by the bytecode instrumentation.
*/
class AssertionMethodImpl {
private static void assertArgumentIsSystemIndependent(String className, String methodName, String parameterName, String argument) {
if (argument != null && argument.indexOf('\\') > -1) {
String message = String.format("Argument for @SystemIndependent parameter '%s' of %s.%s must be system-independent: %s",
parameterName, className, methodName, argument);
IllegalArgumentException exception = new IllegalArgumentException(message);
StackTraceElement[] stackTrace = new StackTraceElement[exception.getStackTrace().length - 1];
System.arraycopy(exception.getStackTrace(), 1, stackTrace, 0, stackTrace.length);
exception.setStackTrace(stackTrace);
Logger.getInstance("#org.jetbrains.jps.incremental.instrumentation.AssertionMethodImpl").error(exception);
}
}
}
@@ -1,62 +0,0 @@
/*
* Copyright 2000-2017 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.jps.incremental.instrumentation.internal;
import com.intellij.openapi.util.io.FileUtil;
import org.jetbrains.org.objectweb.asm.ClassReader;
import org.jetbrains.org.objectweb.asm.ClassVisitor;
import org.jetbrains.org.objectweb.asm.MethodVisitor;
import org.jetbrains.org.objectweb.asm.Opcodes;
import java.io.IOException;
import java.io.InputStream;
class MethodTemplate {
private final byte[] myBytecode;
MethodTemplate(Class aClass) {
try {
myBytecode = readBytecodeOf(aClass);
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
private static byte[] readBytecodeOf(Class aClass) throws IOException {
InputStream stream = aClass.getResourceAsStream(aClass.getSimpleName() + ".class");
if (stream == null) {
throw new IllegalArgumentException("Cannot read class bytecode: " + aClass);
}
try {
return FileUtil.loadBytes(stream);
}
finally {
stream.close();
}
}
public void write(ClassVisitor writer, int accessModifier, String methodName) {
ClassReader reader = new ClassReader(myBytecode);
reader.accept(new ClassVisitor(Opcodes.API_VERSION) {
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
return "<init>".equals(name) ? null : writer.visitMethod(access | accessModifier, methodName, desc, signature, exceptions);
}
}, 0);
}
}
@@ -1,118 +0,0 @@
/*
* Copyright 2000-2017 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.jps.incremental.instrumentation.internal;
import org.jetbrains.org.objectweb.asm.*;
import sun.management.counter.perf.InstrumentationException;
class SystemIndependentInstrumenter extends ClassVisitor {
static final String ANNOTATION = "@SystemIndependent";
static final String ANNOTATION_CLASS = "org/jetbrains/annotations/SystemIndependent";
static final String ASSERTION_SIGNATURE = "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V";
private final String myAssertionMethodName;
private String myClassName;
private boolean myAssertionAdded;
SystemIndependentInstrumenter(ClassWriter writer, String assertionMethodName) {
super(Opcodes.API_VERSION, writer);
myAssertionMethodName = assertionMethodName;
}
public boolean isAssertionAdded() {
return myAssertionAdded;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
myClassName = name;
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
Type[] argumentTypes = Type.getArgumentTypes(desc);
Parameter[] parameters = new Parameter[argumentTypes.length];
int slot = (access & Opcodes.ACC_STATIC) != 0 ? 0 : 1;
for (int i = 0; i < argumentTypes.length; i++) {
Type argumentType = argumentTypes[i];
Parameter parameter = new Parameter();
parameter.name = Integer.toString(i);
parameter.slot = slot;
parameter.isString = argumentType.getSort() == Type.OBJECT && "java.lang.String".equals(argumentType.getClassName());
parameters[i] = parameter;
slot += argumentType.getSize();
}
return new MethodVisitor(api, super.visitMethod(access, name, desc, signature, exceptions)) {
private int myParameterIndex = 0;
@Override
public void visitParameter(String name, int access) {
parameters[myParameterIndex].name = name;
myParameterIndex++;
super.visitParameter(name, access);
}
@Override
public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
if (typePath == null) {
TypeReference ref = new TypeReference(typeRef);
if (ref.getSort() == TypeReference.METHOD_FORMAL_PARAMETER && ("L" + ANNOTATION_CLASS + ";").equals(desc)) {
parameters[ref.getFormalParameterIndex()].isSystemIndependent = true;
}
}
return super.visitTypeAnnotation(typeRef, typePath, desc, visible);
}
@Override
public void visitCode() {
super.visitCode();
for (Parameter parameter : parameters) {
if (parameter.isSystemIndependent) {
if (!parameter.isString) {
throw new InstrumentationException("Only String can be annotated as " + ANNOTATION);
}
addAssertionFor(parameter.name, parameter.slot);
myAssertionAdded = true;
}
}
}
private void addAssertionFor(String parameterName, int parameterSlot) {
visitLdcInsn(myClassName);
visitLdcInsn(name);
visitLdcInsn(parameterName);
visitVarInsn(Opcodes.ALOAD, parameterSlot);
visitMethodInsn(Opcodes.INVOKESTATIC, myClassName, myAssertionMethodName, ASSERTION_SIGNATURE, false);
}
};
}
private static class Parameter {
String name;
int slot;
boolean isString;
boolean isSystemIndependent;
}
}
@@ -1,117 +0,0 @@
/*
* Copyright 2000-2017 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.jps.incremental.instrumentation.internal;
import com.intellij.compiler.instrumentation.InstrumentationClassFinder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.ModuleChunk;
import org.jetbrains.jps.incremental.BinaryContent;
import org.jetbrains.jps.incremental.CompileContext;
import org.jetbrains.jps.incremental.CompiledClass;
import org.jetbrains.jps.incremental.instrumentation.BaseInstrumentingBuilder;
import org.jetbrains.jps.incremental.instrumentation.NotNullInstrumentingBuilder;
import org.jetbrains.jps.incremental.messages.BuildMessage;
import org.jetbrains.jps.incremental.messages.CompilerMessage;
import org.jetbrains.org.objectweb.asm.*;
import sun.management.counter.perf.InstrumentationException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static org.jetbrains.org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
/**
* Adds assertions for method / constructor parameters that are annotated as <code>@SystemDependent</code>.
* <p>
* TODO Add other kind of checks (method return, etc).
*/
public class SystemIndependentInstrumentingBuilder extends BaseInstrumentingBuilder {
private MethodTemplate myAssertionMethodTemplate = new MethodTemplate(AssertionMethodImpl.class);
@NotNull
@Override
public String getPresentableName() {
return "Path type instrumentation";
}
@Override
protected String getProgressMessage() {
return "Adding path type assertions...";
}
@Override
protected boolean isEnabled(CompileContext context, ModuleChunk chunk) {
return NotNullInstrumentingBuilder.isEnabledIn(context);
}
@Override
protected boolean isEnabled(CompileContext context, InstrumentationClassFinder finder) throws IOException {
return finder.isAvaiable(SystemIndependentInstrumenter.ANNOTATION_CLASS + ".class");
}
@Override
protected boolean canInstrument(CompiledClass compiledClass, int classFileVersion) {
return !"module-info".equals(compiledClass.getClassName());
}
@Nullable
@Override
protected BinaryContent instrument(CompileContext context,
CompiledClass compiled,
ClassReader reader,
ClassWriter writer,
InstrumentationClassFinder finder) {
String assertionMethodName = unique("$$$assertArgumentIsSystemIndependent$$$", methodNamesFrom(reader));
try {
SystemIndependentInstrumenter instrumenter = new SystemIndependentInstrumenter(writer, assertionMethodName);
reader.accept(instrumenter, 0);
if (instrumenter.isAssertionAdded()) {
myAssertionMethodTemplate.write(writer, ACC_SYNTHETIC, assertionMethodName);
return new BinaryContent(writer.toByteArray());
}
}
catch (InstrumentationException e) {
context.processMessage(new CompilerMessage(getPresentableName(), BuildMessage.Kind.ERROR, e.getMessage()));
}
return null;
}
private static List<String> methodNamesFrom(ClassReader reader) {
List<String> myNames = new ArrayList<>();
reader.accept(new ClassVisitor(Opcodes.API_VERSION) {
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
myNames.add(name);
return null;
}
}, 0);
return myNames;
}
private static String unique(String string, List<String> strings) {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
String name = i == 0 ? string : string + i;
if (!strings.contains(name)) {
return name;
}
}
throw new RuntimeException("Cannot make unique: " + string);
}
}