lubomir@1029: /** lubomir@1029: * Back 2 Browser Bytecode Translator lubomir@1029: * Copyright (C) 2012 Jaroslav Tulach lubomir@1029: * lubomir@1029: * This program is free software: you can redistribute it and/or modify lubomir@1029: * it under the terms of the GNU General Public License as published by lubomir@1029: * the Free Software Foundation, version 2 of the License. lubomir@1029: * lubomir@1029: * This program is distributed in the hope that it will be useful, lubomir@1029: * but WITHOUT ANY WARRANTY; without even the implied warranty of lubomir@1029: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the lubomir@1029: * GNU General Public License for more details. lubomir@1029: * lubomir@1029: * You should have received a copy of the GNU General Public License lubomir@1029: * along with this program. Look for COPYING file in the top folder. lubomir@1029: * If not, see http://opensource.org/licenses/GPL-2.0. lubomir@1029: */ lubomir@1029: package org.apidesign.vm4brwsr; lubomir@1029: lubomir@1029: import java.io.IOException; lubomir@1029: import java.io.InputStream; lubomir@1029: import java.util.HashMap; lubomir@1029: import java.util.Map; lubomir@1029: import org.apidesign.bck2brwsr.core.ExtraJavaScript; lubomir@1029: import org.apidesign.vm4brwsr.ByteCodeParser.AnnotationParser; lubomir@1029: import org.apidesign.vm4brwsr.ByteCodeParser.ClassData; lubomir@1029: import org.apidesign.vm4brwsr.ByteCodeParser.FieldData; lubomir@1029: import org.apidesign.vm4brwsr.ByteCodeParser.MethodData; lubomir@1029: lubomir@1029: @ExtraJavaScript(processByteCode = false, resource="") lubomir@1029: final class ExportedSymbols { lubomir@1029: private final Bck2Brwsr.Resources resources; jaroslav@1491: private final StringArray exported; lubomir@1029: private final Map isMarkedAsExportedCache; lubomir@1029: jaroslav@1491: ExportedSymbols(final Bck2Brwsr.Resources resources, StringArray explicitlyExported) { lubomir@1029: this.resources = resources; jaroslav@1491: this.exported = explicitlyExported; lubomir@1029: lubomir@1029: isMarkedAsExportedCache = new HashMap(); lubomir@1029: } lubomir@1029: lubomir@1029: boolean isExported(ClassData classData) throws IOException { jaroslav@1491: if (exported.contains(classData.getClassName())) { jaroslav@1491: return true; jaroslav@1491: } lubomir@1029: return classData.isPublic() && isMarkedAsExportedPackage( lubomir@1029: classData.getPkgName()) lubomir@1029: || isMarkedAsExported(classData); lubomir@1029: } lubomir@1029: lubomir@1029: boolean isExported(MethodData methodData) throws IOException { lubomir@1029: return isAccessible(methodData.access) && isExported(methodData.cls) lubomir@1029: || isMarkedAsExported(methodData); lubomir@1029: } lubomir@1029: lubomir@1029: boolean isExported(FieldData fieldData) throws IOException { jaroslav@1561: if ( jaroslav@1561: isAccessible(fieldData.access) && jaroslav@1561: isExported(fieldData.cls) || isMarkedAsExported(fieldData) jaroslav@1561: ) { jaroslav@1561: return true; jaroslav@1561: } jaroslav@1561: if ( jaroslav@1561: fieldData.isStatic() && fieldData.getName().equals("$VALUES") && jaroslav@1561: "java/lang/Enum".equals(fieldData.cls.getSuperClassName()) jaroslav@1561: ) { jaroslav@1561: // enum values need to be exported jaroslav@1561: return true; jaroslav@1561: } jaroslav@1561: return false; lubomir@1029: } lubomir@1029: lubomir@1029: private boolean isMarkedAsExportedPackage(String pkgName) { lubomir@1029: if (pkgName == null) { lubomir@1029: return false; lubomir@1029: } jaroslav@1592: if (pkgName.startsWith("java/")) { jaroslav@1592: return true; jaroslav@1592: } lubomir@1029: lubomir@1029: final Boolean cachedValue = isMarkedAsExportedCache.get(pkgName); lubomir@1029: if (cachedValue != null) { lubomir@1029: return cachedValue; lubomir@1029: } lubomir@1029: lubomir@1029: final boolean newValue = resolveIsMarkedAsExportedPackage(pkgName); lubomir@1029: isMarkedAsExportedCache.put(pkgName, newValue); lubomir@1029: lubomir@1029: return newValue; lubomir@1029: } lubomir@1029: lubomir@1029: private boolean isMarkedAsExported(ClassData classData) lubomir@1029: throws IOException { lubomir@1029: final Boolean cachedValue = isMarkedAsExportedCache.get(classData); lubomir@1029: if (cachedValue != null) { lubomir@1029: return cachedValue; lubomir@1029: } lubomir@1029: lubomir@1029: final boolean newValue = lubomir@1029: isMarkedAsExported(classData.findAnnotationData(true), lubomir@1029: classData); lubomir@1029: isMarkedAsExportedCache.put(classData, newValue); lubomir@1029: lubomir@1029: return newValue; lubomir@1029: } lubomir@1029: lubomir@1029: private boolean isMarkedAsExported(MethodData methodData) lubomir@1029: throws IOException { lubomir@1029: return isMarkedAsExported(methodData.findAnnotationData(true), lubomir@1029: methodData.cls); lubomir@1029: } lubomir@1029: lubomir@1029: private boolean isMarkedAsExported(FieldData fieldData) lubomir@1029: throws IOException { lubomir@1029: return isMarkedAsExported(fieldData.findAnnotationData(true), lubomir@1029: fieldData.cls); lubomir@1029: } lubomir@1029: lubomir@1029: private boolean resolveIsMarkedAsExportedPackage(String pkgName) { jaroslav@1583: if (exported.contains(pkgName + '/')) { jaroslav@1583: return true; jaroslav@1583: } lubomir@1029: try { lubomir@1029: final InputStream is = lubomir@1029: resources.get(pkgName + "/package-info.class"); lubomir@1029: if (is == null) { lubomir@1029: return false; lubomir@1029: } lubomir@1029: lubomir@1029: try { lubomir@1029: final ClassData pkgInfoClass = new ClassData(is); lubomir@1029: return isMarkedAsExported( lubomir@1029: pkgInfoClass.findAnnotationData(true), lubomir@1029: pkgInfoClass); lubomir@1029: } finally { lubomir@1029: is.close(); lubomir@1029: } lubomir@1029: } catch (final IOException e) { lubomir@1029: return false; lubomir@1029: } lubomir@1029: } lubomir@1029: jaroslav@1586: static boolean isMarkedAsExported(byte[] arrData, ClassData cd) lubomir@1029: throws IOException { lubomir@1029: if (arrData == null) { lubomir@1029: return false; lubomir@1029: } lubomir@1029: lubomir@1029: final boolean[] found = { false }; lubomir@1029: final AnnotationParser annotationParser = lubomir@1029: new AnnotationParser(false, false) { lubomir@1029: @Override lubomir@1029: protected void visitAnnotationStart( lubomir@1029: String type, lubomir@1029: boolean top) { lubomir@1029: if (top && type.equals("Lorg/apidesign/bck2brwsr" lubomir@1029: + "/core/Exported;")) { lubomir@1029: found[0] = true; lubomir@1029: } lubomir@1029: } lubomir@1029: }; lubomir@1029: annotationParser.parse(arrData, cd); lubomir@1029: return found[0]; lubomir@1029: } lubomir@1029: lubomir@1029: private static boolean isAccessible(int access) { lubomir@1029: return (access & (ByteCodeParser.ACC_PUBLIC lubomir@1029: | ByteCodeParser.ACC_PROTECTED)) != 0; lubomir@1029: } lubomir@1029: }