/* * 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.devtools.j2objc.javac; import com.google.devtools.j2objc.util.ParserEnvironment; import com.sun.tools.javac.api.JavacTaskImpl; import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symtab; import com.sun.tools.javac.file.JavacFileManager; import com.sun.tools.javac.jvm.ClassReader; import com.sun.tools.javac.model.JavacElements; import com.sun.tools.javac.model.JavacTypes; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.Name; import com.sun.tools.javac.util.Names; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Map; import javax.lang.model.element.Element; import javax.lang.model.element.PackageElement; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import javax.tools.DiagnosticCollector; import javax.tools.JavaFileObject; class JavacEnvironment implements ParserEnvironment { private final JavacTaskImpl task; private final JavacFileManager fileManager; private final DiagnosticCollector<JavaFileObject> diagnostics; private final Context context; private final ClassReader classReader; private final Names javacNames; private final Symtab symbolTable; private final JavacElements javacElements; private final JavacTypes javacTypes; JavacEnvironment(JavacTaskImpl task, JavacFileManager fileManager, DiagnosticCollector<JavaFileObject> diagnostics) { this.task = task; this.fileManager = fileManager; this.diagnostics = diagnostics; context = task.getContext(); classReader = ClassReader.instance(context); javacNames = Names.instance(context); symbolTable = Symtab.instance(context); javacElements = JavacElements.instance(context); javacTypes = JavacTypes.instance(context); } @Override public Element resolve(String name) { Name className = javacNames.fromString(name); // Check first if compiler already created or loaded the class. ClassSymbol symbol = getClass(className); if (symbol == null) { // Not available, read it from a class file. // Note: the enterName(Name) method moved from ClassReader to // Symtab in Java 9. Reflection is used to support both locations. symbol = enterClassJavac(className); if (symbol != null) { putClass(className, symbol); } else { symbol = enterClassJavac9(className); // The symbolTable is already updated in Java 9. } } return symbol; } @SuppressWarnings("unchecked") private ClassSymbol getClass(Name className) { try { return (ClassSymbol) ((Map<Name, ClassSymbol>) Symtab.class.getField("classes").get(symbolTable)) .get(className); } catch (ReflectiveOperationException e) { // continue } try { // Java 9 Class<?> moduleSymbolCls = Class.forName("com.sun.tools.javac.code.Symbol$ModuleSymbol"); Object javaBaseModule = Symtab.class.getDeclaredField("java_base").get(symbolTable); return (ClassSymbol) Symtab.class .getMethod("getClass", moduleSymbolCls, Name.class) .invoke(symbolTable, javaBaseModule, className); } catch (ReflectiveOperationException e) { throw new LinkageError(e.getMessage(), e); } } @SuppressWarnings("unchecked") private void putClass(Name className, ClassSymbol symbol) { try { ((Map<Name, ClassSymbol>) Symtab.class.getField("classes").get(symbolTable)) .put(className, symbol); } catch (ReflectiveOperationException e) { throw new LinkageError(e.getMessage(), e); } } public PackageElement defaultPackage() { try { return (PackageElement) Symtab.class.getField("unnamedPackage").get(symbolTable); } catch (NoSuchFieldException e) { // continue } catch (ReflectiveOperationException e) { throw new LinkageError(e.getMessage(), e); } try { Object unnamedModule = Symtab.class.getField("unnamedModule"); return (PackageElement) unnamedModule.getClass().getField("unnamedPackage").get(symbolTable); } catch (ReflectiveOperationException e) { throw new LinkageError(e.getMessage(), e); } } private ClassSymbol enterClassJavac(Name className) { try { Method m = ClassReader.class.getDeclaredMethod("enterClass", Name.class); return (ClassSymbol) m.invoke(classReader, className); } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { return null; } } private ClassSymbol enterClassJavac9(Name className) { // TODO(tball): remove reflection use when Java 9 is minimum version. try { Method m = Symtab.class.getDeclaredMethod("enterClass", Name.class); return (ClassSymbol) m.invoke(symbolTable, className); } catch (NoSuchMethodException e) { // continue } catch (ReflectiveOperationException e) { throw new LinkageError(e.getMessage(), e); } try { Class<?> moduleSymbolCls = Class.forName("com.sun.tools.javac.code.Symbol$ModuleSymbol"); Object javaBaseModule = Symtab.class.getDeclaredField("java_base").get(symbolTable); Method enterClass = Symtab.class.getDeclaredMethod("enterClass", moduleSymbolCls, Name.class); return (ClassSymbol) enterClass.invoke(symbolTable, javaBaseModule, className); } catch (ReflectiveOperationException e) { throw new LinkageError(e.getMessage(), e); } } public Context getContext() { return context; } @Override public Elements elementUtilities() { return javacElements; } @Override public Types typeUtilities() { return javacTypes; } public JavacTaskImpl task() { return task; } public JavacFileManager fileManager() { return fileManager; } public DiagnosticCollector<JavaFileObject> diagnostics() { return diagnostics; } }