Introduces new JsInterop annotations behind a flag.

The transition work is mostly completed though we are still
missing some jsinterop features hence they don't apply to
new annotations as well (e.g. native types).

After all new features submitted, I'll send another patch to
switch all tests to new annotations and then deprecate the
old annotations. Until that point, the new annotations should
be still considered as experimental and I recommend developers
to wait before migrating.

The annotation package is named "jsinterop.annotations" to make it
compiler/SDK indifferent. I wanted to with "js.annotations"
but didn't do that mostly because "js" was too generic and may
cause headache if  Oracle decides to utilize the namespace at some
point.

Change-Id: If7b35f00848ee23eb76a25d9b5cb415b90d1b429
Review-Link: https://gwt-review.googlesource.com/#/c/13523/
diff --git a/dev/core/src/com/google/gwt/dev/javac/JdtUtil.java b/dev/core/src/com/google/gwt/dev/javac/JdtUtil.java
index ffb8ef4..7d92311 100644
--- a/dev/core/src/com/google/gwt/dev/javac/JdtUtil.java
+++ b/dev/core/src/com/google/gwt/dev/javac/JdtUtil.java
@@ -184,7 +184,13 @@
     return null;
   }
 
-  public static boolean getAnnotationParameterBoolean(AnnotationBinding a, String paramName) {
+  public static boolean getAnnotationParameterBoolean(
+      AnnotationBinding a, String paramName, boolean defaultValue) {
+    Boolean rv = getAnnotationParameterBoolean(a, paramName);
+    return rv == null ? defaultValue : rv;
+  }
+
+  public static Boolean getAnnotationParameterBoolean(AnnotationBinding a, String paramName) {
     if (a != null) {
       for (ElementValuePair maybeValue : a.getElementValuePairs()) {
         if (maybeValue.getValue() instanceof BooleanConstant &&
@@ -193,7 +199,7 @@
         }
       }
     }
-    return false;
+    return null;
   }
 
   static AnnotationBinding getAnnotation(AnnotationBinding[] annotations, String nameToFind) {
diff --git a/dev/core/src/com/google/gwt/dev/javac/JsInteropUtil.java b/dev/core/src/com/google/gwt/dev/javac/JsInteropUtil.java
index 28cccea..f09d1d5 100644
--- a/dev/core/src/com/google/gwt/dev/javac/JsInteropUtil.java
+++ b/dev/core/src/com/google/gwt/dev/javac/JsInteropUtil.java
@@ -15,9 +15,11 @@
  */
 package com.google.gwt.dev.javac;
 
+import com.google.gwt.dev.jjs.ast.JClassType;
 import com.google.gwt.dev.jjs.ast.JConstructor;
 import com.google.gwt.dev.jjs.ast.JDeclaredType;
 import com.google.gwt.dev.jjs.ast.JField;
+import com.google.gwt.dev.jjs.ast.JInterfaceType;
 import com.google.gwt.dev.jjs.ast.JMember;
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JMethod.JsPropertyAccessorType;
@@ -51,9 +53,33 @@
       namespace = indexOf == -1 ? "" : jsPrototype.substring(0, indexOf);
       exportName = jsPrototype.substring(indexOf + 1);
     }
-    type.setJsTypeInfo(jsType != null, isJsNative, namespace, exportName, exportName != null);
+    boolean isJsType = jsType != null;
+    boolean isClassWideExport = exportName != null;
+    boolean isJsFunction = JdtUtil.getAnnotation(annotations, JSFUNCTION_CLASS) != null;
+    boolean canBeImplementedExternally =
+        (type instanceof JInterfaceType && (isJsType || isJsFunction))
+        || (type instanceof JClassType && isJsNative);
+    type.setJsTypeInfo(isJsType, isJsNative, isJsFunction, namespace, exportName, isClassWideExport,
+        canBeImplementedExternally);
+  }
 
-    type.setJsFunctionInfo(JdtUtil.getAnnotation(annotations, JSFUNCTION_CLASS) != null);
+  public static void maybeSetJsInteropPropertiesNew(JDeclaredType type, Annotation[] annotations) {
+    AnnotationBinding jsType = getInteropAnnotation(annotations, "JsType");
+    String namespace = JdtUtil.getAnnotationParameterString(jsType, "namespace");
+    String name = JdtUtil.getAnnotationParameterString(jsType, "name");
+    boolean isJsNative = JdtUtil.getAnnotationParameterBoolean(jsType, "isNative", false);
+
+    AnnotationBinding jsPackage = getInteropAnnotation(annotations, "JsPackage");
+    String packageNamespace = JdtUtil.getAnnotationParameterString(jsPackage, "namespace");
+    if (packageNamespace != null) {
+      namespace = packageNamespace;
+    }
+
+    boolean isJsType = jsType != null;
+    boolean isJsFunction = getInteropAnnotation(annotations, "JsFunction") != null;
+    boolean canBeImplementedExternally = isJsNative || isJsFunction;
+    type.setJsTypeInfo(isJsType, isJsNative, isJsFunction, namespace, name, isJsType,
+        canBeImplementedExternally);
   }
 
   public static void maybeSetJsInteropProperties(JMethod method, Annotation... annotations) {
@@ -63,10 +89,30 @@
     }
   }
 
+  public static void maybeSetJsInteropPropertiesNew(JMethod method, Annotation... annotations) {
+    AnnotationBinding annotation = getInteropAnnotation(annotations, "JsMethod");
+    if (annotation == null) {
+      annotation = getInteropAnnotation(annotations, "JsConstructor");
+    }
+    if (annotation == null) {
+      annotation = getInteropAnnotation(annotations, "JsProperty");
+    }
+
+    setJsInteropPropertiesNew(method, annotations, annotation);
+    if (getInteropAnnotation(annotations, "JsProperty") != null) {
+      setJsPropertyProperties(method);
+    }
+  }
+
   public static void maybeSetJsInteropProperties(JField field, Annotation... annotations) {
     setJsInteropProperties(field, annotations);
   }
 
+  public static void maybeSetJsInteropPropertiesNew(JField field, Annotation... annotations) {
+    AnnotationBinding annotation = getInteropAnnotation(annotations, "JsProperty");
+    setJsInteropPropertiesNew(field, annotations, annotation);
+  }
+
   private static void setJsInteropProperties(JMember member, Annotation... annotations) {
     String namespace = maybeGetJsNamespace(annotations);
     String exportName = maybeGetJsExportName(annotations, computeName(member));
@@ -94,6 +140,22 @@
     return member instanceof JConstructor && member.isJsNative();
   }
 
+  private static void setJsInteropPropertiesNew(
+      JMember member, Annotation[] annotations, AnnotationBinding memberAnnotation) {
+    if (getInteropAnnotation(annotations, "JsIgnore") != null) {
+      return;
+    }
+
+    boolean isPublicMemberForJsType = member.getEnclosingType().isJsType() && member.isPublic();
+    if (!isPublicMemberForJsType && memberAnnotation == null) {
+      return;
+    }
+
+    String namespace = JdtUtil.getAnnotationParameterString(memberAnnotation, "namespace");
+    String name = JdtUtil.getAnnotationParameterString(memberAnnotation, "name");
+    member.setJsMemberInfo(namespace, name == null ? computeName(member) : name, true);
+  }
+
   private static void setJsPropertyProperties(JMethod method) {
     String methodName = method.getName();
     if (startsWithCamelCase(methodName, "set")) {
@@ -110,6 +172,10 @@
     }
   }
 
+  private static AnnotationBinding getInteropAnnotation(Annotation[] annotations, String name) {
+    return JdtUtil.getAnnotation(annotations, "jsinterop.annotations." + name);
+  }
+
   private static String computeName(JMember member) {
     return member instanceof JConstructor ? "" : member.getName();
   }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
index 98f61bf..2f443fa 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -170,6 +170,7 @@
 import com.google.gwt.dev.util.Name.SourceName;
 import com.google.gwt.dev.util.Pair;
 import com.google.gwt.dev.util.Util;
+import com.google.gwt.dev.util.arg.OptionJsInteropMode.Mode;
 import com.google.gwt.dev.util.arg.OptionOptimize;
 import com.google.gwt.dev.util.log.speedtracer.CompilerEventType;
 import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
@@ -1160,7 +1161,8 @@
        */
 
       // (1) Initialize local state
-      jprogram = new JProgram(compilerContext.getMinimalRebuildCache());
+      boolean legacyJsInterop = compilerContext.getOptions().getJsInteropMode() == Mode.JS;
+      jprogram = new JProgram(compilerContext.getMinimalRebuildCache(), legacyJsInterop);
       // Synchronize JTypeOracle with compile optimization behavior.
       jprogram.typeOracle.setOptimize(
           options.getOptimizationLevel() > OptionOptimize.OPTIMIZE_LEVEL_DRAFT);
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java
index d2a7f6a..2d87063 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java
@@ -112,11 +112,6 @@
     return false;
   }
 
-  @Override
-  public boolean canBeImplementedExternally() {
-    return isJsNative();
-  }
-
   /**
    * Sets this type's super class.
    */
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
index 55c98c7..a881f96 100755
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
@@ -55,6 +55,7 @@
   private boolean isJsType;
   private boolean isClassWideExport;
   private boolean isJsNative;
+  private boolean canBeImplementedExternally;
   private String jsNamespace = null;
   private String jsName = null;
 
@@ -392,6 +393,11 @@
     return isJsNative;
   }
 
+  @Override
+  public boolean canBeImplementedExternally() {
+    return canBeImplementedExternally;
+  }
+
   /**
    * Returns this type's super class, or <code>null</code> if this type is
    * {@link Object} or an interface.
@@ -473,18 +479,18 @@
     this.isExternal = isExternal;
   }
 
-  public void setJsTypeInfo(boolean isJsType, boolean isJsNative, String jsNamespace,
-      String jsName, boolean isClassWideExport) {
+  public void setJsTypeInfo(boolean isJsType, boolean isJsNative, boolean isJsFunction,
+      String jsNamespace, String jsName, boolean isClassWideExport,
+      boolean canBeImplementedExternally) {
     this.isJsType = isJsType;
     this.isJsNative = isJsNative;
+    this.isJsFunction = isJsFunction;
     this.jsNamespace = jsNamespace;
     this.jsName = jsName;
     this.isClassWideExport = isClassWideExport;
+    this.canBeImplementedExternally = canBeImplementedExternally;
   }
 
-  public void setJsFunctionInfo(boolean isJsFunction) {
-    this.isJsFunction = isJsFunction;
-  }
   /**
    * Sorts this type's fields according to the specified sort.
    */
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JInterfaceType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JInterfaceType.java
index 0ad9d82..322ac59 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JInterfaceType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JInterfaceType.java
@@ -50,11 +50,6 @@
   }
 
   @Override
-  public boolean canBeImplementedExternally() {
-    return isJsType() || isJsFunction();
-  }
-
-  @Override
   public final JMethod getInitMethod() {
     return null;
   }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
index 8f1652f..5a423fb 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
@@ -366,8 +366,12 @@
   }
 
   public JProgram(MinimalRebuildCache minimalRebuildCache) {
+    this(minimalRebuildCache, true);
+  }
+
+  public JProgram(MinimalRebuildCache minimalRebuildCache, boolean legacyJsInterop) {
     super(SourceOrigin.UNKNOWN);
-    typeOracle = new JTypeOracle(this, minimalRebuildCache);
+    typeOracle = new JTypeOracle(this, minimalRebuildCache, legacyJsInterop);
   }
 
   public void addEntryMethod(JMethod entryPoint) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
index 9da9d30..37610ab 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
@@ -392,13 +392,19 @@
   private ImmediateTypeRelations immediateTypeRelations;
   private ArrayTypeCreator arrayTypeCreator;
   private StandardTypes standardTypes;
+  private boolean legacyJsInterop;
 
+  public JTypeOracle(ArrayTypeCreator arrayTypeCreator, MinimalRebuildCache minimalRebuildCache) {
+    this(arrayTypeCreator, minimalRebuildCache, true);
+  }
   /**
    * Constructs a new JTypeOracle.
    */
-  public JTypeOracle(ArrayTypeCreator arrayTypeCreator, MinimalRebuildCache minimalRebuildCache) {
+  public JTypeOracle(ArrayTypeCreator arrayTypeCreator, MinimalRebuildCache minimalRebuildCache,
+      boolean legacyJsInterop) {
     this.immediateTypeRelations = minimalRebuildCache.getImmediateTypeRelations();
     this.arrayTypeCreator = arrayTypeCreator;
+    this.legacyJsInterop = legacyJsInterop;
 
     // Be ready to answer simple questions (type hierarchy) even before recompute...().
     computeExtendedTypeRelations();
@@ -413,11 +419,36 @@
     return type.isJsoType() || isSingleJsoImpl(type);
   }
 
+  public boolean isCastableLikeDualJsoInterface(JType type) {
+    if (legacyJsInterop) {
+      return isDualJsoInterface(type) || isNonNativeJsTypeInterface(type);
+    }
+    return isDualJsoInterface(type);
+  }
+
+  public boolean isCastableByPrototype(JType type) {
+    if (legacyJsInterop) {
+      return type.isJsNative();
+    }
+    return type instanceof JClassType && type.isJsNative();
+  }
+
+  public boolean isNoOpCast(JType type) {
+    if (legacyJsInterop) {
+      return false;
+    }
+    return type instanceof JInterfaceType && type.isJsNative();
+  }
+
   /**
    * True if the type can be casted across different Java types that are unrelated.
    */
-  public boolean canCrossCastLikeJso(JType type) {
-    return canBeJavaScriptObject(type) || isNativeJsType(type) || isNonNativeJsTypeInterface(type);
+  private boolean canCrossCastLikeJso(JType type) {
+    if (legacyJsInterop) {
+      return canBeJavaScriptObject(type) || isNativeJsType(type)
+          || isNonNativeJsTypeInterface(type);
+    }
+    return canBeJavaScriptObject(type) || type.isJsNative();
   }
 
   public boolean isNonNativeJsTypeInterface(JType type) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
index f0b2f0c..14d8bba 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
@@ -3784,7 +3784,9 @@
 
   private CompilerContext compilerContext;
 
-  private boolean isJsInteropEnabled;
+  private boolean isLegacyJsInteropEnabled;
+
+  private boolean isNewJsInteropEnabled;
 
   /**
    * Externalized class and method form for Exceptions.safeClose() to provide support
@@ -3833,7 +3835,8 @@
     this.jsniRefs = jsniRefs;
     this.jsniMethods = jsniMethods;
     this.compilerContext = compilerContext;
-    this.isJsInteropEnabled = compilerContext.getOptions().getJsInteropMode() == Mode.JS;
+    this.isLegacyJsInteropEnabled = compilerContext.getOptions().getJsInteropMode() == Mode.JS;
+    this.isNewJsInteropEnabled = compilerContext.getOptions().getJsInteropMode() == Mode.JS_RC;
     this.newTypes = Lists.newArrayList();
     this.curCud = new CudInfo(cud);
   }
@@ -3906,8 +3909,10 @@
               getFieldDisposition(binding), AccessModifier.fromFieldBinding(binding));
     }
     enclosingType.addField(field);
-    if (isJsInteropEnabled) {
+    if (isLegacyJsInteropEnabled) {
       JsInteropUtil.maybeSetJsInteropProperties(field, x.annotations);
+    } else if (isNewJsInteropEnabled) {
+      JsInteropUtil.maybeSetJsInteropPropertiesNew(field, x.annotations);
     }
     typeMap.setField(binding, field);
   }
@@ -4078,8 +4083,10 @@
     maybeAddMethodSpecialization(x, method);
     maybeSetInliningMode(x, method);
     maybeSetHasNoSideEffects(x, method);
-    if (isJsInteropEnabled) {
+    if (isLegacyJsInteropEnabled) {
       JsInteropUtil.maybeSetJsInteropProperties(method, x.annotations);
+    } else if (isNewJsInteropEnabled) {
+      JsInteropUtil.maybeSetJsInteropPropertiesNew(method, x.annotations);
     }
   }
 
@@ -4170,8 +4177,10 @@
     JMethod method = typeMap.createMethod(info, binding, paramNames);
     assert !method.isExternal();
     method.setBody(new JMethodBody(info));
-    if (isJsInteropEnabled) {
+    if (isLegacyJsInteropEnabled) {
       JsInteropUtil.maybeSetJsInteropProperties(method);
+    } else if (isNewJsInteropEnabled) {
+      JsInteropUtil.maybeSetJsInteropPropertiesNew(method);
     }
     typeMap.setMethod(binding, method);
     return method;
@@ -4205,8 +4214,10 @@
       } else {
         throw new InternalCompilerException("ReferenceBinding is not a class, interface, or enum.");
       }
-      if (isJsInteropEnabled) {
+      if (isLegacyJsInteropEnabled) {
         JsInteropUtil.maybeSetJsInteropProperties(type, x.annotations);
+      } else if (isNewJsInteropEnabled) {
+        JsInteropUtil.maybeSetJsInteropPropertiesNew(type, x.annotations);
       }
       JdtUtil.setClassDispositionFromBinding(binding, type);
       typeMap.setSourceType(binding, type);
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementCastsAndTypeChecks.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementCastsAndTypeChecks.java
index aafc9a3..b5ba176 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementCastsAndTypeChecks.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementCastsAndTypeChecks.java
@@ -229,6 +229,7 @@
         TypeCategory.TYPE_JAVA_LANG_DOUBLE,
         TypeCategory.TYPE_JAVA_LANG_BOOLEAN,
         TypeCategory.TYPE_JAVA_OBJECT,
+        TypeCategory.TYPE_JS_UNKNOWN_NATIVE,
         TypeCategory.TYPE_JS_NATIVE,
         TypeCategory.TYPE_JS_FUNCTION).contains(typeCategory);
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeCategory.java b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeCategory.java
index efb6be7..af229e6 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeCategory.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeCategory.java
@@ -46,6 +46,7 @@
   TYPE_JAVA_LANG_DOUBLE("Double"),
   TYPE_JAVA_LANG_BOOLEAN("Boolean"),
   TYPE_JS_NATIVE("Native"),
+  TYPE_JS_UNKNOWN_NATIVE("UnknownNative"),
   TYPE_JS_FUNCTION("Function"),
   TYPE_PRIMITIVE_LONG,
   TYPE_PRIMITIVE_NUMBER,
@@ -87,10 +88,11 @@
       return program.getRepresentedAsNativeTypesDispatchMap().get(type).getTypeCategory();
     } else if (program.typeOracle.isEffectivelyJavaScriptObject(type)) {
       return TypeCategory.TYPE_JSO;
-    } else if (program.typeOracle.isDualJsoInterface(type)
-        || program.typeOracle.isNonNativeJsTypeInterface(type)) {
+    } else if (program.typeOracle.isCastableLikeDualJsoInterface(type)) {
       return TypeCategory.TYPE_JAVA_OBJECT_OR_JSO;
-    } else if (type.isJsNative()) {
+    } else if (program.typeOracle.isNoOpCast(type)) {
+      return TypeCategory.TYPE_JS_UNKNOWN_NATIVE;
+    } else if (program.typeOracle.isCastableByPrototype(type)) {
       return TypeCategory.TYPE_JS_NATIVE;
     } else if (type.isJsFunction()) {
       return TypeCategory.TYPE_JS_FUNCTION;
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/OptionJsInteropMode.java b/dev/core/src/com/google/gwt/dev/util/arg/OptionJsInteropMode.java
index a9aa9bf..1ff499c 100644
--- a/dev/core/src/com/google/gwt/dev/util/arg/OptionJsInteropMode.java
+++ b/dev/core/src/com/google/gwt/dev/util/arg/OptionJsInteropMode.java
@@ -29,9 +29,13 @@
      */
     NONE,
     /**
-     * For hand coded, external JS, not run through an external compiler.
+     * Legacy JsInterop using old annotations.
      */
-    JS
+    JS,
+    /**
+     * Release candidate JsInterop using new annotations.
+     */
+    JS_RC
   }
 
   Mode getJsInteropMode();
diff --git a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java
index a2f3e79..e35b162 100644
--- a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java
+++ b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java
@@ -37,10 +37,11 @@
   private static final int TYPE_JAVA_LANG_DOUBLE = 5;
   private static final int TYPE_JAVA_LANG_BOOLEAN = 6;
   private static final int TYPE_JS_NATIVE = 7;
-  private static final int TYPE_JS_FUNCTION = 8;
-  private static final int TYPE_PRIMITIVE_LONG = 9;
-  private static final int TYPE_PRIMITIVE_NUMBER = 10;
-  private static final int TYPE_PRIMITIVE_BOOLEAN = 11;
+  private static final int TYPE_JS_NATIVE_PROTOTYPE = 8;
+  private static final int TYPE_JS_FUNCTION = 9;
+  private static final int TYPE_PRIMITIVE_LONG = 10;
+  private static final int TYPE_PRIMITIVE_NUMBER = 11;
+  private static final int TYPE_PRIMITIVE_BOOLEAN = 12;
 
   public static <T> T[] stampJavaTypeInfo(Object array, T[] referenceType) {
     initValues(referenceType.getClass(), Util.getCastableTypeMap(referenceType),
diff --git a/user/BUILD b/user/BUILD
index f4fe504..9c5e90d 100644
--- a/user/BUILD
+++ b/user/BUILD
@@ -404,7 +404,7 @@
 # GWT JsInterop annotations in a separate library used by hybrid app projects
 java_library(
     name = "gwt-jsinterop-annotations",
-    srcs = [
+    srcs = glob(["src/jsinterop/**/*.java"]) + [
         "src/com/google/gwt/core/client/js/JsExport.java",
         "src/com/google/gwt/core/client/js/JsFunction.java",
         "src/com/google/gwt/core/client/js/JsNamespace.java",
diff --git a/user/src/com/google/gwt/uibinder/rebind/AbstractFieldWriter.java b/user/src/com/google/gwt/uibinder/rebind/AbstractFieldWriter.java
index 37f8fdc..165521c 100644
--- a/user/src/com/google/gwt/uibinder/rebind/AbstractFieldWriter.java
+++ b/user/src/com/google/gwt/uibinder/rebind/AbstractFieldWriter.java
@@ -333,11 +333,13 @@
       // TODO: When we know better how this is used, we might want to loosen the annotation
       // constraint (e.g. it might be sufficient for the declared type to extend another
       // interface that is a JsType).
-      if (!ownerField.getRawType().isAssignableTo(getDomElement(typeOracle))
-          && ownerField.getRawType().getAnnotation(JsType.class) != null) {
+      JClassType rawType = ownerField.getRawType();
+      if (!rawType.isAssignableTo(getDomElement(typeOracle))
+          && (rawType.getAnnotation(JsType.class) != null
+              || rawType.getAnnotation(jsinterop.annotations.JsType.class) != null)) {
         w.write(
             "this.owner.%1$s = (%2$s) %1$s;", name,
-            ownerField.getRawType().getQualifiedSourceName());
+            rawType.getQualifiedSourceName());
       } else {
         w.write("this.owner.%1$s = %1$s;", name);
       }
diff --git a/user/src/jsinterop/annotations/JsConstructor.java b/user/src/jsinterop/annotations/JsConstructor.java
new file mode 100644
index 0000000..321dc30
--- /dev/null
+++ b/user/src/jsinterop/annotations/JsConstructor.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * 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 jsinterop.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * JsConstructor marks a constructor so that it will be the constructor function for the JavaScript
+ * type.
+ * <p>
+ * Note that, there could be only one JsConstructor in a type and all other constructors should be
+ * delegating to it.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.CONSTRUCTOR)
+@Documented
+public @interface JsConstructor {
+}
diff --git a/user/src/jsinterop/annotations/JsFunction.java b/user/src/jsinterop/annotations/JsFunction.java
new file mode 100644
index 0000000..4ed484a
--- /dev/null
+++ b/user/src/jsinterop/annotations/JsFunction.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * 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 jsinterop.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a type containing a Single Abstract Method (SAM) as eligible for automatic conversion into
+ * a JavaScript function.
+ * <p>
+ * This enables lambda expressions to be passed directly to JavaScript as callbacks.
+ * <p>
+ * However there are some additional limitations that are imposed to make this practical and
+ * efficient:
+ * <li>A class may not implement more than one @JsFunction type. This restriction allows the
+ * compiler to construct a one-to-one mapping to the JavaScript function generated and the SAM to be
+ * invoked in Java and to preserve referential equality.
+ * <li>A JsFunction interface cannot extend any other interfaces.
+ * <li>A JsFunction interface cannot have defender methods.
+ * <li>A class that implements a @JsFunction type (directly or indirectly) cannot be a @JsType.
+ * <p>
+ * As a best practice, we recommend marking @JsFunction interfaces also with @FunctionalInterface to
+ * get improved checking in IDEs.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Documented
+public @interface JsFunction {
+}
diff --git a/user/src/jsinterop/annotations/JsIgnore.java b/user/src/jsinterop/annotations/JsIgnore.java
new file mode 100644
index 0000000..b2d745a
--- /dev/null
+++ b/user/src/jsinterop/annotations/JsIgnore.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * 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 jsinterop.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a member to be ignored for JsInterop purposes.
+ * <p>
+ * This is particularly useful when {@link JsType} applied to a class and some members are needed to
+ * be ignored as they don't comply with restrictions (e.g. overloading) or shouldn't be exported.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.FIELD})
+@Documented
+public @interface JsIgnore {
+}
diff --git a/user/src/jsinterop/annotations/JsMethod.java b/user/src/jsinterop/annotations/JsMethod.java
new file mode 100644
index 0000000..d8a0116
--- /dev/null
+++ b/user/src/jsinterop/annotations/JsMethod.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * 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 jsinterop.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * JsMethod marks a method in a type as a method that will be directly translated into a JavaScript
+ * method without any obfuscation to its name.
+ * <p>
+ * Note that, while instance members are slotted in the prototype, class members will be defined
+ * under the constructor function of the type.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+@Documented
+public @interface JsMethod {
+
+  /**
+   * Customizes the name of the member in generated JavaScript. If not provided, the Java name will
+   * be used.
+   */
+  String name() default "";
+
+  /**
+   * Customizes the namespace of the static member in generated JavaScript. If none is provided,
+   * namespace is the enclosing class' fully qualified JavaScript name.
+   */
+  String namespace() default "";
+}
diff --git a/user/src/jsinterop/annotations/JsPackage.java b/user/src/jsinterop/annotations/JsPackage.java
new file mode 100644
index 0000000..92be8ec
--- /dev/null
+++ b/user/src/jsinterop/annotations/JsPackage.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * 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 jsinterop.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Provides a default namespace for all @JsType classes in a package by applying to
+ * 'package-info.java'.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.PACKAGE)
+@Documented
+public @interface JsPackage {
+  String GLOBAL = "";
+
+  String namespace();
+}
diff --git a/user/src/jsinterop/annotations/JsProperty.java b/user/src/jsinterop/annotations/JsProperty.java
new file mode 100644
index 0000000..2705b58
--- /dev/null
+++ b/user/src/jsinterop/annotations/JsProperty.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * 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 jsinterop.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * JsProperty marks a field in a type as a method that will be directly translated into a JavaScript
+ * property without any obfuscation to its name.
+ * <p>
+ * If it is applied to a method, it will be treated as a property accessor. As a result, instead of
+ * translating method calls to JsProperty methods as method calls in JS, they will be translated as
+ * property lookups. When a JsProperty method implemented by a Java class, such methods will be
+ * generated as custom property setter and getter in JavaScript, hence the property access will
+ * trigger the execution of the matching getter or setter methods.
+ * <p>
+ * JsProperty follows JavaBean style naming convention to extract the default property name. If the
+ * JavaBean convention is not followed, the name should be set explicitly. For example:
+ * <ul>
+ * <li> {@code @JsProperty getX()} or {@code @JsProperty isX()} translates as <tt>this.x</tt>
+ * <li> {@code @JsProperty setX(int y)} translates as <tt>this.x=y</tt>
+ * </ul>
+ * <p>
+ * Note that, while non-static member are slotted in the prototype, static members will be defined
+ * under the constructor function of the type.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.FIELD})
+@Documented
+public @interface JsProperty {
+
+  /**
+   * Customizes the name of the member in generated JavaScript. If none is provided;
+   * <p>
+   * <li>if it is field, the simple Java name will be used.
+   * <li>if it is a method, the name will be generated based on JavaBean conventions.
+   */
+  String name() default "";
+
+  /**
+   * Customizes the namespace of the static member in generated JavaScript. If none is provided,
+   * namespace is the enclosing class' fully qualified JavaScript name.
+   */
+  String namespace() default "";
+}
diff --git a/user/src/jsinterop/annotations/JsType.java b/user/src/jsinterop/annotations/JsType.java
new file mode 100644
index 0000000..52eeefa
--- /dev/null
+++ b/user/src/jsinterop/annotations/JsType.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * 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 jsinterop.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * JsType is used to describe the JavaScript API of an object, either one that already exists from
+ * the external JavaScript environment, or one that will be accessible from the external JavaScript
+ * environment.
+ * <p>
+ * Marking an object with JsType is similar to marking each public member of the class with
+ * {@link JsProperty}/{@link JsMethod}/{@link JsConstructor} respectively. In order for this to work
+ * correctly the JavaScript name needs to be unique for each member. Some unobvious ways to cause
+ * name collisions are;
+ * <p>
+ * <li>Having method or constructor overloads.
+ * <li>Using the same name for a method and a field.
+ * <li>Shadowing a field from parent.
+ * <p>
+ * A name collision needs to be avoided by providing a custom name (e.g. {@link JsProperty#name}) or
+ * by completely ignoring the member using {@link JsIgnore}.
+ * <p>
+ * If the JsType is marked as "native", then the type is considered as stub for an existing class
+ * that is available in native JavaScript. If it is concrete type, the subclass will use the
+ * designated type as super type opposed to the ordinary one (e.g. java.lang.Object).
+ * <p>
+ * Instanceof and Castability:
+ * <p>
+ * If the JsTypes is native then the generated code will try to mimic Javascript semantics.
+ * <li>If it is concrete native JsType then cast checks and instanceof checks will be delegated to
+ * the native JavaScript instanceof operator.
+ * <li>If it is an interface and marked as native, no checks will be performed.
+ * <p>
+ * All non-native JsTypes will follow regular Java semantics in terms of castability.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Documented
+public @interface JsType {
+
+  /**
+   * Customizes the name of the type in generated JavaScript. If not provided, the simple Java name
+   * will be used.
+   */
+  String name() default "";
+
+  /**
+   * Customizes the namespace of the type in generated JavaScript.
+   */
+  String namespace() default "";
+
+  /**
+   * Set to {@code true}, this JsType is a native JavaScript type.
+   */
+  boolean isNative() default false;
+}