/* * Copyright 2014 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 com.google.gwt.dev.javac; import com.google.gwt.dev.jjs.ast.HasJsInfo.JsMemberType; 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.JMember; import com.google.gwt.dev.jjs.ast.JMethod; import com.google.gwt.dev.jjs.ast.JParameter; import com.google.gwt.dev.jjs.ast.JPrimitiveType; import com.google.gwt.thirdparty.guava.common.base.Joiner; import com.google.gwt.thirdparty.guava.common.collect.Lists; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding; import java.util.List; /** * Utility functions to interact with JDT classes for JsInterop. */ public final class JsInteropUtil { public static final String UNUSABLE_BY_JS = "unusable-by-js"; public static final String INVALID_JSNAME = "<invalid>"; public static boolean isGlobal(String jsNamespace) { return "<global>".equals(jsNamespace); } public static boolean isWindow(String jsNamespace) { return "<window>".equals(jsNamespace); } public static String normalizeQualifier(String qualifier) { assert !qualifier.isEmpty(); List<String> components = Lists.newArrayList(qualifier.split("\\.")); if (isWindow(components.get(0))) { // Emit unqualified if '<window>' namespace was specified. components.remove(0); } else if (isGlobal(components.get(0))) { // Replace global with $wnd. components.set(0, "$wnd"); } else { // still emit $wnd as rest implicitly points to global. components.add(0, "$wnd"); } return Joiner.on('.').join(components); } public static void maybeSetJsInteropProperties(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; type.setJsTypeInfo(isJsType, isJsNative, isJsFunction, namespace, name, isJsType); } public static void maybeSetJsInteropProperties( JMethod method, boolean generateExport, Annotation... annotations) { AnnotationBinding annotation = getInteropAnnotation(annotations, "JsMethod"); if (annotation == null) { annotation = getInteropAnnotation(annotations, "JsConstructor"); } if (annotation == null) { annotation = getInteropAnnotation(annotations, "JsProperty"); } boolean isPropertyAccessor = getInteropAnnotation(annotations, "JsProperty") != null; setJsInteropProperties(method, annotations, annotation, isPropertyAccessor, generateExport); } public static void maybeSetJsInteropProperties(JParameter parameter, Annotation... annotations) { if (getInteropAnnotation(annotations, "JsOptional") != null) { parameter.setOptional(); } } public static void maybeSetJsInteropProperties( JField field, boolean generateExport, Annotation... annotations) { AnnotationBinding annotation = getInteropAnnotation(annotations, "JsProperty"); setJsInteropProperties(field, annotations, annotation, false, generateExport); } private static void setJsInteropProperties(JMember member, Annotation[] annotations, AnnotationBinding memberAnnotation, boolean isAccessor, boolean generateExport) { if (getInteropAnnotation(annotations, "JsOverlay") != null) { member.setJsOverlay(); } if (getInteropAnnotation(annotations, "JsIgnore") != null) { return; } boolean isPublicMemberForJsType = member.getEnclosingType().isJsType() && member.isPublic(); boolean memberForNativeType = member.getEnclosingType().isJsNative(); if (memberAnnotation == null && (!isPublicMemberForJsType && !memberForNativeType || member.isJsOverlay())) { return; } String namespace = JdtUtil.getAnnotationParameterString(memberAnnotation, "namespace"); String name = JdtUtil.getAnnotationParameterString(memberAnnotation, "name"); JsMemberType memberType = getJsMemberType(member, isAccessor); member.setJsMemberInfo(memberType, namespace, name, generateExport); } private static JsMemberType getJsMemberType(JMember member, boolean isPropertyAccessor) { if (member instanceof JField) { return JsMemberType.PROPERTY; } if (member instanceof JConstructor) { return JsMemberType.CONSTRUCTOR; } if (isPropertyAccessor) { return getJsPropertyAccessorType((JMethod) member); } return JsMemberType.METHOD; } private static JsMemberType getJsPropertyAccessorType(JMethod method) { if (method.getParams().size() == 1 && method.getType() == JPrimitiveType.VOID) { return JsMemberType.SETTER; } else if (method.getParams().isEmpty() && (method.getType() != JPrimitiveType.VOID || isDebugger(method))) { return JsMemberType.GETTER; } return JsMemberType.UNDEFINED_ACCESSOR; } private static boolean isDebugger(JMethod method) { return method.isStatic() && method.getName().equals("debugger") && (method.isJsNative() || method.isAbstract()); } private static AnnotationBinding getInteropAnnotation(Annotation[] annotations, String name) { return JdtUtil.getAnnotationByName(annotations, "jsinterop.annotations." + name); } }