Skip to content

Commit

Permalink
core: Rewrite builder class signatures to avoid internal class
Browse files Browse the repository at this point in the history
This provides us a path forward with #7211 (hiding
AbstractManagedChannelImplBuilder and AbstractServerImplBuilder) while
providing users a migration path to manage the ABI breakage (#7552). We
do a .class hack so that recompiling avoids the internal class reference
yet the old methods are still available.

Leaving the classes as-is causes javac to compile two versions of each
method, one returning the public class (e.g. ServerBuilder) and one
returning the internal class (e.g., AbstractServerImplBuilder). However,
we rewrite the signature that is used at compile time so that new
compilations will not reference internal-returning methods.

This is intended to be temporary, just to give a migration path. Once we
have given users some time to recompile we will remove this rewriting
and change the generics to use public classes.
  • Loading branch information
ejona86 committed Jan 26, 2021
1 parent ec270a7 commit dbd903c
Showing 1 changed file with 55 additions and 0 deletions.
55 changes: 55 additions & 0 deletions core/build.gradle
@@ -1,3 +1,9 @@
buildscript {
dependencies {
classpath 'com.google.guava:guava:30.0-android'
}
}

plugins {
id "java-library"
id "maven-publish"
Expand All @@ -7,6 +13,10 @@ plugins {
id "ru.vyarus.animalsniffer"
}

import static java.nio.charset.StandardCharsets.US_ASCII;

import com.google.common.primitives.Bytes;

description = 'gRPC: Core'

evaluationDependsOn(project(':grpc-context').path)
Expand Down Expand Up @@ -53,7 +63,52 @@ animalsniffer {

import net.ltgt.gradle.errorprone.CheckSeverity

def replaceBytes(byte[] haystack, byte[] needle, byte[] replacement) {
int i = Bytes.indexOf(haystack, needle);
assert i != -1;
byte[] result = new byte[haystack.length - needle.length + replacement.length];
System.arraycopy(haystack, 0, result, 0, i);
System.arraycopy(replacement, 0, result, i, replacement.length);
System.arraycopy(haystack, i + needle.length, result, i + replacement.length, haystack.length - i - needle.length);
return result;
}

def bigEndianShortBytes(int value) {
return [value >> 8, value & 0xFF] as byte[];
}

def replaceConstant(File file, String needle, String replacement) {
// CONSTANT_Utf8_info. https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4.7
byte[] needleBytes = Bytes.concat(
[1] as byte[], bigEndianShortBytes(needle.length()), needle.getBytes(US_ASCII));
byte[] replacementBytes = Bytes.concat(
[1] as byte[], bigEndianShortBytes(replacement.length()), replacement.getBytes(US_ASCII));
file.setBytes(replaceBytes(file.getBytes(), needleBytes, replacementBytes));
}

plugins.withId("java") {
compileJava {
doLast {
// Replace value of Signature Attribute.
// https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.9
//
// Have new compilations compile against a public class, without breaking the internal
// ABI for the moment. After giving users some time to recompile, this should be removed
// and #7211 can continue. When this is removed, at the very least the generics need to
// be changed to avoid <? extends io.grpc.internal.*>.
project.replaceConstant(
destinationDirectory.file(
"io/grpc/internal/AbstractManagedChannelImplBuilder.class").get().getAsFile(),
"<T:Lio/grpc/internal/AbstractManagedChannelImplBuilder<TT;>;>Lio/grpc/ManagedChannelBuilder<TT;>;",
"<T:Lio/grpc/ManagedChannelBuilder<TT;>;>Lio/grpc/ManagedChannelBuilder<TT;>;");
project.replaceConstant(
destinationDirectory.file(
"io/grpc/internal/AbstractServerImplBuilder.class").get().getAsFile(),
"<T:Lio/grpc/internal/AbstractServerImplBuilder<TT;>;>Lio/grpc/ServerBuilder<TT;>;",
"<T:Lio/grpc/ServerBuilder<TT;>;>Lio/grpc/ServerBuilder<TT;>;");
}
}

compileJmhJava {
// This project targets Java 7 (no method references)
options.errorprone.check("UnnecessaryAnonymousClass", CheckSeverity.OFF)
Expand Down

0 comments on commit dbd903c

Please sign in to comment.