Skip to content

Commit

Permalink
[WASM] Start emitting a summary for modular compilation.
Browse files Browse the repository at this point in the history
This initial summary contains type - supertype relationships that will be used to synthesize itables in the bundler.

PiperOrigin-RevId: 572957936
  • Loading branch information
rluble authored and copybara-github committed Oct 12, 2023
1 parent c0c217e commit b5a3de2
Show file tree
Hide file tree
Showing 9 changed files with 179 additions and 5 deletions.
5 changes: 5 additions & 0 deletions transpiler/java/com/google/j2cl/common/OutputUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ private Output(Problems problems, Path root) {
this.root = root;
}

public void write(String path, byte[] content) {
Path outputPath = root.resolve(path);
fileService.execute(() -> OutputUtils.writeToFile(outputPath, content, problems));
}

public void write(String path, String content) {
Path outputPath = root.resolve(path);
fileService.execute(() -> OutputUtils.writeToFile(outputPath, content, problems));
Expand Down
17 changes: 16 additions & 1 deletion transpiler/java/com/google/j2cl/transpiler/backend/wasm/BUILD
Original file line number Diff line number Diff line change
@@ -1,17 +1,32 @@
package(
default_applicable_licenses = ["//:j2cl_license"],
default_visibility = ["//transpiler:__subpackages__"],
default_visibility = [
"//tools:__subpackages__",
"//transpiler:__subpackages__",
],
licenses = ["notice"],
)

proto_library(
name = "summary_proto",
srcs = ["summary.proto"],
)

java_proto_library(
name = "summary_java_proto",
deps = [":summary_proto"],
)

java_library(
name = "wasm",
srcs = glob(["*.java"]),
deps = [
":summary_java_proto",
"//third_party:auto_value",
"//third_party:gson",
"//third_party:guava",
"//third_party:jsr305_annotations",
"//third_party:protobuf_util_json",
"//transpiler/java/com/google/j2cl/common",
"//transpiler/java/com/google/j2cl/transpiler/ast",
"//transpiler/java/com/google/j2cl/transpiler/backend/closure",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Copyright 2023 Google Inc.
*
* 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
*
* https://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 com.google.j2cl.transpiler.backend.wasm;

import static java.util.function.Predicate.not;

import com.google.j2cl.common.Problems;
import com.google.j2cl.common.Problems.FatalError;
import com.google.j2cl.transpiler.ast.DeclaredTypeDescriptor;
import com.google.j2cl.transpiler.ast.Library;
import com.google.j2cl.transpiler.ast.Type;
import com.google.protobuf.util.JsonFormat;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nullable;

/** Summarizes information where global knowledge will be required for bundling. */
public final class SummaryBuilder {

public static final int NULL_TYPE = 0;

private final Summary.Builder summary = Summary.newBuilder();
private final Map<String, Integer> types = new HashMap<>();
private final Map<String, Integer> declaredTypes = new HashMap<>();

private final WasmGenerationEnvironment environment;

SummaryBuilder(Library library, WasmGenerationEnvironment environment, Problems problems) {
this.environment = environment;

library.streamTypes().forEach(this::addType);
}

private void addType(Type type) {
if (type.isInterface()) {
return;
}

int typeId = getDeclaredTypeId(type.getTypeDescriptor());

TypeHierarchyInfo.Builder typeHierarchyInfoBuilder =
TypeHierarchyInfo.newBuilder().setDeclaredTypeId(typeId);

DeclaredTypeDescriptor superTypeDescriptor = type.getSuperTypeDescriptor();
if (superTypeDescriptor != null && !superTypeDescriptor.isNative()) {
typeHierarchyInfoBuilder.setExtendsDeclaredTypeId(getDeclaredTypeId(superTypeDescriptor));
}

type.getSuperInterfaceTypeDescriptors().stream()
.filter(not(DeclaredTypeDescriptor::isNative))
.filter(not(DeclaredTypeDescriptor::isJsFunctionInterface))
.forEach(t -> typeHierarchyInfoBuilder.addImplementsDeclaredTypeId(getDeclaredTypeId(t)));

summary.addType(typeHierarchyInfoBuilder.build());
}

private int getDeclaredTypeId(DeclaredTypeDescriptor typeDescriptor) {
String typeName = environment.getTypeSignature(typeDescriptor);
// Note that the IDs start from '1' to reserve '0' for NULL_TYPE.
return declaredTypes.computeIfAbsent(typeName, x -> declaredTypes.size() + 1);
}

private Summary build() {
summary.clearTypeReferenceMap();
String[] typeReferenceMap = new String[types.size()];
types.forEach((name, i) -> typeReferenceMap[i] = name);
summary.addAllTypeReferenceMap(Arrays.asList(typeReferenceMap));
String[] declaredTypeMap = new String[declaredTypes.size() + 1];
// Add a spurious mapping for 0 for the cases where the type is absent, e.g. the supertype
// of java.lang.Object.
declaredTypeMap[NULL_TYPE] = "<no-type>";
declaredTypes.forEach((name, i) -> declaredTypeMap[i] = name);
summary.addAllDeclaredTypeMap(Arrays.asList(declaredTypeMap));
return summary.build();
}

/** Serialize a LibraryInfo object into a JSON string. */
@Nullable
public String toJson(Problems problems) {
try {
return JsonFormat.printer().print(build());
} catch (IOException e) {
problems.fatal(FatalError.CANNOT_WRITE_FILE, e.toString());
return null;
}
}

public byte[] toByteArray() {
return build().toByteArray();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ String getWasmTypeName(TypeDescriptor typeDescriptor) {
return getTypeSignature(typeDescriptor);
}

private static String getTypeSignature(TypeDescriptor typeDescriptor) {
public String getTypeSignature(TypeDescriptor typeDescriptor) {
if (typeDescriptor.isPrimitive()) {
return "$" + typeDescriptor.getReadableDescription();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,20 @@ public WasmOutputsGenerator(Output output, Path libraryInfoOutputPath, Problems
}

public void generateModularOutput(Library library) {
// TODO(b/283154838): Add the modular summaries and the library info output.
if (libraryInfoOutputPath != null) {
OutputUtils.writeToFile(libraryInfoOutputPath, new byte[0], problems);
}

environment =
new WasmGenerationEnvironment(
library, JsImportsGenerator.collectImports(library, problems));
SummaryBuilder summaryBuilder = new SummaryBuilder(library, environment, problems);

// TODO(rluble): Introduce/use flags to emit the readable version of the summary. For now emit
// summaries in both binary and text form for now.
output.write("summary.txtpb", summaryBuilder.toJson(problems));
output.write("summary.binpb", summaryBuilder.toByteArray());

List<ArrayTypeDescriptor> usedNativeArrayTypes = collectUsedNativeArrayTypes(library);

copyJavaSources(library);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
syntax = "proto3";

package j2cl;

option java_multiple_files = true;
option java_package = "com.google.j2cl.transpiler.backend.wasm";

message Summary {
repeated string declared_type_map = 1;
repeated string type_reference_map = 2;
repeated TypeHierarchyInfo type = 3;
}

message TypeHierarchyInfo {
int32 declared_type_id = 1;
int32 extends_declared_type_id = 2;
repeated int32 implements_declared_type_id = 3;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"declaredTypeMap": ["\u003cno-type\u003e", "$emptymethod.EmptyMethod", "$java.lang.Object"],
"type": [{
"declaredTypeId": 1,
"extendsDeclaredTypeId": 2
}]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"declaredTypeMap": ["\u003cno-type\u003e", "$interfaces.Main", "$java.lang.Object", "$interfaces.Main.Implementor", "$interfaces.Main.SubInterface", "$interfaces.Main.Interface", "$interfaces.Main.AbstractImplementor"],
"type": [{
"declaredTypeId": 1,
"extendsDeclaredTypeId": 2
}, {
"declaredTypeId": 3,
"extendsDeclaredTypeId": 2,
"implementsDeclaredTypeId": [4, 5]
}, {
"declaredTypeId": 6,
"extendsDeclaredTypeId": 2,
"implementsDeclaredTypeId": [4]
}]
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ readable_example(
"""

load("@io_bazel_rules_closure//closure:defs.bzl", "js_binary")
load("@bazel_skylib//rules:write_file.bzl", "write_file")
load(
"//build_defs:rules.bzl",
Expand All @@ -23,6 +22,7 @@ load(
)
load("@bazel_tools//tools/build_defs/apple:ios.bzl", "ios_build_test")
load("@bazel_skylib//rules:build_test.bzl", "build_test")
load("@io_bazel_rules_closure//closure:defs.bzl", "js_binary")

JAVAC_FLAGS = [
"-XepDisableAllChecks",
Expand Down Expand Up @@ -57,6 +57,7 @@ def readable_example(
javacopts: javacopts to apply j2cl_library
**kwargs: passes to j2cl_library
"""

readable_source_maps = True
if any([src for src in srcs if src.endswith(".kt")]):
# J2KT doesn't make sense for Kotlin Frontend.
Expand Down Expand Up @@ -235,7 +236,7 @@ def _golden_output_impl(ctx):
readable_name = ctx.label.package.rsplit("/", 1)[1]

if input.is_directory:
excluded_extensions = ["java", "map"]
excluded_extensions = ["java", "map", "binpb"]

# We exclude kotlin files only if they are not generated by the transpiler (J2KT).
# 'input' is the output tree artifact of the transpiler.
Expand Down

0 comments on commit b5a3de2

Please sign in to comment.