Search in sources :

Example 1 with ListType

use of com.google.template.soy.types.ListType in project closure-templates by google.

the class JsType method forSoyType.

/**
 * Returns a {@link JsType} corresponding to the given {@link SoyType}
 *
 * <p>TODO(lukes): consider adding a cache for all the computed types. The same type is probably
 * accessed many many times.
 *
 * @param soyType the soy type
 * @param isIncrementalDom whether or not this is for incremental dom.
 */
static JsType forSoyType(SoyType soyType, boolean isIncrementalDom) {
    switch(soyType.getKind()) {
        case NULL:
            return NULL_OR_UNDEFINED_TYPE;
        case ANY:
            return ANY_TYPE;
        case UNKNOWN:
            return UNKNOWN_TYPE;
        case BOOL:
            return BOOLEAN_TYPE;
        case PROTO_ENUM:
            SoyProtoEnumType enumType = (SoyProtoEnumType) soyType;
            String enumTypeName = enumType.getNameForBackend(SoyBackendKind.JS_SRC);
            return builder().addType("number").addType(enumTypeName).addRequire(GoogRequire.create(enumTypeName)).setPredicate(GOOG_IS_NUMBER).build();
        case FLOAT:
        case INT:
            return NUMBER_TYPE;
        case STRING:
            return STRING_OR_UNSANITIZED_TEXT;
        case ATTRIBUTES:
            if (isIncrementalDom) {
                // idom has a different strategy for handling these
                return IDOM_ATTRIBUTES;
            }
        // fall through
        case HTML:
            if (isIncrementalDom) {
                // idom has a different strategy for handling these
                return IDOM_HTML;
            }
        // fall-through
        case CSS:
        case JS:
        case URI:
        case TRUSTED_RESOURCE_URI:
            return STRICT_TYPES.get(((SanitizedType) soyType).getContentKind());
        case LIST:
            ListType listType = (ListType) soyType;
            if (listType.getElementType().getKind() == SoyType.Kind.ANY) {
                return RAW_ARRAY_TYPE;
            }
            JsType element = forSoyType(listType.getElementType(), isIncrementalDom);
            return builder().addType("!Array<" + element.typeExpr() + ">").addRequires(element.getGoogRequires()).setPredicate(GOOG_IS_ARRAY).build();
        case LEGACY_OBJECT_MAP:
            {
                LegacyObjectMapType mapType = (LegacyObjectMapType) soyType;
                if (mapType.getKeyType().getKind() == SoyType.Kind.ANY && mapType.getValueType().getKind() == SoyType.Kind.ANY) {
                    return RAW_OBJECT_TYPE;
                }
                JsType keyTypeName = forSoyType(mapType.getKeyType(), isIncrementalDom);
                JsType valueTypeName = forSoyType(mapType.getValueType(), isIncrementalDom);
                return builder().addType(String.format("!Object<%s,%s>", keyTypeName.typeExpr(), valueTypeName.typeExpr())).addRequires(keyTypeName.getGoogRequires()).addRequires(valueTypeName.getGoogRequires()).setPredicate(GOOG_IS_OBJECT).build();
            }
        case MAP:
            {
                MapType mapType = (MapType) soyType;
                SoyType keyType = mapType.getKeyType();
                SoyType.Kind keyKind = keyType.getKind();
                Preconditions.checkState(MapType.isAllowedKeyType(keyType));
                // Soy key type of string should translate to a JS key type of string.
                // forSoyType(StringType.getInstance()) normally translates to
                // string|!goog.soy.data.UnsanitizedText, but ES6 Maps always use instance equality for
                // lookups. Using UnsanitizedText instances as keys in Soy maps would cause unexpected
                // behavior (usually a failed map lookup), so don't generate signatures that allow it.
                JsType keyTypeName = keyKind == SoyType.Kind.STRING ? STRING_TYPE : forSoyType(keyType, isIncrementalDom);
                JsType valueTypeName = forSoyType(mapType.getValueType(), isIncrementalDom);
                return builder().addType(String.format("!soy.map.Map<%s,%s>", keyTypeName.typeExpr(), valueTypeName.typeExpr())).addRequires(keyTypeName.getGoogRequires()).addRequires(valueTypeName.getGoogRequires()).addRequire(GoogRequire.create("soy.map")).setPredicate(TypePredicate.NO_OP).build();
            }
        case PROTO:
            final SoyProtoType protoType = (SoyProtoType) soyType;
            final String protoTypeName = protoType.getNameForBackend(SoyBackendKind.JS_SRC);
            // isn't clear that this is very useful for users.
            return builder().addType(protoTypeName).addRequire(GoogRequire.create(protoTypeName)).addCoercionStrategy(ValueCoercionStrategy.PROTO).setPredicate(new TypePredicate() {

                @Override
                public Optional<CodeChunk.WithValue> maybeCheck(CodeChunk.WithValue value, Generator codeGenerator) {
                    return Optional.of(value.instanceof_(JsRuntime.protoConstructor(protoType)));
                }
            }).build();
        case RECORD:
            {
                RecordType recordType = (RecordType) soyType;
                if (recordType.getMembers().isEmpty()) {
                    return RAW_OBJECT_TYPE;
                }
                Builder builder = builder();
                Map<String, String> members = new LinkedHashMap<>();
                for (Map.Entry<String, SoyType> member : recordType.getMembers().entrySet()) {
                    JsType forSoyType = forSoyType(member.getValue(), isIncrementalDom);
                    builder.addRequires(forSoyType.getGoogRequires());
                    members.put(member.getKey(), forSoyType.typeExprForRecordMember(/* isOptional= */
                    false));
                }
                return builder.addType("{" + Joiner.on(", ").withKeyValueSeparator(": ").join(members) + ",}").setPredicate(GOOG_IS_OBJECT).build();
            }
        case UNION:
            {
                UnionType unionType = (UnionType) soyType;
                Builder builder = builder();
                final Set<JsType> types = new LinkedHashSet<>();
                final boolean isNullable = unionType.isNullable();
                // handle null first so that if other type tests dereference the param they won't fail
                if (isNullable) {
                    builder.addTypes(NULL_OR_UNDEFINED_TYPE.typeExpressions);
                    builder.addCoercionStrategy(ValueCoercionStrategy.NULL);
                    types.add(NULL_OR_UNDEFINED_TYPE);
                }
                for (SoyType member : unionType.getMembers()) {
                    if (member.getKind() == Kind.NULL) {
                        // handled above
                        continue;
                    }
                    JsType memberType = forSoyType(member, isIncrementalDom);
                    builder.addRequires(memberType.extraRequires);
                    builder.addTypes(memberType.typeExpressions);
                    builder.addCoercionStrategies(memberType.coercionStrategies);
                    types.add(memberType);
                }
                return builder.setPredicate(new TypePredicate() {

                    @Override
                    public Optional<CodeChunk.WithValue> maybeCheck(CodeChunk.WithValue value, Generator codeGenerator) {
                        CodeChunk.WithValue result = null;
                        // this automatically.
                        for (JsType memberType : types) {
                            Optional<CodeChunk.WithValue> typeAssertion = memberType.getTypeAssertion(value, codeGenerator);
                            if (!typeAssertion.isPresent()) {
                                return Optional.absent();
                            }
                            if (result == null) {
                                result = typeAssertion.get();
                            } else {
                                result = result.or(typeAssertion.get(), codeGenerator);
                            }
                        }
                        return Optional.of(result);
                    }
                }).build();
            }
        default:
            throw new AssertionError("unhandled soytype: " + soyType);
    }
}
Also used : UnionType(com.google.template.soy.types.UnionType) EnumSet(java.util.EnumSet) LinkedHashSet(java.util.LinkedHashSet) ImmutableSortedSet(com.google.common.collect.ImmutableSortedSet) ImmutableSet(com.google.common.collect.ImmutableSet) Set(java.util.Set) LegacyObjectMapType(com.google.template.soy.types.LegacyObjectMapType) MapType(com.google.template.soy.types.MapType) RecordType(com.google.template.soy.types.RecordType) CodeChunk(com.google.template.soy.jssrc.dsl.CodeChunk) ListType(com.google.template.soy.types.ListType) Kind(com.google.template.soy.types.SoyType.Kind) SoyBackendKind(com.google.template.soy.base.SoyBackendKind) SanitizedContentKind(com.google.template.soy.base.internal.SanitizedContentKind) Optional(com.google.common.base.Optional) LegacyObjectMapType(com.google.template.soy.types.LegacyObjectMapType) SoyProtoType(com.google.template.soy.types.SoyProtoType) SoyType(com.google.template.soy.types.SoyType) SoyProtoEnumType(com.google.template.soy.types.SoyProtoEnumType) LinkedHashMap(java.util.LinkedHashMap) Map(java.util.Map) ImmutableMap(com.google.common.collect.ImmutableMap) EnumMap(java.util.EnumMap) Generator(com.google.template.soy.jssrc.dsl.CodeChunk.Generator)

Example 2 with ListType

use of com.google.template.soy.types.ListType in project closure-templates by google.

the class GenerateParseInfoVisitor method findProtoTypesRecurse.

/**
 * Recursively search for protocol buffer types within the given type.
 *
 * @param type The type to search.
 * @param protoTypes Output set.
 */
private static void findProtoTypesRecurse(SoyType type, SortedSet<String> protoTypes) {
    if (type.getKind() == SoyType.Kind.PROTO) {
        protoTypes.add(((SoyProtoType) type).getDescriptorExpression());
    } else if (type.getKind() == SoyType.Kind.PROTO_ENUM) {
        protoTypes.add(((SoyProtoEnumType) type).getDescriptorExpression());
    } else {
        switch(type.getKind()) {
            case UNION:
                for (SoyType member : ((UnionType) type).getMembers()) {
                    findProtoTypesRecurse(member, protoTypes);
                }
                break;
            case LIST:
                {
                    ListType listType = (ListType) type;
                    findProtoTypesRecurse(listType.getElementType(), protoTypes);
                    break;
                }
            case LEGACY_OBJECT_MAP:
                {
                    LegacyObjectMapType mapType = (LegacyObjectMapType) type;
                    findProtoTypesRecurse(mapType.getKeyType(), protoTypes);
                    findProtoTypesRecurse(mapType.getValueType(), protoTypes);
                    break;
                }
            case RECORD:
                {
                    RecordType recordType = (RecordType) type;
                    for (SoyType fieldType : recordType.getMembers().values()) {
                        findProtoTypesRecurse(fieldType, protoTypes);
                    }
                    break;
                }
            default:
                break;
        }
    }
}
Also used : RecordType(com.google.template.soy.types.RecordType) SoyType(com.google.template.soy.types.SoyType) ListType(com.google.template.soy.types.ListType) SoyProtoEnumType(com.google.template.soy.types.SoyProtoEnumType) LegacyObjectMapType(com.google.template.soy.types.LegacyObjectMapType)

Example 3 with ListType

use of com.google.template.soy.types.ListType in project closure-templates by google.

the class CheckProtoInitCallsPass method checkProto.

private void checkProto(ProtoInitNode node, SoyProtoType soyType) {
    // Check that all proto required fields are present.
    // TODO(user): Consider writing a soyProtoTypeImpl.getRequiredFields()
    Set<String> givenParams = Sets.newHashSet(node.getParamNames());
    for (FieldDescriptor field : soyType.getDescriptor().getFields()) {
        if (field.isRequired() && !givenParams.contains(field.getName())) {
            errorReporter.report(node.getSourceLocation(), MISSING_REQUIRED_FIELD, field.getName());
        }
    }
    ImmutableSet<String> fields = soyType.getFieldNames();
    for (int i = 0; i < node.numChildren(); i++) {
        String fieldName = node.getParamNames().get(i);
        ExprNode expr = node.getChild(i);
        // Check that each arg exists in the proto.
        if (!fields.contains(fieldName)) {
            String extraErrorMessage = SoyErrors.getDidYouMeanMessageForProtoFields(fields, fieldName);
            errorReporter.report(expr.getSourceLocation(), FIELD_DOES_NOT_EXIST, fieldName, extraErrorMessage);
            continue;
        }
        // Check that the arg type is not null and that it matches the expected field type.
        SoyType argType = expr.getType();
        if (argType.equals(NullType.getInstance())) {
            errorReporter.report(expr.getSourceLocation(), NULL_ARG_TYPE, fieldName);
        }
        SoyType fieldType = soyType.getFieldType(fieldName);
        // Let args with unknown or error types pass
        if (argType.equals(UnknownType.getInstance()) || argType.equals(ErrorType.getInstance())) {
            return;
        }
        // Same for List<?>, for repeated fields
        if (fieldType.getKind() == Kind.LIST && argType.getKind() == Kind.LIST) {
            SoyType argElementType = ((ListType) argType).getElementType();
            if (argElementType == null || argElementType.equals(UnknownType.getInstance())) {
                return;
            }
        }
        SoyType expectedType = SoyTypes.makeNullable(fieldType);
        if (!expectedType.isAssignableFrom(argType)) {
            errorReporter.report(expr.getSourceLocation(), ARGUMENT_TYPE_MISMATCH, fieldName, expectedType, argType);
        }
    }
}
Also used : ExprNode(com.google.template.soy.exprtree.ExprNode) SoyType(com.google.template.soy.types.SoyType) ListType(com.google.template.soy.types.ListType) FieldDescriptor(com.google.protobuf.Descriptors.FieldDescriptor)

Example 4 with ListType

use of com.google.template.soy.types.ListType in project closure-templates by google.

the class ResolveExpressionTypesVisitorTest method testTypeParser.

@Test
public void testTypeParser() {
    SoyType type = parseSoyType("string");
    assertThat(type).isEqualTo(StringType.getInstance());
    type = parseSoyType("int");
    assertThat(type).isEqualTo(IntType.getInstance());
    type = parseSoyType("list<any>");
    assertThat(type.getKind()).isEqualTo(SoyType.Kind.LIST);
    assertThat(((ListType) type).getElementType()).isEqualTo(AnyType.getInstance());
    type = parseSoyType("map<string, ?>");
    assertThat(type.getKind()).isEqualTo(Kind.MAP);
    assertThat(((MapType) type).getKeyType()).isEqualTo(StringType.getInstance());
    assertThat(((MapType) type).getValueType()).isEqualTo(UnknownType.getInstance());
}
Also used : SoyType(com.google.template.soy.types.SoyType) ListType(com.google.template.soy.types.ListType) MapType(com.google.template.soy.types.MapType) Test(org.junit.Test)

Example 5 with ListType

use of com.google.template.soy.types.ListType in project closure-templates by google.

the class SoyExpressionTest method testListExpression.

@Test
public void testListExpression() {
    ListType list = ListType.of(IntType.getInstance());
    assertThatExpression(forList(list, constantNull(Type.getType(List.class))).coerceToBoolean()).evaluatesTo(false);
    assertThatExpression(forList(list, constantNull(Type.getType(List.class))).box()).evaluatesTo(null);
    assertThatExpression(forList(list, MethodRef.IMMUTABLE_LIST_OF.get(0).invoke()).coerceToBoolean()).evaluatesTo(true);
    // SoyList uses Object identity for equality so we can't really assert on the value.
    assertThatExpression(forList(list, MethodRef.IMMUTABLE_LIST_OF.get(0).invoke()).box()).evaluatesToInstanceOf(ListImpl.class);
}
Also used : ListType(com.google.template.soy.types.ListType) SoyExpression.forList(com.google.template.soy.jbcsrc.restricted.SoyExpression.forList) List(java.util.List) Test(org.junit.Test)

Aggregations

ListType (com.google.template.soy.types.ListType)6 SoyType (com.google.template.soy.types.SoyType)4 Test (org.junit.Test)3 LegacyObjectMapType (com.google.template.soy.types.LegacyObjectMapType)2 MapType (com.google.template.soy.types.MapType)2 RecordType (com.google.template.soy.types.RecordType)2 SoyProtoEnumType (com.google.template.soy.types.SoyProtoEnumType)2 Optional (com.google.common.base.Optional)1 ImmutableMap (com.google.common.collect.ImmutableMap)1 ImmutableSet (com.google.common.collect.ImmutableSet)1 ImmutableSortedSet (com.google.common.collect.ImmutableSortedSet)1 FieldDescriptor (com.google.protobuf.Descriptors.FieldDescriptor)1 SoyBackendKind (com.google.template.soy.base.SoyBackendKind)1 SanitizedContentKind (com.google.template.soy.base.internal.SanitizedContentKind)1 ExprNode (com.google.template.soy.exprtree.ExprNode)1 SoyExpression.forList (com.google.template.soy.jbcsrc.restricted.SoyExpression.forList)1 CodeChunk (com.google.template.soy.jssrc.dsl.CodeChunk)1 Generator (com.google.template.soy.jssrc.dsl.CodeChunk.Generator)1 SoyProtoType (com.google.template.soy.types.SoyProtoType)1 Kind (com.google.template.soy.types.SoyType.Kind)1