Bound nodes with types, and initial lowering to bytecode
Add the model used by all remaining binding phases, and lowering from
that model to bytecode.
MOE_MIGRATED_REVID=135135482
diff --git a/java/com/google/turbine/binder/ClassPathBinder.java b/java/com/google/turbine/binder/ClassPathBinder.java
index 7b6b830..73609fc 100644
--- a/java/com/google/turbine/binder/ClassPathBinder.java
+++ b/java/com/google/turbine/binder/ClassPathBinder.java
@@ -39,7 +39,7 @@
* Creates an environment containing symbols in the given classpath and bootclasspath, and adds
* them to the top-level index.
*/
- static CompoundEnv<BytecodeBoundClass> bind(
+ public static CompoundEnv<BytecodeBoundClass> bind(
Iterable<Path> classpath, Iterable<Path> bootclasspath, TopLevelIndex.Builder tli)
throws IOException {
// TODO(cushon): this is going to require an env eventually,
diff --git a/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java b/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java
new file mode 100644
index 0000000..3518278
--- /dev/null
+++ b/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.turbine.binder.bound;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.binder.sym.FieldSymbol;
+import com.google.turbine.binder.sym.MethodSymbol;
+import com.google.turbine.binder.sym.TyVarSymbol;
+import com.google.turbine.model.TurbineTyKind;
+import com.google.turbine.type.Type;
+import javax.annotation.Nullable;
+
+/** A HeaderBoundClass for classes compiled from source. */
+public class SourceTypeBoundClass implements HeaderBoundClass {
+
+ private final TurbineTyKind kind;
+ private final ClassSymbol owner;
+ private final ImmutableMap<String, ClassSymbol> children;
+
+ private final int access;
+ private final ClassSymbol superclass;
+ private final ImmutableList<ClassSymbol> interfaces;
+ private final ImmutableMap<String, TyVarSymbol> typeParameters;
+
+ private final ImmutableMap<TyVarSymbol, TyVarInfo> typeParameterTypes;
+ private final Type.ClassTy superClassType;
+ private final ImmutableList<Type.ClassTy> interfaceTypes;
+ private final ImmutableList<MethodInfo> methods;
+ private final ImmutableList<FieldInfo> fields;
+
+ public SourceTypeBoundClass(
+ ImmutableList<Type.ClassTy> interfaceTypes,
+ Type.ClassTy superClassType,
+ ImmutableMap<TyVarSymbol, TyVarInfo> typeParameterTypes,
+ int access,
+ ImmutableList<MethodInfo> methods,
+ ImmutableList<FieldInfo> fields,
+ ClassSymbol owner,
+ TurbineTyKind kind,
+ ImmutableMap<String, ClassSymbol> children,
+ ClassSymbol superclass,
+ ImmutableList<ClassSymbol> interfaces,
+ ImmutableMap<String, TyVarSymbol> typeParameters) {
+ this.interfaceTypes = interfaceTypes;
+ this.superClassType = superClassType;
+ this.typeParameterTypes = typeParameterTypes;
+ this.access = access;
+ this.methods = methods;
+ this.fields = fields;
+ this.owner = owner;
+ this.kind = kind;
+ this.children = children;
+ this.superclass = superclass;
+ this.interfaces = interfaces;
+ this.typeParameters = typeParameters;
+ }
+
+ @Override
+ public ClassSymbol superclass() {
+ return superclass;
+ }
+
+ @Override
+ public ImmutableList<ClassSymbol> interfaces() {
+ return interfaces;
+ }
+
+ @Override
+ public int access() {
+ return access;
+ }
+
+ @Override
+ public TurbineTyKind kind() {
+ return kind;
+ }
+
+ @Nullable
+ @Override
+ public ClassSymbol owner() {
+ return owner;
+ }
+
+ @Override
+ public ImmutableMap<String, ClassSymbol> children() {
+ return children;
+ }
+
+ /** Declared type parameters. */
+ public ImmutableMap<String, TyVarSymbol> typeParameters() {
+ return typeParameters;
+ }
+
+ /** Implemented interface types. */
+ public ImmutableList<Type.ClassTy> interfaceTypes() {
+ return interfaceTypes;
+ }
+
+ /** The super-class type. */
+ public Type.ClassTy superClassType() {
+ return superClassType;
+ }
+
+ /** Declared methods. */
+ public ImmutableList<MethodInfo> methods() {
+ return methods;
+ }
+
+ /** Declared fields. */
+ public ImmutableList<FieldInfo> fields() {
+ return fields;
+ }
+
+ /** Declared type parameters. */
+ public ImmutableMap<TyVarSymbol, TyVarInfo> typeParameterTypes() {
+ return typeParameterTypes;
+ }
+
+ /** A declared method. */
+ public static class MethodInfo {
+ private final MethodSymbol sym;
+ private final ImmutableMap<TyVarSymbol, TyVarInfo> tyParams;
+ private final Type returnType;
+ private final ImmutableList<ParamInfo> parameters;
+ private final ImmutableList<Type> exceptions;
+ private final int access;
+
+ public MethodInfo(
+ MethodSymbol sym,
+ ImmutableMap<TyVarSymbol, TyVarInfo> tyParams,
+ Type returnType,
+ ImmutableList<ParamInfo> parameters,
+ ImmutableList<Type> exceptions,
+ int access) {
+ this.sym = sym;
+ this.tyParams = tyParams;
+ this.returnType = returnType;
+ this.parameters = parameters;
+ this.exceptions = exceptions;
+ this.access = access;
+ }
+
+ /** The method symbol. */
+ public MethodSymbol sym() {
+ return sym;
+ }
+
+ /** The method name. */
+ public String name() {
+ return sym.name();
+ }
+
+ /** The type parameters */
+ public ImmutableMap<TyVarSymbol, TyVarInfo> tyParams() {
+ return tyParams;
+ }
+
+ /** Type return type, possibly {#link Type#VOID}. */
+ public Type returnType() {
+ return returnType;
+ }
+
+ /** The formal parameters. */
+ public ImmutableList<ParamInfo> parameters() {
+ return parameters;
+ }
+
+ /** Thrown exceptions. */
+ public ImmutableList<Type> exceptions() {
+ return exceptions;
+ }
+
+ /** Access bits. */
+ public int access() {
+ return access;
+ }
+ }
+
+ /** A formal parameter declaration. */
+ public static class ParamInfo {
+ private final Type type;
+ private final boolean synthetic;
+
+ public ParamInfo(Type type, boolean synthetic) {
+ this.type = type;
+ this.synthetic = synthetic;
+ }
+
+ /** The parameter type. */
+ public Type type() {
+ return type;
+ }
+
+ /**
+ * Returns true if the parameter is synthetic, e.g. the enclosing instance parameter in an inner
+ * class constructor.
+ */
+ public boolean synthetic() {
+ return synthetic;
+ }
+ }
+
+ /** A field declaration. */
+ public static class FieldInfo {
+ private final FieldSymbol sym;
+ private final Type type;
+ private final int access;
+
+ public FieldInfo(FieldSymbol sym, Type type, int access) {
+ this.sym = sym;
+ this.type = type;
+ this.access = access;
+ }
+
+ /** The field symbol. */
+ public FieldSymbol sym() {
+ return sym;
+ }
+
+ /** The field name. */
+ public String name() {
+ return sym.name();
+ }
+
+ /** The field type. */
+ public Type type() {
+ return type;
+ }
+
+ /** Access bits. */
+ public int access() {
+ return access;
+ }
+ }
+
+ /** A type parameter declaration. */
+ public static class TyVarInfo {
+ private final Type superClassBound;
+ private final ImmutableList<Type> interfaceBounds;
+
+ public TyVarInfo(Type superClassBound, ImmutableList<Type> interfaceBounds) {
+ this.superClassBound = superClassBound;
+ this.interfaceBounds = interfaceBounds;
+ }
+
+ /** A class bound, or {@code null}. */
+ public Type superClassBound() {
+ return superClassBound;
+ }
+
+ /** Interface type bounds. */
+ public ImmutableList<Type> interfaceBounds() {
+ return interfaceBounds;
+ }
+ }
+}
diff --git a/java/com/google/turbine/binder/sym/FieldSymbol.java b/java/com/google/turbine/binder/sym/FieldSymbol.java
new file mode 100644
index 0000000..9ff8fb1
--- /dev/null
+++ b/java/com/google/turbine/binder/sym/FieldSymbol.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.turbine.binder.sym;
+
+import com.google.errorprone.annotations.Immutable;
+import java.util.Objects;
+
+/** A field symbol. */
+@Immutable
+public class FieldSymbol implements Symbol {
+ private final ClassSymbol owner;
+ private final String name;
+
+ public FieldSymbol(ClassSymbol owner, String name) {
+ this.owner = owner;
+ this.name = name;
+ }
+
+ /** The enclosing class. */
+ public ClassSymbol owner() {
+ return owner;
+ }
+
+ /** The field name. */
+ public String name() {
+ return name;
+ }
+
+ @Override
+ public Kind symKind() {
+ return Kind.FIELD;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, owner);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof FieldSymbol)) {
+ return false;
+ }
+ FieldSymbol other = (FieldSymbol) obj;
+ return name().equals(other.name()) && owner().equals(other.owner());
+ }
+}
diff --git a/java/com/google/turbine/binder/sym/MethodSymbol.java b/java/com/google/turbine/binder/sym/MethodSymbol.java
new file mode 100644
index 0000000..17459b4
--- /dev/null
+++ b/java/com/google/turbine/binder/sym/MethodSymbol.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.turbine.binder.sym;
+
+import com.google.errorprone.annotations.Immutable;
+import java.util.Objects;
+
+/** A method symbol. */
+@Immutable
+public class MethodSymbol implements Symbol {
+ private final ClassSymbol owner;
+ private final String name;
+
+ public MethodSymbol(ClassSymbol owner, String name) {
+ this.owner = owner;
+ this.name = name;
+ }
+
+ /** The enclosing class. */
+ public ClassSymbol owner() {
+ return owner;
+ }
+
+ /** The method name. */
+ public String name() {
+ return name;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, owner);
+ }
+
+ @Override
+ public Kind symKind() {
+ return Kind.METHOD;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof MethodSymbol)) {
+ return false;
+ }
+ MethodSymbol other = (MethodSymbol) obj;
+ return name().equals(other.name()) && owner().equals(other.owner());
+ }
+}
diff --git a/java/com/google/turbine/binder/sym/Symbol.java b/java/com/google/turbine/binder/sym/Symbol.java
index 0014aae..51b5fe3 100644
--- a/java/com/google/turbine/binder/sym/Symbol.java
+++ b/java/com/google/turbine/binder/sym/Symbol.java
@@ -16,13 +16,17 @@
package com.google.turbine.binder.sym;
+import com.google.errorprone.annotations.Immutable;
+
/** The top interface for all symbols. */
+@Immutable
public interface Symbol {
/** The symbol kind. */
enum Kind {
CLASS,
- TY_PARAM
- // TODO(cushon): FIELD, METHOD
+ TY_PARAM,
+ METHOD,
+ FIELD
}
/** The symbol kind. */
diff --git a/java/com/google/turbine/binder/sym/TyVarSymbol.java b/java/com/google/turbine/binder/sym/TyVarSymbol.java
index 91e4985..9276f91 100644
--- a/java/com/google/turbine/binder/sym/TyVarSymbol.java
+++ b/java/com/google/turbine/binder/sym/TyVarSymbol.java
@@ -16,7 +16,11 @@
package com.google.turbine.binder.sym;
+import com.google.errorprone.annotations.Immutable;
+import java.util.Objects;
+
/** A type variable symbol. */
+@Immutable
public class TyVarSymbol implements Symbol {
private final Symbol owner;
@@ -41,4 +45,18 @@
public Kind symKind() {
return Kind.TY_PARAM;
}
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, owner);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof TyVarSymbol)) {
+ return false;
+ }
+ TyVarSymbol other = (TyVarSymbol) obj;
+ return name.equals(other.name()) && owner().equals(other.owner());
+ }
}
diff --git a/java/com/google/turbine/bytecode/ConstantPool.java b/java/com/google/turbine/bytecode/ConstantPool.java
index 3b51c04..9279ed8 100644
--- a/java/com/google/turbine/bytecode/ConstantPool.java
+++ b/java/com/google/turbine/bytecode/ConstantPool.java
@@ -90,7 +90,9 @@
if (classInfoPool.containsKey(utf8)) {
return classInfoPool.get(utf8);
}
- return insert(new Entry(Kind.CLASS_INFO, new ShortValue(utf8)));
+ short index = insert(new Entry(Kind.CLASS_INFO, new ShortValue(utf8)));
+ classInfoPool.put(utf8, index);
+ return index;
}
/** Adds a CONSTANT_Utf8_info entry to the pool. */
@@ -98,7 +100,9 @@
if (utf8Pool.containsKey(value)) {
return utf8Pool.get(value);
}
- return insert(new Entry(Kind.UTF8, new StringValue(value)));
+ short index = insert(new Entry(Kind.UTF8, new StringValue(value)));
+ utf8Pool.put(value, index);
+ return index;
}
private short insert(Entry key) {
diff --git a/java/com/google/turbine/bytecode/LowerAttributes.java b/java/com/google/turbine/bytecode/LowerAttributes.java
index 5e71d17..75d3f42 100644
--- a/java/com/google/turbine/bytecode/LowerAttributes.java
+++ b/java/com/google/turbine/bytecode/LowerAttributes.java
@@ -28,7 +28,9 @@
/** Collects the {@link Attribute}s for a {@link ClassFile}. */
static List<Attribute> classAttributes(ClassFile classfile) {
List<Attribute> attributes = new ArrayList<>();
- attributes.add(new InnerClasses(classfile.innerClasses()));
+ if (!classfile.innerClasses().isEmpty()) {
+ attributes.add(new InnerClasses(classfile.innerClasses()));
+ }
if (classfile.signature() != null) {
attributes.add(new Signature(classfile.signature()));
}
diff --git a/java/com/google/turbine/lower/Lower.java b/java/com/google/turbine/lower/Lower.java
new file mode 100644
index 0000000..cf8c7e7
--- /dev/null
+++ b/java/com/google/turbine/lower/Lower.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.turbine.lower;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.turbine.binder.bound.HeaderBoundClass;
+import com.google.turbine.binder.bound.SourceTypeBoundClass;
+import com.google.turbine.binder.bound.SourceTypeBoundClass.FieldInfo;
+import com.google.turbine.binder.bound.SourceTypeBoundClass.MethodInfo;
+import com.google.turbine.binder.bound.SourceTypeBoundClass.ParamInfo;
+import com.google.turbine.binder.bound.SourceTypeBoundClass.TyVarInfo;
+import com.google.turbine.binder.env.Env;
+import com.google.turbine.binder.lookup.Scope;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.binder.sym.Symbol;
+import com.google.turbine.binder.sym.TyVarSymbol;
+import com.google.turbine.bytecode.ClassFile;
+import com.google.turbine.bytecode.ClassWriter;
+import com.google.turbine.bytecode.sig.Sig;
+import com.google.turbine.bytecode.sig.Sig.MethodSig;
+import com.google.turbine.bytecode.sig.Sig.TySig;
+import com.google.turbine.bytecode.sig.SigWriter;
+import com.google.turbine.model.Const;
+import com.google.turbine.model.TurbineFlag;
+import com.google.turbine.model.TurbineVisibility;
+import com.google.turbine.type.Type;
+import com.google.turbine.type.Type.ClassTy;
+import com.google.turbine.types.Erasure;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/** Lowering from bound classes to bytecode. */
+public class Lower {
+
+ /** Lowers all given classes to bytecode. */
+ public static Map<String, byte[]> lowerAll(
+ Env<HeaderBoundClass> env, Iterable<ClassSymbol> syms) {
+ ImmutableMap.Builder<String, byte[]> result = ImmutableMap.builder();
+ for (ClassSymbol sym : syms) {
+ result.put(
+ sym.binaryName(), new Lower().lower((SourceTypeBoundClass) env.get(sym), env, sym));
+ }
+ return result.build();
+ }
+
+ private final LowerSignature sig = new LowerSignature();
+
+ /** Lowers a class to bytecode. */
+ public byte[] lower(SourceTypeBoundClass info, Env<HeaderBoundClass> env, ClassSymbol sym) {
+
+ int access = classAccess(info);
+ String name = sym.binaryName();
+ String signature = sig.classSignature(info);
+ String superName = info.superclass().binaryName();
+ List<String> interfaces = new ArrayList<>();
+ for (ClassSymbol i : info.interfaces()) {
+ interfaces.add(i.binaryName());
+ }
+
+ List<ClassFile.MethodInfo> methods = new ArrayList<>();
+ for (MethodInfo m : info.methods()) {
+ if (TurbineVisibility.fromAccess(m.access()) == TurbineVisibility.PRIVATE
+ && !m.name().equals("<init>")) {
+ // TODO(cushon): drop private members earlier?
+ continue;
+ }
+ methods.add(lowerMethod(env, m, sym));
+ }
+
+ ImmutableList.Builder<ClassFile.FieldInfo> fields = ImmutableList.builder();
+ for (FieldInfo f : info.fields()) {
+ if ((f.access() & TurbineFlag.ACC_PRIVATE) == TurbineFlag.ACC_PRIVATE) {
+ // TODO(cushon): drop private members earlier?
+ continue;
+ }
+ fields.add(lowerField(env, f));
+ }
+
+ // TODO(cushon): annotations
+ ImmutableList<ClassFile.AnnotationInfo> annotations = ImmutableList.of();
+
+ ImmutableList<ClassFile.InnerClass> inners = collectInnerClasses(sym, info, env);
+
+ ClassFile classfile =
+ new ClassFile(
+ access,
+ name,
+ signature,
+ superName,
+ interfaces,
+ methods,
+ fields.build(),
+ annotations,
+ inners);
+
+ return ClassWriter.writeClass(classfile);
+ }
+
+ private ClassFile.MethodInfo lowerMethod(
+ final Env<HeaderBoundClass> env, final MethodInfo m, final ClassSymbol sym) {
+ int access = m.access();
+ Function<TyVarSymbol, TyVarInfo> tenv = new TyVarEnv(m.tyParams(), env);
+ String name = m.name();
+ String desc = methodDescriptor(m, tenv);
+ String signature = sig.methodSignature(env, m, sym);
+ ImmutableList.Builder<String> exceptions = ImmutableList.builder();
+ if (!m.exceptions().isEmpty()) {
+ for (Type e : m.exceptions()) {
+ exceptions.add(((ClassTy) Erasure.erase(e, tenv)).sym().binaryName());
+ }
+ }
+
+ // TODO(cushon): annotations
+ ClassFile.AnnotationInfo.ElementValue defaultValue = null;
+ ImmutableList<ClassFile.AnnotationInfo> annotations = ImmutableList.of();
+ List<List<ClassFile.AnnotationInfo>> paramAnnotations = ImmutableList.of();
+
+ return new ClassFile.MethodInfo(
+ access,
+ name,
+ desc,
+ signature,
+ exceptions.build(),
+ defaultValue,
+ annotations,
+ paramAnnotations);
+ }
+
+ private String methodDescriptor(MethodInfo m, Function<TyVarSymbol, TyVarInfo> tenv) {
+ ImmutableList<Sig.TyParamSig> typarams = ImmutableList.of();
+ ImmutableList.Builder<TySig> fparams = ImmutableList.builder();
+ for (ParamInfo t : m.parameters()) {
+ fparams.add(sig.signature(Erasure.erase(t.type(), tenv)));
+ }
+ TySig result = sig.signature(Erasure.erase(m.returnType(), tenv));
+ ImmutableList<TySig> excns = ImmutableList.of();
+ return SigWriter.method(new MethodSig(typarams, fparams.build(), result, excns));
+ }
+
+ private ClassFile.FieldInfo lowerField(final Env<HeaderBoundClass> env, FieldInfo f) {
+ final String name = f.name();
+ Function<TyVarSymbol, TyVarInfo> tenv = new TyVarEnv(Collections.emptyMap(), env);
+ String desc = SigWriter.type(sig.signature(Erasure.erase(f.type(), tenv)));
+ String signature = SigWriter.type(sig.signature(f.type()));
+
+ // TODO(cushon): annotations
+ ImmutableList<ClassFile.AnnotationInfo> annotations = ImmutableList.of();
+ // TODO(cushon): constants
+ Const.Value value = null;
+
+ return new ClassFile.FieldInfo(f.access(), name, desc, signature, value, annotations);
+ }
+
+ /** Creates inner class attributes for all referenced inner classes. */
+ private ImmutableList<ClassFile.InnerClass> collectInnerClasses(
+ ClassSymbol origin, SourceTypeBoundClass info, Env<HeaderBoundClass> env) {
+ Set<ClassSymbol> all = new LinkedHashSet<>();
+ addEnclosing(env, all, origin);
+ for (ClassSymbol sym : info.children().values()) {
+ addEnclosing(env, all, sym);
+ }
+ for (ClassSymbol sym : sig.classes) {
+ addEnclosing(env, all, sym);
+ }
+ ImmutableList.Builder<ClassFile.InnerClass> inners = ImmutableList.builder();
+ for (ClassSymbol innerSym : all) {
+ inners.add(innerClass(env, innerSym));
+ }
+ return inners.build();
+ }
+
+ private void addEnclosing(Env<HeaderBoundClass> env, Set<ClassSymbol> all, ClassSymbol sym) {
+ HeaderBoundClass innerinfo = env.get(sym);
+ while (innerinfo.owner() != null) {
+ all.add(sym);
+ sym = innerinfo.owner();
+ innerinfo = env.get(sym);
+ }
+ }
+
+ /**
+ * Creates an inner class attribute, given an inner class that was referenced somewhere in the
+ * class.
+ */
+ private ClassFile.InnerClass innerClass(Env<HeaderBoundClass> env, ClassSymbol innerSym) {
+ HeaderBoundClass inner = env.get(innerSym);
+
+ String innerName = innerSym.binaryName().substring(inner.owner().binaryName().length() + 1);
+
+ int access = inner.access();
+ access &= ~TurbineFlag.ACC_SUPER;
+
+ return new ClassFile.InnerClass(
+ innerSym.binaryName(), inner.owner().binaryName(), innerName, access);
+ }
+
+ /** Updates visibility, and unsets access bits that can only be set in InnerClass. */
+ private int classAccess(SourceTypeBoundClass info) {
+ int access = info.access();
+ access &= ~(TurbineFlag.ACC_STATIC | TurbineFlag.ACC_PRIVATE);
+ if ((access & TurbineFlag.ACC_PROTECTED) != 0) {
+ access &= ~TurbineFlag.ACC_PROTECTED;
+ access |= TurbineFlag.ACC_PUBLIC;
+ }
+ return access;
+ }
+
+ /**
+ * Looks up {@link TyVarInfo}s.
+ *
+ * <p>We could generalize {@link Scope} instead, but this isn't needed anywhere else.
+ */
+ static class TyVarEnv implements Function<TyVarSymbol, TyVarInfo> {
+
+ private final Env<HeaderBoundClass> env;
+ private final Map<TyVarSymbol, TyVarInfo> tyParams;
+
+ /**
+ * @param tyParams the initial lookup scope, e.g. a method's formal type parameters.
+ * @param env the environment to look up a type variable's owning declaration in.
+ */
+ public TyVarEnv(Map<TyVarSymbol, TyVarInfo> tyParams, Env<HeaderBoundClass> env) {
+ this.tyParams = tyParams;
+ this.env = env;
+ }
+
+ @Override
+ public TyVarInfo apply(TyVarSymbol sym) {
+ TyVarInfo result = tyParams.get(sym);
+ if (result != null) {
+ return result;
+ }
+ // type variables can only be declared by methods and classes,
+ // and we've already handled methods
+ Symbol ownerSym = sym.owner();
+ if (ownerSym.symKind() != Symbol.Kind.CLASS) {
+ throw new AssertionError(sym);
+ }
+ // anything that lexically encloses the class being lowered
+ // must be in the same compilation unit, so we have source
+ // information for it
+ // TODO(cushon): remove this cast once we're reading type parameters from bytecode
+ HeaderBoundClass owner = env.get((ClassSymbol) ownerSym);
+ if (!(owner instanceof SourceTypeBoundClass)) {
+ throw new AssertionError(sym);
+ }
+ return ((SourceTypeBoundClass) owner).typeParameterTypes().get(sym);
+ }
+ }
+}
diff --git a/java/com/google/turbine/lower/LowerSignature.java b/java/com/google/turbine/lower/LowerSignature.java
index d88e3c3..bc4706f 100644
--- a/java/com/google/turbine/lower/LowerSignature.java
+++ b/java/com/google/turbine/lower/LowerSignature.java
@@ -17,9 +17,19 @@
package com.google.turbine.lower;
import com.google.common.collect.ImmutableList;
+import com.google.turbine.binder.bound.HeaderBoundClass;
+import com.google.turbine.binder.bound.SourceTypeBoundClass;
+import com.google.turbine.binder.env.Env;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.binder.sym.TyVarSymbol;
import com.google.turbine.bytecode.sig.Sig;
+import com.google.turbine.bytecode.sig.Sig.ClassSig;
+import com.google.turbine.bytecode.sig.Sig.ClassTySig;
+import com.google.turbine.bytecode.sig.Sig.MethodSig;
import com.google.turbine.bytecode.sig.Sig.SimpleClassTySig;
import com.google.turbine.bytecode.sig.Sig.TyArgSig;
+import com.google.turbine.bytecode.sig.SigWriter;
+import com.google.turbine.model.TurbineFlag;
import com.google.turbine.type.Type;
import com.google.turbine.type.Type.ArrayTy;
import com.google.turbine.type.Type.ClassTy;
@@ -29,14 +39,18 @@
import com.google.turbine.type.Type.TyVar;
import com.google.turbine.type.Type.WildLowerBoundedTy;
import com.google.turbine.type.Type.WildUpperBoundedTy;
-
import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
/** Translator from {@link Type}s to {@link Sig}natures. */
public class LowerSignature {
+ final Set<ClassSymbol> classes = new LinkedHashSet<>();
+
/** Translates types to signatures. */
- public static Sig.TySig signature(Type ty) {
+ public Sig.TySig signature(Type ty) {
switch (ty.tyKind()) {
case CLASS_TY:
return classTySig((Type.ClassTy) ty);
@@ -53,19 +67,20 @@
}
}
- private static Sig.BaseTySig refBaseTy(PrimTy t) {
+ private Sig.BaseTySig refBaseTy(PrimTy t) {
return new Sig.BaseTySig(t.primkind());
}
- private static Sig.ArrayTySig arrayTySig(ArrayTy t) {
+ private Sig.ArrayTySig arrayTySig(ArrayTy t) {
return new Sig.ArrayTySig(t.dimension(), signature(t.elementType()));
}
- private static Sig.TyVarSig tyVarSig(TyVar t) {
+ private Sig.TyVarSig tyVarSig(TyVar t) {
return new Sig.TyVarSig(t.sym().name());
}
- private static Sig.ClassTySig classTySig(ClassTy t) {
+ private ClassTySig classTySig(ClassTy t) {
+ classes.add(t.sym());
ImmutableList.Builder<SimpleClassTySig> classes = ImmutableList.builder();
Iterator<SimpleClassTy> it = t.classes.iterator();
SimpleClassTy curr = it.next();
@@ -89,10 +104,10 @@
String shortname = curr.sym().binaryName().substring(outer.sym().binaryName().length() + 1);
classes.add(new Sig.SimpleClassTySig(shortname, tyArgSigs(curr)));
}
- return new Sig.ClassTySig(pkg, classes.build());
+ return new ClassTySig(pkg, classes.build());
}
- private static ImmutableList<TyArgSig> tyArgSigs(SimpleClassTy part) {
+ private ImmutableList<TyArgSig> tyArgSigs(SimpleClassTy part) {
ImmutableList.Builder<TyArgSig> tyargs = ImmutableList.builder();
for (TyArg targ : part.targs()) {
tyargs.add(tyArgSig(targ));
@@ -100,7 +115,7 @@
return tyargs.build();
}
- private static TyArgSig tyArgSig(TyArg targ) {
+ private TyArgSig tyArgSig(TyArg targ) {
switch (targ.tyArgKind()) {
case CONCRETE:
return new Sig.ConcreteTyArgSig(signature(((Type.ConcreteTyArg) targ).type()));
@@ -114,4 +129,157 @@
throw new AssertionError(targ.tyArgKind());
}
}
+
+ /**
+ * Produces a method signature attribute for a generic method, or {@code null} if the signature is
+ * unnecessary.
+ */
+ public String methodSignature(
+ Env<HeaderBoundClass> env, SourceTypeBoundClass.MethodInfo method, ClassSymbol sym) {
+ if (!needsMethodSig(sym, env, method)) {
+ return null;
+ }
+ ImmutableList<Sig.TyParamSig> typarams = tyParamSig(method.tyParams());
+ ImmutableList.Builder<Sig.TySig> fparams = ImmutableList.builder();
+ for (SourceTypeBoundClass.ParamInfo t : method.parameters()) {
+ if (t.synthetic()) {
+ continue;
+ }
+ fparams.add(signature(t.type()));
+ }
+ Sig.TySig ret = signature(method.returnType());
+ ImmutableList.Builder<Sig.TySig> excn = ImmutableList.builder();
+ boolean needsExnSig = false;
+ for (Type e : method.exceptions()) {
+ if (needsSig(e)) {
+ needsExnSig = true;
+ break;
+ }
+ }
+ if (needsExnSig) {
+ for (Type e : method.exceptions()) {
+ excn.add(signature(e));
+ }
+ }
+ MethodSig sig = new MethodSig(typarams, fparams.build(), ret, excn.build());
+ return SigWriter.method(sig);
+ }
+
+ private boolean needsMethodSig(
+ ClassSymbol sym, Env<HeaderBoundClass> env, SourceTypeBoundClass.MethodInfo m) {
+ if ((env.get(sym).access() & TurbineFlag.ACC_ENUM) == TurbineFlag.ACC_ENUM
+ && m.name().equals("<init>")) {
+ // JDK-8024694: javac always expects signature attribute for enum constructors
+ return true;
+ }
+ if ((m.access() & TurbineFlag.ACC_SYNTH_CTOR) == TurbineFlag.ACC_SYNTH_CTOR) {
+ return false;
+ }
+ if (!m.tyParams().isEmpty()) {
+ return true;
+ }
+ if (m.returnType() != null && needsSig(m.returnType())) {
+ return true;
+ }
+ for (SourceTypeBoundClass.ParamInfo t : m.parameters()) {
+ if (t.synthetic()) {
+ continue;
+ }
+ if (needsSig(t.type())) {
+ return true;
+ }
+ }
+ for (Type t : m.exceptions()) {
+ if (needsSig(t)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Produces a class signature attribute for a generic class, or {@code null} if the signature is
+ * unnecessary.
+ */
+ public String classSignature(SourceTypeBoundClass info) {
+ if (!classNeedsSig(info)) {
+ return null;
+ }
+ ImmutableList<Sig.TyParamSig> typarams = tyParamSig(info.typeParameterTypes());
+
+ ClassTySig xtnd = null;
+ if (info.superClassType() != null) {
+ xtnd = classTySig(info.superClassType());
+ }
+ ImmutableList.Builder<ClassTySig> impl = ImmutableList.builder();
+ for (ClassTy i : info.interfaceTypes()) {
+ impl.add(classTySig(i));
+ }
+ ClassSig sig = new ClassSig(typarams, xtnd, impl.build());
+ return SigWriter.classSig(sig);
+ }
+
+ private boolean classNeedsSig(SourceTypeBoundClass ci) {
+ if (!ci.typeParameters().isEmpty()) {
+ return true;
+ }
+ if (ci.superClassType() != null && needsSig(ci.superClassType())) {
+ return true;
+ }
+ for (ClassTy i : ci.interfaceTypes()) {
+ if (needsSig(i)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean needsSig(Type ty) {
+ switch (ty.tyKind()) {
+ case PRIM_TY:
+ case VOID_TY:
+ return false;
+ case CLASS_TY:
+ {
+ for (SimpleClassTy s : ((ClassTy) ty).classes) {
+ if (!s.targs().isEmpty()) {
+ return true;
+ }
+ }
+ return false;
+ }
+ case ARRAY_TY:
+ return needsSig(((ArrayTy) ty).elementType());
+ case TY_VAR:
+ return true;
+ default:
+ throw new AssertionError(ty.tyKind());
+ }
+ }
+
+ private ImmutableList<Sig.TyParamSig> tyParamSig(
+ Map<TyVarSymbol, SourceTypeBoundClass.TyVarInfo> px) {
+ ImmutableList.Builder<Sig.TyParamSig> result = ImmutableList.builder();
+ for (Map.Entry<TyVarSymbol, SourceTypeBoundClass.TyVarInfo> entry : px.entrySet()) {
+ result.add(tyParamSig(entry.getKey(), entry.getValue()));
+ }
+ return result.build();
+ }
+
+ private Sig.TyParamSig tyParamSig(TyVarSymbol sym, SourceTypeBoundClass.TyVarInfo info) {
+ String identifier = sym.name();
+ Sig.TySig cbound = null;
+ if (info.superClassBound() != null) {
+ cbound = signature(info.superClassBound());
+ } else if (info.interfaceBounds().isEmpty()) {
+ cbound =
+ new ClassTySig(
+ "java/lang", ImmutableList.of(new SimpleClassTySig("Object", ImmutableList.of())));
+ }
+ ImmutableList.Builder<Sig.TySig> ibounds = ImmutableList.builder();
+ for (Type i : info.interfaceBounds()) {
+ ibounds.add(signature(i));
+ }
+ return new Sig.TyParamSig(identifier, cbound, ibounds.build());
+ }
}
diff --git a/java/com/google/turbine/type/Type.java b/java/com/google/turbine/type/Type.java
index 678c27b..004f974 100644
--- a/java/com/google/turbine/type/Type.java
+++ b/java/com/google/turbine/type/Type.java
@@ -22,7 +22,6 @@
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.binder.sym.TyVarSymbol;
import com.google.turbine.model.TurbineConstantTypeKind;
-
import java.util.Arrays;
/** JLS 4 types. */
@@ -67,6 +66,9 @@
*/
public static final ClassTy OBJECT = asNonParametricClassTy(ClassSymbol.OBJECT);
+ /** The {@link ClassTy} for {@code java.lang.String}. */
+ public static final ClassTy STRING = asNonParametricClassTy(ClassSymbol.STRING);
+
/** Returns a {@link ClassTy} with no type arguments for the given {@link ClassSymbol}. */
public static ClassTy asNonParametricClassTy(ClassSymbol i) {
return new ClassTy(Arrays.asList(new SimpleClassTy(i, ImmutableList.of())));
diff --git a/java/com/google/turbine/types/Erasure.java b/java/com/google/turbine/types/Erasure.java
new file mode 100644
index 0000000..80762be
--- /dev/null
+++ b/java/com/google/turbine/types/Erasure.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.turbine.types;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.turbine.binder.bound.SourceTypeBoundClass;
+import com.google.turbine.binder.sym.TyVarSymbol;
+import com.google.turbine.type.Type;
+import com.google.turbine.type.Type.TyVar;
+
+/** Generic type erasure. */
+public class Erasure {
+ public static Type erase(Type ty, Function<TyVarSymbol, SourceTypeBoundClass.TyVarInfo> tenv) {
+ switch (ty.tyKind()) {
+ case PRIM_TY:
+ case VOID_TY:
+ return ty;
+ case CLASS_TY:
+ return eraseClassTy((Type.ClassTy) ty);
+ case ARRAY_TY:
+ return eraseArrayTy((Type.ArrayTy) ty, tenv);
+ case TY_VAR:
+ return eraseTyVar((TyVar) ty, tenv);
+ default:
+ throw new AssertionError(ty.tyKind());
+ }
+ }
+
+ private static Type eraseTyVar(
+ TyVar ty, Function<TyVarSymbol, SourceTypeBoundClass.TyVarInfo> tenv) {
+ SourceTypeBoundClass.TyVarInfo info = tenv.apply(ty.sym());
+ if (info.superClassBound() != null) {
+ return erase(info.superClassBound(), tenv);
+ }
+ if (!info.interfaceBounds().isEmpty()) {
+ return erase(info.interfaceBounds().get(0), tenv);
+ }
+ return Type.ClassTy.OBJECT;
+ }
+
+ private static Type eraseArrayTy(
+ Type.ArrayTy ty, Function<TyVarSymbol, SourceTypeBoundClass.TyVarInfo> tenv) {
+ return new Type.ArrayTy(ty.dimension(), erase(ty.elementType(), tenv));
+ }
+
+ private static Type eraseClassTy(Type.ClassTy ty) {
+ ImmutableList.Builder<Type.ClassTy.SimpleClassTy> classes = ImmutableList.builder();
+ for (Type.ClassTy.SimpleClassTy c : ty.classes) {
+ if (c.targs().isEmpty()) {
+ classes.add(c);
+ } else {
+ classes.add(new Type.ClassTy.SimpleClassTy(c.sym(), ImmutableList.<Type.TyArg>of()));
+ }
+ }
+ return new Type.ClassTy(classes.build());
+ }
+}
diff --git a/javatests/com/google/turbine/bytecode/AsmUtils.java b/javatests/com/google/turbine/bytecode/AsmUtils.java
index 955f88f..6030e16 100644
--- a/javatests/com/google/turbine/bytecode/AsmUtils.java
+++ b/javatests/com/google/turbine/bytecode/AsmUtils.java
@@ -27,7 +27,7 @@
* com.google.turbine.bytecode.ClassReader}.
*/
public class AsmUtils {
- static String textify(byte[] bytes) {
+ public static String textify(byte[] bytes) {
Textifier textifier = new Textifier();
StringWriter sw = new StringWriter();
new ClassReader(bytes)
diff --git a/javatests/com/google/turbine/lower/LowerSignatureTest.java b/javatests/com/google/turbine/lower/LowerSignatureTest.java
index bad07bf..cfd57cf 100644
--- a/javatests/com/google/turbine/lower/LowerSignatureTest.java
+++ b/javatests/com/google/turbine/lower/LowerSignatureTest.java
@@ -37,20 +37,21 @@
ImmutableList.of(
new Type.ClassTy.SimpleClassTy(
new ClassSymbol("java/util/List"), ImmutableList.of())));
- assertThat(SigWriter.type(LowerSignature.signature(type))).isEqualTo("Ljava/util/List;");
+ assertThat(SigWriter.type(new LowerSignature().signature(type))).isEqualTo("Ljava/util/List;");
}
@Test
public void inner() {
assertThat(
SigWriter.type(
- LowerSignature.signature(
- new Type.ClassTy(
- ImmutableList.of(
- new Type.ClassTy.SimpleClassTy(
- new ClassSymbol("test/Outer"), ImmutableList.of()),
- new Type.ClassTy.SimpleClassTy(
- new ClassSymbol("test/Outer$Inner"), ImmutableList.of()))))))
+ new LowerSignature()
+ .signature(
+ new Type.ClassTy(
+ ImmutableList.of(
+ new Type.ClassTy.SimpleClassTy(
+ new ClassSymbol("test/Outer"), ImmutableList.of()),
+ new Type.ClassTy.SimpleClassTy(
+ new ClassSymbol("test/Outer$Inner"), ImmutableList.of()))))))
.isEqualTo("Ltest/Outer$Inner;");
}
@@ -65,7 +66,7 @@
new Type.ClassTy.SimpleClassTy(
new ClassSymbol("test/Outer$Inner"),
ImmutableList.of(new Type.ConcreteTyArg(Type.ClassTy.OBJECT)))));
- assertThat(SigWriter.type(LowerSignature.signature(type)))
+ assertThat(SigWriter.type(new LowerSignature().signature(type)))
.isEqualTo("Ltest/Outer<Ljava/lang/Object;>.Inner<Ljava/lang/Object;>;");
// Type#toString is only for debugging
assertThat(type.toString()).isEqualTo("test/Outer<java/lang/Object>.Inner<java/lang/Object>");
@@ -75,13 +76,14 @@
public void innerDefaultPackage() {
assertThat(
SigWriter.type(
- LowerSignature.signature(
- new Type.ClassTy(
- ImmutableList.of(
- new Type.ClassTy.SimpleClassTy(
- new ClassSymbol("Outer"), ImmutableList.of()),
- new Type.ClassTy.SimpleClassTy(
- new ClassSymbol("Outer$Inner"), ImmutableList.of()))))))
+ new LowerSignature()
+ .signature(
+ new Type.ClassTy(
+ ImmutableList.of(
+ new Type.ClassTy.SimpleClassTy(
+ new ClassSymbol("Outer"), ImmutableList.of()),
+ new Type.ClassTy.SimpleClassTy(
+ new ClassSymbol("Outer$Inner"), ImmutableList.of()))))))
.isEqualTo("LOuter$Inner;");
}
@@ -89,15 +91,16 @@
public void wildcard() {
assertThat(
SigWriter.type(
- LowerSignature.signature(
- new Type.ClassTy(
- ImmutableList.of(
- new Type.ClassTy.SimpleClassTy(
- new ClassSymbol("test/Test"),
- ImmutableList.of(
- new Type.WildTy(),
- new Type.WildLowerBoundedTy(Type.ClassTy.OBJECT),
- new Type.WildUpperBoundedTy(Type.ClassTy.OBJECT))))))))
+ new LowerSignature()
+ .signature(
+ new Type.ClassTy(
+ ImmutableList.of(
+ new Type.ClassTy.SimpleClassTy(
+ new ClassSymbol("test/Test"),
+ ImmutableList.of(
+ new Type.WildTy(),
+ new Type.WildLowerBoundedTy(Type.ClassTy.OBJECT),
+ new Type.WildUpperBoundedTy(Type.ClassTy.OBJECT))))))))
.isEqualTo("Ltest/Test<*-Ljava/lang/Object;+Ljava/lang/Object;>;");
}
@@ -105,7 +108,8 @@
public void tyVar() {
assertThat(
SigWriter.type(
- LowerSignature.signature(new Type.TyVar(new TyVarSymbol(ClassSymbol.OBJECT, "X")))))
+ new LowerSignature()
+ .signature(new Type.TyVar(new TyVarSymbol(ClassSymbol.OBJECT, "X")))))
.isEqualTo("TX;");
}
@@ -113,21 +117,22 @@
public void primitive() {
assertThat(
SigWriter.type(
- LowerSignature.signature(new Type.PrimTy(TurbineConstantTypeKind.BOOLEAN))))
+ new LowerSignature().signature(new Type.PrimTy(TurbineConstantTypeKind.BOOLEAN))))
.isEqualTo("Z");
}
@Test
public void voidType() {
- assertThat(SigWriter.type(LowerSignature.signature(Type.VOID))).isEqualTo("V");
+ assertThat(SigWriter.type(new LowerSignature().signature(Type.VOID))).isEqualTo("V");
}
@Test
public void array() {
assertThat(
SigWriter.type(
- LowerSignature.signature(
- new Type.ArrayTy(3, new Type.PrimTy(TurbineConstantTypeKind.BOOLEAN)))))
+ new LowerSignature()
+ .signature(
+ new Type.ArrayTy(3, new Type.PrimTy(TurbineConstantTypeKind.BOOLEAN)))))
.isEqualTo("[[[Z");
}
}
diff --git a/javatests/com/google/turbine/lower/LowerTest.java b/javatests/com/google/turbine/lower/LowerTest.java
new file mode 100644
index 0000000..c88883e
--- /dev/null
+++ b/javatests/com/google/turbine/lower/LowerTest.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.turbine.lower;
+
+import static com.google.common.truth.Truth.assertThat;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.io.ByteStreams;
+import com.google.turbine.binder.ClassPathBinder;
+import com.google.turbine.binder.bound.HeaderBoundClass;
+import com.google.turbine.binder.bound.SourceTypeBoundClass;
+import com.google.turbine.binder.bytecode.BytecodeBoundClass;
+import com.google.turbine.binder.env.CompoundEnv;
+import com.google.turbine.binder.env.SimpleEnv;
+import com.google.turbine.binder.lookup.TopLevelIndex;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.binder.sym.FieldSymbol;
+import com.google.turbine.binder.sym.MethodSymbol;
+import com.google.turbine.binder.sym.TyVarSymbol;
+import com.google.turbine.bytecode.AsmUtils;
+import com.google.turbine.model.TurbineConstantTypeKind;
+import com.google.turbine.model.TurbineFlag;
+import com.google.turbine.model.TurbineTyKind;
+import com.google.turbine.type.Type;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Map;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class LowerTest {
+
+ private static final ImmutableList<Path> BOOTCLASSPATH =
+ ImmutableList.of(Paths.get(System.getProperty("java.home")).resolve("lib/rt.jar"));
+
+ @Test
+ public void hello() throws Exception {
+ CompoundEnv<BytecodeBoundClass> classpath =
+ ClassPathBinder.bind(ImmutableList.of(), BOOTCLASSPATH, TopLevelIndex.builder());
+
+ ImmutableList<Type.ClassTy> interfaceTypes =
+ ImmutableList.of(
+ new Type.ClassTy(
+ ImmutableList.of(
+ new Type.ClassTy.SimpleClassTy(
+ new ClassSymbol("java/util/List"),
+ ImmutableList.of(
+ new Type.ConcreteTyArg(
+ new Type.TyVar(
+ new TyVarSymbol(new ClassSymbol("test/Test"), "V"))))))));
+ Type.ClassTy xtnds = Type.ClassTy.OBJECT;
+ ImmutableMap<TyVarSymbol, SourceTypeBoundClass.TyVarInfo> tps =
+ ImmutableMap.of(
+ new TyVarSymbol(new ClassSymbol("test/Test"), "V"),
+ new SourceTypeBoundClass.TyVarInfo(
+ new Type.ClassTy(
+ ImmutableList.of(
+ new Type.ClassTy.SimpleClassTy(
+ new ClassSymbol("test/Test$Inner"), ImmutableList.of()))),
+ ImmutableList.of()));
+ int access = TurbineFlag.ACC_SUPER | TurbineFlag.ACC_PUBLIC;
+ ImmutableList<SourceTypeBoundClass.MethodInfo> methods =
+ ImmutableList.of(
+ new SourceTypeBoundClass.MethodInfo(
+ new MethodSymbol(new ClassSymbol("test/Test"), "f"),
+ ImmutableMap.of(),
+ new Type.PrimTy(TurbineConstantTypeKind.INT),
+ ImmutableList.of(),
+ ImmutableList.of(),
+ TurbineFlag.ACC_STATIC | TurbineFlag.ACC_PUBLIC),
+ new SourceTypeBoundClass.MethodInfo(
+ new MethodSymbol(new ClassSymbol("test/Test"), "g"),
+ ImmutableMap.of(
+ new TyVarSymbol(new MethodSymbol(new ClassSymbol("test/Test"), "g"), "V"),
+ new SourceTypeBoundClass.TyVarInfo(
+ null,
+ ImmutableList.of(
+ new Type.ClassTy(
+ ImmutableList.of(
+ new Type.ClassTy.SimpleClassTy(
+ new ClassSymbol("java/lang/Runnable"),
+ ImmutableList.of()))))),
+ new TyVarSymbol(new MethodSymbol(new ClassSymbol("test/Test"), "g"), "E"),
+ new SourceTypeBoundClass.TyVarInfo(
+ new Type.ClassTy(
+ ImmutableList.of(
+ new Type.ClassTy.SimpleClassTy(
+ new ClassSymbol("java/lang/Error"), ImmutableList.of()))),
+ ImmutableList.of())),
+ Type.VOID,
+ ImmutableList.of(
+ new SourceTypeBoundClass.ParamInfo(
+ new Type.PrimTy(TurbineConstantTypeKind.INT), false)),
+ ImmutableList.of(
+ new Type.TyVar(
+ new TyVarSymbol(new MethodSymbol(new ClassSymbol("test/Test"), "g"), "E"))),
+ TurbineFlag.ACC_PUBLIC));
+ ImmutableList<SourceTypeBoundClass.FieldInfo> fields =
+ ImmutableList.of(
+ new SourceTypeBoundClass.FieldInfo(
+ new FieldSymbol(new ClassSymbol("test/Test"), "theField"),
+ Type.ClassTy.asNonParametricClassTy(new ClassSymbol("test/Test$Inner")),
+ TurbineFlag.ACC_STATIC | TurbineFlag.ACC_FINAL | TurbineFlag.ACC_PUBLIC));
+ ClassSymbol owner = null;
+ TurbineTyKind kind = TurbineTyKind.CLASS;
+ ImmutableMap<String, ClassSymbol> children = ImmutableMap.of();
+ ClassSymbol superclass = ClassSymbol.OBJECT;
+ ImmutableList<ClassSymbol> interfaces = ImmutableList.of(new ClassSymbol("java/util/List"));
+ ImmutableMap<String, TyVarSymbol> tyParams =
+ ImmutableMap.of("V", new TyVarSymbol(new ClassSymbol("test/Test"), "V"));
+
+ SourceTypeBoundClass c =
+ new SourceTypeBoundClass(
+ interfaceTypes,
+ xtnds,
+ tps,
+ access,
+ methods,
+ fields,
+ owner,
+ kind,
+ children,
+ superclass,
+ interfaces,
+ tyParams);
+
+ SourceTypeBoundClass i =
+ new SourceTypeBoundClass(
+ ImmutableList.of(),
+ Type.ClassTy.OBJECT,
+ ImmutableMap.of(),
+ TurbineFlag.ACC_STATIC | TurbineFlag.ACC_PROTECTED,
+ ImmutableList.of(),
+ ImmutableList.of(),
+ new ClassSymbol("test/Test"),
+ TurbineTyKind.CLASS,
+ ImmutableMap.of("Inner", new ClassSymbol("test/Test$Inner")),
+ ClassSymbol.OBJECT,
+ ImmutableList.of(),
+ ImmutableMap.of());
+
+ SimpleEnv.Builder<SourceTypeBoundClass> b = SimpleEnv.builder();
+ b.putIfAbsent(new ClassSymbol("test/Test"), c);
+ b.putIfAbsent(new ClassSymbol("test/Test$Inner"), i);
+ SimpleEnv<SourceTypeBoundClass> env = b.build();
+
+ Map<String, byte[]> bytes =
+ Lower.lowerAll(
+ CompoundEnv.<HeaderBoundClass>of(classpath).append(env),
+ ImmutableList.of(new ClassSymbol("test/Test"), new ClassSymbol("test/Test$Inner")));
+
+ assertThat(AsmUtils.textify(bytes.get("test/Test")))
+ .isEqualTo(
+ new String(
+ ByteStreams.toByteArray(
+ LowerTest.class.getResourceAsStream("testdata/golden/outer.txt")),
+ UTF_8));
+ assertThat(AsmUtils.textify(bytes.get("test/Test$Inner")))
+ .isEqualTo(
+ new String(
+ ByteStreams.toByteArray(
+ LowerTest.class.getResourceAsStream("testdata/golden/inner.txt")),
+ UTF_8));
+ }
+}
diff --git a/javatests/com/google/turbine/lower/testdata/golden/inner.txt b/javatests/com/google/turbine/lower/testdata/golden/inner.txt
new file mode 100644
index 0000000..6ce4974
--- /dev/null
+++ b/javatests/com/google/turbine/lower/testdata/golden/inner.txt
@@ -0,0 +1,7 @@
+// class version 52.0 (52)
+// access flags 0x1
+public class test/Test$Inner {
+
+ // access flags 0xC
+ protected static INNERCLASS test/Test$Inner test/Test Inner
+}
diff --git a/javatests/com/google/turbine/lower/testdata/golden/outer.txt b/javatests/com/google/turbine/lower/testdata/golden/outer.txt
new file mode 100644
index 0000000..083b030
--- /dev/null
+++ b/javatests/com/google/turbine/lower/testdata/golden/outer.txt
@@ -0,0 +1,22 @@
+// class version 52.0 (52)
+// access flags 0x21
+// signature <V:Ltest/Test$Inner;>Ljava/lang/Object;Ljava/util/List<TV;>;
+// declaration: test/Test<V extends test.Test$Inner> implements java.util.List<V>
+public class test/Test implements java/util/List {
+
+ // access flags 0xC
+ protected static INNERCLASS test/Test$Inner test/Test Inner
+
+ // access flags 0x19
+ // signature Ltest/Test$Inner;
+ // declaration: test.Test$Inner
+ public final static Ltest/Test$Inner; theField
+
+ // access flags 0x9
+ public static f()I
+
+ // access flags 0x1
+ // signature <V::Ljava/lang/Runnable;E:Ljava/lang/Error;>(I)V^TE;
+ // declaration: void g<V extends java.lang.Runnable, E extends java.lang.Error>(int) throws E
+ public g(I)V throws java/lang/Error
+}