# HG changeset patch # User Lubomir Nerad # Date 1365785328 -7200 # Node ID f19f17f8f8dcf196fdf1a66e41cae81dd5032894 # Parent fe7ff18eae8db7b88bd7a969b86956ce1673571d Introduced Exported annotation diff -r fe7ff18eae8d -r f19f17f8f8dc rt/core/src/main/java/org/apidesign/bck2brwsr/core/Exported.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/core/src/main/java/org/apidesign/bck2brwsr/core/Exported.java Fri Apr 12 18:48:48 2013 +0200 @@ -0,0 +1,38 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.core; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Marks the corresponding program element as exported. Exported elements are + * visible from other modules. Can be used on packages, classes, methods, + * constructors and fields. + * + * @since 0.6 + */ +@Retention(RetentionPolicy.CLASS) +@Target({ ElementType.PACKAGE, ElementType.TYPE, + ElementType.METHOD, ElementType.CONSTRUCTOR, + ElementType.FIELD }) +public @interface Exported { + +} diff -r fe7ff18eae8d -r f19f17f8f8dc rt/emul/mini/src/main/java/java/lang/reflect/Array.java --- a/rt/emul/mini/src/main/java/java/lang/reflect/Array.java Thu Apr 11 16:59:42 2013 +0200 +++ b/rt/emul/mini/src/main/java/java/lang/reflect/Array.java Fri Apr 12 18:48:48 2013 +0200 @@ -25,6 +25,7 @@ package java.lang.reflect; +import org.apidesign.bck2brwsr.core.Exported; import org.apidesign.bck2brwsr.core.JavaScriptBody; import org.apidesign.bck2brwsr.core.JavaScriptPrototype; @@ -636,9 +637,12 @@ + "arr.jvmName = sig;\n" + "return arr;" ) - static native Object newArray(boolean primitive, String sig, int length); + @Exported + private static native Object newArray(boolean primitive, String sig, int length); - static Object multiNewArray(String sig, int[] dims, int index) + + @Exported + private static Object multiNewArray(String sig, int[] dims, int index) throws IllegalArgumentException, NegativeArraySizeException { if (dims.length == index + 1) { return newArray(sig.length() == 2, sig, dims[index]); diff -r fe7ff18eae8d -r f19f17f8f8dc rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeParser.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeParser.java Thu Apr 11 16:59:42 2013 +0200 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeParser.java Fri Apr 12 18:48:48 2013 +0200 @@ -1981,7 +1981,7 @@ pkgPrefixLen = classname.lastIndexOf("/") + 1; if (pkgPrefixLen != 0) { pkgPrefix = classname.substring(0, pkgPrefixLen); - return ("package " + pkgPrefix.substring(0, pkgPrefixLen - 1) + ";\n"); + return /* ("package " + */ pkgPrefix.substring(0, pkgPrefixLen - 1) /* + ";\n") */; } else { return null; } diff -r fe7ff18eae8d -r f19f17f8f8dc rt/vm/src/main/java/org/apidesign/vm4brwsr/ClosureWrapper.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ClosureWrapper.java Thu Apr 11 16:59:42 2013 +0200 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ClosureWrapper.java Fri Apr 12 18:48:48 2013 +0200 @@ -20,14 +20,18 @@ import com.google.javascript.jscomp.CommandLineRunner; import com.google.javascript.jscomp.SourceFile; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.apidesign.bck2brwsr.core.ExtraJavaScript; +import org.apidesign.vm4brwsr.ByteCodeParser.AnnotationParser; import org.apidesign.vm4brwsr.ByteCodeParser.ClassData; import org.apidesign.vm4brwsr.ByteCodeParser.FieldData; import org.apidesign.vm4brwsr.ByteCodeParser.MethodData; @@ -163,12 +167,14 @@ /* case MEDIUM: return new ClosureWrapper(w, "ADVANCED_OPTIMIZATIONS", - new MediumObfuscationDelegate(), + new MediumObfuscationDelegate( + resources), resources, arr); */ case FULL: return new ClosureWrapper(w, "ADVANCED_OPTIMIZATIONS", - new FullObfuscationDelegate(), + new FullObfuscationDelegate( + resources), resources, arr); default: throw new IllegalArgumentException( @@ -264,10 +270,16 @@ "clone__Ljava_lang_Object_2" }; + private final Bck2Brwsr.Resources resources; + private final Collection externs; + private final Map isMarkedAsExportedCache; - protected AdvancedObfuscationDelegate() { + protected AdvancedObfuscationDelegate(Bck2Brwsr.Resources resources) { + this.resources = resources; + externs = new ArrayList(Arrays.asList(INITIAL_EXTERNS)); + isMarkedAsExportedCache = new HashMap(); } @Override @@ -275,7 +287,9 @@ String destObject, String mangledName, ClassData classData) throws IOException { - exportJSProperty(out, destObject, mangledName); + if (isExportedClass(classData)) { + exportJSProperty(out, destObject, mangledName); + } } @Override @@ -283,7 +297,9 @@ String destObject, String mangledName, MethodData methodData) throws IOException { - if ((methodData.access & ByteCodeParser.ACC_PRIVATE) == 0) { + if (isAccessible(methodData.access) + && isExportedClass(methodData.cls) + || isMarkedAsExported(methodData)) { exportJSProperty(out, destObject, mangledName); } } @@ -293,7 +309,9 @@ String destObject, String mangledName, FieldData fieldData) throws IOException { - if ((fieldData.access & ByteCodeParser.ACC_PRIVATE) == 0) { + if (isAccessible(fieldData.access) + && isExportedClass(fieldData.cls) + || isMarkedAsExported(fieldData)) { exportJSProperty(out, destObject, mangledName); } } @@ -306,10 +324,110 @@ protected void addExtern(String extern) { externs.add(extern); } + + private boolean isExportedClass(ClassData classData) + throws IOException { + return classData.isPublic() && isMarkedAsExportedPackage( + classData.getPkgName()) + || isMarkedAsExported(classData); + } + + private boolean isMarkedAsExportedPackage(String pkgName) { + if (pkgName == null) { + return false; + } + + final Boolean cachedValue = isMarkedAsExportedCache.get(pkgName); + if (cachedValue != null) { + return cachedValue; + } + + final boolean newValue = resolveIsMarkedAsExportedPackage(pkgName); + isMarkedAsExportedCache.put(pkgName, newValue); + + return newValue; + } + + private boolean isMarkedAsExported(ClassData classData) + throws IOException { + final Boolean cachedValue = isMarkedAsExportedCache.get(classData); + if (cachedValue != null) { + return cachedValue; + } + + final boolean newValue = + isMarkedAsExported(classData.findAnnotationData(true), + classData); + isMarkedAsExportedCache.put(classData, newValue); + + return newValue; + } + + private boolean isMarkedAsExported(MethodData methodData) + throws IOException { + return isMarkedAsExported(methodData.findAnnotationData(true), + methodData.cls); + } + + private boolean isMarkedAsExported(FieldData fieldData) + throws IOException { + return isMarkedAsExported(fieldData.findAnnotationData(true), + fieldData.cls); + } + + private boolean resolveIsMarkedAsExportedPackage(String pkgName) { + try { + final InputStream is = + resources.get(pkgName + "/package-info.class"); + + try { + final ClassData pkgInfoClass = new ClassData(is); + return isMarkedAsExported( + pkgInfoClass.findAnnotationData(true), + pkgInfoClass); + } finally { + is.close(); + } + } catch (final IOException e) { + return false; + } + } + + private boolean isMarkedAsExported(byte[] arrData, ClassData cd) + throws IOException { + if (arrData == null) { + return false; + } + + final boolean[] found = { false }; + final AnnotationParser annotationParser = + new AnnotationParser(false, false) { + @Override + protected void visitAnnotationStart( + String type, + boolean top) { + if (top && type.equals("Lorg/apidesign/bck2brwsr" + + "/core/Exported;")) { + found[0] = true; + } + } + }; + annotationParser.parse(arrData, cd); + return found[0]; + } + + private static boolean isAccessible(int access) { + return (access & (ByteCodeParser.ACC_PUBLIC + | ByteCodeParser.ACC_PROTECTED)) != 0; + } } private static final class MediumObfuscationDelegate extends AdvancedObfuscationDelegate { + public MediumObfuscationDelegate(Bck2Brwsr.Resources resources) { + super(resources); + } + @Override public void exportJSProperty(Appendable out, String destObject, @@ -320,6 +438,10 @@ private static final class FullObfuscationDelegate extends AdvancedObfuscationDelegate { + public FullObfuscationDelegate(Bck2Brwsr.Resources resources) { + super(resources); + } + @Override public void exportJSProperty(Appendable out, String destObject,