rt/vm/src/main/java/org/apidesign/vm4brwsr/ExportedSymbols.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Mon, 26 May 2014 10:18:57 +0200
branchclosure
changeset 1592 f0433f790aaa
parent 1586 d4ee65642d8d
child 1787 ea12a3bb4b33
permissions -rw-r--r--
Export all java.** packages
lubomir@1029
     1
/**
lubomir@1029
     2
 * Back 2 Browser Bytecode Translator
lubomir@1029
     3
 * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
lubomir@1029
     4
 *
lubomir@1029
     5
 * This program is free software: you can redistribute it and/or modify
lubomir@1029
     6
 * it under the terms of the GNU General Public License as published by
lubomir@1029
     7
 * the Free Software Foundation, version 2 of the License.
lubomir@1029
     8
 *
lubomir@1029
     9
 * This program is distributed in the hope that it will be useful,
lubomir@1029
    10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
lubomir@1029
    11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
lubomir@1029
    12
 * GNU General Public License for more details.
lubomir@1029
    13
 *
lubomir@1029
    14
 * You should have received a copy of the GNU General Public License
lubomir@1029
    15
 * along with this program. Look for COPYING file in the top folder.
lubomir@1029
    16
 * If not, see http://opensource.org/licenses/GPL-2.0.
lubomir@1029
    17
 */
lubomir@1029
    18
package org.apidesign.vm4brwsr;
lubomir@1029
    19
lubomir@1029
    20
import java.io.IOException;
lubomir@1029
    21
import java.io.InputStream;
lubomir@1029
    22
import java.util.HashMap;
lubomir@1029
    23
import java.util.Map;
lubomir@1029
    24
import org.apidesign.bck2brwsr.core.ExtraJavaScript;
lubomir@1029
    25
import org.apidesign.vm4brwsr.ByteCodeParser.AnnotationParser;
lubomir@1029
    26
import org.apidesign.vm4brwsr.ByteCodeParser.ClassData;
lubomir@1029
    27
import org.apidesign.vm4brwsr.ByteCodeParser.FieldData;
lubomir@1029
    28
import org.apidesign.vm4brwsr.ByteCodeParser.MethodData;
lubomir@1029
    29
lubomir@1029
    30
@ExtraJavaScript(processByteCode = false, resource="")
lubomir@1029
    31
final class ExportedSymbols {
lubomir@1029
    32
    private final Bck2Brwsr.Resources resources;
jaroslav@1491
    33
    private final StringArray exported;
lubomir@1029
    34
    private final Map<Object, Boolean> isMarkedAsExportedCache;
lubomir@1029
    35
jaroslav@1491
    36
    ExportedSymbols(final Bck2Brwsr.Resources resources, StringArray explicitlyExported) {
lubomir@1029
    37
        this.resources = resources;
jaroslav@1491
    38
        this.exported = explicitlyExported;
lubomir@1029
    39
lubomir@1029
    40
        isMarkedAsExportedCache = new HashMap<Object, Boolean>();
lubomir@1029
    41
    }
lubomir@1029
    42
lubomir@1029
    43
    boolean isExported(ClassData classData) throws IOException {
jaroslav@1491
    44
        if (exported.contains(classData.getClassName())) {
jaroslav@1491
    45
            return true;
jaroslav@1491
    46
        }
lubomir@1029
    47
        return classData.isPublic() && isMarkedAsExportedPackage(
lubomir@1029
    48
                                           classData.getPkgName())
lubomir@1029
    49
                   || isMarkedAsExported(classData);
lubomir@1029
    50
    }
lubomir@1029
    51
lubomir@1029
    52
    boolean isExported(MethodData methodData) throws IOException {
lubomir@1029
    53
        return isAccessible(methodData.access) && isExported(methodData.cls)
lubomir@1029
    54
                   || isMarkedAsExported(methodData);
lubomir@1029
    55
    }
lubomir@1029
    56
lubomir@1029
    57
    boolean isExported(FieldData fieldData) throws IOException {
jaroslav@1561
    58
        if (
jaroslav@1561
    59
            isAccessible(fieldData.access) && 
jaroslav@1561
    60
            isExported(fieldData.cls) || isMarkedAsExported(fieldData)
jaroslav@1561
    61
        ) {
jaroslav@1561
    62
            return true;
jaroslav@1561
    63
        }
jaroslav@1561
    64
        if (
jaroslav@1561
    65
            fieldData.isStatic() && fieldData.getName().equals("$VALUES") &&
jaroslav@1561
    66
            "java/lang/Enum".equals(fieldData.cls.getSuperClassName())
jaroslav@1561
    67
        ) {
jaroslav@1561
    68
            // enum values need to be exported
jaroslav@1561
    69
            return true;
jaroslav@1561
    70
        }
jaroslav@1561
    71
        return false;
lubomir@1029
    72
    }
lubomir@1029
    73
lubomir@1029
    74
    private boolean isMarkedAsExportedPackage(String pkgName) {
lubomir@1029
    75
        if (pkgName == null) {
lubomir@1029
    76
            return false;
lubomir@1029
    77
        }
jaroslav@1592
    78
        if (pkgName.startsWith("java/")) {
jaroslav@1592
    79
            return true;
jaroslav@1592
    80
        }
lubomir@1029
    81
lubomir@1029
    82
        final Boolean cachedValue = isMarkedAsExportedCache.get(pkgName);
lubomir@1029
    83
        if (cachedValue != null) {
lubomir@1029
    84
            return cachedValue;
lubomir@1029
    85
        }
lubomir@1029
    86
lubomir@1029
    87
        final boolean newValue = resolveIsMarkedAsExportedPackage(pkgName);
lubomir@1029
    88
        isMarkedAsExportedCache.put(pkgName, newValue);
lubomir@1029
    89
lubomir@1029
    90
        return newValue;
lubomir@1029
    91
    }
lubomir@1029
    92
lubomir@1029
    93
    private boolean isMarkedAsExported(ClassData classData)
lubomir@1029
    94
            throws IOException {
lubomir@1029
    95
        final Boolean cachedValue = isMarkedAsExportedCache.get(classData);
lubomir@1029
    96
        if (cachedValue != null) {
lubomir@1029
    97
            return cachedValue;
lubomir@1029
    98
        }
lubomir@1029
    99
lubomir@1029
   100
        final boolean newValue =
lubomir@1029
   101
                isMarkedAsExported(classData.findAnnotationData(true),
lubomir@1029
   102
                                   classData);
lubomir@1029
   103
        isMarkedAsExportedCache.put(classData, newValue);
lubomir@1029
   104
lubomir@1029
   105
        return newValue;
lubomir@1029
   106
    }
lubomir@1029
   107
lubomir@1029
   108
    private boolean isMarkedAsExported(MethodData methodData)
lubomir@1029
   109
            throws IOException {
lubomir@1029
   110
        return isMarkedAsExported(methodData.findAnnotationData(true),
lubomir@1029
   111
                                  methodData.cls);
lubomir@1029
   112
    }
lubomir@1029
   113
lubomir@1029
   114
    private boolean isMarkedAsExported(FieldData fieldData)
lubomir@1029
   115
            throws IOException {
lubomir@1029
   116
        return isMarkedAsExported(fieldData.findAnnotationData(true),
lubomir@1029
   117
                                  fieldData.cls);
lubomir@1029
   118
    }
lubomir@1029
   119
lubomir@1029
   120
    private boolean resolveIsMarkedAsExportedPackage(String pkgName) {
jaroslav@1583
   121
        if (exported.contains(pkgName + '/')) {
jaroslav@1583
   122
            return true;
jaroslav@1583
   123
        }
lubomir@1029
   124
        try {
lubomir@1029
   125
            final InputStream is =
lubomir@1029
   126
                    resources.get(pkgName + "/package-info.class");
lubomir@1029
   127
            if (is == null) {
lubomir@1029
   128
                return false;
lubomir@1029
   129
            }
lubomir@1029
   130
lubomir@1029
   131
            try {
lubomir@1029
   132
                final ClassData pkgInfoClass = new ClassData(is);
lubomir@1029
   133
                return isMarkedAsExported(
lubomir@1029
   134
                               pkgInfoClass.findAnnotationData(true),
lubomir@1029
   135
                               pkgInfoClass);
lubomir@1029
   136
            } finally {
lubomir@1029
   137
                is.close();
lubomir@1029
   138
            }
lubomir@1029
   139
        } catch (final IOException e) {
lubomir@1029
   140
            return false;
lubomir@1029
   141
        }
lubomir@1029
   142
    }
lubomir@1029
   143
jaroslav@1586
   144
    static boolean isMarkedAsExported(byte[] arrData, ClassData cd)
lubomir@1029
   145
            throws IOException {
lubomir@1029
   146
        if (arrData == null) {
lubomir@1029
   147
            return false;
lubomir@1029
   148
        }
lubomir@1029
   149
lubomir@1029
   150
        final boolean[] found = { false };
lubomir@1029
   151
        final AnnotationParser annotationParser =
lubomir@1029
   152
                new AnnotationParser(false, false) {
lubomir@1029
   153
                    @Override
lubomir@1029
   154
                    protected void visitAnnotationStart(
lubomir@1029
   155
                            String type,
lubomir@1029
   156
                            boolean top) {
lubomir@1029
   157
                        if (top && type.equals("Lorg/apidesign/bck2brwsr"
lubomir@1029
   158
                                                   + "/core/Exported;")) {
lubomir@1029
   159
                            found[0] = true;
lubomir@1029
   160
                        }
lubomir@1029
   161
                    }
lubomir@1029
   162
                };
lubomir@1029
   163
        annotationParser.parse(arrData, cd);
lubomir@1029
   164
        return found[0];
lubomir@1029
   165
    }
lubomir@1029
   166
lubomir@1029
   167
    private static boolean isAccessible(int access) {
lubomir@1029
   168
        return (access & (ByteCodeParser.ACC_PUBLIC
lubomir@1029
   169
                              | ByteCodeParser.ACC_PROTECTED)) != 0;
lubomir@1029
   170
    }
lubomir@1029
   171
}