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 grpc#7211 (hiding
AbstractManagedChannelImplBuilder and AbstractServerImplBuilder) while
providing users a migration path to manage the ABI breakage (grpc#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 22, 2021
1 parent 43d2e53 commit dd4abae
Showing 1 changed file with 50 additions and 0 deletions.
50 changes: 50 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,47 @@ 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 bigEndian(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[], bigEndian(needle.length()), needle.getBytes(US_ASCII));
byte[] replacementBytes = Bytes.concat(
[1] as byte[], bigEndian(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
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 dd4abae

Please sign in to comment.