| /* |
| * 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.jjs.impl; |
| |
| import com.google.gwt.dev.PrecompileTaskOptions; |
| import com.google.gwt.dev.jjs.SourceInfo; |
| import com.google.gwt.dev.jjs.SourceOrigin; |
| import com.google.gwt.dev.jjs.ast.HasJsInfo.JsMemberType; |
| import com.google.gwt.dev.jjs.ast.HasName; |
| import com.google.gwt.dev.jjs.ast.HasType; |
| import com.google.gwt.dev.jjs.ast.JArrayType; |
| import com.google.gwt.dev.jjs.ast.JBinaryOperation; |
| import com.google.gwt.dev.jjs.ast.JBinaryOperator; |
| import com.google.gwt.dev.jjs.ast.JBlock; |
| import com.google.gwt.dev.jjs.ast.JBooleanLiteral; |
| import com.google.gwt.dev.jjs.ast.JCharLiteral; |
| 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.JDoubleLiteral; |
| import com.google.gwt.dev.jjs.ast.JExpression; |
| import com.google.gwt.dev.jjs.ast.JExpressionStatement; |
| import com.google.gwt.dev.jjs.ast.JField; |
| import com.google.gwt.dev.jjs.ast.JFloatLiteral; |
| import com.google.gwt.dev.jjs.ast.JIntLiteral; |
| import com.google.gwt.dev.jjs.ast.JInterfaceType; |
| import com.google.gwt.dev.jjs.ast.JLiteral; |
| import com.google.gwt.dev.jjs.ast.JLongLiteral; |
| import com.google.gwt.dev.jjs.ast.JMember; |
| import com.google.gwt.dev.jjs.ast.JMethod; |
| import com.google.gwt.dev.jjs.ast.JMethodBody; |
| import com.google.gwt.dev.jjs.ast.JMethodCall; |
| import com.google.gwt.dev.jjs.ast.JNewInstance; |
| import com.google.gwt.dev.jjs.ast.JNullLiteral; |
| import com.google.gwt.dev.jjs.ast.JParameter; |
| import com.google.gwt.dev.jjs.ast.JPrimitiveType; |
| import com.google.gwt.dev.jjs.ast.JProgram; |
| import com.google.gwt.dev.jjs.ast.JReferenceType; |
| import com.google.gwt.dev.jjs.ast.JStatement; |
| import com.google.gwt.dev.jjs.ast.JStringLiteral; |
| import com.google.gwt.dev.jjs.ast.JThisRef; |
| import com.google.gwt.dev.jjs.ast.JType; |
| import com.google.gwt.dev.jjs.ast.js.JMultiExpression; |
| import com.google.gwt.dev.js.ast.JsBooleanLiteral; |
| import com.google.gwt.dev.js.ast.JsExpression; |
| import com.google.gwt.dev.js.ast.JsLiteral; |
| import com.google.gwt.dev.js.ast.JsNameRef; |
| import com.google.gwt.dev.js.ast.JsNullLiteral; |
| import com.google.gwt.dev.js.ast.JsNumberLiteral; |
| import com.google.gwt.dev.js.ast.JsObjectLiteral; |
| import com.google.gwt.dev.js.ast.JsStringLiteral; |
| import com.google.gwt.lang.LongLib; |
| import com.google.gwt.thirdparty.guava.common.base.Function; |
| import com.google.gwt.thirdparty.guava.common.base.Joiner; |
| import com.google.gwt.thirdparty.guava.common.base.Predicate; |
| import com.google.gwt.thirdparty.guava.common.base.Predicates; |
| import com.google.gwt.thirdparty.guava.common.base.Strings; |
| import com.google.gwt.thirdparty.guava.common.collect.Collections2; |
| import com.google.gwt.thirdparty.guava.common.collect.FluentIterable; |
| import com.google.gwt.thirdparty.guava.common.collect.ImmutableMap; |
| import com.google.gwt.thirdparty.guava.common.collect.Iterables; |
| import com.google.gwt.thirdparty.guava.common.collect.Lists; |
| |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * General utilities related to Java AST manipulation. |
| */ |
| public class JjsUtils { |
| |
| public static boolean closureStyleLiteralsNeeded(PrecompileTaskOptions options) { |
| return closureStyleLiteralsNeeded(options.isIncrementalCompileEnabled(), |
| options.isClosureCompilerFormatEnabled()); |
| } |
| |
| /** |
| * Returns the class literal field name. |
| */ |
| public static String classLiteralFieldNameFromJavahTypeSignatureName(String javahSignatureName) { |
| return javahSignatureName + "_classLit"; |
| } |
| |
| public static boolean closureStyleLiteralsNeeded(boolean incremental, |
| boolean closureOutputFormat) { |
| return !incremental && closureOutputFormat; |
| } |
| |
| public static String computeSignature( |
| String name, List<JType> params, JType returnType, boolean isCtor) { |
| StringBuilder sb = new StringBuilder(name); |
| sb.append('('); |
| for (JType type : params) { |
| sb.append(type.getJsniSignatureName()); |
| } |
| sb.append(')'); |
| if (!isCtor) { |
| sb.append(returnType.getJsniSignatureName()); |
| } else { |
| sb.append(" <init>"); |
| } |
| return sb.toString(); |
| } |
| |
| public static String constructManglingSignature(JMethod x, String partialSignature) { |
| StringBuilder sb = new StringBuilder(partialSignature); |
| sb.append("__"); |
| for (int i = 0; i < x.getOriginalParamTypes().size(); ++i) { |
| JType type = x.getOriginalParamTypes().get(i); |
| sb.append(type.getJavahSignatureName()); |
| } |
| sb.append(x.getOriginalReturnType().getJavahSignatureName()); |
| return sb.toString(); |
| } |
| |
| /** |
| * Returns an instantiation expression for {@code type} using the default constructor, |
| * Returns {@code null} if {@code type} does not have a default constructor. |
| */ |
| public static JExpression createDefaultConstructorInstantiation( |
| SourceInfo info, JClassType type) { |
| /* |
| * Find the appropriate (noArg) constructor. In our AST, constructors are |
| * instance methods that should be qualified with a new expression. |
| */ |
| JConstructor noArgCtor = (JConstructor) FluentIterable.from(type.getMethods()).firstMatch( |
| new Predicate<JMethod>() { |
| @Override |
| public boolean apply(JMethod method) { |
| return method instanceof JConstructor && method.getOriginalParamTypes().size() == 0; |
| } |
| }).orNull(); |
| if (noArgCtor == null) { |
| return null; |
| } |
| // Call it, using a new expression as a qualifier |
| return new JNewInstance(info, noArgCtor); |
| } |
| |
| /** |
| * Creates a synthetic forwarding stub in {@code type} with the same signature as |
| * {@code superTypeMethod} that dispatchs to that method. |
| */ |
| public static JMethod createForwardingMethod(JDeclaredType type, |
| JMethod methodToDelegateTo) { |
| JMethod forwardingMethod = createEmptyMethodFromExample(type, methodToDelegateTo, false); |
| forwardingMethod.setForwarding(); |
| |
| if (type.isJsNative()) { |
| if (methodToDelegateTo.isJsNative()) { |
| // Accidental override of native methods on native JsTypes are done by just redeclaring the |
| // native method. |
| return forwardingMethod; |
| } |
| // Otherwise the forwarding method is an overlay method with a proper body. |
| forwardingMethod.setJsOverlay(); |
| forwardingMethod.setBody(new JMethodBody(methodToDelegateTo.getSourceInfo())); |
| } |
| |
| // Create the forwarding body. |
| JMethodBody body = (JMethodBody) forwardingMethod.getBody(); |
| // Invoke methodToDelegate |
| JMethodCall forwardingCall = new JMethodCall(methodToDelegateTo.getSourceInfo(), |
| new JThisRef(methodToDelegateTo.getSourceInfo(), type), |
| methodToDelegateTo); |
| forwardingCall.setStaticDispatchOnly(); |
| // copy params |
| for (JParameter p : forwardingMethod.getParams()) { |
| forwardingCall.addArg(p.makeRef(p.getSourceInfo())); |
| } |
| |
| // return statement if not void return type |
| body.getBlock().addStmt(makeMethodEndStatement(forwardingMethod.getType(), forwardingCall)); |
| return forwardingMethod; |
| } |
| |
| /** |
| * Creates a multi expression from a list of expressions, removing expressions that do |
| * not have side effects if possible. |
| */ |
| public static JExpression createOptimizedMultiExpression(JExpression... expressions) { |
| return createOptimizedMultiExpression(false, Arrays.asList(expressions)); |
| } |
| |
| /** |
| * Creates a multi expression from a list of expressions, removing expressions that do |
| * not have side effects if possible. |
| */ |
| public static JExpression createOptimizedMultiExpression(boolean ignoringResult, |
| List<JExpression> expressions) { |
| |
| int numberOfExpressions = expressions.size(); |
| JExpression result = expressions.get(numberOfExpressions - 1); |
| |
| numberOfExpressions = expressions.size(); |
| if (numberOfExpressions == 0) { |
| return new JMultiExpression(SourceOrigin.UNKNOWN); |
| } |
| |
| expressions = Lists.newArrayList(Collections2.filter( |
| expressions.subList(0, numberOfExpressions - 1), |
| Predicates.and( |
| Predicates.notNull(), |
| new Predicate<JExpression>() { |
| @Override |
| public boolean apply(JExpression expression) { |
| return expression.hasSideEffects(); |
| } |
| }))); |
| |
| if (result != null && (!ignoringResult || result.hasSideEffects())) { |
| expressions.add(result); |
| } |
| |
| if (expressions.size() == 1) { |
| // Do not create a multi expression if it consists only of the result. |
| return expressions.iterator().next(); |
| } |
| |
| SourceInfo info = expressions.size() > 0 ? expressions.get(0).getSourceInfo() : |
| SourceOrigin.UNKNOWN; |
| return new JMultiExpression(info, expressions); |
| } |
| |
| /** |
| * Returns an ast node representing the expression {@code expression != null}. |
| */ |
| public static JExpression createOptimizedNotNullComparison( |
| JProgram program, SourceInfo info, JExpression expression) { |
| JReferenceType type = (JReferenceType) expression.getType(); |
| if (type.isNullType()) { |
| return program.getLiteralBoolean(false); |
| } |
| |
| if (!type.canBeNull()) { |
| return createOptimizedMultiExpression(expression, program.getLiteralBoolean(true)); |
| } |
| |
| return new JBinaryOperation(info, program.getTypePrimitiveBoolean(), |
| JBinaryOperator.NEQ, expression, program.getLiteralNull()); |
| } |
| |
| public static String getIndexedName(JMember member) { |
| return member.getEnclosingType().getShortName() + '.' + member.getName(); |
| } |
| |
| /** |
| * Creates a synthetic abstract stub in {@code type} with the same signature as |
| * {@code superTypeMethod}. |
| */ |
| public static JMethod createSyntheticAbstractStub(JDeclaredType type, JMethod superTypeMethod) { |
| assert type.isAbstract(); |
| assert superTypeMethod.isAbstract(); |
| return createEmptyMethodFromExample(type, superTypeMethod, true); |
| } |
| |
| /** |
| * Returns a native constructor of a native JsType class. |
| */ |
| public static JConstructor getJsNativeConstructorOrNull(JType type) { |
| if (!type.isJsNative() || !(type.getUnderlyingType() instanceof JClassType)) { |
| return null; |
| } |
| JMethod jsConstructor = Iterables.getFirst(Iterables.filter( |
| ((JClassType) type).getMethods(), JjsPredicates.IS_JS_CONSTRUCTOR), null); |
| assert jsConstructor != null; |
| return (JConstructor) jsConstructor; |
| } |
| |
| /** |
| * Returns a description for a type suitable for reporting errors to the users. |
| */ |
| public static String getReadableDescription(JType type) { |
| if (type instanceof JArrayType) { |
| JArrayType arrayType = (JArrayType) type; |
| return getReadableDescription(arrayType.getLeafType()) + Strings.repeat("[]", |
| arrayType.getDims()); |
| } |
| return Joiner.on(".").join(type.getCompoundName()); |
| } |
| |
| /** |
| * Returns a description for a member suitable for reporting errors to the users. |
| */ |
| public static String getReadableDescription(JMember member) { |
| if (member instanceof JField) { |
| return String.format("%s %s.%s", |
| getReadableDescription(member.getType()), |
| getReadableDescription(member.getEnclosingType()), |
| member.getName()); |
| } |
| |
| JMethod method = (JMethod) member; |
| String printableDescription = ""; |
| if (!method.isConstructor()) { |
| printableDescription += getReadableDescription(method.getType()) + " "; |
| } |
| printableDescription += String.format("%s.%s(%s)", |
| getReadableDescription(method.getEnclosingType()), |
| method.getName(), |
| Joiner.on(", ").join( |
| Iterables.transform(method.getOriginalParamTypes(), new Function<JType, String>() { |
| @Override |
| public String apply(JType type) { |
| return getReadableDescription(type); |
| } |
| } |
| ))); |
| return printableDescription; |
| } |
| |
| public static void replaceMethodBody(JMethod method, JExpression returnValue) { |
| JMethodBody body = (JMethodBody) method.getBody(); |
| JBlock block = body.getBlock(); |
| block.clear(); |
| block.addStmt(returnValue.makeReturnStatement()); |
| } |
| |
| /** |
| * Returns types from typed nodes. |
| */ |
| public static Iterable<JReferenceType> getExpressionTypes(Iterable<? extends HasType> nodes) { |
| if (nodes == null) { |
| return Collections.emptyList(); |
| } |
| return FluentIterable.from(nodes).transform( |
| new Function<HasType, JReferenceType>() { |
| @Override |
| public JReferenceType apply(HasType typedNode) { |
| return (JReferenceType) typedNode.getType(); |
| } |
| }); |
| } |
| |
| /** |
| * Returns true if the method is a synthetic accidental override that trivially dispatches to its |
| * same name super. |
| */ |
| public static boolean isJsMemberUnnecessaryAccidentalOverride(JMethod method) { |
| // Assumptions on synthethic overrides, if any of these change. |
| assert !method.isSyntheticAccidentalOverride() || !method.exposesPackagePrivateMethod(); |
| |
| if (!method.isSyntheticAccidentalOverride()) { |
| return false; |
| } |
| |
| boolean overridesConcreteJsMethod = Iterables.any(method.getOverriddenMethods(), |
| new Predicate<JMethod>() { |
| @Override |
| public boolean apply(JMethod method) { |
| return !method.isAbstract() && method.getJsMemberType() != JsMemberType.NONE; |
| } |
| }); |
| // A synthetic accidental override is unnecessary if its due to a JsMethod that does not expose |
| // a non JsMember. |
| return overridesConcreteJsMethod && !method.exposesNonJsMember(); |
| } |
| |
| /** |
| * Mangles a qualified name into a Javah signature. |
| */ |
| public static String javahSignatureFromName(String name) { |
| return "L" + mangledNameString(name) + "_2"; |
| } |
| |
| public static String mangleMemberName(String enclosingTypeName, String fieldName) { |
| return mangledNameString(enclosingTypeName) + '_' + mangledNameString(fieldName); |
| } |
| |
| /** |
| * Returns an valid identifier for a named Java entity. |
| */ |
| public static String mangledNameString(HasName hasName) { |
| return mangledNameString(hasName.getName()); |
| } |
| |
| /** |
| * Returns an valid identifier for a named Java entity. |
| */ |
| public static String mangledNameString(String name) { |
| return name.replaceAll("_", "_1").replace('.', '_'); |
| } |
| |
| /** |
| * Returns the ending statement for a method based on an expression. If the return type is void |
| * then the ending statement just executes the expression otherwise it returns it. |
| */ |
| public static JStatement makeMethodEndStatement(JType returnType, JExpression expression) { |
| // TODO(rluble): Check if something needs to be done here regarding boxing/unboxing/coercions |
| // when one of the types of expression and returnType is a boxed type and the other a primitive |
| // type or both are primitive of differnent coerceable types. Add the proper tests first. |
| return returnType == JPrimitiveType.VOID ? |
| expression.makeStatement() : |
| expression.makeReturnStatement(); |
| } |
| |
| /** |
| * Translates a Java literal into a JavaScript literal. |
| */ |
| public static JsLiteral translateLiteral(JLiteral literal) { |
| return translatorByLiteralClass.get(literal.getClass()).translate(literal); |
| } |
| |
| static void synthesizeStaticInitializerChain( |
| JDeclaredType type, Iterable<JInterfaceType> superInterfacesRequiringStaticInitialization) { |
| // Implement static initialization as described in (Java 8) JLS 12.4.2. |
| List<JStatement> superClinitCalls = Lists.newArrayList(); |
| SourceInfo sourceInfo = type.getSourceInfo(); |
| |
| // First call the static initializer for the superclass. |
| JClassType superClass = type.getSuperClass(); |
| if (superClass != null) { |
| superClinitCalls.add( |
| new JMethodCall(sourceInfo, null, superClass.getClinitMethod()).makeStatement()); |
| } |
| |
| // Recurse over interfaces in preorder initializing the ones that have default methods. |
| for (JInterfaceType interfaceType : superInterfacesRequiringStaticInitialization) { |
| superClinitCalls.add( |
| new JMethodCall(sourceInfo, null, interfaceType.getClinitMethod()).makeStatement()); |
| } |
| |
| JMethodBody body = (JMethodBody) type.getClinitMethod().getBody(); |
| body.getBlock().getStatements().addAll(0, superClinitCalls); |
| } |
| |
| private static Map<Class<? extends JLiteral>, LiteralTranslators> translatorByLiteralClass = |
| new ImmutableMap.Builder<Class<? extends JLiteral>, LiteralTranslators>() |
| .put(JBooleanLiteral.class, LiteralTranslators.BOOLEAN_LITERAL_TRANSLATOR) |
| .put(JCharLiteral.class, LiteralTranslators.CHAR_LITERAL_TRANSLATOR) |
| .put(JFloatLiteral.class, LiteralTranslators.FLOAT_LITERAL_TRANSLATOR) |
| .put(JDoubleLiteral.class, LiteralTranslators.DOUBLE_LITERAL_TRANSLATOR) |
| .put(JIntLiteral.class, LiteralTranslators.INT_LITERAL_TRANSLATOR) |
| .put(JLongLiteral.class, LiteralTranslators.LONG_LITERAL_TRANSLATOR) |
| .put(JNullLiteral.class, LiteralTranslators.NULL_LITERAL_TRANSLATOR) |
| .put(JStringLiteral.class, LiteralTranslators.STRING_LITERAL_TRANSLATOR) |
| .build(); |
| |
| private enum LiteralTranslators { |
| BOOLEAN_LITERAL_TRANSLATOR() { |
| @Override |
| JsLiteral translate(JExpression literal) { |
| return JsBooleanLiteral.get(((JBooleanLiteral) literal).getValue()); |
| } |
| }, |
| CHAR_LITERAL_TRANSLATOR() { |
| @Override |
| JsLiteral translate(JExpression literal) { |
| return new JsNumberLiteral(literal.getSourceInfo(), ((JCharLiteral) literal).getValue()); |
| } |
| }, |
| FLOAT_LITERAL_TRANSLATOR() { |
| @Override |
| JsLiteral translate(JExpression literal) { |
| return new JsNumberLiteral(literal.getSourceInfo(), ((JFloatLiteral) literal).getValue()); |
| } |
| }, |
| DOUBLE_LITERAL_TRANSLATOR() { |
| @Override |
| JsLiteral translate(JExpression literal) { |
| return new JsNumberLiteral(literal.getSourceInfo(), ((JDoubleLiteral) literal).getValue()); |
| } |
| }, |
| INT_LITERAL_TRANSLATOR() { |
| @Override |
| JsLiteral translate(JExpression literal) { |
| return new JsNumberLiteral(literal.getSourceInfo(), ((JIntLiteral) literal).getValue()); |
| } |
| }, |
| LONG_LITERAL_TRANSLATOR() { |
| @Override |
| JsLiteral translate(JExpression literal) { |
| SourceInfo sourceInfo = literal.getSourceInfo(); |
| long[] values = LongLib.getAsLongArray(((JLongLiteral) literal).getValue()); |
| if (values.length == 1) { |
| return new JsNumberLiteral(literal.getSourceInfo(), ((JLongLiteral) literal).getValue()); |
| } |
| JsObjectLiteral.Builder objectLiteralBuilder = JsObjectLiteral.builder(sourceInfo) |
| .setInternable(); |
| |
| assert values.length == longComponentNames.length; |
| for (int i = 0; i < longComponentNames.length; i++) { |
| addPropertyToObject(sourceInfo, longComponentNames[i], values[i], objectLiteralBuilder); |
| } |
| return objectLiteralBuilder.build(); |
| } |
| }, |
| STRING_LITERAL_TRANSLATOR() { |
| @Override |
| JsLiteral translate(JExpression literal) { |
| return new JsStringLiteral(literal.getSourceInfo(), ((JStringLiteral) literal).getValue()); |
| } |
| }, |
| NULL_LITERAL_TRANSLATOR() { |
| @Override |
| JsLiteral translate(JExpression literal) { |
| return JsNullLiteral.INSTANCE; |
| } |
| }; |
| |
| private static String[] longComponentNames = { "l", "m", "h" }; |
| |
| abstract JsLiteral translate(JExpression literal); |
| } |
| |
| private static void addPropertyToObject(SourceInfo sourceInfo, String propertyName, |
| long propertyValue, JsObjectLiteral.Builder objectLiteralBuilder) { |
| JsExpression value = new JsNumberLiteral(sourceInfo, propertyValue); |
| objectLiteralBuilder.add(new JsNameRef(sourceInfo, propertyName), value); |
| } |
| |
| private static JMethod createEmptyMethodFromExample( |
| JDeclaredType inType, JMethod exampleMethod, boolean isAbstract) { |
| JMethod emptyMethod = new JMethod(exampleMethod.getSourceInfo(), exampleMethod.getName(), |
| inType, exampleMethod.getType(), isAbstract, false, false, exampleMethod.getAccess()); |
| emptyMethod.addThrownExceptions(exampleMethod.getThrownExceptions()); |
| emptyMethod.setSynthetic(); |
| // Copy parameters. |
| for (JParameter param : exampleMethod.getParams()) { |
| emptyMethod.cloneParameter(param); |
| } |
| // If the enclosing type is native, make sure the synthetic empty method is native by leaving |
| // the body = null. |
| if (!inType.isJsNative()) { |
| JMethodBody body = new JMethodBody(exampleMethod.getSourceInfo()); |
| emptyMethod.setBody(body); |
| } |
| emptyMethod.freezeParamTypes(); |
| inType.addMethod(emptyMethod); |
| return emptyMethod; |
| } |
| |
| /** |
| * Extracts the this(..) or super(..) call from a statement if the statement is of the expected |
| * form. Otherwise returns null. |
| */ |
| public static JMethodCall getThisOrSuperConstructorCall( |
| JStatement statement) { |
| if (!(statement instanceof JExpressionStatement)) { |
| return null; |
| } |
| |
| JExpressionStatement expressionStatement = (JExpressionStatement) statement; |
| if (!(expressionStatement.getExpr() instanceof JMethodCall) |
| || expressionStatement.getExpr() instanceof JNewInstance) { |
| return null; |
| } |
| |
| JMethodCall call = (JMethodCall) expressionStatement.getExpr(); |
| if (call.getTarget() instanceof JConstructor && call.isStaticDispatchOnly()) { |
| return call; |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the JsConstructor for a class or null if it does not have any. |
| */ |
| public static JConstructor getJsConstructor(JDeclaredType type) { |
| return |
| FluentIterable |
| .from(type.getConstructors()) |
| .filter(new Predicate<JConstructor>() { |
| @Override |
| public boolean apply(JConstructor constructor) { |
| return constructor.isJsConstructor(); |
| } |
| }).first().orNull(); |
| } |
| |
| /** |
| * Returns the constructor which this constructor delegates or null if none. |
| */ |
| public static JConstructor getDelegatedThisOrSuperConstructor(JConstructor constructor) { |
| JStatement contructorInvocaton = FluentIterable |
| .from(constructor.getBody().getStatements()) |
| .filter(new Predicate<JStatement>() { |
| @Override |
| public boolean apply(JStatement statement) { |
| return getThisOrSuperConstructorCall(statement) != null; |
| } |
| }).first().orNull(); |
| |
| return contructorInvocaton != null |
| ? (JConstructor) getThisOrSuperConstructorCall(contructorInvocaton).getTarget() |
| : null; |
| } |
| |
| /** |
| * Returns the constructor which all others delegate to if any, otherwise null. |
| */ |
| public static JConstructor getPrimaryConstructor(final JDeclaredType type) { |
| List<JConstructor> delegatedSuperConstructors = FluentIterable |
| .from(type.getConstructors()) |
| .filter(new Predicate<JConstructor>() { |
| @Override |
| public boolean apply(JConstructor constructor) { |
| // Calls super constructor. |
| return getDelegatedThisOrSuperConstructor(constructor).getEnclosingType() != type; |
| } |
| }) |
| .limit(2) |
| .toList(); |
| if (delegatedSuperConstructors.size() == 1) { |
| return delegatedSuperConstructors.get(0); |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the nearest native superclass of {@code type} if any, null otherwise. |
| */ |
| public static JClassType getNativeSuperClassOrNull(JDeclaredType type) { |
| JClassType superClass = type.getSuperClass(); |
| if (superClass == null || superClass.isJsNative()) { |
| return superClass; |
| } |
| return getNativeSuperClassOrNull(superClass); |
| } |
| |
| /** |
| * Whether or not to use the JsName when implementing this member. |
| * |
| * <p>A member should only expose a JsName when a JsName has been assigned and the compilation |
| * has been configured to honor those names. |
| * */ |
| public static boolean exposesJsName(JMember member) { |
| // JsFunction interfaces and implementations do not have JsNames but canBeReferencedExternally |
| // or canBeImplementedExternally. |
| return member.getJsMemberType() != JsMemberType.NONE |
| && (member.canBeImplementedExternally() || member.canBeReferencedExternally()); |
| } |
| |
| private JjsUtils() { |
| } |
| } |