From c2a78d95ce90106db2df2ccd0952eaab85ae505f Mon Sep 17 00:00:00 2001 From: Jesse Costello-Good Date: Wed, 12 Feb 2025 14:30:37 -0800 Subject: [PATCH] One number type to rule them all. PiperOrigin-RevId: 726199896 --- documentation/reference/types.md | 30 +- .../template/soy/base/internal/BaseUtils.java | 19 ++ .../InsertWordBreaksDirective.java | 4 +- .../basicdirectives/TruncateDirective.java | 4 +- .../basicfunctions/BasicFunctionsRuntime.java | 56 +--- .../soy/basicfunctions/ListFlatMethod.java | 4 +- .../soy/data/ProtoFieldInterpreter.java | 11 +- .../google/template/soy/data/SoyValue.java | 10 - .../template/soy/data/SoyValueConverter.java | 4 +- .../soy/data/SoyValueUnconverter.java | 2 + .../template/soy/data/internal/DictImpl.java | 3 +- .../internalutils/InternalValueUtils.java | 23 +- .../soy/data/restricted/FloatData.java | 81 +---- .../soy/data/restricted/GbigintData.java | 5 - .../soy/data/restricted/IntegerData.java | 84 +---- .../soy/data/restricted/NullishData.java | 10 - .../soy/data/restricted/NumberData.java | 76 ++++- .../soy/exprtree/AbstractExprNodeVisitor.java | 13 +- .../AbstractReturningExprNodeVisitor.java | 12 +- .../soy/exprtree/ExprEquivalence.java | 17 +- .../template/soy/exprtree/ExprNode.java | 3 +- .../template/soy/exprtree/ExprNodes.java | 11 +- .../template/soy/exprtree/FloatNode.java | 72 ----- .../{IntegerNode.java => NumberNode.java} | 51 ++- .../javagencode/javatypes/JavaTypeUtils.java | 18 +- .../javagencode/javatypes/SimpleJavaType.java | 20 -- .../EnhancedAbstractExprNodeVisitor.java | 6 - .../soy/jbcsrc/ExpressionCompiler.java | 117 ++----- .../ExpressionToSoyValueProviderCompiler.java | 4 +- .../template/soy/jbcsrc/ExternCompiler.java | 52 ++- .../soy/jbcsrc/JbcSrcValueFactory.java | 29 +- .../template/soy/jbcsrc/ProtoUtils.java | 56 ++-- .../template/soy/jbcsrc/SoyNodeCompiler.java | 17 +- .../soy/jbcsrc/TemplateAnalysisImpl.java | 27 +- .../soy/jbcsrc/restricted/Branch.java | 6 - .../soy/jbcsrc/restricted/BytecodeUtils.java | 17 +- .../soy/jbcsrc/restricted/Expression.java | 21 +- .../soy/jbcsrc/restricted/MethodRefs.java | 22 +- .../soy/jbcsrc/restricted/SoyExpression.java | 108 +------ .../soy/jbcsrc/restricted/SoyRuntimeType.java | 36 +-- .../jbcsrc/runtime/JbcSrcExternRuntime.java | 9 +- .../jbcsrc/runtime/JbcSrcPluginRuntime.java | 19 ++ .../soy/jbcsrc/runtime/JbcSrcRuntime.java | 4 +- .../soy/jbcsrc/shared/SwitchFactory.java | 9 +- .../template/soy/jssrc/dsl/Expressions.java | 7 +- .../template/soy/jssrc/internal/JsType.java | 3 +- .../internal/TranslateExprNodeVisitor.java | 23 +- .../soy/msgs/internal/InsertMsgsVisitor.java | 6 +- .../soy/passes/CheckTemplateCallsPass.java | 3 +- .../template/soy/passes/KeyCommandPass.java | 3 +- .../soy/passes/MsgWithIdFunctionPass.java | 7 +- .../passes/ResolveExpressionTypesPass.java | 74 +++-- .../soy/passes/RewriteRemaindersPass.java | 6 +- .../soy/passes/RuntimeTypeCoercion.java | 101 ------ .../soy/passes/ValidateExternsPass.java | 14 +- .../soy/passes/VeDefValidationPass.java | 7 +- .../java/internal/JavaPluginValidator.java | 6 +- .../java/internal/ValidatorFactory.java | 35 +- .../plugin/java/internal/ValidatorValue.java | 21 +- .../internal/TranslateToPyExprVisitor.java | 6 +- .../soy/shared/internal/BuiltinFunction.java | 3 - .../soy/shared/internal/SharedRuntime.java | 67 +--- .../opti/SimplifyExprVisitor.java | 21 +- .../soy/sharedpasses/render/EvalVisitor.java | 29 +- .../sharedpasses/render/RenderVisitor.java | 4 +- .../sharedpasses/render/TofuJavaValue.java | 10 +- .../sharedpasses/render/TofuTypeChecks.java | 11 +- .../sharedpasses/render/TofuValueFactory.java | 12 +- .../template/soy/soyparse/ParseErrors.java | 5 +- .../template/soy/soyparse/SoyFileParser.jj | 63 ++-- .../MsgSubstUnitPlaceholderNameUtils.java | 8 +- .../soy/soytree/TemplateBasicNode.java | 4 +- .../soy/soytree/TemplateDelegateNode.java | 11 +- .../soytree/TemplateMetadataSerializer.java | 9 +- .../template/soy/soytree/TemplateNode.java | 8 +- .../template/soy/treebuilder/ExprNodes.java | 11 +- .../google/template/soy/types/FloatType.java | 50 --- .../google/template/soy/types/MapType.java | 2 +- .../types/{IntType.java => NumberType.java} | 30 +- .../template/soy/types/SoyProtoType.java | 11 +- .../google/template/soy/types/SoyType.java | 3 +- .../google/template/soy/types/SoyTypes.java | 18 +- .../template/soy/types/TypeRegistries.java | 10 +- .../basicfunctions/CeilingFunctionTest.java | 6 +- .../soy/basicfunctions/FloorFunctionTest.java | 6 +- .../basicfunctions/RandomIntFunctionTest.java | 2 +- .../template/soy/data/SoyListDataTest.java | 12 - .../template/soy/data/SoyMapDataTest.java | 12 - .../internalutils/InternalValueUtilsTest.java | 18 +- .../data/restricted/PrimitiveDataTest.java | 23 +- .../exprtree/AbstractExprNodeVisitorTest.java | 17 +- .../soy/exprtree/FunctionNodeTest.java | 8 +- .../soy/exprtree/ListLiteralNodeTest.java | 2 +- .../soy/exprtree/MethodCallNodeTest.java | 2 +- .../soy/exprtree/RecordLiteralNodeTest.java | 2 +- .../soy/jbcsrc/ExpressionCompilerTest.java | 56 ++-- .../soy/jbcsrc/LazyClosureCompilerTest.java | 4 +- .../jbcsrc/StreamingPrintDirectivesTest.java | 2 +- .../jbcsrc/restricted/SoyExpressionTest.java | 38 +-- .../jbcsrc/restricted/SoyRuntimeTypeTest.java | 23 +- .../soy/jssrc/internal/JsTypeTest.java | 18 +- .../soy/passes/DesugarGroupNodesPassTest.java | 10 +- .../ResolveExpressionTypesPassTest.java | 180 +++++------ .../testing/SoyJavaSourceFunctionTester.java | 7 +- .../opti/SimplifyExprVisitorTest.java | 2 +- .../sharedpasses/render/EvalVisitorTest.java | 35 +- .../render/RenderVisitorTest.java | 2 +- .../render/TofuTypeChecksTest.java | 23 +- .../soy/soyparse/ParseExpressionTest.java | 31 +- .../soy/soyparse/SourceLocationTest.java | 26 +- .../soy/soyparse/TemplateParserTest.java | 14 +- .../soy/soytree/SoyTreeUtilsTest.java | 6 +- .../soy/tofu/internal/TofuExceptionsTest.java | 6 +- .../soy/types/SoyTypeRegistryTest.java | 63 ++-- .../template/soy/types/SoyTypesTest.java | 304 ++++++++++-------- .../template/soy/types/TypeParserTest.java | 23 +- src/main/protobuf/template_metadata.proto | 3 +- 117 files changed, 1055 insertions(+), 1855 deletions(-) delete mode 100644 java/src/com/google/template/soy/exprtree/FloatNode.java rename java/src/com/google/template/soy/exprtree/{IntegerNode.java => NumberNode.java} (62%) delete mode 100644 java/src/com/google/template/soy/passes/RuntimeTypeCoercion.java delete mode 100644 java/src/com/google/template/soy/types/FloatType.java rename java/src/com/google/template/soy/types/{IntType.java => NumberType.java} (69%) diff --git a/documentation/reference/types.md b/documentation/reference/types.md index 92d4ca44ed..c189fcfc07 100644 --- a/documentation/reference/types.md +++ b/documentation/reference/types.md @@ -62,33 +62,25 @@ SoySauce | `boolean`, `com.google.template.soy.data.restricted.BooleanData` Tofu | `com.google.template.soy.data.restricted.BooleanData` Python | `bool` -### `int` {#int} +### `number` {#number} -An integer. Due to limitations of JavaScript this type is only guaranteed to -have 52 bit of precision. +A number. Due to limitations of JavaScript this type is only guaranteed to have +52 bit of precision. Backend | type in host language ----------- | ---------------------------------------------------------- +---------- | -------------------------------------------------------------- JavaScript | `number` -SoySauce | `long`, `com.google.template.soy.data.restricted.LongData` -Tofu | `com.google.template.soy.data.restricted.LongData` -Python | `long` +SoySauce | `double`, `com.google.template.soy.data.restricted.NumberData` +Tofu | `com.google.template.soy.data.restricted.NumberData` +Python | `long` or `float` -### `float` {#float} - -A floating point number with 64 bits of precision. +### `int` {#int} -Backend | type in host language ----------- | --------------------- -JavaScript | `number` -SoySauce | `double`, `FloatData` -Tofu | `FloatData` -Python | `float` +A deprecated alias for [`number`](#number). -### `number` {#number} +### `float` {#float} -An alias for `int|float`. (Technically a [composite type](#union), not a -primitive.) +A deprecated alias for [`number`](#number). ### `gbigint` {#gbigint} diff --git a/java/src/com/google/template/soy/base/internal/BaseUtils.java b/java/src/com/google/template/soy/base/internal/BaseUtils.java index 620f3437c5..08944192c3 100644 --- a/java/src/com/google/template/soy/base/internal/BaseUtils.java +++ b/java/src/com/google/template/soy/base/internal/BaseUtils.java @@ -284,4 +284,23 @@ public static void trimStackTraceTo(Throwable t, Class cls) { } } } + + /** Returns Soy's idea of a double as a string. */ + public static String formatDouble(double value) { + // This approximately consistent with Javascript for important cases. + // Reference: http://www.ecma-international.org/ecma-262/5.1/#sec-9.8.1 + if (value % 1 == 0 && Math.abs(value) < Long.MAX_VALUE) { + // The value is non-fractional and within the magnitude of a long, so print as an integer + // instead of scientific notation. Note that Javascript uses 1.0e19 as the cutoff, but + // Long.MAX_VALUE is not that far off (9.2e18), and it is both easy and efficient to coerce + // to a long. + return String.valueOf((long) value); + } else { + // Note: This differs from JS in how it rendered values that have a zero fractional component + // and in how it renders the value -0.0. + // JavaScript specifies that the string form of -0 is signless, and that the string form of + // fractionless numeric values has no decimal point. + return Double.toString(value).replace('E', 'e'); + } + } } diff --git a/java/src/com/google/template/soy/basicdirectives/InsertWordBreaksDirective.java b/java/src/com/google/template/soy/basicdirectives/InsertWordBreaksDirective.java index 4d63399e78..f8e58fbe38 100644 --- a/java/src/com/google/template/soy/basicdirectives/InsertWordBreaksDirective.java +++ b/java/src/com/google/template/soy/basicdirectives/InsertWordBreaksDirective.java @@ -104,7 +104,7 @@ public SoyExpression applyForJbcSrc( UnionType.of(StringType.getInstance(), HtmlType.getInstance()), JbcSrcMethods.INSERT_WORD_BREAKS.invoke( value.box(), - BytecodeUtils.numericConversion(args.get(0).unboxAsLong(), Type.INT_TYPE))); + BytecodeUtils.numericConversion(args.get(0).unboxAsDouble(), Type.INT_TYPE))); } @Override @@ -113,7 +113,7 @@ public AppendableAndOptions applyForJbcSrcStreaming( return AppendableAndOptions.create( JbcSrcMethods.INSERT_WORD_BREAKS_STREAMING.invoke( delegateAppendable, - BytecodeUtils.numericConversion(args.get(0).unboxAsLong(), Type.INT_TYPE))); + BytecodeUtils.numericConversion(args.get(0).unboxAsDouble(), Type.INT_TYPE))); } @Override diff --git a/java/src/com/google/template/soy/basicdirectives/TruncateDirective.java b/java/src/com/google/template/soy/basicdirectives/TruncateDirective.java index cf8dbc0460..b5b1f86d92 100644 --- a/java/src/com/google/template/soy/basicdirectives/TruncateDirective.java +++ b/java/src/com/google/template/soy/basicdirectives/TruncateDirective.java @@ -107,7 +107,7 @@ public SoyExpression applyForJbcSrc( return SoyExpression.forString( JbcSrcMethods.TRUNCATE.invoke( value.coerceToString(), - BytecodeUtils.numericConversion(args.get(0).unboxAsLong(), Type.INT_TYPE), + BytecodeUtils.numericConversion(args.get(0).unboxAsDouble(), Type.INT_TYPE), args.size() > 1 ? args.get(1).unboxAsBoolean() : BytecodeUtils.constant(true))); } @@ -117,7 +117,7 @@ public AppendableAndOptions applyForJbcSrcStreaming( return AppendableAndOptions.createCloseable( JbcSrcMethods.TRUNCATE_STREAMING.invoke( delegateAppendable, - BytecodeUtils.numericConversion(args.get(0).unboxAsLong(), Type.INT_TYPE), + BytecodeUtils.numericConversion(args.get(0).unboxAsDouble(), Type.INT_TYPE), args.size() > 1 ? args.get(1).unboxAsBoolean() : BytecodeUtils.constant(true))); } diff --git a/java/src/com/google/template/soy/basicfunctions/BasicFunctionsRuntime.java b/java/src/com/google/template/soy/basicfunctions/BasicFunctionsRuntime.java index b69c79511e..908df23174 100644 --- a/java/src/com/google/template/soy/basicfunctions/BasicFunctionsRuntime.java +++ b/java/src/com/google/template/soy/basicfunctions/BasicFunctionsRuntime.java @@ -44,8 +44,6 @@ import com.google.template.soy.data.internal.RuntimeMapTypeTracker; import com.google.template.soy.data.internal.SoyMapImpl; import com.google.template.soy.data.internal.SoyRecordImpl; -import com.google.template.soy.data.restricted.FloatData; -import com.google.template.soy.data.restricted.IntegerData; import com.google.template.soy.data.restricted.NumberData; import com.google.template.soy.data.restricted.StringData; import com.google.template.soy.shared.internal.Sanitizers; @@ -74,11 +72,7 @@ public static boolean veHasSameId(SoyVisualElement ve1, SoyVisualElement ve2) { * to the argument. */ public static long ceil(SoyValue arg) { - if (arg instanceof IntegerData) { - return arg.longValue(); - } else { - return (long) Math.ceil(arg.floatValue()); - } + return (long) Math.ceil(arg.floatValue()); } /** Concatenates its arguments. */ @@ -218,8 +212,8 @@ public static ImmutableList listFlat( @Nonnull public static ImmutableList listFlat( - List list, IntegerData data) { - return listFlatImpl(list, (int) data.getValue()); + List list, NumberData data) { + return listFlatImpl(list, data.integerValue()); } @Nonnull @@ -272,11 +266,7 @@ public static ImmutableList stringListSort( * the argument. */ public static long floor(SoyValue arg) { - if (arg instanceof IntegerData) { - return arg.longValue(); - } else { - return (long) Math.floor(arg.floatValue()); - } + return (long) Math.floor(arg.floatValue()); } /** @@ -327,36 +317,28 @@ public static SoyDict mapToLegacyObjectMap(SoyMap map) { /** Returns the numeric maximum of the two arguments. */ public static NumberData max(SoyValue arg0, SoyValue arg1) { - if (arg0 instanceof IntegerData && arg1 instanceof IntegerData) { - return IntegerData.forValue(Math.max(arg0.longValue(), arg1.longValue())); - } else { - return FloatData.forValue(Math.max(arg0.numberValue(), arg1.numberValue())); - } + return NumberData.forValue(Math.max(arg0.floatValue(), arg1.floatValue())); } /** Returns the numeric minimum of the two arguments. */ public static NumberData min(SoyValue arg0, SoyValue arg1) { - if (arg0 instanceof IntegerData && arg1 instanceof IntegerData) { - return IntegerData.forValue(Math.min(arg0.longValue(), arg1.longValue())); - } else { - return FloatData.forValue(Math.min(arg0.numberValue(), arg1.numberValue())); - } + return NumberData.forValue(Math.min(arg0.floatValue(), arg1.floatValue())); } @Nullable - public static FloatData parseFloat(String str) { + public static NumberData parseFloat(String str) { Double d = Doubles.tryParse(str); - return (d == null || d.isNaN()) ? null : FloatData.forValue(d); + return (d == null || d.isNaN()) ? null : NumberData.forValue(d); } @Nullable - public static IntegerData parseInt(String str, SoyValue radixVal) { + public static NumberData parseInt(String str, SoyValue radixVal) { int radix = SoyValue.isNullish(radixVal) ? 10 : (int) radixVal.numberValue(); if (radix < 2 || radix > 36) { return null; } Long l = Longs.tryParse(str, radix); - return (l == null) ? null : IntegerData.forValue(l); + return (l == null) ? null : NumberData.forValue(l); } /** Returns a random integer between {@code 0} and the provided argument. */ @@ -373,29 +355,25 @@ public static NumberData round(SoyValue value, int numDigitsAfterPoint) { // NOTE: for more accurate rounding, this should really be using BigDecimal which can do correct // decimal arithmetic. However, for compatibility with js, that probably isn't an option. if (numDigitsAfterPoint == 0) { - return IntegerData.forValue(round(value)); + return NumberData.forValue(round(value)); } else if (numDigitsAfterPoint > 0) { double valueDouble = value.numberValue(); double shift = Math.pow(10, numDigitsAfterPoint); - return FloatData.forValue(Math.round(valueDouble * shift) / shift); + return NumberData.forValue(Math.round(valueDouble * shift) / shift); } else { double valueDouble = value.numberValue(); double shift = Math.pow(10, -numDigitsAfterPoint); - return IntegerData.forValue((int) (Math.round(valueDouble / shift) * shift)); + return NumberData.forValue((int) (Math.round(valueDouble / shift) * shift)); } } /** Rounds the given value to the closest integer. */ public static long round(SoyValue value) { - if (value instanceof IntegerData) { - return value.longValue(); - } else { - return Math.round(value.numberValue()); - } + return Math.round(value.floatValue()); } @Nonnull - public static List range(int start, int end, int step) { + public static List range(int start, int end, int step) { if (step == 0) { throw new IllegalArgumentException(String.format("step must be non-zero: %d", step)); } @@ -409,8 +387,8 @@ public static List range(int start, int end, int step) { return new AbstractList<>() { @Override - public IntegerData get(int index) { - return IntegerData.forValue(start + step * index); + public NumberData get(int index) { + return NumberData.forValue(start + step * index); } @Override diff --git a/java/src/com/google/template/soy/basicfunctions/ListFlatMethod.java b/java/src/com/google/template/soy/basicfunctions/ListFlatMethod.java index db199ab8af..ffef9251c0 100644 --- a/java/src/com/google/template/soy/basicfunctions/ListFlatMethod.java +++ b/java/src/com/google/template/soy/basicfunctions/ListFlatMethod.java @@ -16,7 +16,7 @@ package com.google.template.soy.basicfunctions; -import com.google.template.soy.data.restricted.IntegerData; +import com.google.template.soy.data.restricted.NumberData; import com.google.template.soy.plugin.java.restricted.JavaPluginContext; import com.google.template.soy.plugin.java.restricted.JavaValue; import com.google.template.soy.plugin.java.restricted.JavaValueFactory; @@ -78,7 +78,7 @@ private static final class Methods { JavaValueFactory.createMethod(BasicFunctionsRuntime.class, "listFlat", List.class); static final Method LIST_FLAT_ARG1_FN = JavaValueFactory.createMethod( - BasicFunctionsRuntime.class, "listFlat", List.class, IntegerData.class); + BasicFunctionsRuntime.class, "listFlat", List.class, NumberData.class); } @Override diff --git a/java/src/com/google/template/soy/data/ProtoFieldInterpreter.java b/java/src/com/google/template/soy/data/ProtoFieldInterpreter.java index cec9a160ea..cb8e046f1d 100644 --- a/java/src/com/google/template/soy/data/ProtoFieldInterpreter.java +++ b/java/src/com/google/template/soy/data/ProtoFieldInterpreter.java @@ -34,7 +34,6 @@ import com.google.protobuf.Descriptors.EnumDescriptor; import com.google.protobuf.Descriptors.EnumValueDescriptor; import com.google.protobuf.Descriptors.FieldDescriptor; -import com.google.protobuf.Descriptors.FileDescriptor.Syntax; import com.google.protobuf.DynamicMessage; import com.google.protobuf.Message; import com.google.protobuf.ProtocolMessageEnum; @@ -43,7 +42,7 @@ import com.google.template.soy.data.restricted.BooleanData; import com.google.template.soy.data.restricted.FloatData; import com.google.template.soy.data.restricted.GbigintData; -import com.google.template.soy.data.restricted.IntegerData; +import com.google.template.soy.data.restricted.NumberData; import com.google.template.soy.data.restricted.StringData; import com.google.template.soy.internal.proto.FieldVisitor; import com.google.template.soy.internal.proto.Int64ConversionMode; @@ -317,7 +316,7 @@ public Object protoFromSoy(SoyValue field) { new ProtoFieldInterpreter() { @Override public SoyValue soyFromProto(Object field) { - return IntegerData.forValue(((Number) field).longValue()); + return NumberData.forValue(((Number) field).longValue()); } @Override @@ -331,7 +330,7 @@ public Object protoFromSoy(SoyValue field) { new ProtoFieldInterpreter() { @Override public SoyValue soyFromProto(Object field) { - return IntegerData.forValue(UnsignedInts.toLong(((Number) field).intValue())); + return NumberData.forValue(UnsignedInts.toLong(((Number) field).intValue())); } @Override @@ -345,7 +344,7 @@ public Object protoFromSoy(SoyValue field) { new ProtoFieldInterpreter() { @Override public SoyValue soyFromProto(Object field) { - return IntegerData.forValue((Long) field); + return NumberData.forValue((Long) field); } @Override @@ -567,7 +566,7 @@ public SoyValue soyFromProto(Object field) { // ProtocolMessageEnum otherwise. Who knows why. value = ((EnumValueDescriptor) field).getNumber(); } - return IntegerData.forValue(value); + return NumberData.forValue(value); } @Override diff --git a/java/src/com/google/template/soy/data/SoyValue.java b/java/src/com/google/template/soy/data/SoyValue.java index 06150cae4f..59e2611124 100644 --- a/java/src/com/google/template/soy/data/SoyValue.java +++ b/java/src/com/google/template/soy/data/SoyValue.java @@ -261,16 +261,6 @@ public SoyValue checkNullishType(Class type) { return type.cast(this); } - /** A runtime type check for this boxed Soy value. */ - public SoyValue checkNullishInt() { - throw new ClassCastException(classCastErrorMessage(this, "int")); - } - - /** A runtime type check for this boxed Soy value. */ - public SoyValue checkNullishFloat() { - throw new ClassCastException(classCastErrorMessage(this, "float")); - } - /** A runtime type check for this boxed Soy value. */ public SoyValue checkNullishNumber() { throw new ClassCastException(classCastErrorMessage(this, "number")); diff --git a/java/src/com/google/template/soy/data/SoyValueConverter.java b/java/src/com/google/template/soy/data/SoyValueConverter.java index fabf5b5972..a6fb5f467f 100644 --- a/java/src/com/google/template/soy/data/SoyValueConverter.java +++ b/java/src/com/google/template/soy/data/SoyValueConverter.java @@ -85,11 +85,11 @@ private SoyValueConverter() { cheapConverterMap.put(String.class, StringData::forValue); cheapConverterMap.put(Boolean.class, BooleanData::forValue); cheapConverterMap.put(Integer.class, input -> IntegerData.forValue(input.longValue())); - cheapConverterMap.put(Long.class, IntegerData::forValue); + cheapConverterMap.put(Long.class, input -> IntegerData.forValue(input.longValue())); cheapConverterMap.put(BigInteger.class, GbigintData::forValue); cheapConverterMap.put(Float.class, input -> FloatData.forValue(input.doubleValue())); - cheapConverterMap.put(Double.class, FloatData::forValue); + cheapConverterMap.put(Double.class, input -> FloatData.forValue(input.doubleValue())); cheapConverterMap.put(Future.class, (f) -> new SoyFutureValueProvider(f, this::convert)); // Proto enum that was obtained via reflection (e.g. from SoyProtoValue) cheapConverterMap.put( diff --git a/java/src/com/google/template/soy/data/SoyValueUnconverter.java b/java/src/com/google/template/soy/data/SoyValueUnconverter.java index d59c70cb3b..bf0fa2ca0c 100644 --- a/java/src/com/google/template/soy/data/SoyValueUnconverter.java +++ b/java/src/com/google/template/soy/data/SoyValueUnconverter.java @@ -25,6 +25,7 @@ import com.google.template.soy.data.restricted.GbigintData; import com.google.template.soy.data.restricted.IntegerData; import com.google.template.soy.data.restricted.NullData; +import com.google.template.soy.data.restricted.NumberData; import com.google.template.soy.data.restricted.StringData; import com.google.template.soy.data.restricted.UndefinedData; import java.util.LinkedHashMap; @@ -46,6 +47,7 @@ private SoyValueUnconverter() {} CONVERTERS.put(BooleanData.class, BooleanData::getValue); CONVERTERS.put(IntegerData.class, IntegerData::getValue); CONVERTERS.put(FloatData.class, FloatData::getValue); + CONVERTERS.put(NumberData.class, NumberData::floatValue); CONVERTERS.put(StringData.class, StringData::getValue); CONVERTERS.put(GbigintData.class, GbigintData::getValue); CONVERTERS.put( diff --git a/java/src/com/google/template/soy/data/internal/DictImpl.java b/java/src/com/google/template/soy/data/internal/DictImpl.java index 5f8865c3fa..a451b3194d 100644 --- a/java/src/com/google/template/soy/data/internal/DictImpl.java +++ b/java/src/com/google/template/soy/data/internal/DictImpl.java @@ -236,8 +236,7 @@ private String getStringKey(SoyValue key) { return key.stringValue(); } catch (SoyDataException e) { throw new SoyDataException( - "SoyDict accessed with non-string key (got key type " + key.getClass().getName() + ").", - e); + "SoyDict accessed with non-string key (got key type " + key.getSoyTypeName() + ").", e); } } diff --git a/java/src/com/google/template/soy/data/internalutils/InternalValueUtils.java b/java/src/com/google/template/soy/data/internalutils/InternalValueUtils.java index 997531b6bf..9ef25b884f 100644 --- a/java/src/com/google/template/soy/data/internalutils/InternalValueUtils.java +++ b/java/src/com/google/template/soy/data/internalutils/InternalValueUtils.java @@ -23,17 +23,15 @@ import com.google.template.soy.data.SoyValue; import com.google.template.soy.data.SoyValueConverter; import com.google.template.soy.data.restricted.BooleanData; -import com.google.template.soy.data.restricted.FloatData; -import com.google.template.soy.data.restricted.IntegerData; import com.google.template.soy.data.restricted.NullData; +import com.google.template.soy.data.restricted.NumberData; import com.google.template.soy.data.restricted.PrimitiveData; import com.google.template.soy.data.restricted.StringData; import com.google.template.soy.data.restricted.UndefinedData; import com.google.template.soy.exprtree.BooleanNode; import com.google.template.soy.exprtree.ExprNode.PrimitiveNode; -import com.google.template.soy.exprtree.FloatNode; -import com.google.template.soy.exprtree.IntegerNode; import com.google.template.soy.exprtree.NullNode; +import com.google.template.soy.exprtree.NumberNode; import com.google.template.soy.exprtree.StringNode; import com.google.template.soy.exprtree.UndefinedNode; import java.util.Map; @@ -60,15 +58,8 @@ public static PrimitiveNode convertPrimitiveDataToExpr( return new StringNode(primitiveData.stringValue(), QuoteStyle.SINGLE, location); } else if (primitiveData instanceof BooleanData) { return new BooleanNode(primitiveData.booleanValue(), location); - } else if (primitiveData instanceof IntegerData) { - // NOTE: We only support numbers in the range of JS [MIN_SAFE_INTEGER, MAX_SAFE_INTEGER] - if (!IntegerNode.isInRange(primitiveData.longValue())) { - return null; - } else { - return new IntegerNode(primitiveData.longValue(), location); - } - } else if (primitiveData instanceof FloatData) { - return new FloatNode(primitiveData.floatValue(), location); + } else if (primitiveData instanceof NumberData) { + return new NumberNode(primitiveData.floatValue(), location); } else if (primitiveData instanceof NullData) { return new NullNode(location); } else if (primitiveData instanceof UndefinedData) { @@ -91,10 +82,8 @@ public static PrimitiveData convertPrimitiveExprToData(PrimitiveNode primitiveNo return StringData.forValue(((StringNode) primitiveNode).getValue()); } else if (primitiveNode instanceof BooleanNode) { return BooleanData.forValue(((BooleanNode) primitiveNode).getValue()); - } else if (primitiveNode instanceof IntegerNode) { - return IntegerData.forValue(((IntegerNode) primitiveNode).getValue()); - } else if (primitiveNode instanceof FloatNode) { - return FloatData.forValue(((FloatNode) primitiveNode).getValue()); + } else if (primitiveNode instanceof NumberNode) { + return NumberData.forValue(((NumberNode) primitiveNode).getValue()); } else if (primitiveNode instanceof NullNode) { return NullData.INSTANCE; } else if (primitiveNode instanceof UndefinedNode) { diff --git a/java/src/com/google/template/soy/data/restricted/FloatData.java b/java/src/com/google/template/soy/data/restricted/FloatData.java index 5db6f1e68c..7b36e4a466 100644 --- a/java/src/com/google/template/soy/data/restricted/FloatData.java +++ b/java/src/com/google/template/soy/data/restricted/FloatData.java @@ -17,18 +17,19 @@ package com.google.template.soy.data.restricted; import com.google.errorprone.annotations.Immutable; -import com.google.template.soy.data.SoyValue; import javax.annotation.Nonnull; /** Float data. */ @Immutable +@Deprecated public final class FloatData extends NumberData { - /** The float value. */ - private final double value; - private FloatData(double value) { - this.value = value; + super(value); + } + + public double getValue() { + return floatValue(); } /** @@ -41,74 +42,4 @@ private FloatData(double value) { public static FloatData forValue(double value) { return new FloatData(value); } - - /** Returns the float value. */ - public double getValue() { - return value; - } - - @Override - public double floatValue() { - return value; - } - - @Override - @Nonnull - public String toString() { - return toString(value); - } - - /** Returns Soy's idea of a double as a string. */ - public static String toString(double value) { - // This approximately consistent with Javascript for important cases. - // Reference: http://www.ecma-international.org/ecma-262/5.1/#sec-9.8.1 - if (value % 1 == 0 && Math.abs(value) < Long.MAX_VALUE) { - // The value is non-fractional and within the magnitude of a long, so print as an integer - // instead of scientific notation. Note that Javascript uses 1.0e19 as the cutoff, but - // Long.MAX_VALUE is not that far off (9.2e18), and it is both easy and efficient to coerce - // to a long. - return String.valueOf((long) value); - } else { - // Note: This differs from JS in how it rendered values that have a zero fractional component - // and in how it renders the value -0.0. - // JavaScript specifies that the string form of -0 is signless, and that the string form of - // fractionless numeric values has no decimal point. - return Double.toString(value).replace('E', 'e'); - } - } - - /** - * {@inheritDoc} - * - *

0.0 is falsy as is NaN. - */ - @Override - public boolean coerceToBoolean() { - return value != 0.0 && !Double.isNaN(value); - } - - @Override - public String coerceToString() { - return toString(); - } - - @Override - public double toFloat() { - return value; - } - - @Override - public Number javaNumberValue() { - return value; - } - - @Override - public SoyValue checkNullishFloat() { - return this; - } - - @Override - public String getSoyTypeName() { - return "float"; - } } diff --git a/java/src/com/google/template/soy/data/restricted/GbigintData.java b/java/src/com/google/template/soy/data/restricted/GbigintData.java index bfb6008c9e..3b847ca797 100644 --- a/java/src/com/google/template/soy/data/restricted/GbigintData.java +++ b/java/src/com/google/template/soy/data/restricted/GbigintData.java @@ -126,11 +126,6 @@ public boolean equals(Object other) { return false; } - @Override - public SoyValue checkNullishInt() { - return this; - } - @Override public String getSoyTypeName() { return "gbigint"; diff --git a/java/src/com/google/template/soy/data/restricted/IntegerData.java b/java/src/com/google/template/soy/data/restricted/IntegerData.java index 3a561af134..5d1dcbcf85 100644 --- a/java/src/com/google/template/soy/data/restricted/IntegerData.java +++ b/java/src/com/google/template/soy/data/restricted/IntegerData.java @@ -16,13 +16,12 @@ package com.google.template.soy.data.restricted; -import com.google.common.base.Preconditions; import com.google.errorprone.annotations.Immutable; -import com.google.template.soy.data.SoyValue; import javax.annotation.Nonnull; /** Integer data. */ @Immutable +@Deprecated public final class IntegerData extends NumberData { // Note: ZERO, ONE, and MINUS_ONE are public. The rest are private. @@ -63,11 +62,12 @@ public final class IntegerData extends NumberData { /** Static instance of IntegerData with value 10. */ private static final IntegerData TEN = new IntegerData(10); - /** The integer value. */ - private final long value; - private IntegerData(long value) { - this.value = value; + super(value); + } + + public long getValue() { + return (long) floatValue(); } /** @@ -110,76 +110,4 @@ public static IntegerData forValue(long value) { throw new AssertionError("Impossible case"); } } - - /** Returns the integer value. */ - public long getValue() { - return value; - } - - @Override - public int integerValue() { - Preconditions.checkState( - value >= Integer.MIN_VALUE && value <= Integer.MAX_VALUE, - "Casting long to integer results in overflow: %s", - value); - return (int) value; - } - - @Override - public long longValue() { - return value; - } - - @Override - @Nonnull - public String toString() { - return String.valueOf(value); - } - - /** - * {@inheritDoc} - * - *

0 is falsy. - */ - @Override - public boolean coerceToBoolean() { - return value != 0; - } - - @Override - public String coerceToString() { - return toString(); - } - - @Override - public double toFloat() { - return value; - } - - @Override - public Number javaNumberValue() { - return value; - } - - @Override - public boolean equals(Object other) { - if (!(other instanceof NumberData)) { - return false; - } - if (other instanceof IntegerData) { - return this.value == ((IntegerData) other).value; - } else { - return super.equals(other); - } - } - - @Override - public SoyValue checkNullishInt() { - return this; - } - - @Override - public String getSoyTypeName() { - return "int"; - } } diff --git a/java/src/com/google/template/soy/data/restricted/NullishData.java b/java/src/com/google/template/soy/data/restricted/NullishData.java index a073be0d7b..9c684a5eaf 100644 --- a/java/src/com/google/template/soy/data/restricted/NullishData.java +++ b/java/src/com/google/template/soy/data/restricted/NullishData.java @@ -61,16 +61,6 @@ public final SoyValue checkNullishType(Class type) { return this; } - @Override - public final SoyValue checkNullishInt() { - return this; - } - - @Override - public final SoyValue checkNullishFloat() { - return this; - } - @Override public final SoyValue checkNullishNumber() { return this; diff --git a/java/src/com/google/template/soy/data/restricted/NumberData.java b/java/src/com/google/template/soy/data/restricted/NumberData.java index 7f491b1815..7b5e802cea 100644 --- a/java/src/com/google/template/soy/data/restricted/NumberData.java +++ b/java/src/com/google/template/soy/data/restricted/NumberData.java @@ -17,11 +17,43 @@ package com.google.template.soy.data.restricted; import com.google.common.primitives.Longs; +import com.google.template.soy.base.internal.BaseUtils; import com.google.template.soy.data.SoyValue; import javax.annotation.Nonnull; /** Abstract superclass for number data (integers and floats). */ -public abstract class NumberData extends PrimitiveData { +public class NumberData extends PrimitiveData { + + /** The float value. */ + private final double value; + + protected NumberData(double value) { + this.value = value; + } + + /** + * Gets a FloatData instance for the given value. + * + * @param value The desired value. + * @return A FloatData instance with the given value. + */ + @Nonnull + public static NumberData forValue(double value) { + return new NumberData(value); + } + + public static NumberData forValue(Number value) { + return new NumberData(value.doubleValue()); + } + + @Override + public double floatValue() { + return value; + } + + public int integerValue() { + return coerceToInt(); + } /** * Gets the float value of this number data object. If this object is actually an integer, its @@ -29,10 +61,17 @@ public abstract class NumberData extends PrimitiveData { * * @return The float value of this number data object. */ - public abstract double toFloat(); + public double toFloat() { + return value; + } @Override public long coerceToLong() { + return longValue(); + } + + @Override + public long longValue() { return javaNumberValue().longValue(); } @@ -50,11 +89,13 @@ public double numberValue() { } @Nonnull - public abstract Number javaNumberValue(); + public Number javaNumberValue() { + return value; + } @Override public boolean equals(Object other) { - return other instanceof NumberData && ((NumberData) other).toFloat() == this.toFloat(); + return other instanceof NumberData && ((NumberData) other).value == this.value; } @Override @@ -63,7 +104,32 @@ public int hashCode() { } @Override - public final SoyValue checkNullishNumber() { + public SoyValue checkNullishNumber() { return this; } + + @Override + public String toString() { + return BaseUtils.formatDouble(value); + } + + /** + * {@inheritDoc} + * + *

0.0 is falsy as is NaN. + */ + @Override + public boolean coerceToBoolean() { + return value != 0.0 && !Double.isNaN(value); + } + + @Override + public String coerceToString() { + return toString(); + } + + @Override + public String getSoyTypeName() { + return "number"; + } } diff --git a/java/src/com/google/template/soy/exprtree/AbstractExprNodeVisitor.java b/java/src/com/google/template/soy/exprtree/AbstractExprNodeVisitor.java index 98786a1c98..6760969393 100644 --- a/java/src/com/google/template/soy/exprtree/AbstractExprNodeVisitor.java +++ b/java/src/com/google/template/soy/exprtree/AbstractExprNodeVisitor.java @@ -93,11 +93,8 @@ protected final void visit(ExprNode node) { case BOOLEAN_NODE: visitBooleanNode((BooleanNode) node); break; - case INTEGER_NODE: - visitIntegerNode((IntegerNode) node); - break; - case FLOAT_NODE: - visitFloatNode((FloatNode) node); + case NUMBER_NODE: + visitNumberNode((NumberNode) node); break; case STRING_NODE: visitStringNode((StringNode) node); @@ -292,11 +289,7 @@ protected void visitBooleanNode(BooleanNode node) { visitPrimitiveNode(node); } - protected void visitIntegerNode(IntegerNode node) { - visitPrimitiveNode(node); - } - - protected void visitFloatNode(FloatNode node) { + protected void visitNumberNode(NumberNode node) { visitPrimitiveNode(node); } diff --git a/java/src/com/google/template/soy/exprtree/AbstractReturningExprNodeVisitor.java b/java/src/com/google/template/soy/exprtree/AbstractReturningExprNodeVisitor.java index 3598c87c83..42ac604c91 100644 --- a/java/src/com/google/template/soy/exprtree/AbstractReturningExprNodeVisitor.java +++ b/java/src/com/google/template/soy/exprtree/AbstractReturningExprNodeVisitor.java @@ -91,10 +91,8 @@ protected R visit(ExprNode node) { return visitUndefinedNode((UndefinedNode) node); case BOOLEAN_NODE: return visitBooleanNode((BooleanNode) node); - case INTEGER_NODE: - return visitIntegerNode((IntegerNode) node); - case FLOAT_NODE: - return visitFloatNode((FloatNode) node); + case NUMBER_NODE: + return visitNumberNode((NumberNode) node); case STRING_NODE: return visitStringNode((StringNode) node); case PROTO_ENUM_VALUE_NODE: @@ -231,11 +229,7 @@ protected R visitBooleanNode(BooleanNode node) { return visitPrimitiveNode(node); } - protected R visitIntegerNode(IntegerNode node) { - return visitPrimitiveNode(node); - } - - protected R visitFloatNode(FloatNode node) { + protected R visitNumberNode(NumberNode node) { return visitPrimitiveNode(node); } diff --git a/java/src/com/google/template/soy/exprtree/ExprEquivalence.java b/java/src/com/google/template/soy/exprtree/ExprEquivalence.java index 91142f1585..a7f603ea25 100644 --- a/java/src/com/google/template/soy/exprtree/ExprEquivalence.java +++ b/java/src/com/google/template/soy/exprtree/ExprEquivalence.java @@ -21,7 +21,6 @@ import com.google.common.base.Equivalence; import com.google.common.primitives.Booleans; import com.google.common.primitives.Doubles; -import com.google.common.primitives.Longs; import com.google.template.soy.exprtree.ExprNode.CallableExpr.ParamsStyle; import com.google.template.soy.exprtree.ExprNode.OperatorNode; import com.google.template.soy.exprtree.ExprNode.ParentExprNode; @@ -167,12 +166,7 @@ protected Integer visitBooleanNode(BooleanNode node) { } @Override - protected Integer visitIntegerNode(IntegerNode node) { - return Longs.hashCode(node.getValue()); - } - - @Override - protected Integer visitFloatNode(FloatNode node) { + protected Integer visitNumberNode(NumberNode node) { return Doubles.hashCode(node.getValue()); } @@ -345,13 +339,8 @@ protected Boolean visitBooleanNode(BooleanNode node) { } @Override - protected Boolean visitIntegerNode(IntegerNode node) { - return node.getValue() == ((IntegerNode) other).getValue(); - } - - @Override - protected Boolean visitFloatNode(FloatNode node) { - return node.getValue() == ((FloatNode) other).getValue(); + protected Boolean visitNumberNode(NumberNode node) { + return node.getValue() == ((NumberNode) other).getValue(); } @Override diff --git a/java/src/com/google/template/soy/exprtree/ExprNode.java b/java/src/com/google/template/soy/exprtree/ExprNode.java index cc6bae3fcb..f0519283ab 100644 --- a/java/src/com/google/template/soy/exprtree/ExprNode.java +++ b/java/src/com/google/template/soy/exprtree/ExprNode.java @@ -45,8 +45,7 @@ enum Kind { NULL_NODE, UNDEFINED_NODE, BOOLEAN_NODE, - INTEGER_NODE, - FLOAT_NODE, + NUMBER_NODE, STRING_NODE, PROTO_ENUM_VALUE_NODE, TYPE_LITERAL_NODE, diff --git a/java/src/com/google/template/soy/exprtree/ExprNodes.java b/java/src/com/google/template/soy/exprtree/ExprNodes.java index 4822903e43..61f0a36ce5 100644 --- a/java/src/com/google/template/soy/exprtree/ExprNodes.java +++ b/java/src/com/google/template/soy/exprtree/ExprNodes.java @@ -46,9 +46,8 @@ public static boolean isNonNullishLiteral(ExprNode node) { case GLOBAL_NODE: case NULL_SAFE_ACCESS_NODE: return false; - // Actual literals - case FLOAT_NODE: - case INTEGER_NODE: + // Actual literals + case NUMBER_NODE: case STRING_NODE: case BOOLEAN_NODE: case MAP_LITERAL_NODE: @@ -81,10 +80,8 @@ public static boolean isNonNullishLiteral(ExprNode node) { public static boolean isNonFalsyLiteral(ExprNode node) { switch (node.getKind()) { - case INTEGER_NODE: - return ((IntegerNode) node).getValue() != 0; - case FLOAT_NODE: - return ((FloatNode) node).getValue() != 0.0; + case NUMBER_NODE: + return ((NumberNode) node).getValue() != 0.0; case BOOLEAN_NODE: return ((BooleanNode) node).getValue(); case STRING_NODE: diff --git a/java/src/com/google/template/soy/exprtree/FloatNode.java b/java/src/com/google/template/soy/exprtree/FloatNode.java deleted file mode 100644 index f8d99cedad..0000000000 --- a/java/src/com/google/template/soy/exprtree/FloatNode.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2008 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.template.soy.exprtree; - -import com.google.template.soy.base.SourceLocation; -import com.google.template.soy.basetree.CopyState; -import com.google.template.soy.types.FloatType; - -/** Node representing a float value. */ -public final class FloatNode extends AbstractPrimitiveNode { - - /** The float value */ - private final double value; - - /** - * @param value The float value. - * @param sourceLocation The node's source location. - */ - public FloatNode(double value, SourceLocation sourceLocation) { - super(sourceLocation); - this.value = value; - } - - /** - * Copy constructor. - * - * @param orig The node to copy. - */ - private FloatNode(FloatNode orig, CopyState copyState) { - super(orig, copyState); - this.value = orig.value; - } - - @Override - public Kind getKind() { - return Kind.FLOAT_NODE; - } - - @Override - public FloatType getAuthoredType() { - return FloatType.getInstance(); - } - - /** Returns the float value. */ - public double getValue() { - return value; - } - - @Override - public String toSourceString() { - return Double.toString(value).replace('E', 'e'); - } - - @Override - public FloatNode copy(CopyState copyState) { - return new FloatNode(this, copyState); - } -} diff --git a/java/src/com/google/template/soy/exprtree/IntegerNode.java b/java/src/com/google/template/soy/exprtree/NumberNode.java similarity index 62% rename from java/src/com/google/template/soy/exprtree/IntegerNode.java rename to java/src/com/google/template/soy/exprtree/NumberNode.java index c9cc8b21ce..d3ce67d1f3 100644 --- a/java/src/com/google/template/soy/exprtree/IntegerNode.java +++ b/java/src/com/google/template/soy/exprtree/NumberNode.java @@ -17,33 +17,26 @@ package com.google.template.soy.exprtree; import com.google.template.soy.base.SourceLocation; +import com.google.template.soy.base.internal.BaseUtils; import com.google.template.soy.basetree.CopyState; -import com.google.template.soy.types.IntType; +import com.google.template.soy.types.NumberType; -/** - * Node representing a Soy integer value. Note that Soy supports up to JavaScript - * +-Number.MAX_SAFE_INTEGER at the least; Java and Python backends support full 64 bit longs. - */ -public final class IntegerNode extends AbstractPrimitiveNode { +/** Node representing a float value. */ +public final class NumberNode extends AbstractPrimitiveNode { // JavaScript Number.MAX_SAFE_INTEGER: // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER private static final long JS_MAX_SAFE_INTEGER = (1L << 53) - 1; private static final long JS_MIN_SAFE_INTEGER = -1 * JS_MAX_SAFE_INTEGER; - /** Returns true if {@code value} is within JavaScript safe range. */ - public static boolean isInRange(long value) { - return JS_MIN_SAFE_INTEGER <= value && value <= JS_MAX_SAFE_INTEGER; - } - - /** The Soy integer value. */ - private final long value; + /** The float value */ + private final double value; /** - * @param value The Soy integer value. + * @param value The float value. * @param sourceLocation The node's source location. */ - public IntegerNode(long value, SourceLocation sourceLocation) { + public NumberNode(double value, SourceLocation sourceLocation) { super(sourceLocation); this.value = value; } @@ -53,38 +46,42 @@ public IntegerNode(long value, SourceLocation sourceLocation) { * * @param orig The node to copy. */ - private IntegerNode(IntegerNode orig, CopyState copyState) { + private NumberNode(NumberNode orig, CopyState copyState) { super(orig, copyState); this.value = orig.value; } + /** Returns true if {@code value} is within JavaScript safe range. */ + public static boolean isInRange(long value) { + return JS_MIN_SAFE_INTEGER <= value && value <= JS_MAX_SAFE_INTEGER; + } + @Override public Kind getKind() { - return Kind.INTEGER_NODE; + return Kind.NUMBER_NODE; } @Override - public IntType getAuthoredType() { - return IntType.getInstance(); + public NumberType getAuthoredType() { + return NumberType.getInstance(); } - /** Returns the Soy integer value. */ - public long getValue() { + /** Returns the float value. */ + public double getValue() { return value; } - /** Returns true if the value stored by the node is a 32-bit integer. */ - public boolean isInt() { - return Integer.MIN_VALUE <= value && value <= Integer.MAX_VALUE; + public boolean isInteger() { + return value % 1 == 0 && isInRange((long) value); } @Override public String toSourceString() { - return Long.toString(value); + return BaseUtils.formatDouble(value); } @Override - public IntegerNode copy(CopyState copyState) { - return new IntegerNode(this, copyState); + public NumberNode copy(CopyState copyState) { + return new NumberNode(this, copyState); } } diff --git a/java/src/com/google/template/soy/javagencode/javatypes/JavaTypeUtils.java b/java/src/com/google/template/soy/javagencode/javatypes/JavaTypeUtils.java index 481058cfb4..ed2023b79b 100644 --- a/java/src/com/google/template/soy/javagencode/javatypes/JavaTypeUtils.java +++ b/java/src/com/google/template/soy/javagencode/javatypes/JavaTypeUtils.java @@ -24,8 +24,6 @@ import com.google.common.collect.Iterables; import com.google.template.soy.types.AbstractIterableType; import com.google.template.soy.types.AbstractMapType; -import com.google.template.soy.types.FloatType; -import com.google.template.soy.types.IntType; import com.google.template.soy.types.RecordType; import com.google.template.soy.types.SoyProtoEnumType; import com.google.template.soy.types.SoyProtoType; @@ -67,11 +65,8 @@ public static ImmutableList getJavaTypes( case BOOL: types = ImmutableList.of(SimpleJavaType.BOOLEAN); break; - case INT: - types = ImmutableList.of(SimpleJavaType.INT); - break; - case FLOAT: - types = ImmutableList.of(SimpleJavaType.FLOAT); + case NUMBER: + types = ImmutableList.of(SimpleJavaType.NUMBER); break; case STRING: types = ImmutableList.of(SimpleJavaType.STRING); @@ -268,15 +263,6 @@ public static Optional upcastTypesForIndirectParams(Set allTyp */ private static ImmutableList convertSoyUnionTypeToJavaTypes( UnionType unionType, Set skipSoyTypes) { - if (SoyTypes.isNullish(unionType) - && SoyTypes.tryRemoveNullish(unionType).equals(SoyTypes.NUMBER_TYPE)) { - return ImmutableList.of(SimpleJavaType.NUMBER.asNullable()); - } - - if (unionType.equals(UnionType.of(IntType.getInstance(), FloatType.getInstance()))) { - return ImmutableList.of(SimpleJavaType.NUMBER); - } - // Figure out if the union contains the {@link NullType}, which tells us if the param setters // should be nullable. boolean unionAllowsNull = unionType.getMembers().stream().anyMatch(SoyType::isNullOrUndefined); diff --git a/java/src/com/google/template/soy/javagencode/javatypes/SimpleJavaType.java b/java/src/com/google/template/soy/javagencode/javatypes/SimpleJavaType.java index af477c4bae..1dcc5b931a 100644 --- a/java/src/com/google/template/soy/javagencode/javatypes/SimpleJavaType.java +++ b/java/src/com/google/template/soy/javagencode/javatypes/SimpleJavaType.java @@ -43,26 +43,6 @@ public class SimpleJavaType extends JavaType { castFunction("asBool"), castFunction("asNullableBool")); - public static final SimpleJavaType INT = - new PrimitiveJavaType( - /* boxedType= */ "java.lang.Long", - /* primitiveType= */ "long", - /* genericType= */ "? extends java.lang.Number", - /* isNullable= */ false, - castFunction("asInt"), - castFunction("asBoxedInt"), - castFunction("asNullableInt")); - - public static final SimpleJavaType FLOAT = - new PrimitiveJavaType( - /* boxedType= */ "java.lang.Double", - /* primitiveType= */ "double", - /* genericType= */ "? extends java.lang.Number", - /* isNullable= */ false, - castFunction("asFloat"), - castFunction("asBoxedFloat"), - castFunction("asNullableFloat")); - public static final SimpleJavaType NUMBER = new SimpleJavaType( "java.lang.Number", diff --git a/java/src/com/google/template/soy/jbcsrc/EnhancedAbstractExprNodeVisitor.java b/java/src/com/google/template/soy/jbcsrc/EnhancedAbstractExprNodeVisitor.java index 9762759441..77a5c3d6f2 100644 --- a/java/src/com/google/template/soy/jbcsrc/EnhancedAbstractExprNodeVisitor.java +++ b/java/src/com/google/template/soy/jbcsrc/EnhancedAbstractExprNodeVisitor.java @@ -97,8 +97,6 @@ protected final T visitFunctionNode(FunctionNode node) { return visitXidFunction(node); case IS_PRIMARY_MSG_IN_USE: return visitIsPrimaryMsgInUse(node); - case TO_FLOAT: - return visitToFloatFunction(node); case DEBUG_SOY_TEMPLATE_INFO: return visitDebugSoyTemplateInfoFunction(node); case VE_DATA: @@ -186,10 +184,6 @@ T visitIsPrimaryMsgInUse(FunctionNode node) { return visitExprNode(node); } - T visitToFloatFunction(FunctionNode node) { - return visitExprNode(node); - } - T visitDebugSoyTemplateInfoFunction(FunctionNode node) { return visitExprNode(node); } diff --git a/java/src/com/google/template/soy/jbcsrc/ExpressionCompiler.java b/java/src/com/google/template/soy/jbcsrc/ExpressionCompiler.java index 4f9a9a2ddf..09f518c49b 100644 --- a/java/src/com/google/template/soy/jbcsrc/ExpressionCompiler.java +++ b/java/src/com/google/template/soy/jbcsrc/ExpressionCompiler.java @@ -45,12 +45,10 @@ import com.google.template.soy.exprtree.ExprNodes; import com.google.template.soy.exprtree.ExprRootNode; import com.google.template.soy.exprtree.FieldAccessNode; -import com.google.template.soy.exprtree.FloatNode; import com.google.template.soy.exprtree.FunctionNode; import com.google.template.soy.exprtree.FunctionNode.ExternRef; import com.google.template.soy.exprtree.GlobalNode; import com.google.template.soy.exprtree.GroupNode; -import com.google.template.soy.exprtree.IntegerNode; import com.google.template.soy.exprtree.ItemAccessNode; import com.google.template.soy.exprtree.ListComprehensionNode; import com.google.template.soy.exprtree.ListComprehensionNode.ComprehensionVarDefn; @@ -60,6 +58,7 @@ import com.google.template.soy.exprtree.MethodCallNode; import com.google.template.soy.exprtree.NullNode; import com.google.template.soy.exprtree.NullSafeAccessNode; +import com.google.template.soy.exprtree.NumberNode; import com.google.template.soy.exprtree.OperatorNodes.AmpAmpOpNode; import com.google.template.soy.exprtree.OperatorNodes.AsOpNode; import com.google.template.soy.exprtree.OperatorNodes.AssertNonNullOpNode; @@ -130,8 +129,8 @@ import com.google.template.soy.soytree.defn.ImportedVar; import com.google.template.soy.soytree.defn.LocalVar; import com.google.template.soy.soytree.defn.TemplateParam; -import com.google.template.soy.types.FloatType; import com.google.template.soy.types.ListType; +import com.google.template.soy.types.NumberType; import com.google.template.soy.types.SetType; import com.google.template.soy.types.SoyProtoType; import com.google.template.soy.types.SoyType; @@ -467,7 +466,7 @@ protected SoyExpression visitUndefinedNode(UndefinedNode node) { } @Override - protected SoyExpression visitFloatNode(FloatNode node) { + protected SoyExpression visitNumberNode(NumberNode node) { return SoyExpression.forFloat(constant(node.getValue())); } @@ -478,7 +477,7 @@ protected SoyExpression visitStringNode(StringNode node) { @Override protected SoyExpression visitProtoEnumValueNode(ProtoEnumValueNode node) { - return SoyExpression.forInt(BytecodeUtils.constant(node.getValue())); + return SoyExpression.forFloat(BytecodeUtils.constant((double) node.getValue())); } @Override @@ -486,11 +485,6 @@ protected SoyExpression visitBooleanNode(BooleanNode node) { return node.getValue() ? SoyExpression.TRUE : SoyExpression.FALSE; } - @Override - protected SoyExpression visitIntegerNode(IntegerNode node) { - return SoyExpression.forInt(BytecodeUtils.constant(node.getValue())); - } - @Override protected SoyExpression visitAsOpNode(AsOpNode node) { return SoyExpression.forSoyValue( @@ -842,10 +836,6 @@ protected SoyExpression visitTripleNotEqualOpNode(TripleNotEqualOpNode node) { protected SoyExpression visitLessThanOpNode(LessThanOpNode node) { SoyExpression left = visit(node.getChild(0)); SoyExpression right = visit(node.getChild(1)); - if (left.isRuntimeInt() && right.isRuntimeInt()) { - return SoyExpression.forBool( - Branch.compare(Opcodes.IFLT, left.unboxAsLong(), right.unboxAsLong()).asBoolean()); - } if (left.isRuntimeNumber() && right.isRuntimeNumber()) { return SoyExpression.forBool( Branch.compare(Opcodes.IFLT, left.coerceToDouble(), right.coerceToDouble()) @@ -861,10 +851,6 @@ protected SoyExpression visitLessThanOpNode(LessThanOpNode node) { protected SoyExpression visitGreaterThanOpNode(GreaterThanOpNode node) { SoyExpression left = visit(node.getChild(0)); SoyExpression right = visit(node.getChild(1)); - if (left.isRuntimeInt() && right.isRuntimeInt()) { - return SoyExpression.forBool( - Branch.compare(Opcodes.IFGT, left.unboxAsLong(), right.unboxAsLong()).asBoolean()); - } if (left.isRuntimeNumber() && right.isRuntimeNumber()) { return SoyExpression.forBool( Branch.compare(Opcodes.IFGT, left.coerceToDouble(), right.coerceToDouble()) @@ -881,10 +867,6 @@ protected SoyExpression visitGreaterThanOpNode(GreaterThanOpNode node) { protected SoyExpression visitLessThanOrEqualOpNode(LessThanOrEqualOpNode node) { SoyExpression left = visit(node.getChild(0)); SoyExpression right = visit(node.getChild(1)); - if (left.isRuntimeInt() && right.isRuntimeInt()) { - return SoyExpression.forBool( - Branch.compare(Opcodes.IFLE, left.unboxAsLong(), right.unboxAsLong()).asBoolean()); - } if (left.isRuntimeNumber() && right.isRuntimeNumber()) { return SoyExpression.forBool( Branch.compare(Opcodes.IFLE, left.coerceToDouble(), right.coerceToDouble()) @@ -901,10 +883,6 @@ protected SoyExpression visitLessThanOrEqualOpNode(LessThanOrEqualOpNode node) { protected SoyExpression visitGreaterThanOrEqualOpNode(GreaterThanOrEqualOpNode node) { SoyExpression left = visit(node.getChild(0)); SoyExpression right = visit(node.getChild(1)); - if (left.isRuntimeInt() && right.isRuntimeInt()) { - return SoyExpression.forBool( - Branch.compare(Opcodes.IFGE, left.unboxAsLong(), right.unboxAsLong()).asBoolean()); - } if (left.isRuntimeNumber() && right.isRuntimeNumber()) { return SoyExpression.forBool( Branch.compare(Opcodes.IFGE, left.coerceToDouble(), right.coerceToDouble()) @@ -941,11 +919,8 @@ protected SoyExpression visitPlusOpNode(PlusOpNode node) { SoyExpression right = visit(node.getChild(1)); // They are both definitely numbers if (left.isRuntimeNumber() && right.isRuntimeNumber()) { - if (left.isRuntimeInt() && right.isRuntimeInt()) { - return applyBinaryIntOperator(Opcodes.LADD, left, right); - } // if either is definitely a float, then we are definitely coercing so just do it now - if (left.isRuntimeFloat() || right.isRuntimeFloat()) { + if (left.isRuntimeNumber() || right.isRuntimeNumber()) { return applyBinaryFloatOperator(Opcodes.DADD, left, right); } } @@ -957,7 +932,7 @@ protected SoyExpression visitPlusOpNode(PlusOpNode node) { leftString.invoke(MethodRefs.STRING_CONCAT, rightString).toMaybeConstant()); } return SoyExpression.forSoyValue( - SoyTypes.NUMBER_TYPE, MethodRefs.RUNTIME_PLUS.invoke(left.box(), right.box())); + NumberType.getInstance(), MethodRefs.RUNTIME_PLUS.invoke(left.box(), right.box())); } private SoyExpression visitBinaryOperator( @@ -966,16 +941,13 @@ private SoyExpression visitBinaryOperator( SoyExpression right = visit(node.getChild(1)); // They are both definitely numbers if (left.isRuntimeNumber() && right.isRuntimeNumber()) { - if (left.isRuntimeInt() && right.isRuntimeInt()) { - return applyBinaryIntOperator(longOpcode, left, right); - } // if either is definitely a float, then we are definitely coercing so just do it now - if (left.isRuntimeFloat() || right.isRuntimeFloat()) { + if (left.isRuntimeNumber() || right.isRuntimeNumber()) { return applyBinaryFloatOperator(doubleOpcode, left, right); } } return SoyExpression.forSoyValue( - SoyTypes.NUMBER_TYPE, runtimeMethod.invoke(left.box(), right.box())); + NumberType.getInstance(), runtimeMethod.invoke(left.box(), right.box())); } @Override @@ -998,7 +970,7 @@ protected SoyExpression visitDivideByOpNode(DivideByOpNode node) { return applyBinaryFloatOperator(Opcodes.DDIV, left, right); } return SoyExpression.forSoyValue( - FloatType.getInstance(), MethodRefs.RUNTIME_DIVIDED_BY.invoke(left.box(), right.box())); + NumberType.getInstance(), MethodRefs.RUNTIME_DIVIDED_BY.invoke(left.box(), right.box())); } @Override @@ -1042,38 +1014,24 @@ private SoyExpression applyBitwiseIntOperator( SoyExpression lhe = visit(node.getChild(0)); SoyExpression rhe = visit(node.getChild(1)); - if (lhe.isRuntimeInt() && rhe.isRuntimeInt()) { - Expression left = lhe.unboxAsLong(); + if (lhe.isRuntimeNumber() && rhe.isRuntimeNumber()) { + Expression left = numericConversion(lhe.unboxAsDouble(), Type.LONG_TYPE); // Shift operators require INT on right side. - Expression right = numericConversion(rhe.unboxAsLong(), rht); - return SoyExpression.forInt( - new Expression(Type.LONG_TYPE) { + Expression right = numericConversion(rhe.unboxAsDouble(), rht); + return SoyExpression.forFloat( + new Expression(Type.DOUBLE_TYPE) { @Override protected void doGen(CodeBuilder mv) { left.gen(mv); right.gen(mv); mv.visitInsn(operator); + mv.cast(Type.LONG_TYPE, Type.DOUBLE_TYPE); } }); } return SoyExpression.forSoyValue( - SoyTypes.NUMBER_TYPE, runtimeMethod.invoke(lhe.box(), rhe.box())); - } - - private static SoyExpression applyBinaryIntOperator( - int operator, SoyExpression left, SoyExpression right) { - SoyExpression leftInt = left.unboxAsLong(); - SoyExpression rightInt = right.unboxAsLong(); - return SoyExpression.forInt( - new Expression(Type.LONG_TYPE) { - @Override - protected void doGen(CodeBuilder mv) { - leftInt.gen(mv); - rightInt.gen(mv); - mv.visitInsn(operator); - } - }); + NumberType.getInstance(), runtimeMethod.invoke(lhe.box(), rhe.box())); } private static SoyExpression applyBinaryFloatOperator( @@ -1096,18 +1054,7 @@ protected void doGen(CodeBuilder mv) { @Override protected SoyExpression visitNegativeOpNode(NegativeOpNode node) { SoyExpression child = visit(node.getChild(0)); - if (child.isRuntimeInt()) { - SoyExpression intExpr = child.unboxAsLong(); - return SoyExpression.forInt( - new Expression(Type.LONG_TYPE, child.features()) { - @Override - protected void doGen(CodeBuilder mv) { - intExpr.gen(mv); - mv.visitInsn(Opcodes.LNEG); - } - }); - } - if (child.isRuntimeFloat()) { + if (child.isRuntimeNumber()) { SoyExpression floatExpr = child.unboxAsDouble(); return SoyExpression.forFloat( new Expression(Type.DOUBLE_TYPE, child.features()) { @@ -1119,7 +1066,7 @@ protected void doGen(CodeBuilder mv) { }); } return SoyExpression.forSoyValue( - SoyTypes.NUMBER_TYPE, MethodRefs.RUNTIME_NEGATIVE.invoke(child.box())); + NumberType.getInstance(), MethodRefs.RUNTIME_NEGATIVE.invoke(child.box())); } @Override @@ -1270,9 +1217,9 @@ private SoyExpression processConditionalOp( @Override SoyExpression visitForLoopVar(VarRefNode varRef, LocalVar local) { Expression expression = parameters.getLocal(local); - if (expression.resultType().equals(Type.INT_TYPE)) { + if (BytecodeUtils.isNumericPrimitive(expression.resultType())) { // The optional index var is an int. - return SoyExpression.forInt(numericConversion(expression, Type.LONG_TYPE)); + return SoyExpression.forFloat(numericConversion(expression, Type.DOUBLE_TYPE)); } return resolveVarRefNode(varRef, expression); } @@ -1375,7 +1322,8 @@ SoyExpression visitLetNodeVar(VarRefNode varRef, LocalVar local) { SoyExpression visitListComprehensionVar(VarRefNode varRef, ComprehensionVarDefn var) { // Index vars are always simple ints if (var.declaringNode().getIndexVar() == var) { - return SoyExpression.forInt(numericConversion(parameters.getLocal(var), Type.LONG_TYPE)); + return SoyExpression.forFloat( + numericConversion(parameters.getLocal(var), Type.DOUBLE_TYPE)); } return resolveVarRefNode(varRef, parameters.getLocal(var)); } @@ -1532,7 +1480,7 @@ private SoyExpression visitItemAccess(SoyExpression baseExpr, ItemAccessNode nod // optimized the same way because there is no real way to 'unbox' a SoyLegacyObjectMap. if (baseExpr.soyRuntimeType().isKnownListOrUnionOfLists()) { SoyExpression list = baseExpr.unboxAsListUnchecked(); - SoyExpression index = keyExpr.unboxAsLong(); + SoyExpression index = keyExpr.unboxAsDouble(); if (analysis.isResolved(node)) { soyValueProvider = MethodRefs.RUNTIME_GET_LIST_ITEM.invoke(list, index); } else { @@ -1792,14 +1740,8 @@ SoyExpression visitIsPrimaryMsgInUse(FunctionNode node) { parameters .getRenderContext() .usePrimaryMsgIfFallback( - ((IntegerNode) node.getParam(1)).getValue(), - ((IntegerNode) node.getParam(2)).getValue())); - } - - @Override - SoyExpression visitToFloatFunction(FunctionNode node) { - SoyExpression arg = visit(node.getParam(0)); - return SoyExpression.forFloat(numericConversion(arg.unboxAsLong(), Type.DOUBLE_TYPE)); + Long.parseLong(((StringNode) node.getParam(1)).getValue()), + Long.parseLong(((StringNode) node.getParam(2)).getValue()))); } @Override @@ -1971,7 +1913,7 @@ private static Expression adaptExternArg(SoyExpression soyExpression, SoyType ty if (javaType.equals(Type.BOOLEAN_TYPE)) { return soyExpression.coerceToBoolean().unboxAsBoolean(); } else if (javaType.equals(Type.LONG_TYPE)) { - return soyExpression.unboxAsLong(); + return numericConversion(soyExpression, Type.LONG_TYPE); } else if (javaType.equals(BytecodeUtils.STRING_TYPE)) { return soyExpression.unboxAsStringOrJavaNull(); } else if (javaType.equals(Type.DOUBLE_TYPE)) { @@ -1988,7 +1930,7 @@ private static Expression adaptExternArg(SoyExpression soyExpression, SoyType ty return soyExpression.unboxAsMessageOrJavaNull(BytecodeUtils.MESSAGE_TYPE); } else if (type.getKind() == Kind.PROTO_ENUM) { // TODO(b/217186858): support nullable proto enum parameters. - return soyExpression.unboxAsLong(); + return numericConversion(soyExpression, Type.LONG_TYPE); } else if (nonNullableType.getKind() == Kind.LIST) { return soyExpression.unboxAsListOrJavaNull(); } else if (nonNullableType.getKind() == Kind.ITERABLE) { @@ -2012,7 +1954,7 @@ protected SoyExpression visitProtoInitFunction(FunctionNode node) { @Override protected SoyExpression visitVeDefNode(FunctionNode node) { - Expression id = constant(((IntegerNode) node.getParam(1)).getValue()); + Expression id = constant((long) ((NumberNode) node.getParam(1)).getValue()); Expression name = constant(((StringNode) node.getParam(0)).getValue()); Expression visualElement; if (node.numParams() == 4) { @@ -2213,8 +2155,7 @@ && visit(node.getParam(1)) } if (function == BuiltinFunction.PROTO_INIT || function == BuiltinFunction.VE_DATA - || function == BuiltinFunction.CHECK_NOT_NULL - || function == BuiltinFunction.TO_FLOAT) { + || function == BuiltinFunction.CHECK_NOT_NULL) { // All of these are either constructing a data structure or performing some kind of simple // coercion. return true; diff --git a/java/src/com/google/template/soy/jbcsrc/ExpressionToSoyValueProviderCompiler.java b/java/src/com/google/template/soy/jbcsrc/ExpressionToSoyValueProviderCompiler.java index bc9584e229..303a079df8 100644 --- a/java/src/com/google/template/soy/jbcsrc/ExpressionToSoyValueProviderCompiler.java +++ b/java/src/com/google/template/soy/jbcsrc/ExpressionToSoyValueProviderCompiler.java @@ -264,11 +264,11 @@ && isDefinitelyAssignableFrom( @Override Optional visitForLoopVar(VarRefNode varRef, LocalVar local) { Expression loopVar = variables.getLocal(local); - if (loopVar.resultType().equals(Type.INT_TYPE)) { + if (BytecodeUtils.isNumericPrimitive(loopVar.resultType())) { // The optional index var is an int. if (allowsBoxing()) { return Optional.of( - SoyExpression.forInt(numericConversion(loopVar, Type.LONG_TYPE)).box()); + SoyExpression.forFloat(numericConversion(loopVar, Type.DOUBLE_TYPE)).box()); } return Optional.empty(); } else { diff --git a/java/src/com/google/template/soy/jbcsrc/ExternCompiler.java b/java/src/com/google/template/soy/jbcsrc/ExternCompiler.java index 56e9aa11c0..c2c6b561cc 100644 --- a/java/src/com/google/template/soy/jbcsrc/ExternCompiler.java +++ b/java/src/com/google/template/soy/jbcsrc/ExternCompiler.java @@ -17,7 +17,11 @@ package com.google.template.soy.jbcsrc; import static com.google.common.base.Preconditions.checkState; +import static com.google.template.soy.jbcsrc.restricted.BytecodeUtils.NUMBER_TYPE; +import static com.google.template.soy.jbcsrc.restricted.BytecodeUtils.isDefinitelyAssignableFrom; +import static com.google.template.soy.jbcsrc.restricted.BytecodeUtils.isNumericPrimitive; import static com.google.template.soy.jbcsrc.restricted.BytecodeUtils.newLabel; +import static com.google.template.soy.jbcsrc.restricted.BytecodeUtils.numericConversion; import static java.util.Arrays.stream; import com.google.common.collect.ImmutableList; @@ -343,12 +347,12 @@ private static Expression adaptParameter( // int needs special-casing for overflow, and because we can't unbox as int if (javaType.equals(Type.INT_TYPE)) { - return JbcSrcExternRuntime.LONG_TO_INT.invoke(actualParam); + return JbcSrcExternRuntime.DOUBLE_TO_INT.invoke(actualParam); } else if (javaType.equals(BytecodeUtils.BOXED_INTEGER_TYPE)) { if (soyTypeBoxed) { return JbcSrcExternRuntime.SOY_VALUE_TO_BOXED_INTEGER.invoke(actualParam); } - return MethodRefs.BOX_INTEGER.invoke(JbcSrcExternRuntime.LONG_TO_INT.invoke(actualParam)); + return MethodRefs.BOX_INTEGER.invoke(JbcSrcExternRuntime.DOUBLE_TO_INT.invoke(actualParam)); } else if (javaType.equals(Type.DOUBLE_TYPE)) { return actualParam.coerceToDouble(); } else if (javaType.equals(BytecodeUtils.BOXED_DOUBLE_TYPE)) { @@ -357,13 +361,13 @@ private static Expression adaptParameter( } return MethodRefs.BOX_DOUBLE.invoke(actualParam.coerceToDouble()); } else if (javaType.equals(Type.FLOAT_TYPE)) { - return BytecodeUtils.numericConversion(actualParam.coerceToDouble(), Type.FLOAT_TYPE); + return numericConversion(actualParam.coerceToDouble(), Type.FLOAT_TYPE); } else if (javaType.equals(BytecodeUtils.BOXED_FLOAT_TYPE)) { if (soyTypeBoxed) { return JbcSrcExternRuntime.SOY_VALUE_TO_BOXED_FLOAT.invoke(actualParam); } return MethodRefs.BOX_FLOAT.invoke( - BytecodeUtils.numericConversion(actualParam.coerceToDouble(), Type.FLOAT_TYPE)); + numericConversion(actualParam.coerceToDouble(), Type.FLOAT_TYPE)); } else if (javaType.equals(BytecodeUtils.NUMBER_TYPE)) { return actualParam.unboxAsNumberOrJavaNull(); } else if (javaType.equals(Type.getType(BigInteger.class))) { @@ -395,7 +399,7 @@ private static Expression adaptParameter( javaTypeInfo, new Method("forNumber", javaType, new Type[] {Type.INT_TYPE}), MethodPureness.PURE) - .invoke(BytecodeUtils.numericConversion(actualParam.unboxAsLong(), Type.INT_TYPE)); + .invoke(numericConversion(actualParam.unboxAsDouble(), Type.INT_TYPE)); } if (javaType.equals(BytecodeUtils.SAFE_URL_TYPE)) { @@ -420,12 +424,13 @@ private static Expression adaptParameter( } return MethodRefs.BOX_BOOLEAN.invoke(actualParam.unboxAsBoolean()); } else if (javaType.equals(Type.LONG_TYPE)) { - return actualParam.unboxAsLong(); + return numericConversion(actualParam.unboxAsDouble(), Type.LONG_TYPE); } else if (javaType.equals(BytecodeUtils.BOXED_LONG_TYPE)) { if (soyTypeBoxed) { return JbcSrcExternRuntime.SOY_VALUE_TO_BOXED_LONG.invoke(actualParam); } - return MethodRefs.BOX_LONG.invoke(actualParam.unboxAsLong()); + return MethodRefs.BOX_LONG.invoke( + numericConversion(actualParam.unboxAsDouble(), Type.LONG_TYPE)); } else if (javaType.equals(BytecodeUtils.STRING_TYPE)) { return actualParam.unboxAsStringOrJavaNull(); } else if (!isObject @@ -434,9 +439,7 @@ private static Expression adaptParameter( SoyExpression unboxedList = actualParam.isBoxed() ? actualParam.unboxAsListOrJavaNull() : actualParam; switch (elmType.getKind()) { - case INT: - return JbcSrcExternRuntime.LIST_UNBOX_INTS.invoke(unboxedList); - case FLOAT: + case NUMBER: return JbcSrcExternRuntime.LIST_UNBOX_FLOATS.invoke(unboxedList); case STRING: return JbcSrcExternRuntime.LIST_UNBOX_STRINGS.invoke(unboxedList); @@ -450,11 +453,6 @@ private static Expression adaptParameter( JavaQualifiedNames.getClassName(((SoyProtoEnumType) elmType).getDescriptor()); return JbcSrcExternRuntime.LIST_UNBOX_ENUMS.invoke( unboxedList, BytecodeUtils.constant(BytecodeUtils.getTypeForClassName(javaClass))); - case UNION: - if (SoyTypes.NUMBER_TYPE.equals(elmType)) { - return JbcSrcExternRuntime.LIST_UNBOX_NUMBERS.invoke(unboxedList); - } - // fall through default: return actualParam.isBoxed() ? JbcSrcExternRuntime.UNBOX_OBJECT.invoke(actualParam) @@ -525,22 +523,14 @@ private static Expression adaptReturnType( return JbcSrcExternRuntime.CONVERT_OBJECT_TO_SOY_VALUE_PROVIDER.invoke(externCall); } - if (!nullish && externType.equals(BytecodeUtils.BOXED_INTEGER_TYPE)) { - return JbcSrcExternRuntime.UNBOX_INTEGER.invoke(externCall); - } else if (externType.equals(Type.INT_TYPE)) { - return BytecodeUtils.numericConversion(externCall, Type.LONG_TYPE); - } else if (!nullish && externType.equals(BytecodeUtils.BOXED_LONG_TYPE)) { - return JbcSrcExternRuntime.UNBOX_LONG.invoke(externCall); - } else if (!nullish && externType.equals(BytecodeUtils.BOXED_DOUBLE_TYPE)) { - return JbcSrcExternRuntime.UNBOX_DOUBLE.invoke(externCall); - } else if (externType.equals(Type.FLOAT_TYPE)) { - return BytecodeUtils.numericConversion(externCall, Type.DOUBLE_TYPE); - } else if (!nullish && externType.equals(BytecodeUtils.BOXED_FLOAT_TYPE)) { - return JbcSrcExternRuntime.UNBOX_FLOAT.invoke(externCall); - } else if (!nullish && externType.equals(BytecodeUtils.BOXED_BOOLEAN_TYPE)) { - return JbcSrcExternRuntime.UNBOX_BOOLEAN.invoke(externCall); + if (isNumericPrimitive(externType)) { + return numericConversion(externCall, Type.DOUBLE_TYPE); } else if (!nullish && externType.equals(BytecodeUtils.BIG_INTEGER_TYPE)) { return JbcSrcExternRuntime.GBIGINT_FOR_VALUE.invoke(externCall); + } else if (!nullish && isDefinitelyAssignableFrom(NUMBER_TYPE, externType)) { + return JbcSrcExternRuntime.UNBOX_NUMBER.invoke(externCall); + } else if (!nullish && externType.equals(BytecodeUtils.BOXED_BOOLEAN_TYPE)) { + return JbcSrcExternRuntime.UNBOX_BOOLEAN.invoke(externCall); } else if (externType.equals(BytecodeUtils.OBJECT.type()) || externType.equals(BytecodeUtils.NUMBER_TYPE)) { checkState(soyReturnType.getKind() != SoyType.Kind.MAP); @@ -573,8 +563,8 @@ private static Expression adaptReturnType( return JbcSrcExternRuntime.CONVERT_TRUSTED_RESOURCE_URL_TO_SOY_VALUE_PROVIDER.invoke( externCall); } else if (soyReturnType.getKind() == SoyType.Kind.PROTO_ENUM) { - return BytecodeUtils.numericConversion( - MethodRefs.PROTOCOL_ENUM_GET_NUMBER.invoke(externCall), Type.LONG_TYPE); + return numericConversion( + MethodRefs.PROTOCOL_ENUM_GET_NUMBER.invoke(externCall), Type.DOUBLE_TYPE); } else if (BytecodeUtils.SOY_VALUE_TYPE.equals(getRuntimeType(soyReturnType).runtimeType())) { // If the Soy return type of the extern is SoyValue, then we need to make sure the value // returned from the implementation is boxed. diff --git a/java/src/com/google/template/soy/jbcsrc/JbcSrcValueFactory.java b/java/src/com/google/template/soy/jbcsrc/JbcSrcValueFactory.java index 4d451b9c5b..a88668b49a 100644 --- a/java/src/com/google/template/soy/jbcsrc/JbcSrcValueFactory.java +++ b/java/src/com/google/template/soy/jbcsrc/JbcSrcValueFactory.java @@ -19,6 +19,7 @@ import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.template.soy.jbcsrc.restricted.BytecodeUtils.newLabel; +import static com.google.template.soy.jbcsrc.restricted.BytecodeUtils.numericConversion; import static com.google.template.soy.jbcsrc.runtime.JbcSrcPluginRuntime.BOX_JAVA_MAP_AS_SOY_LEGACY_OBJECT_MAP; import static com.google.template.soy.jbcsrc.runtime.JbcSrcPluginRuntime.BOX_JAVA_MAP_AS_SOY_MAP; import static com.google.template.soy.jbcsrc.runtime.JbcSrcPluginRuntime.BOX_JAVA_MAP_AS_SOY_RECORD; @@ -26,6 +27,8 @@ import static com.google.template.soy.jbcsrc.runtime.JbcSrcPluginRuntime.JAVA_NULL_TO_SOY_NULL; import static com.google.template.soy.jbcsrc.runtime.JbcSrcPluginRuntime.NULLISH_TO_JAVA_NULL; import static com.google.template.soy.jbcsrc.runtime.JbcSrcPluginRuntime.NULL_TO_JAVA_NULL; +import static com.google.template.soy.jbcsrc.runtime.JbcSrcPluginRuntime.NUMBER_TO_FLOAT; +import static com.google.template.soy.jbcsrc.runtime.JbcSrcPluginRuntime.NUMBER_TO_INT; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; @@ -36,6 +39,9 @@ import com.google.template.soy.data.PartialSoyTemplate; import com.google.template.soy.data.SoyTemplate; import com.google.template.soy.data.SoyValue; +import com.google.template.soy.data.restricted.FloatData; +import com.google.template.soy.data.restricted.IntegerData; +import com.google.template.soy.data.restricted.NumberData; import com.google.template.soy.data.restricted.PrimitiveData; import com.google.template.soy.error.ErrorReporter; import com.google.template.soy.jbcsrc.restricted.BytecodeUtils; @@ -300,7 +306,7 @@ public JbcSrcJavaValue constant(double value) { @Override public JbcSrcJavaValue constant(long value) { - return JbcSrcJavaValue.of(SoyExpression.forInt(BytecodeUtils.constant(value))); + return constant((double) value); } @Override @@ -372,6 +378,12 @@ private static Expression adaptParameter(Class expectedParamType, JbcSrcJavaV // NullData -> null, UndefinedData -> UndefinedData return maybeSoyNullToJavaNull(actualParam.box()).checkedCast(expectedParamType); } + if (expectedParamType == IntegerData.class) { + return NUMBER_TO_INT.invoke(actualParam.box().checkedCast(NumberData.class)); + } + if (expectedParamType == FloatData.class) { + return NUMBER_TO_FLOAT.invoke(actualParam.box().checkedCast(NumberData.class)); + } // If we expect a specific SoyValue subclass, then box + cast. if (SoyValue.class.isAssignableFrom(expectedParamType)) { // NullData -> null, UndefinedData -> null @@ -382,9 +394,7 @@ private static Expression adaptParameter(Class expectedParamType, JbcSrcJavaV // int needs special-casing for overflow, and because we can't unbox as int if (expectedParamType == int.class) { - // We box + invoke rather than unboxAsLong() + numericConversion so that we get overflow - // checking (built into integerValue()). - return actualParam.unboxAsInt(); + return actualParam.coerceToInt(); } // double needs special casing since we allow soy int -> double conversions (since double // has enough precision to hold soy int data). We can't unbox longs as double, so we coerce. @@ -427,7 +437,7 @@ protected void doGen(CodeBuilder mv) { if (expectedParamType.equals(boolean.class)) { return actualParam.unboxAsBoolean(); } else if (expectedParamType.equals(long.class)) { - return actualParam.unboxAsLong(); + return numericConversion(actualParam.unboxAsDouble(), Type.LONG_TYPE); } else if (expectedParamType.equals(String.class)) { return actualParam.unboxAsStringOrJavaNull(); } else if (expectedParamType.equals(List.class)) { @@ -439,7 +449,7 @@ protected void doGen(CodeBuilder mv) { private static Expression convertToEnum(Class enumType, SoyExpression e) { return getForNumberMethod(enumType) - .invoke(BytecodeUtils.numericConversion(e.unboxAsLong(), Type.INT_TYPE)); + .invoke(BytecodeUtils.numericConversion(e.unboxAsDouble(), Type.INT_TYPE)); } private static MethodRef getForNumberMethod(Class enumType) { @@ -464,9 +474,8 @@ private Expression tryToWrapInSoyExpression(Expression expr) { case Type.BOOLEAN: return SoyExpression.forBool(expr); case Type.INT: - return SoyExpression.forInt(BytecodeUtils.numericConversion(expr, Type.LONG_TYPE)); case Type.LONG: - return SoyExpression.forInt(expr); + return SoyExpression.forFloat(BytecodeUtils.numericConversion(expr, Type.DOUBLE_TYPE)); case Type.DOUBLE: return SoyExpression.forFloat(expr); case Type.OBJECT: @@ -553,9 +562,9 @@ private SoyExpression toSoyExpression(JbcSrcJavaValue pluginReturnValue) { // TODO(lukes): SoyExpression should have a way to track type information with an unboxed // int that is actually a proto enum. Like we do with SanitizedContents soyExpr = - SoyExpression.forInt( + SoyExpression.forFloat( BytecodeUtils.numericConversion( - MethodRefs.PROTOCOL_ENUM_GET_NUMBER.invoke(expr), Type.LONG_TYPE)); + MethodRefs.PROTOCOL_ENUM_GET_NUMBER.invoke(expr), Type.DOUBLE_TYPE)); } else if (PartialSoyTemplate.class.isAssignableFrom(type) || SoyTemplate.class.isAssignableFrom(type)) { diff --git a/java/src/com/google/template/soy/jbcsrc/ProtoUtils.java b/java/src/com/google/template/soy/jbcsrc/ProtoUtils.java index 7dad42d5ad..6245b5ecea 100644 --- a/java/src/com/google/template/soy/jbcsrc/ProtoUtils.java +++ b/java/src/com/google/template/soy/jbcsrc/ProtoUtils.java @@ -65,12 +65,11 @@ import com.google.template.soy.data.SanitizedContents; import com.google.template.soy.data.SoyValue; import com.google.template.soy.exprtree.ExprNode; -import com.google.template.soy.exprtree.FloatNode; import com.google.template.soy.exprtree.FunctionNode; -import com.google.template.soy.exprtree.IntegerNode; import com.google.template.soy.exprtree.ListLiteralNode; import com.google.template.soy.exprtree.MapLiteralNode; import com.google.template.soy.exprtree.MethodCallNode; +import com.google.template.soy.exprtree.NumberNode; import com.google.template.soy.exprtree.ProtoEnumValueNode; import com.google.template.soy.exprtree.VarDefn; import com.google.template.soy.exprtree.VarRefNode; @@ -500,10 +499,8 @@ protected void doGen(CodeBuilder adapter) { private static void doBox(CodeBuilder adapter, SoyRuntimeType soyRuntimeType) { if (soyRuntimeType.isKnownBool()) { MethodRefs.BOOLEAN_DATA_FOR_VALUE.invokeUnchecked(adapter); - } else if (soyRuntimeType.isKnownInt()) { - MethodRefs.INTEGER_DATA_FOR_VALUE.invokeUnchecked(adapter); - } else if (soyRuntimeType.isKnownFloat()) { - MethodRefs.FLOAT_DATA_FOR_VALUE.invokeUnchecked(adapter); + } else if (soyRuntimeType.isKnownNumber()) { + MethodRefs.NUMBER_DATA_FOR_VALUE.invokeUnchecked(adapter); } else { SoyExpression.doBox(adapter, soyRuntimeType); } @@ -544,18 +541,21 @@ private SoyExpression interpretField(Expression field, Int64ConversionMode int64 case ENUM: if (isOpenEnumField(descriptor)) { // it already is an integer, cast to long - return SoyExpression.forInt(numericConversion(field, Type.LONG_TYPE)); + return SoyExpression.forFloat(numericConversion(field, Type.DOUBLE_TYPE)); } // otherwise it is closed and we need to extract the number. - return SoyExpression.forInt( - numericConversion(field.invoke(MethodRefs.PROTOCOL_ENUM_GET_NUMBER), Type.LONG_TYPE)); + return SoyExpression.forFloat( + numericConversion( + field.invoke(MethodRefs.PROTOCOL_ENUM_GET_NUMBER), Type.DOUBLE_TYPE)); case INT: // Since soy 'int's are java longs, we can actually fully represent an unsigned 32bit // integer. if (isUnsigned(descriptor)) { - return SoyExpression.forInt(MethodRefs.UNSIGNED_INTS_TO_LONG.invoke(field)); + return SoyExpression.forFloat( + numericConversion( + MethodRefs.UNSIGNED_INTS_TO_LONG.invoke(field), Type.DOUBLE_TYPE)); } else { - return SoyExpression.forInt(numericConversion(field, Type.LONG_TYPE)); + return SoyExpression.forFloat(numericConversion(field, Type.DOUBLE_TYPE)); } case LONG: if (int64Mode == Int64ConversionMode.FORCE_GBIGINT) { @@ -582,7 +582,7 @@ private SoyExpression interpretField(Expression field, Int64ConversionMode int64 // appear negative). We don't have a solution for this currently. In theory we could add // the concept of unsigned integers to soy and then implement arithmetic on them... but // that is insane! - return SoyExpression.forInt(field); + return SoyExpression.forFloat(numericConversion(field, Type.DOUBLE_TYPE)); case BOOLEAN: return SoyExpression.forBool(field); case STRING: @@ -664,20 +664,22 @@ private SoyExpression interpretExtensionField(Expression field) { return SoyExpression.forFloat( field.checkedCast(Number.class).invoke(MethodRefs.NUMBER_DOUBLE_VALUE)); case ENUM: - return SoyExpression.forInt( + return SoyExpression.forFloat( numericConversion( field .checkedCast(ProtocolMessageEnum.class) .invoke(MethodRefs.PROTOCOL_ENUM_GET_NUMBER), - Type.LONG_TYPE)); + Type.DOUBLE_TYPE)); case INT: if (isUnsigned(descriptor)) { - return SoyExpression.forInt( - MethodRefs.UNSIGNED_INTS_TO_LONG.invoke( - field.checkedCast(Integer.class).invoke(MethodRefs.NUMBER_INT_VALUE))); + return SoyExpression.forFloat( + numericConversion( + MethodRefs.UNSIGNED_INTS_TO_LONG.invoke( + field.checkedCast(Integer.class).invoke(MethodRefs.NUMBER_INT_VALUE)), + Type.DOUBLE_TYPE)); } else { - return SoyExpression.forInt( - field.checkedCast(Integer.class).invoke(MethodRefs.NUMBER_LONG_VALUE)); + return SoyExpression.forFloat( + field.checkedCast(Integer.class).invoke(MethodRefs.NUMBER_DOUBLE_VALUE)); } case LONG: SoyRuntimeType gbigintType = SoyRuntimeType.getBoxedType(GbigintType.getInstance()); @@ -1034,9 +1036,9 @@ protected void doGen(CodeBuilder cb) { } }; } - if (field.getJavaType() == JavaType.INT && arg.getKind() == ExprNode.Kind.INTEGER_NODE) { + if (field.getJavaType() == JavaType.INT && arg.getKind() == ExprNode.Kind.NUMBER_NODE) { // similar to the above, we can avoid a L2I instruction or a method call - long value = ((IntegerNode) arg).getValue(); + long value = (long) ((NumberNode) arg).getValue(); return new Statement() { @Override protected void doGen(CodeBuilder cb) { @@ -1049,8 +1051,8 @@ protected void doGen(CodeBuilder cb) { } }; } - if (field.getJavaType() == JavaType.FLOAT && arg.getKind() == ExprNode.Kind.FLOAT_NODE) { - float value = (float) ((FloatNode) arg).getValue(); + if (field.getJavaType() == JavaType.FLOAT && arg.getKind() == ExprNode.Kind.NUMBER_NODE) { + float value = (float) ((NumberNode) arg).getValue(); return new Statement() { @Override protected void doGen(CodeBuilder cb) { @@ -1384,9 +1386,9 @@ protected void doGen(CodeBuilder cb) { } }; } - if (field.getJavaType() == JavaType.INT && arg.getKind() == ExprNode.Kind.INTEGER_NODE) { + if (field.getJavaType() == JavaType.INT && arg.getKind() == ExprNode.Kind.NUMBER_NODE) { // similar to the above, we can avoid a L2I instruction or a method call - long value = ((IntegerNode) arg).getValue(); + long value = (long) ((NumberNode) arg).getValue(); int intValue = isUnsigned(field) ? UnsignedInts.saturatedCast(value) : (int) value; Expression boxedInt = BytecodeUtils.boxJavaPrimitive(Type.INT_TYPE, constant(intValue)); return new Statement() { @@ -1399,8 +1401,8 @@ protected void doGen(CodeBuilder cb) { } }; } - if (field.getJavaType() == JavaType.FLOAT && arg.getKind() == ExprNode.Kind.FLOAT_NODE) { - float value = (float) ((FloatNode) arg).getValue(); + if (field.getJavaType() == JavaType.FLOAT && arg.getKind() == ExprNode.Kind.NUMBER_NODE) { + float value = (float) ((NumberNode) arg).getValue(); Expression boxedFloat = BytecodeUtils.boxJavaPrimitive(Type.FLOAT_TYPE, constant(value)); return new Statement() { @Override diff --git a/java/src/com/google/template/soy/jbcsrc/SoyNodeCompiler.java b/java/src/com/google/template/soy/jbcsrc/SoyNodeCompiler.java index de54fc3a71..3265364b67 100644 --- a/java/src/com/google/template/soy/jbcsrc/SoyNodeCompiler.java +++ b/java/src/com/google/template/soy/jbcsrc/SoyNodeCompiler.java @@ -43,10 +43,9 @@ import com.google.template.soy.basetree.ParentNode; import com.google.template.soy.exprtree.BooleanNode; import com.google.template.soy.exprtree.ExprRootNode; -import com.google.template.soy.exprtree.FloatNode; import com.google.template.soy.exprtree.FunctionNode; -import com.google.template.soy.exprtree.IntegerNode; import com.google.template.soy.exprtree.NullNode; +import com.google.template.soy.exprtree.NumberNode; import com.google.template.soy.exprtree.ProtoEnumValueNode; import com.google.template.soy.exprtree.StringNode; import com.google.template.soy.exprtree.UndefinedNode; @@ -680,21 +679,13 @@ private Optional tryCompileSwitchToSwitchInstruction( SwitchCaseNode caseNode = (SwitchCaseNode) child; for (ExprRootNode caseExpr : caseNode.getExprList()) { var root = caseExpr.getRoot(); - if (root instanceof IntegerNode) { - long intValue = ((IntegerNode) root).getValue(); - // If a case expression is used multiple times, only use the first occurrence - if (intValue == (int) intValue) { - cases.putIfAbsent((int) intValue, caseNode); - } else { - cases.putIfAbsent(intValue, caseNode); - } - } else if (root instanceof ProtoEnumValueNode) { + if (root instanceof ProtoEnumValueNode) { cases.putIfAbsent(((ProtoEnumValueNode) root).getValueAsInt(), caseNode); } else if (root instanceof BooleanNode) { cases.putIfAbsent( constant(((BooleanNode) root).getValue()).constantBytecodeValue(), caseNode); - } else if (root instanceof FloatNode) { - double floatValue = ((FloatNode) root).getValue(); + } else if (root instanceof NumberNode) { + double floatValue = ((NumberNode) root).getValue(); if (floatValue == (int) floatValue) { cases.putIfAbsent((int) floatValue, caseNode); } else if (floatValue == (long) floatValue) { diff --git a/java/src/com/google/template/soy/jbcsrc/TemplateAnalysisImpl.java b/java/src/com/google/template/soy/jbcsrc/TemplateAnalysisImpl.java index 817daeb53a..496194e2b0 100644 --- a/java/src/com/google/template/soy/jbcsrc/TemplateAnalysisImpl.java +++ b/java/src/com/google/template/soy/jbcsrc/TemplateAnalysisImpl.java @@ -38,12 +38,12 @@ import com.google.template.soy.exprtree.ExprRootNode; import com.google.template.soy.exprtree.FunctionNode; import com.google.template.soy.exprtree.GlobalNode; -import com.google.template.soy.exprtree.IntegerNode; import com.google.template.soy.exprtree.ListComprehensionNode; import com.google.template.soy.exprtree.ListLiteralNode; import com.google.template.soy.exprtree.MapLiteralFromListNode; import com.google.template.soy.exprtree.MapLiteralNode; import com.google.template.soy.exprtree.NullSafeAccessNode; +import com.google.template.soy.exprtree.NumberNode; import com.google.template.soy.exprtree.OperatorNodes.AmpAmpOpNode; import com.google.template.soy.exprtree.OperatorNodes.AssertNonNullOpNode; import com.google.template.soy.exprtree.OperatorNodes.BarBarOpNode; @@ -641,12 +641,12 @@ private static StaticAnalysisResult isListExpressionEmpty(ForNode node) { private static StaticAnalysisResult isRangeExpressionEmpty(RangeArgs range) { int start = 0; if (range.start().isPresent()) { - if (range.start().get() instanceof IntegerNode) { - long startAsLong = ((IntegerNode) range.start().get()).getValue(); - if (startAsLong != (int) startAsLong) { + if (range.start().get() instanceof NumberNode) { + double startAsDouble = ((NumberNode) range.start().get()).getValue(); + if (startAsDouble != (int) startAsDouble) { return StaticAnalysisResult.UNKNOWN; } - start = (int) startAsLong; + start = (int) startAsDouble; } else { // if the start is not a constant then we don't know anything return StaticAnalysisResult.UNKNOWN; @@ -654,24 +654,24 @@ private static StaticAnalysisResult isRangeExpressionEmpty(RangeArgs range) { } int limit; - if (range.limit() instanceof IntegerNode) { - long limitAsLong = ((IntegerNode) range.limit()).getValue(); - if (limitAsLong != (int) limitAsLong) { + if (range.limit() instanceof NumberNode) { + double limitAsDouble = ((NumberNode) range.limit()).getValue(); + if (limitAsDouble != (int) limitAsDouble) { return StaticAnalysisResult.UNKNOWN; } - limit = (int) limitAsLong; + limit = (int) limitAsDouble; } else { return StaticAnalysisResult.UNKNOWN; } int step = 1; if (range.increment().isPresent()) { - if (range.increment().get() instanceof IntegerNode) { - long stepAsLong = ((IntegerNode) range.increment().get()).getValue(); - if (stepAsLong != (int) stepAsLong) { + if (range.increment().get() instanceof NumberNode) { + double stepAsDouble = ((NumberNode) range.increment().get()).getValue(); + if (stepAsDouble != (int) stepAsDouble) { return StaticAnalysisResult.UNKNOWN; } - step = (int) stepAsLong; + step = (int) stepAsDouble; } else { return StaticAnalysisResult.UNKNOWN; } @@ -829,7 +829,6 @@ protected void visitFunctionNode(FunctionNode node) { case DEBUG_SOY_TEMPLATE_INFO: case PROTO_INIT: case SOY_SERVER_KEY: - case TO_FLOAT: case VE_DATA: case XID: case EMPTY_TO_UNDEFINED: diff --git a/java/src/com/google/template/soy/jbcsrc/restricted/Branch.java b/java/src/com/google/template/soy/jbcsrc/restricted/Branch.java index 0fae5cae48..5e32b2da10 100644 --- a/java/src/com/google/template/soy/jbcsrc/restricted/Branch.java +++ b/java/src/com/google/template/soy/jbcsrc/restricted/Branch.java @@ -22,7 +22,6 @@ import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.template.soy.jbcsrc.restricted.BytecodeUtils.SOY_VALUE_PROVIDER_TYPE; import static com.google.template.soy.jbcsrc.restricted.BytecodeUtils.SOY_VALUE_TYPE; -import static com.google.template.soy.jbcsrc.restricted.BytecodeUtils.constant; import static com.google.template.soy.jbcsrc.restricted.BytecodeUtils.isDefinitelyAssignableFrom; import static com.google.template.soy.jbcsrc.restricted.BytecodeUtils.newLabel; @@ -505,11 +504,6 @@ public static Branch always() { return never().negate(); } - public static Branch ifNotZero(Expression expression) { - checkState(expression.resultType().equals(Type.LONG_TYPE)); - return Branch.compare(Opcodes.IFNE, expression, constant(0L)); - } - public static Branch ifEqual(Expression left, Expression right) { return Branch.compare(Opcodes.IFEQ, left, right); } diff --git a/java/src/com/google/template/soy/jbcsrc/restricted/BytecodeUtils.java b/java/src/com/google/template/soy/jbcsrc/restricted/BytecodeUtils.java index 46a2c998a0..54f9fd4e7b 100644 --- a/java/src/com/google/template/soy/jbcsrc/restricted/BytecodeUtils.java +++ b/java/src/com/google/template/soy/jbcsrc/restricted/BytecodeUtils.java @@ -18,7 +18,6 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; -import static com.google.template.soy.jbcsrc.restricted.BytecodeUtils.newLabel; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; @@ -61,7 +60,6 @@ import com.google.template.soy.data.restricted.BooleanData; import com.google.template.soy.data.restricted.FloatData; import com.google.template.soy.data.restricted.GbigintData; -import com.google.template.soy.data.restricted.IntegerData; import com.google.template.soy.data.restricted.NullData; import com.google.template.soy.data.restricted.NullishData; import com.google.template.soy.data.restricted.NumberData; @@ -140,7 +138,6 @@ public final class BytecodeUtils { public static final Type UNDEFINED_DATA_TYPE = Type.getType(UndefinedData.class); public static final Type PRIMITIVE_DATA_TYPE = Type.getType(PrimitiveData.class); public static final Type NUMBER_DATA_TYPE = Type.getType(NumberData.class); - public static final Type INTEGER_DATA_TYPE = Type.getType(IntegerData.class); public static final Type BIG_INTEGER_TYPE = Type.getType(BigInteger.class); public static final Type GBIGINT_DATA_TYPE = Type.getType(GbigintData.class); public static final Type FLOAT_DATA_TYPE = Type.getType(FloatData.class); @@ -728,7 +725,7 @@ protected void doGen(CodeBuilder adapter) { }; } - static boolean isNumericPrimitive(Type type) { + public static boolean isNumericPrimitive(Type type) { int sort = type.getSort(); switch (sort) { case Type.OBJECT: @@ -839,17 +836,11 @@ public static Expression compareSoySwitchCaseEquals(SoyExpression left, SoyExpre private static Expression maybeFastCompareNumbers(SoyExpression left, SoyExpression right) { SoyRuntimeType leftRuntimeType = left.soyRuntimeType(); SoyRuntimeType rightRuntimeType = right.soyRuntimeType(); - if (leftRuntimeType.isKnownInt() - && rightRuntimeType.isKnownInt() - && left.isNonSoyNullish() - && right.isNonSoyNullish()) { - return Branch.ifEqual(left.unboxAsLong(), right.unboxAsLong()).asBoolean(); - } if (leftRuntimeType.isKnownNumber() && rightRuntimeType.isKnownNumber() && left.isNonSoyNullish() && right.isNonSoyNullish() - && (leftRuntimeType.isKnownFloat() || rightRuntimeType.isKnownFloat())) { + && (leftRuntimeType.isKnownNumber() || rightRuntimeType.isKnownNumber())) { return Branch.ifEqual(left.coerceToDouble(), right.coerceToDouble()).asBoolean(); } return null; @@ -1360,9 +1351,7 @@ public static Type getTypeForClassName(String name) { public static Type getTypeForSoyType(SoyType type) { switch (type.getKind()) { - case INT: - return BOXED_LONG_TYPE; - case FLOAT: + case NUMBER: return BOXED_DOUBLE_TYPE; case BOOL: return BOXED_BOOLEAN_TYPE; diff --git a/java/src/com/google/template/soy/jbcsrc/restricted/Expression.java b/java/src/com/google/template/soy/jbcsrc/restricted/Expression.java index 8b948e7a53..6b0f4490f4 100644 --- a/java/src/com/google/template/soy/jbcsrc/restricted/Expression.java +++ b/java/src/com/google/template/soy/jbcsrc/restricted/Expression.java @@ -39,7 +39,7 @@ import com.google.template.soy.data.SoyValue; import com.google.template.soy.data.TemplateValue; import com.google.template.soy.data.restricted.GbigintData; -import com.google.template.soy.data.restricted.IntegerData; +import com.google.template.soy.data.restricted.NumberData; import com.google.template.soy.internal.proto.JavaQualifiedNames; import com.google.template.soy.jbcsrc.shared.Names; import com.google.template.soy.types.SoyProtoType; @@ -788,13 +788,6 @@ private Optional doCheckedSoyCast(SoyType type) { case INDEXED: return doCheckedSoyCast(type.getEffectiveType()); case UNION: - if (type.equals(SoyTypes.NUMBER_TYPE)) { - if (BytecodeUtils.isDefinitelyAssignableFrom( - BytecodeUtils.NUMBER_DATA_TYPE, resultType)) { - return Optional.empty(); - } - return Optional.of(MethodRefs.CHECK_NUMBER.invoke(this)); - } return Optional.empty(); case NULL: return this.maybeCheckedCast(BytecodeUtils.NULL_DATA_TYPE); @@ -811,21 +804,15 @@ private Optional doCheckedSoyCast(SoyType type) { return Optional.empty(); } return Optional.of(MethodRefs.CHECK_BOOLEAN.invoke(this)); - case FLOAT: + case NUMBER: if (BytecodeUtils.isDefinitelyAssignableFrom(BytecodeUtils.FLOAT_DATA_TYPE, resultType)) { return Optional.empty(); } - return Optional.of(MethodRefs.CHECK_FLOAT.invoke(this)); + return Optional.of(MethodRefs.CHECK_NUMBER.invoke(this)); case HTML: case ELEMENT: return Optional.of( MethodRefs.CHECK_CONTENT_KIND.invoke(this, constant(ContentKind.HTML))); - case INT: - if (BytecodeUtils.isDefinitelyAssignableFrom( - BytecodeUtils.INTEGER_DATA_TYPE, resultType)) { - return Optional.empty(); - } - return Optional.of(MethodRefs.CHECK_INT.invoke(this)); case GBIGINT: expectedClass = GbigintData.class; @@ -857,7 +844,7 @@ private Optional doCheckedSoyCast(SoyType type) { .type(); return Optional.of(MethodRefs.CHECK_PROTO.invoke(this, constant(protoType))); case PROTO_ENUM: - expectedClass = IntegerData.class; + expectedClass = NumberData.class; break; case RECORD: expectedClass = SoyRecord.class; diff --git a/java/src/com/google/template/soy/jbcsrc/restricted/MethodRefs.java b/java/src/com/google/template/soy/jbcsrc/restricted/MethodRefs.java index 9355abc677..44aec59959 100644 --- a/java/src/com/google/template/soy/jbcsrc/restricted/MethodRefs.java +++ b/java/src/com/google/template/soy/jbcsrc/restricted/MethodRefs.java @@ -32,6 +32,7 @@ import com.google.protobuf.GeneratedMessage.ExtendableMessage; import com.google.protobuf.Message; import com.google.protobuf.ProtocolMessageEnum; +import com.google.template.soy.base.internal.BaseUtils; import com.google.template.soy.data.Dir; import com.google.template.soy.data.LoggingAdvisingAppendable; import com.google.template.soy.data.LoggingAdvisingAppendable.BufferingAppendable; @@ -57,9 +58,7 @@ import com.google.template.soy.data.internal.SoyMapImpl; import com.google.template.soy.data.internal.SoyRecordImpl; import com.google.template.soy.data.restricted.BooleanData; -import com.google.template.soy.data.restricted.FloatData; import com.google.template.soy.data.restricted.GbigintData; -import com.google.template.soy.data.restricted.IntegerData; import com.google.template.soy.data.restricted.NumberData; import com.google.template.soy.data.restricted.StringData; import com.google.template.soy.jbcsrc.api.RenderResult; @@ -121,15 +120,13 @@ public final class MethodRefs { createPure(SoyMapImpl.class, "forProviderMapNoNullKeys", Map.class); public static final MethodRef DOUBLE_TO_STRING = - createPure(FloatData.class, "toString", double.class); - - public static final MethodRef EQUALS = createPure(Object.class, "equals", Object.class); + createPure(BaseUtils.class, "formatDouble", double.class); public static final MethodRef STRING_COMPARE_TO = createPure(String.class, "compareTo", String.class); - public static final MethodRef FLOAT_DATA_FOR_VALUE = - createPure(FloatData.class, "forValue", double.class); + public static final MethodRef NUMBER_DATA_FOR_VALUE = + createPure(NumberData.class, "forValue", double.class); public static final MethodRef RENDER_RESULT_ASSERT_DONE = createPure(RenderResult.class, "assertDone"); @@ -215,9 +212,6 @@ public final class MethodRefs { IMMUTABLE_MAP_OF = ImmutableList.copyOf(immutableMapOfMethods.values()); } - public static final MethodRef INTEGER_DATA_FOR_VALUE = - createPure(IntegerData.class, "forValue", long.class); - public static final MethodRef INTS_CHECKED_CAST = createPure(Ints.class, "checkedCast", long.class).asCheap(); @@ -387,10 +381,10 @@ public final class MethodRefs { .asCheap(); public static final MethodRef RUNTIME_GET_LIST_ITEM = - createPure(JbcSrcRuntime.class, "getSoyListItem", List.class, long.class); + createPure(JbcSrcRuntime.class, "getSoyListItem", List.class, double.class); public static final MethodRef RUNTIME_GET_LIST_ITEM_PROVIDER = - createPure(JbcSrcRuntime.class, "getSoyListItemProvider", List.class, long.class); + createPure(JbcSrcRuntime.class, "getSoyListItemProvider", List.class, double.class); public static final MethodRef RUNTIME_GET_LIST_STATUS = createNonPure(JbcSrcRuntime.class, "getListStatus", List.class); @@ -536,10 +530,6 @@ public final class MethodRefs { public static final MethodRef CHECK_TYPE = createNonPure(SoyValue.class, "checkNullishType", Class.class); - public static final MethodRef CHECK_INT = createNonPure(SoyValue.class, "checkNullishInt"); - - public static final MethodRef CHECK_FLOAT = createNonPure(SoyValue.class, "checkNullishFloat"); - public static final MethodRef CHECK_NUMBER = createNonPure(SoyValue.class, "checkNullishNumber"); public static final MethodRef CHECK_STRING = createNonPure(SoyValue.class, "checkNullishString"); diff --git a/java/src/com/google/template/soy/jbcsrc/restricted/SoyExpression.java b/java/src/com/google/template/soy/jbcsrc/restricted/SoyExpression.java index a136b6a102..ef6e29625e 100644 --- a/java/src/com/google/template/soy/jbcsrc/restricted/SoyExpression.java +++ b/java/src/com/google/template/soy/jbcsrc/restricted/SoyExpression.java @@ -32,7 +32,6 @@ import static com.google.template.soy.jbcsrc.restricted.BytecodeUtils.STRING_TYPE; import static com.google.template.soy.jbcsrc.restricted.BytecodeUtils.constant; import static com.google.template.soy.jbcsrc.restricted.BytecodeUtils.isDefinitelyAssignableFrom; -import static com.google.template.soy.jbcsrc.restricted.BytecodeUtils.isNumericPrimitive; import static com.google.template.soy.jbcsrc.restricted.BytecodeUtils.newLabel; import com.google.common.base.MoreObjects; @@ -44,12 +43,11 @@ import com.google.template.soy.data.internal.Converters; import com.google.template.soy.data.internal.RuntimeMapTypeTracker; import com.google.template.soy.types.BoolType; -import com.google.template.soy.types.FloatType; -import com.google.template.soy.types.IntType; import com.google.template.soy.types.LegacyObjectMapType; import com.google.template.soy.types.ListType; import com.google.template.soy.types.MapType; import com.google.template.soy.types.NullType; +import com.google.template.soy.types.NumberType; import com.google.template.soy.types.SanitizedType; import com.google.template.soy.types.SetType; import com.google.template.soy.types.SoyProtoType; @@ -97,7 +95,7 @@ public static SoyExpression forBool(SoyExpression delegate) { } public static SoyExpression forFloat(Expression delegate) { - return new SoyExpression(getUnboxedType(FloatType.getInstance()), delegate); + return new SoyExpression(getUnboxedType(NumberType.getInstance()), delegate); } @DoNotCall @@ -105,15 +103,6 @@ public static SoyExpression forFloat(SoyExpression delegate) { throw new UnsupportedOperationException(); } - public static SoyExpression forInt(Expression delegate) { - return new SoyExpression(getUnboxedType(IntType.getInstance()), delegate); - } - - @DoNotCall - public static SoyExpression forInt(SoyExpression delegate) { - throw new UnsupportedOperationException(); - } - public static SoyExpression forString(Expression delegate) { return new SoyExpression(getUnboxedType(StringType.getInstance()), delegate); } @@ -317,20 +306,11 @@ public SourceLocation location() { return delegate.location(); } - public boolean isRuntimeInt() { - return soyRuntimeType.runtimeType().getSort() == Type.INT - || soyRuntimeType.runtimeType().getSort() == Type.LONG; - } - - public boolean isRuntimeFloat() { + public boolean isRuntimeNumber() { return soyRuntimeType.runtimeType().getSort() == Type.FLOAT || soyRuntimeType.runtimeType().getSort() == Type.DOUBLE; } - public boolean isRuntimeNumber() { - return isRuntimeInt() || isRuntimeFloat(); - } - public boolean isKnownString() { return soyRuntimeType.isKnownString(); } @@ -361,11 +341,8 @@ public SoyExpression box() { } return asBoxed(MethodRefs.BOOLEAN_DATA_FOR_VALUE.invoke(delegate).toMaybeConstant()); } - if (soyRuntimeType.isKnownInt()) { - return asBoxed(MethodRefs.INTEGER_DATA_FOR_VALUE.invoke(delegate).toMaybeConstant()); - } - if (soyRuntimeType.isKnownFloat()) { - return asBoxed(MethodRefs.FLOAT_DATA_FOR_VALUE.invoke(delegate).toMaybeConstant()); + if (soyRuntimeType.isKnownNumber()) { + return asBoxed(MethodRefs.NUMBER_DATA_FOR_VALUE.invoke(delegate).toMaybeConstant()); } // If null is expected and it is a reference type we want to propagate null through the boxing // operation @@ -522,14 +499,6 @@ public Branch compileToBranch() { } else if (resultType().equals(Type.DOUBLE_TYPE)) { return Branch.ifTrue( MethodRefs.RUNTIME_COERCE_DOUBLE_TO_BOOLEAN.invoke(delegate).toMaybeConstant()); - } else if (resultType().equals(Type.LONG_TYPE)) { - if (delegate.isConstant()) { - Long maybeLong = delegate.constantValue().getJavaValueAsType(Long.class).orElse(null); - if (maybeLong == null) { - return maybeLong == 0L ? Branch.never() : Branch.never().negate(); - } - } - return Branch.ifNotZero(delegate); } else { throw new AssertionError( "resultType(): " + resultType() + " is not a valid type for a SoyExpression"); @@ -591,16 +560,12 @@ public SoyExpression coerceToString() { /** Coerce this expression to a double value. Useful for float-int comparisons. */ public SoyExpression coerceToDouble() { if (!isBoxed()) { - if (soyRuntimeType.isKnownFloat()) { + if (soyRuntimeType.isKnownNumber()) { return this; } - if (soyRuntimeType.isKnownInt()) { - return forFloat( - BytecodeUtils.numericConversion(delegate, Type.DOUBLE_TYPE).toMaybeConstant()); - } throw new UnsupportedOperationException("Can't convert " + resultType() + " to a double"); } - if (soyRuntimeType.isKnownFloat()) { + if (soyRuntimeType.isKnownNumber()) { return forFloat(delegate.invoke(MethodRefs.SOY_VALUE_FLOAT_VALUE).toMaybeConstant()); } return forFloat(delegate.invoke(MethodRefs.SOY_VALUE_NUMBER_VALUE).toMaybeConstant()); @@ -613,12 +578,9 @@ public SoyExpression coerceToDouble() { */ public Expression unboxAsNumberOrJavaNull() { if (!isBoxed()) { - if (soyRuntimeType.isKnownFloat()) { + if (soyRuntimeType.isKnownNumber()) { return MethodRefs.BOX_DOUBLE.invoke(this).toMaybeConstant(); } - if (soyRuntimeType.isKnownInt()) { - return MethodRefs.BOX_LONG.invoke(this).toMaybeConstant(); - } throw new UnsupportedOperationException("Can't convert " + resultType() + " to a Number"); } if (isNonSoyNullish()) { @@ -656,46 +618,10 @@ public SoyExpression unboxAsBoolean() { delegateWithoutCast().invoke(MethodRefs.SOY_VALUE_BOOLEAN_VALUE).toMaybeConstant()); } - /** - * Unboxes this to a {@link SoyExpression} with a long runtime type. - * - *

This method is appropriate when you know (likely via inspection of the {@link #soyType()}, - * or other means) that the value does have the appropriate type but you prefer to interact with - * it as its unboxed representation. If you simply want to 'coerce' the given value to a new type - * consider {@link #coerceToDouble()} which is designed for that use case. - */ - public SoyExpression unboxAsLong() { - if (alreadyUnboxed(long.class)) { - return this; - } - assertBoxed(long.class); - - return forInt(delegateWithoutCast().invoke(MethodRefs.SOY_VALUE_LONG_VALUE)); - } - - /** - * Unboxes this to a {@link SoyExpression} with an `int` runtime type. - * - *

This method is appropriate when you know (likely via inspection of the {@link #soyType()}, - * or other means) that the value does have the appropriate type but you prefer to interact with - * it as its unboxed representation. If you simply want to 'coerce' the given value to a new type - * consider {@link #coerceToDouble()} which is designed for that use case. - */ - public Expression unboxAsInt() { - if (alreadyUnboxed(long.class)) { - return checkedCastLongToInt(this); - } - assertBoxed(long.class); - return delegateWithoutCast().invoke(MethodRefs.SOY_VALUE_INTEGER_VALUE); - } - public Expression coerceToInt() { - if (soyRuntimeType.isKnownInt()) { - return unboxAsInt(); - } return checkedCastLongToInt( BytecodeUtils.numericConversion( - soyRuntimeType.isKnownFloat() ? unboxAsDouble() : coerceToDouble(), Type.LONG_TYPE)); + soyRuntimeType.isKnownNumber() ? unboxAsDouble() : coerceToDouble(), Type.LONG_TYPE)); } private static Expression checkedCastLongToInt(Expression delegate) { @@ -862,14 +788,12 @@ private SoyExpression instanceOfUnboxed(SoyType soyType) { case STRING: staticValue = alreadyUnboxed(String.class); break; + case NUMBER: + staticValue = Type.DOUBLE_TYPE == soyRuntimeType.runtimeType(); + break; case BOOL: staticValue = Type.BOOLEAN_TYPE == soyRuntimeType.runtimeType(); break; - case UNION: - staticValue = - soyType.equals(SoyTypes.NUMBER_TYPE) - && isNumericPrimitive(soyRuntimeType.runtimeType()); - break; case LIST: staticValue = alreadyUnboxed(List.class); break; @@ -902,14 +826,12 @@ public SoyExpression instanceOf(SoyType soyType) { case STRING: type = STRING_DATA_TYPE; break; + case NUMBER: + type = NUMBER_DATA_TYPE; + break; case BOOL: type = BOOLEAN_DATA_TYPE; break; - case UNION: - if (soyType.equals(SoyTypes.NUMBER_TYPE)) { - type = NUMBER_DATA_TYPE; - } - break; case RECORD: type = SOY_RECORD_IMPL_TYPE; break; diff --git a/java/src/com/google/template/soy/jbcsrc/restricted/SoyRuntimeType.java b/java/src/com/google/template/soy/jbcsrc/restricted/SoyRuntimeType.java index 12ab54dff6..f30efa2882 100644 --- a/java/src/com/google/template/soy/jbcsrc/restricted/SoyRuntimeType.java +++ b/java/src/com/google/template/soy/jbcsrc/restricted/SoyRuntimeType.java @@ -25,12 +25,11 @@ import com.google.template.soy.internal.proto.JavaQualifiedNames; import com.google.template.soy.types.AbstractIterableType; import com.google.template.soy.types.BoolType; -import com.google.template.soy.types.FloatType; -import com.google.template.soy.types.IntType; import com.google.template.soy.types.IterableType; import com.google.template.soy.types.ListType; import com.google.template.soy.types.MapType; import com.google.template.soy.types.MessageType; +import com.google.template.soy.types.NumberType; import com.google.template.soy.types.SanitizedType.AttributesType; import com.google.template.soy.types.SanitizedType.HtmlType; import com.google.template.soy.types.SetType; @@ -76,12 +75,10 @@ private static PrimitiveSoyType unboxedTypeImpl(SoyType soyType) { return new PrimitiveSoyType(BoolType.getInstance(), Type.BOOLEAN_TYPE); case STRING: return new PrimitiveSoyType(StringType.getInstance(), BytecodeUtils.STRING_TYPE); - case INT: - return new PrimitiveSoyType(IntType.getInstance(), Type.LONG_TYPE); - case FLOAT: - return new PrimitiveSoyType(FloatType.getInstance(), Type.DOUBLE_TYPE); + case NUMBER: + return new PrimitiveSoyType(NumberType.getInstance(), Type.DOUBLE_TYPE); case PROTO_ENUM: - return new PrimitiveSoyType(soyType, Type.LONG_TYPE); + return new PrimitiveSoyType(soyType, Type.DOUBLE_TYPE); case MESSAGE: return new PrimitiveSoyType(soyType, BytecodeUtils.MESSAGE_TYPE); case PROTO: @@ -227,24 +224,14 @@ public boolean isKnownSanitizedContent() { return soyType.getKind().isKnownSanitizedContent(); } - /** - * Returns {@code true} if the expression is known to be an int at compile time. - * - *

Note: If this returns {@code false}, there is no guarantee that this expression is - * not a int, just that it is not known to be a int at compile time. - */ - public boolean isKnownInt() { - return soyType.getKind() == Kind.INT || SoyTypes.isKindOrUnionOfKind(soyType, Kind.PROTO_ENUM); - } - /** * Returns {@code true} if the expression is known to be a float at compile time. * *

Note: If this returns {@code false}, there is no guarantee that this expression is * not a float, just that it is not known to be a float at compile time. */ - public final boolean isKnownFloat() { - return soyType.getKind() == Kind.FLOAT; + public final boolean isKnownNumber() { + return NumberType.getInstance().isAssignableFromStrict(soyType); } public final boolean isKnownListOrUnionOfLists() { @@ -292,17 +279,6 @@ public final boolean isKnownProtoOrUnionOfProtos() { return MessageType.getInstance().isAssignableFromStrict(soyType); } - /** - * Returns {@code true} if the expression is known to be an {@linkplain #isKnownInt() int} or a - * {@linkplain #isKnownFloat() float} at compile time. - * - *

Note: If this returns {@code false}, there is no guarantee that this expression is - * not a number, just that it is not known to be a number at compile time. - */ - public final boolean isKnownNumber() { - return SoyTypes.NUMBER_TYPE.isAssignableFromStrict(soyType); - } - public final SoyRuntimeType asNonSoyNullish() { // Use tryRemoveNull instead of removeNull because there are times where the jbcsrc backend // infers stronger types than Soy proper (e.g. for `@state` params initialized to `null` but diff --git a/java/src/com/google/template/soy/jbcsrc/runtime/JbcSrcExternRuntime.java b/java/src/com/google/template/soy/jbcsrc/runtime/JbcSrcExternRuntime.java index e27d7d305f..3758ffd56e 100644 --- a/java/src/com/google/template/soy/jbcsrc/runtime/JbcSrcExternRuntime.java +++ b/java/src/com/google/template/soy/jbcsrc/runtime/JbcSrcExternRuntime.java @@ -217,10 +217,10 @@ public static ImmutableList listUnboxStrings(List values) { return values.stream().map(SoyValue::coerceToString).collect(toImmutableList()); } - public static final MethodRef LONG_TO_INT = create("longToInt", long.class); + public static final MethodRef DOUBLE_TO_INT = create("doubleToInt", double.class); @Keep - public static int longToInt(long value) { + public static int doubleToInt(double value) { Preconditions.checkState( value >= Integer.MIN_VALUE && value <= Integer.MAX_VALUE, "Casting long to integer results in overflow: %s", @@ -315,8 +315,9 @@ public static T toEnum(SoyValue value, Class clazz) { public static final MethodRef UNBOX_BOOLEAN = MethodRef.createPure(Boolean.class, "booleanValue"); public static final MethodRef UNBOX_DOUBLE = MethodRef.createPure(Double.class, "doubleValue"); public static final MethodRef UNBOX_FLOAT = MethodRef.createPure(Float.class, "doubleValue"); - public static final MethodRef UNBOX_INTEGER = MethodRef.createPure(Integer.class, "longValue"); - public static final MethodRef UNBOX_LONG = MethodRef.createPure(Long.class, "longValue"); + public static final MethodRef UNBOX_INTEGER = MethodRef.createPure(Integer.class, "doubleValue"); + public static final MethodRef UNBOX_LONG = MethodRef.createPure(Long.class, "doubleValue"); + public static final MethodRef UNBOX_NUMBER = MethodRef.createPure(Number.class, "doubleValue"); public static final MethodRef UNBOX_MAP = create("unboxMap", SoyValue.class, Class.class, Class.class); diff --git a/java/src/com/google/template/soy/jbcsrc/runtime/JbcSrcPluginRuntime.java b/java/src/com/google/template/soy/jbcsrc/runtime/JbcSrcPluginRuntime.java index 7d0f4ae359..e59dcc4008 100644 --- a/java/src/com/google/template/soy/jbcsrc/runtime/JbcSrcPluginRuntime.java +++ b/java/src/com/google/template/soy/jbcsrc/runtime/JbcSrcPluginRuntime.java @@ -31,7 +31,10 @@ import com.google.template.soy.data.internal.SoyLegacyObjectMapImpl; import com.google.template.soy.data.internal.SoyMapImpl; import com.google.template.soy.data.internal.SoyRecordImpl; +import com.google.template.soy.data.restricted.FloatData; +import com.google.template.soy.data.restricted.IntegerData; import com.google.template.soy.data.restricted.NullData; +import com.google.template.soy.data.restricted.NumberData; import com.google.template.soy.jbcsrc.restricted.MethodRef; import java.util.Map; import java.util.concurrent.Future; @@ -47,6 +50,22 @@ private static MethodRef create(String methodName, Class... params) { return MethodRef.createPure(JbcSrcPluginRuntime.class, methodName, params); } + public static final MethodRef NUMBER_TO_FLOAT = create("numberToFloat", NumberData.class); + + @Keep + @Nonnull + public static FloatData numberToFloat(NumberData value) { + return FloatData.forValue(value.toFloat()); + } + + public static final MethodRef NUMBER_TO_INT = create("numberToInt", NumberData.class); + + @Keep + @Nonnull + public static IntegerData numberToInt(NumberData value) { + return IntegerData.forValue((long) value.toFloat()); + } + public static final MethodRef CONVERT_FUTURE_TO_SOY_VALUE_PROVIDER = create("convertFutureToSoyValueProvider", Future.class); diff --git a/java/src/com/google/template/soy/jbcsrc/runtime/JbcSrcRuntime.java b/java/src/com/google/template/soy/jbcsrc/runtime/JbcSrcRuntime.java index ff97de441b..e42149531d 100644 --- a/java/src/com/google/template/soy/jbcsrc/runtime/JbcSrcRuntime.java +++ b/java/src/com/google/template/soy/jbcsrc/runtime/JbcSrcRuntime.java @@ -167,12 +167,12 @@ public static CompiledTemplate bufferTemplate( } @Keep - public static SoyValue getSoyListItem(List list, long index) { + public static SoyValue getSoyListItem(List list, double index) { return getSoyListItemProvider(list, index).resolve(); } @Keep - public static SoyValueProvider getSoyListItemProvider(List list, long index) { + public static SoyValueProvider getSoyListItemProvider(List list, double index) { if (list == null) { throw new NullPointerException("Attempted to access list item '" + index + "' of null"); } diff --git a/java/src/com/google/template/soy/jbcsrc/shared/SwitchFactory.java b/java/src/com/google/template/soy/jbcsrc/shared/SwitchFactory.java index d40e44e989..5af273aa31 100644 --- a/java/src/com/google/template/soy/jbcsrc/shared/SwitchFactory.java +++ b/java/src/com/google/template/soy/jbcsrc/shared/SwitchFactory.java @@ -22,9 +22,8 @@ import com.google.template.soy.data.SanitizedContent; import com.google.template.soy.data.SoyValue; import com.google.template.soy.data.restricted.BooleanData; -import com.google.template.soy.data.restricted.FloatData; -import com.google.template.soy.data.restricted.IntegerData; import com.google.template.soy.data.restricted.NullData; +import com.google.template.soy.data.restricted.NumberData; import com.google.template.soy.data.restricted.StringData; import com.google.template.soy.data.restricted.UndefinedData; import java.lang.invoke.CallSite; @@ -89,10 +88,8 @@ public static CallSite bootstrapSwitch( // Turn ints and doubles into their SoyValue types since those have equals/hashCode methods // that make them mutually comparable. i.o.w. // FloatData.valueOf(2).equals(IntegerData.valueOf(2)) - if (caseValue instanceof Integer || caseValue instanceof Long) { - caseValue = IntegerData.forValue(((Number) caseValue).longValue()); - } else if (caseValue instanceof Double) { - caseValue = FloatData.forValue(((Double) caseValue).doubleValue()); + if (caseValue instanceof Number) { + caseValue = NumberData.forValue((Number) caseValue); } else if (caseValue instanceof Boolean) { caseValue = BooleanData.forValue(((Boolean) caseValue).booleanValue()); } else if (caseValue instanceof String) { diff --git a/java/src/com/google/template/soy/jssrc/dsl/Expressions.java b/java/src/com/google/template/soy/jssrc/dsl/Expressions.java index b958d75f95..54388c17f3 100644 --- a/java/src/com/google/template/soy/jssrc/dsl/Expressions.java +++ b/java/src/com/google/template/soy/jssrc/dsl/Expressions.java @@ -25,8 +25,9 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Streams; import com.google.template.soy.base.SourceLocation.ByteSpan; +import com.google.template.soy.base.internal.BaseUtils; import com.google.template.soy.base.internal.QuoteStyle; -import com.google.template.soy.exprtree.IntegerNode; +import com.google.template.soy.exprtree.NumberNode; import com.google.template.soy.exprtree.Operator; import com.google.template.soy.jssrc.dsl.CodeChunk.Generator; import com.google.template.soy.jssrc.restricted.JsExpr; @@ -273,13 +274,13 @@ public static Expression regexLiteral(String contents) { /** Creates a code chunk representing a JavaScript number literal. */ public static Expression number(long value) { Preconditions.checkArgument( - IntegerNode.isInRange(value), "Number is outside JS safe integer range: %s", value); + NumberNode.isInRange(value), "Number is outside JS safe integer range: %s", value); return Leaf.createNonNull(Long.toString(value), /* isCheap= */ true); } /** Creates a code chunk representing a JavaScript number literal. */ public static Expression number(double value) { - return Leaf.createNonNull(Double.toString(value), /* isCheap= */ true); + return Leaf.createNonNull(BaseUtils.formatDouble(value), /* isCheap= */ true); } /** Creates a code chunk representing an anonymous function literal. */ diff --git a/java/src/com/google/template/soy/jssrc/internal/JsType.java b/java/src/com/google/template/soy/jssrc/internal/JsType.java index a88d37823c..85b724c042 100644 --- a/java/src/com/google/template/soy/jssrc/internal/JsType.java +++ b/java/src/com/google/template/soy/jssrc/internal/JsType.java @@ -372,8 +372,7 @@ public JsType get(SoyType soyType, JsTypeProducer forRecursion) { .setPredicate(typeofTypePredicate("number")); return enumBuilder.build(); - case FLOAT: - case INT: + case NUMBER: return NUMBER_TYPE; case STRING: diff --git a/java/src/com/google/template/soy/jssrc/internal/TranslateExprNodeVisitor.java b/java/src/com/google/template/soy/jssrc/internal/TranslateExprNodeVisitor.java index edf677869d..4b0aee7984 100644 --- a/java/src/com/google/template/soy/jssrc/internal/TranslateExprNodeVisitor.java +++ b/java/src/com/google/template/soy/jssrc/internal/TranslateExprNodeVisitor.java @@ -84,11 +84,9 @@ import com.google.template.soy.exprtree.ExprNode.OperatorNode; import com.google.template.soy.exprtree.ExprRootNode; import com.google.template.soy.exprtree.FieldAccessNode; -import com.google.template.soy.exprtree.FloatNode; import com.google.template.soy.exprtree.FunctionNode; import com.google.template.soy.exprtree.FunctionNode.ExternRef; import com.google.template.soy.exprtree.GlobalNode; -import com.google.template.soy.exprtree.IntegerNode; import com.google.template.soy.exprtree.ItemAccessNode; import com.google.template.soy.exprtree.ListComprehensionNode; import com.google.template.soy.exprtree.ListLiteralNode; @@ -97,6 +95,7 @@ import com.google.template.soy.exprtree.MethodCallNode; import com.google.template.soy.exprtree.NullNode; import com.google.template.soy.exprtree.NullSafeAccessNode; +import com.google.template.soy.exprtree.NumberNode; import com.google.template.soy.exprtree.Operator; import com.google.template.soy.exprtree.OperatorNodes.AmpAmpOpNode; import com.google.template.soy.exprtree.OperatorNodes.AsOpNode; @@ -305,12 +304,7 @@ protected Expression visitBooleanNode(BooleanNode node) { } @Override - protected Expression visitFloatNode(FloatNode node) { - return number(node.getValue()); - } - - @Override - protected Expression visitIntegerNode(IntegerNode node) { + protected Expression visitNumberNode(NumberNode node) { return number(node.getValue()); } @@ -886,11 +880,8 @@ protected Expression visitInstanceOfOpNode(InstanceOfOpNode node) { return value.typeOf().tripleEquals(stringLiteral("string")); case BOOL: return value.typeOf().tripleEquals(stringLiteral("boolean")); - case UNION: - if (operand.equals(SoyTypes.NUMBER_TYPE)) { - return value.typeOf().tripleEquals(stringLiteral("number")); - } - break; + case NUMBER: + return value.typeOf().tripleEquals(stringLiteral("number")); case RECORD: return JsRuntime.IS_RECORD.call(value); case LIST: @@ -926,8 +917,7 @@ protected Expression visitInstanceOfOpNode(InstanceOfOpNode node) { } private static final ImmutableSet CAN_USE_EQUALS = - Sets.immutableEnumSet( - SoyType.Kind.INT, SoyType.Kind.FLOAT, SoyType.Kind.PROTO_ENUM, Kind.BOOL, Kind.STRING); + Sets.immutableEnumSet(SoyType.Kind.NUMBER, SoyType.Kind.PROTO_ENUM, Kind.BOOL, Kind.STRING); private Expression visitEqualNodeHelper(OperatorNode node, Operator eq) { boolean needsSoyEquals = false; @@ -1101,9 +1091,6 @@ protected Expression visitFunctionNode(FunctionNode node) { return visitUnknownJsGlobal(node); case IS_PRIMARY_MSG_IN_USE: return visitIsPrimaryMsgInUseFunction(node); - case TO_FLOAT: - // this is a no-op in js - return visit(node.getParam(0)); case DEBUG_SOY_TEMPLATE_INFO: // TODO(lukes): does this need a goog.debug guard? it exists in the runtime return GOOG_DEBUG.and( diff --git a/java/src/com/google/template/soy/msgs/internal/InsertMsgsVisitor.java b/java/src/com/google/template/soy/msgs/internal/InsertMsgsVisitor.java index 0e7b348e4a..606107996b 100644 --- a/java/src/com/google/template/soy/msgs/internal/InsertMsgsVisitor.java +++ b/java/src/com/google/template/soy/msgs/internal/InsertMsgsVisitor.java @@ -25,7 +25,7 @@ import com.google.template.soy.error.SoyErrorKind; import com.google.template.soy.exprtree.BooleanNode; import com.google.template.soy.exprtree.FunctionNode; -import com.google.template.soy.exprtree.IntegerNode; +import com.google.template.soy.exprtree.StringNode; import com.google.template.soy.msgs.SoyMsgBundle; import com.google.template.soy.msgs.restricted.SoyMsg; import com.google.template.soy.msgs.restricted.SoyMsgPart; @@ -220,8 +220,8 @@ private void replaceIsPrimaryMsgInUseFunction(FunctionNode node) { } else { // if the primary message id is available or the fallback message is not available, then we // are using the primary message. - long primaryMsgId = ((IntegerNode) node.getParam(1)).getValue(); - long fallbackMsgId = ((IntegerNode) node.getParam(2)).getValue(); + long primaryMsgId = Long.parseLong(((StringNode) node.getParam(1)).getValue()); + long fallbackMsgId = Long.parseLong(((StringNode) node.getParam(2)).getValue()); isPrimaryMsgInUse = msgBundle.hasMsg(primaryMsgId) || !msgBundle.hasMsg(fallbackMsgId); } node.getParent() diff --git a/java/src/com/google/template/soy/passes/CheckTemplateCallsPass.java b/java/src/com/google/template/soy/passes/CheckTemplateCallsPass.java index b4c4b838fa..d5d4ed6d3f 100644 --- a/java/src/com/google/template/soy/passes/CheckTemplateCallsPass.java +++ b/java/src/com/google/template/soy/passes/CheckTemplateCallsPass.java @@ -306,8 +306,7 @@ private void checkCallParamTypes( SoyType argType; if (callerParam.getKind() == SoyNode.Kind.CALL_PARAM_VALUE_NODE) { CallParamValueNode node = (CallParamValueNode) callerParam; - argType = - RuntimeTypeCoercion.maybeCoerceType(node.getExpr().getRoot(), declaredParamTypes); + argType = node.getExpr().getRoot().getType(); } else if (callerParam.getKind() == SoyNode.Kind.CALL_PARAM_CONTENT_NODE) { CallParamContentNode callParamContentNode = (CallParamContentNode) callerParam; if (!callParamContentNode.isImplicitContentKind()) { diff --git a/java/src/com/google/template/soy/passes/KeyCommandPass.java b/java/src/com/google/template/soy/passes/KeyCommandPass.java index 528eaa663c..abba427b2b 100644 --- a/java/src/com/google/template/soy/passes/KeyCommandPass.java +++ b/java/src/com/google/template/soy/passes/KeyCommandPass.java @@ -138,8 +138,7 @@ private void checkNodeIsSupportedType(ExprRootNode exprRootNode) { switch (type.getKind()) { case NULL: case UNDEFINED: - case INT: - case FLOAT: + case NUMBER: case STRING: case PROTO_ENUM: case GBIGINT: diff --git a/java/src/com/google/template/soy/passes/MsgWithIdFunctionPass.java b/java/src/com/google/template/soy/passes/MsgWithIdFunctionPass.java index c1c93cd342..075135e8a7 100644 --- a/java/src/com/google/template/soy/passes/MsgWithIdFunctionPass.java +++ b/java/src/com/google/template/soy/passes/MsgWithIdFunctionPass.java @@ -29,7 +29,6 @@ import com.google.template.soy.error.SoyErrorKind.StyleAllowance; import com.google.template.soy.exprtree.ExprNode; import com.google.template.soy.exprtree.FunctionNode; -import com.google.template.soy.exprtree.IntegerNode; import com.google.template.soy.exprtree.OperatorNodes.ConditionalOpNode; import com.google.template.soy.exprtree.RecordLiteralNode; import com.google.template.soy.exprtree.StringNode; @@ -186,8 +185,10 @@ private void handleMsgIdCall(FunctionNode fn, MsgFallbackGroupNode msgNode) { // We could formalize the hack by providing a way to stash arbitrary data in the FunctionNode // and then just pack this up in a non-AST datastructure. isPrimaryMsgInUse.addChild(fn.getParam(0).copy(new CopyState())); - isPrimaryMsgInUse.addChild(new IntegerNode(primaryMsgId, fn.getSourceLocation())); - isPrimaryMsgInUse.addChild(new IntegerNode(fallbackMsgId, fn.getSourceLocation())); + isPrimaryMsgInUse.addChild( + new StringNode(String.valueOf(primaryMsgId), QuoteStyle.SINGLE, fn.getSourceLocation())); + isPrimaryMsgInUse.addChild( + new StringNode(String.valueOf(fallbackMsgId), QuoteStyle.SINGLE, fn.getSourceLocation())); condOpNode.addChild(isPrimaryMsgInUse); condOpNode.addChild(createMsgIdNode(primaryMsgId, fn.getSourceLocation())); condOpNode.addChild(createMsgIdNode(fallbackMsgId, fn.getSourceLocation())); diff --git a/java/src/com/google/template/soy/passes/ResolveExpressionTypesPass.java b/java/src/com/google/template/soy/passes/ResolveExpressionTypesPass.java index 0e854cd009..e0edf6c7a0 100644 --- a/java/src/com/google/template/soy/passes/ResolveExpressionTypesPass.java +++ b/java/src/com/google/template/soy/passes/ResolveExpressionTypesPass.java @@ -96,7 +96,6 @@ import com.google.template.soy.exprtree.FunctionNode.ExternRef; import com.google.template.soy.exprtree.GlobalNode; import com.google.template.soy.exprtree.GroupNode; -import com.google.template.soy.exprtree.IntegerNode; import com.google.template.soy.exprtree.ItemAccessNode; import com.google.template.soy.exprtree.ListComprehensionNode; import com.google.template.soy.exprtree.ListLiteralNode; @@ -105,6 +104,7 @@ import com.google.template.soy.exprtree.MethodCallNode; import com.google.template.soy.exprtree.NullNode; import com.google.template.soy.exprtree.NullSafeAccessNode; +import com.google.template.soy.exprtree.NumberNode; import com.google.template.soy.exprtree.OperatorNodes.AmpAmpOpNode; import com.google.template.soy.exprtree.OperatorNodes.AsOpNode; import com.google.template.soy.exprtree.OperatorNodes.AssertNonNullOpNode; @@ -188,14 +188,13 @@ import com.google.template.soy.types.AbstractIterableType; import com.google.template.soy.types.AbstractMapType; import com.google.template.soy.types.BoolType; -import com.google.template.soy.types.FloatType; import com.google.template.soy.types.FunctionType; import com.google.template.soy.types.FunctionType.Parameter; -import com.google.template.soy.types.IntType; import com.google.template.soy.types.IterableType; import com.google.template.soy.types.LegacyObjectMapType; import com.google.template.soy.types.ListType; import com.google.template.soy.types.MapType; +import com.google.template.soy.types.NumberType; import com.google.template.soy.types.ProtoImportType; import com.google.template.soy.types.RecordType; import com.google.template.soy.types.RecordType.Member; @@ -655,11 +654,6 @@ protected void visitTemplateNode(TemplateNode node) { SoyType actualType = headerVar.defaultValue().getRoot().getAuthoredType(); SoyType declaredType = headerVar.authoredType(); - if (!declaredType.isAssignableFromStrict(actualType)) { - actualType = - RuntimeTypeCoercion.maybeCoerceType( - headerVar.defaultValue().getRoot(), SoyTypes.expandUnions(declaredType)); - } if (!declaredType.isAssignableFromLoose(actualType)) { SourceLocation loc = headerVar.defaultValue().getSourceLocation(); if (!loc.isKnown()) { @@ -811,7 +805,7 @@ protected void visitIfNode(IfNode node) { private final ImmutableSet allowedSwitchTypes = ImmutableSet.of( - Kind.BOOL, Kind.INT, Kind.FLOAT, Kind.STRING, Kind.PROTO_ENUM, Kind.UNKNOWN, Kind.ANY); + Kind.BOOL, Kind.NUMBER, Kind.STRING, Kind.PROTO_ENUM, Kind.UNKNOWN, Kind.ANY); @Override protected void visitSwitchNode(SwitchNode node) { @@ -828,7 +822,7 @@ protected void visitSwitchNode(SwitchNode node) { exprTypeError = true; } else if (tryRemoveNullish(switchExprType).getKind() == Kind.PROTO_ENUM) { // Allow int cases in proto switch. - switchExprType = UnionType.of(switchExprType, IntType.getInstance()); + switchExprType = UnionType.of(switchExprType, NumberType.getInstance()); } SoyType switchExprNarrowedType = switchExpr.getType(); for (SoyNode child : node.getChildren()) { @@ -893,7 +887,7 @@ protected void visitForNonemptyNode(ForNonemptyNode node) { // Visit the node body if (node.getIndexVar() != null) { // Set the type of the optional index to integer. - node.getIndexVar().setType(IntType.getInstance()); + node.getIndexVar().setType(NumberType.getInstance()); } visitChildren(node); } @@ -913,8 +907,7 @@ protected void visitMsgPluralNode(MsgPluralNode node) { } private final ImmutableSet allowedVariantTypes = - ImmutableSet.of( - SoyType.Kind.STRING, SoyType.Kind.INT, SoyType.Kind.PROTO_ENUM, SoyType.Kind.UNKNOWN); + ImmutableSet.of(SoyType.Kind.STRING, SoyType.Kind.PROTO_ENUM, SoyType.Kind.UNKNOWN); @Override protected void visitCallDelegateNode(CallDelegateNode node) { @@ -939,9 +932,9 @@ protected void visitCallDelegateNode(CallDelegateNode node) { if (!BaseUtils.isIdentifier(variantStr)) { errorReporter.report(location, INVALID_VARIANT_EXPRESSION, variantStr); } - } else if (variant.getRoot().getKind() == ExprNode.Kind.INTEGER_NODE) { - long variantInt = ((IntegerNode) variant.getRoot()).getValue(); - if (variantInt < 0) { + } else if (variant.getRoot().getKind() == ExprNode.Kind.NUMBER_NODE) { + double variantInt = ((NumberNode) variant.getRoot()).getValue(); + if (variantInt < 0 || (int) variantInt != variantInt) { errorReporter.report(location, INVALID_VARIANT_EXPRESSION, variant.toSourceString()); } } @@ -1153,7 +1146,7 @@ protected void visitListComprehensionNode(ListComprehensionNode node) { if (node.getIndexVar() != null) { // Set the type of the optional index to integer ($index in this example). - node.getIndexVar().setType(IntType.getInstance()); + node.getIndexVar().setType(NumberType.getInstance()); } TypeSubstitutions.Checkpoint savedSubstitutions = substitutions.checkpoint(); @@ -1640,8 +1633,8 @@ private void finishMethodCallNode(MethodCallNode node, boolean nullSafe) { int maxDepth; if (node.numParams() == 1) { // This will only work for int literal in the source code. - if (node.getParam(0).getKind() == ExprNode.Kind.INTEGER_NODE) { - maxDepth = (int) ((IntegerNode) node.getParam(0)).getValue(); + if (node.getParam(0).getKind() == ExprNode.Kind.NUMBER_NODE) { + maxDepth = (int) ((NumberNode) node.getParam(0)).getValue(); } else { maxDepth = 0; } @@ -1851,17 +1844,31 @@ protected void visitMinusOpNode(MinusOpNode node) { private void visitLongOnlyOpNode(AbstractOperatorNode node) { visitChildren(node); - SoyType result = IntType.getInstance(); - SoyType left = SoyTypes.tryRemoveNullish(node.getChild(0).getType()); - SoyType right = SoyTypes.tryRemoveNullish(node.getChild(1).getType()); - if (left.getKind() != Kind.INT || right.getKind() != Kind.INT) { + ExprNode lhs = node.getChild(0); + ExprNode rhs = node.getChild(1); + + SoyType result = NumberType.getInstance(); + SoyType left = SoyTypes.tryRemoveNullish(lhs.getType()); + SoyType right = SoyTypes.tryRemoveNullish(rhs.getType()); + if (left.getKind() != Kind.NUMBER || right.getKind() != Kind.NUMBER) { errorReporter.report( node.getOperatorLocation(), INCOMPATIBLE_ARITHMETIC_OP, node.getOperator().getTokenString(), - node.getChild(0).getAuthoredType(), - node.getChild(1).getAuthoredType()); + lhs.getAuthoredType(), + rhs.getAuthoredType()); result = UnknownType.getInstance(); + } else { + boolean leftInt = !(lhs instanceof NumberNode) || ((NumberNode) lhs).isInteger(); + boolean rightInt = !(rhs instanceof NumberNode) || ((NumberNode) rhs).isInteger(); + if (!leftInt || !rightInt) { + errorReporter.report( + node.getOperatorLocation(), + INCOMPATIBLE_ARITHMETIC_OP, + node.getOperator().getTokenString(), + leftInt ? "int" : "float", + rightInt ? "int" : "float"); + } } node.setType(result); } @@ -2298,7 +2305,7 @@ private void visitKeysFunction(FunctionNode node) { if (argType.getKind() == Kind.LEGACY_OBJECT_MAP) { listArg = ((LegacyObjectMapType) argType).getKeyType(); // pretty much just string } else if (argType.getKind() == Kind.LIST) { - listArg = IntType.getInstance(); + listArg = NumberType.getInstance(); } else if (argType.getKind() == Kind.MAP) { errorReporter.report(node.getSourceLocation(), KEYS_PASSED_MAP); listArg = UnknownType.getInstance(); @@ -2468,14 +2475,6 @@ private void visitProtoInitFunction(FunctionNode node) { } SoyType expectedType = SoyTypes.makeNullish(fieldType); - if (!expectedType.isAssignableFromLoose(argType)) { - argType = - RuntimeTypeCoercion.maybeCoerceType( - expr, - expectedType instanceof UnionType - ? ((UnionType) expectedType).getMembers() - : ImmutableList.of(expectedType)); - } if (!expectedType.isAssignableFromLoose(argType)) { errorReporter.report( expr.getSourceLocation(), @@ -2559,7 +2558,7 @@ private void visitArithmeticOpNode(AbstractOperatorNode node) { // returned by getSoyTypeForBinaryOperator. // TODO(b/64098780): Should we add nullability to divide operator? Probably not, but we should // also consolidate the behaviors when we divide something by 0 or null. - node.setType(isDivide ? FloatType.getInstance() : result); + node.setType(isDivide ? NumberType.getInstance() : result); tryApplySubstitution(node); } @@ -2684,7 +2683,7 @@ private SoyType getItemTypeForAccessNode( } // For lists, the key type must either be unknown or assignable to integer. - if (!IntType.getInstance().isAssignableFromLoose(keyType)) { + if (!NumberType.getInstance().isAssignableFromLoose(keyType)) { errorReporter.report(keyLocation, BAD_INDEX_TYPE, keyType, baseType); // fall through and report the element type. This will allow more later type checks to // be evaluated. @@ -2825,9 +2824,8 @@ private void visitBuiltinFunction(BuiltinFunction builtinFunction, FunctionNode node.setType(VeType.NO_DATA); } break; - case TO_FLOAT: // is added to the AST after this pass case REMAINDER: - node.setType(IntType.getInstance()); + node.setType(NumberType.getInstance()); break; case MSG_WITH_ID: node.setType( diff --git a/java/src/com/google/template/soy/passes/RewriteRemaindersPass.java b/java/src/com/google/template/soy/passes/RewriteRemaindersPass.java index 044d980837..8b74b30917 100644 --- a/java/src/com/google/template/soy/passes/RewriteRemaindersPass.java +++ b/java/src/com/google/template/soy/passes/RewriteRemaindersPass.java @@ -22,7 +22,7 @@ import com.google.template.soy.exprtree.ExprEquivalence; import com.google.template.soy.exprtree.ExprNode; import com.google.template.soy.exprtree.FunctionNode; -import com.google.template.soy.exprtree.IntegerNode; +import com.google.template.soy.exprtree.NumberNode; import com.google.template.soy.exprtree.Operator; import com.google.template.soy.shared.internal.BuiltinFunction; import com.google.template.soy.soytree.AbstractSoyNodeVisitor; @@ -129,7 +129,7 @@ private void rewriteRemainder(FunctionNode functionNode) { // Now rewrite the FunctionNode(reusing the old node id). ExprNode plural = currPluralNode.getExpr().getRoot().copy(new CopyState()); ExprNode offset = - new IntegerNode(currPluralNode.getOffset(), functionNode.getSourceLocation()); + new NumberNode(currPluralNode.getOffset(), functionNode.getSourceLocation()); ExprNode remainder = Operator.MINUS.createNode( plural.getSourceLocation().extend(offset.getSourceLocation()), @@ -161,6 +161,6 @@ private static void removeBadRemainder(FunctionNode functionNode) { // parts don't expect the remainder() function. functionNode .getParent() - .replaceChild(functionNode, new IntegerNode(0, functionNode.getSourceLocation())); + .replaceChild(functionNode, new NumberNode(0, functionNode.getSourceLocation())); } } diff --git a/java/src/com/google/template/soy/passes/RuntimeTypeCoercion.java b/java/src/com/google/template/soy/passes/RuntimeTypeCoercion.java deleted file mode 100644 index 240ab6ce7b..0000000000 --- a/java/src/com/google/template/soy/passes/RuntimeTypeCoercion.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2013 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.template.soy.passes; - -import com.google.common.collect.ImmutableTable; -import com.google.errorprone.annotations.CheckReturnValue; -import com.google.template.soy.base.internal.Identifier; -import com.google.template.soy.exprtree.ExprNode; -import com.google.template.soy.exprtree.ExprNode.ParentExprNode; -import com.google.template.soy.exprtree.FunctionNode; -import com.google.template.soy.shared.internal.BuiltinFunction; -import com.google.template.soy.types.FloatType; -import com.google.template.soy.types.IntType; -import com.google.template.soy.types.SoyType; -import java.util.Collection; - -/** Static utility for adding runtime casts to the Soy AST. */ -final class RuntimeTypeCoercion { - private static final ImmutableTable - AVAILABLE_CALL_SITE_COERCIONS = - new ImmutableTable.Builder() - .put(IntType.getInstance(), FloatType.getInstance(), BuiltinFunction.TO_FLOAT) - .buildOrThrow(); - - /** - * For int values passed into template param float, perform automatic type coercion from the call - * param value to the template param type. - * - *

Supported coercions: - * - *

    - *
  • int -> float - *
  • int -> float|null - *
- * - * @param node Node containing expression value to maybe-coerce. - * @param toTypes Acceptable types to attempt to coerce to. - * @return The new coerced type - */ - @CheckReturnValue - static SoyType maybeCoerceType(ExprNode node, Collection toTypes) { - SoyType fromType = node.getType(); - if (AVAILABLE_CALL_SITE_COERCIONS.row(fromType).isEmpty()) { - return fromType; - } - for (SoyType formalType : toTypes) { - if (formalType.isAssignableFromStrict(fromType)) { - return fromType; // template already accepts value, no need to coerce - } - } - for (SoyType coercionTargetType : AVAILABLE_CALL_SITE_COERCIONS.row(fromType).keySet()) { - BuiltinFunction function = null; - for (SoyType formalType : toTypes) { - if (!formalType.isAssignableFromStrict(coercionTargetType)) { - continue; - } - if (function == null) { - function = AVAILABLE_CALL_SITE_COERCIONS.get(fromType, coercionTargetType); - } else { - // This is actually a bad state that shouldn't happen because there should only be one - // coercing function. - function = null; - break; - } - } - if (function == null) { - continue; - } - - // create a node to wrap param in coercion - FunctionNode coercedValue = - FunctionNode.newPositional( - Identifier.create(function.getName(), node.getSourceLocation()), - function, - node.getSourceLocation()); - coercedValue.setType(coercionTargetType); - - ParentExprNode parent = node.getParent(); - parent.replaceChild(node, coercedValue); - coercedValue.addChild(node); - return coercionTargetType; - } - return fromType; - } - - private RuntimeTypeCoercion() {} -} diff --git a/java/src/com/google/template/soy/passes/ValidateExternsPass.java b/java/src/com/google/template/soy/passes/ValidateExternsPass.java index 26a8d07eaa..a82f7662d2 100644 --- a/java/src/com/google/template/soy/passes/ValidateExternsPass.java +++ b/java/src/com/google/template/soy/passes/ValidateExternsPass.java @@ -417,10 +417,12 @@ private boolean typesAreCompatible( preserveUndefined ? SoyTypes.tryRemoveNull(soyType) : SoyTypes.tryRemoveNullish(soyType); javaType = Primitives.wrap(javaType); switch (soyType.getKind()) { - case INT: - return javaType == Integer.class || javaType == Long.class; - case FLOAT: - return javaType == Double.class || javaType == Float.class; + case NUMBER: + return javaType == Double.class + || javaType == Float.class + || javaType == Integer.class + || javaType == Long.class + || javaType == Number.class; case STRING: return javaType == String.class; case BOOL: @@ -428,10 +430,6 @@ private boolean typesAreCompatible( case GBIGINT: return javaType == BigInteger.class; case UNION: - if (soyType.equals(SoyTypes.NUMBER_TYPE)) { - return javaType == Number.class || javaType == Double.class; - } - // fallthrough case ANY: case UNKNOWN: return javaType == Object.class || javaType == SoyValue.class; diff --git a/java/src/com/google/template/soy/passes/VeDefValidationPass.java b/java/src/com/google/template/soy/passes/VeDefValidationPass.java index 751759666b..4369527b20 100644 --- a/java/src/com/google/template/soy/passes/VeDefValidationPass.java +++ b/java/src/com/google/template/soy/passes/VeDefValidationPass.java @@ -24,12 +24,11 @@ import com.google.template.soy.exprtree.ExprEquivalence; import com.google.template.soy.exprtree.ExprNode; import com.google.template.soy.exprtree.FunctionNode; -import com.google.template.soy.exprtree.IntegerNode; import com.google.template.soy.exprtree.NullNode; +import com.google.template.soy.exprtree.NumberNode; import com.google.template.soy.exprtree.StringNode; import com.google.template.soy.logging.LoggingConfigValidator; import com.google.template.soy.logging.LoggingConfigValidator.VisualElement; -import com.google.template.soy.passes.CompilerFileSetPass.Result; import com.google.template.soy.shared.internal.BuiltinFunction; import com.google.template.soy.soytree.ConstNode; import com.google.template.soy.soytree.SoyFileNode; @@ -114,11 +113,11 @@ private void buildVeDefAndValidate(FunctionNode func, List vedefs } String veName = ((StringNode) func.getParam(0)).getValue(); - if (!(func.getParam(1) instanceof IntegerNode)) { + if (!(func.getParam(1) instanceof NumberNode)) { errorReporter.report(func.getParam(1).getSourceLocation(), BAD_VE_DEF_ID); return; } - long id = ((IntegerNode) func.getParam(1)).getValue(); + long id = (long) ((NumberNode) func.getParam(1)).getValue(); Optional dataProtoType; if (func.numParams() < 3 || func.getParam(2) instanceof NullNode) { diff --git a/java/src/com/google/template/soy/plugin/java/internal/JavaPluginValidator.java b/java/src/com/google/template/soy/plugin/java/internal/JavaPluginValidator.java index 8d27137d36..25e3c51c8d 100644 --- a/java/src/com/google/template/soy/plugin/java/internal/JavaPluginValidator.java +++ b/java/src/com/google/template/soy/plugin/java/internal/JavaPluginValidator.java @@ -30,11 +30,11 @@ import com.google.template.soy.plugin.java.restricted.JavaValue; import com.google.template.soy.plugin.java.restricted.MethodSignature; import com.google.template.soy.plugin.java.restricted.SoyJavaSourceFunction; -import com.google.template.soy.types.IntType; import com.google.template.soy.types.LegacyObjectMapType; import com.google.template.soy.types.ListType; import com.google.template.soy.types.MapType; import com.google.template.soy.types.NullType; +import com.google.template.soy.types.NumberType; import com.google.template.soy.types.RecordType; import com.google.template.soy.types.SetType; import com.google.template.soy.types.SoyType; @@ -177,7 +177,7 @@ private void validateReturnValue( reporter.incompatibleReturnType(returnType.get(), expectedType, method); return; } - actualSoyType = IntType.getInstance(); + actualSoyType = NumberType.getInstance(); } else if (PartialSoyTemplate.class.isAssignableFrom(actualClass) || SoyTemplate.class.isAssignableFrom(actualClass)) { if (expectedType instanceof TemplateType) { @@ -195,7 +195,7 @@ private void validateReturnValue( // We special-case proto enums when the return expression is an INT, to allow someone to return // an 'int' representing the enum. boolean isPossibleProtoEnum = - actualSoyType.getKind() == SoyType.Kind.INT + actualSoyType.getKind() == SoyType.Kind.NUMBER && isOrContains(expectedType, SoyType.Kind.PROTO_ENUM); if (!isPossibleProtoEnum && !expectedType.isAssignableFromStrict(actualSoyType)) { reporter.incompatibleReturnType(actualSoyType, expectedType, pluginReturnValue.methodInfo()); diff --git a/java/src/com/google/template/soy/plugin/java/internal/ValidatorFactory.java b/java/src/com/google/template/soy/plugin/java/internal/ValidatorFactory.java index 5805293c91..fb5aee24dd 100644 --- a/java/src/com/google/template/soy/plugin/java/internal/ValidatorFactory.java +++ b/java/src/com/google/template/soy/plugin/java/internal/ValidatorFactory.java @@ -53,9 +53,8 @@ import com.google.template.soy.plugin.java.restricted.JavaValueFactory; import com.google.template.soy.plugin.java.restricted.MethodSignature; import com.google.template.soy.types.BoolType; -import com.google.template.soy.types.FloatType; -import com.google.template.soy.types.IntType; import com.google.template.soy.types.NullType; +import com.google.template.soy.types.NumberType; import com.google.template.soy.types.SoyProtoEnumType; import com.google.template.soy.types.SoyProtoType; import com.google.template.soy.types.SoyType; @@ -83,17 +82,15 @@ final class ValidatorFactory extends JavaValueFactory { private static final ImmutableSet> BOOL_TYPES = ImmutableSet.of(SoyValue.class, boolean.class, BooleanData.class); - private static final ImmutableSet> FLOAT_TYPES = - ImmutableSet.of(SoyValue.class, double.class, FloatData.class, NumberData.class); - private static final ImmutableSet> NUMBER_TYPES = - ImmutableSet.of(SoyValue.class, double.class, NumberData.class); - - // We allow 'double' for soy int types because double has more precision than soy guarantees - // for its int type. - private static final ImmutableSet> INT_TYPES = ImmutableSet.of( - SoyValue.class, long.class, IntegerData.class, NumberData.class, int.class, double.class); + SoyValue.class, + double.class, + long.class, + int.class, + FloatData.class, + NumberData.class, + IntegerData.class); private static final ImmutableSet> GBIGINT_TYPES = ImmutableSet.of(SoyValue.class, BigInteger.class, GbigintData.class); @@ -234,12 +231,12 @@ public ValidatorValue constant(boolean value) { @Override public ValidatorValue constant(double value) { - return ValidatorValue.forSoyType(FloatType.getInstance(), reporter); + return ValidatorValue.forSoyType(NumberType.getInstance(), reporter); } @Override public ValidatorValue constant(long value) { - return ValidatorValue.forSoyType(IntType.getInstance(), reporter); + return ValidatorValue.forSoyType(NumberType.getInstance(), reporter); } @Override @@ -389,11 +386,8 @@ private static ValidationResult isValidClassForType(Class clazz, SoyType type case BOOL: expectedClasses = BOOL_TYPES; break; - case FLOAT: - expectedClasses = FLOAT_TYPES; - break; - case INT: - expectedClasses = INT_TYPES; + case NUMBER: + expectedClasses = NUMBER_TYPES; break; case LEGACY_OBJECT_MAP: expectedClasses = LEGACY_OBJECT_MAP_TYPES; @@ -432,11 +426,6 @@ private static ValidationResult isValidClassForType(Class clazz, SoyType type expectedDescriptor = ((SoyProtoEnumType) type).getDescriptor(); break; case UNION: - // number is a special case, it should work for double and NumberData - if (type.equals(SoyTypes.NUMBER_TYPE)) { - expectedClasses = NUMBER_TYPES; - break; - } // If this is a union, make sure the type is valid for every member. // If the type isn't valid for any member, then there's no guarantee this will work // for an arbitrary template at runtime. diff --git a/java/src/com/google/template/soy/plugin/java/internal/ValidatorValue.java b/java/src/com/google/template/soy/plugin/java/internal/ValidatorValue.java index 5cee758ed3..d85d3cb158 100644 --- a/java/src/com/google/template/soy/plugin/java/internal/ValidatorValue.java +++ b/java/src/com/google/template/soy/plugin/java/internal/ValidatorValue.java @@ -20,8 +20,7 @@ import com.google.template.soy.plugin.java.restricted.JavaValue; import com.google.template.soy.plugin.java.restricted.MethodSignature; import com.google.template.soy.types.BoolType; -import com.google.template.soy.types.FloatType; -import com.google.template.soy.types.IntType; +import com.google.template.soy.types.NumberType; import com.google.template.soy.types.SoyType; import com.google.template.soy.types.StringType; import javax.annotation.Nullable; @@ -79,14 +78,10 @@ static ValidatorValue forMethodReturnType( if (method.returnType() == boolean.class) { type = BoolType.getInstance(); } - if (method.returnType() == int.class || method.returnType() == long.class) { - type = IntType.getInstance(); - } - if (method.returnType() == int.class) { - type = IntType.getInstance(); - } - if (method.returnType() == double.class) { - type = FloatType.getInstance(); + if (method.returnType() == int.class + || method.returnType() == long.class + || method.returnType() == double.class) { + type = NumberType.getInstance(); } if (method.returnType() == String.class) { type = StringType.getInstance(); @@ -132,17 +127,17 @@ public ValidatorValue asSoyString() { @Override public ValidatorValue asSoyInt() { - return asValue(IntType.getInstance(), "asSoyInt"); + return asValue(NumberType.getInstance(), "asSoyInt"); } @Override public ValidatorValue coerceToJavaInt() { - return asValue(IntType.getInstance(), "asJavaInt"); + return asValue(NumberType.getInstance(), "asJavaInt"); } @Override public ValidatorValue asSoyFloat() { - return asValue(FloatType.getInstance(), "asSoyFloat"); + return asValue(NumberType.getInstance(), "asSoyFloat"); } private ValidatorValue asValue(SoyType newType, String methodName) { diff --git a/java/src/com/google/template/soy/pysrc/internal/TranslateToPyExprVisitor.java b/java/src/com/google/template/soy/pysrc/internal/TranslateToPyExprVisitor.java index d639f3beb5..7ee05ba7c8 100644 --- a/java/src/com/google/template/soy/pysrc/internal/TranslateToPyExprVisitor.java +++ b/java/src/com/google/template/soy/pysrc/internal/TranslateToPyExprVisitor.java @@ -37,7 +37,6 @@ import com.google.template.soy.exprtree.FieldAccessNode; import com.google.template.soy.exprtree.FunctionNode; import com.google.template.soy.exprtree.FunctionNode.ExternRef; -import com.google.template.soy.exprtree.IntegerNode; import com.google.template.soy.exprtree.ItemAccessNode; import com.google.template.soy.exprtree.ListComprehensionNode; import com.google.template.soy.exprtree.ListLiteralNode; @@ -654,7 +653,6 @@ private PyExpr visitNonPluginFunction(FunctionNode node, BuiltinFunction nonplug return visitSoyServerKeyFunction(node); case IS_PRIMARY_MSG_IN_USE: return visitIsPrimaryMsgInUseFunction(node); - case TO_FLOAT: case UNDEFINED_TO_NULL: case UNDEFINED_TO_NULL_SSR: // Python runtime does not distinguish between null and undefined. @@ -728,8 +726,8 @@ private PyExpr visitSoyServerKeyFunction(FunctionNode node) { } private PyExpr visitIsPrimaryMsgInUseFunction(FunctionNode node) { - long primaryMsgId = ((IntegerNode) node.getParam(1)).getValue(); - long fallbackMsgId = ((IntegerNode) node.getParam(2)).getValue(); + long primaryMsgId = Long.parseLong(((StringNode) node.getParam(1)).getValue()); + long fallbackMsgId = Long.parseLong(((StringNode) node.getParam(2)).getValue()); return new PyExpr( PyExprUtils.TRANSLATOR_NAME + ".is_msg_available(" diff --git a/java/src/com/google/template/soy/shared/internal/BuiltinFunction.java b/java/src/com/google/template/soy/shared/internal/BuiltinFunction.java index 28af971dab..3a38b11467 100644 --- a/java/src/com/google/template/soy/shared/internal/BuiltinFunction.java +++ b/java/src/com/google/template/soy/shared/internal/BuiltinFunction.java @@ -55,7 +55,6 @@ public enum BuiltinFunction implements SoyFunction { VE_DATA("ve_data"), LEGACY_DYNAMIC_TAG("legacyDynamicTag"), IS_PRIMARY_MSG_IN_USE("$$isPrimaryMsgInUse"), - TO_FLOAT("$$toFloat"), DEBUG_SOY_TEMPLATE_INFO("$$debugSoyTemplateInfo"), PROTO_INIT("$$protoInit"), VE_DEF("ve_def"), @@ -98,7 +97,6 @@ public Set getValidArgsSizes() { case LEGACY_DYNAMIC_TAG: case REMAINDER: case MSG_WITH_ID: - case TO_FLOAT: case EMPTY_TO_UNDEFINED: case UNDEFINED_TO_NULL: case UNDEFINED_TO_NULL_SSR: @@ -156,7 +154,6 @@ public boolean isPure() { case CHECK_NOT_NULL: case MSG_WITH_ID: case VE_DATA: - case TO_FLOAT: case PROTO_INIT: case EMPTY_TO_UNDEFINED: case UNDEFINED_TO_NULL: diff --git a/java/src/com/google/template/soy/shared/internal/SharedRuntime.java b/java/src/com/google/template/soy/shared/internal/SharedRuntime.java index e41e9365ee..5e95c634bd 100644 --- a/java/src/com/google/template/soy/shared/internal/SharedRuntime.java +++ b/java/src/com/google/template/soy/shared/internal/SharedRuntime.java @@ -26,9 +26,7 @@ import com.google.template.soy.data.SoyValueProvider; import com.google.template.soy.data.internal.SoyMapImpl; import com.google.template.soy.data.restricted.BooleanData; -import com.google.template.soy.data.restricted.FloatData; import com.google.template.soy.data.restricted.GbigintData; -import com.google.template.soy.data.restricted.IntegerData; import com.google.template.soy.data.restricted.NullData; import com.google.template.soy.data.restricted.NumberData; import com.google.template.soy.data.restricted.StringData; @@ -96,21 +94,15 @@ public static boolean switchCaseEqual(SoyValue operand0, SoyValue operand1) { return tripleEqual(operand0, operand1); } - private static boolean isNullishInteger(SoyValue value) { - return value instanceof IntegerData || value.isNullish(); - } - private static boolean isNullishNumber(SoyValue value) { - return isNullishInteger(value) || value instanceof NumberData; + return value instanceof NumberData || value.isNullish(); } /** Performs the {@code +} operator on the two values. */ @Nonnull public static SoyValue plus(SoyValue operand0, SoyValue operand1) { - if (isNullishInteger(operand0) && isNullishInteger(operand1)) { - return IntegerData.forValue(toLongForNumericOp(operand0) + toLongForNumericOp(operand1)); - } else if (isNullishNumber(operand0) && isNullishNumber(operand1)) { - return FloatData.forValue(toDoubleForNumericOp(operand0) + toDoubleForNumericOp(operand1)); + if (isNullishNumber(operand0) && isNullishNumber(operand1)) { + return NumberData.forValue(toDoubleForNumericOp(operand0) + toDoubleForNumericOp(operand1)); } else { // String concatenation is the fallback for other types (like in JS). Use the implemented // coerceToString() for the type. @@ -121,71 +113,57 @@ public static SoyValue plus(SoyValue operand0, SoyValue operand1) { /** Performs the {@code -} operator on the two values. */ @Nonnull public static SoyValue minus(SoyValue operand0, SoyValue operand1) { - if (operand0 instanceof IntegerData && operand1 instanceof IntegerData) { - return IntegerData.forValue(toLongForNumericOp(operand0) - toLongForNumericOp(operand1)); - } else { - return FloatData.forValue(toDoubleForNumericOp(operand0) - toDoubleForNumericOp(operand1)); - } + return NumberData.forValue(toDoubleForNumericOp(operand0) - toDoubleForNumericOp(operand1)); } /** Performs the {@code *} operator on the two values. */ @Nonnull public static NumberData times(SoyValue operand0, SoyValue operand1) { - if (operand0 instanceof IntegerData && operand1 instanceof IntegerData) { - return IntegerData.forValue(toLongForNumericOp(operand0) * toLongForNumericOp(operand1)); - } else { - return FloatData.forValue(toDoubleForNumericOp(operand0) * toDoubleForNumericOp(operand1)); - } + return NumberData.forValue(toDoubleForNumericOp(operand0) * toDoubleForNumericOp(operand1)); } /** Performs the {@code /} operator on the two values. */ public static NumberData dividedBy(SoyValue operand0, SoyValue operand1) { // Note: Soy always performs floating-point division, even on two integers (like JavaScript). // Note that this *will* lose precision for longs. - return FloatData.forValue(toDoubleForNumericOp(operand0) / toDoubleForNumericOp(operand1)); + return NumberData.forValue(toDoubleForNumericOp(operand0) / toDoubleForNumericOp(operand1)); } /** Performs the {@code %} operator on the two values. */ @Nonnull public static NumberData mod(SoyValue operand0, SoyValue operand1) { - if (operand0 instanceof IntegerData && operand1 instanceof IntegerData) { - return IntegerData.forValue(toLongForNumericOp(operand0) % toLongForNumericOp(operand1)); - } else { - return FloatData.forValue(toDoubleForNumericOp(operand0) % toDoubleForNumericOp(operand1)); - } + return NumberData.forValue(toDoubleForNumericOp(operand0) % toDoubleForNumericOp(operand1)); } @Nonnull public static NumberData shiftRight(SoyValue operand0, SoyValue operand1) { - return IntegerData.forValue(toLongForBitwiseOp(operand0) >> (int) toLongForBitwiseOp(operand1)); + return NumberData.forValue(toLongForBitwiseOp(operand0) >> (int) toLongForBitwiseOp(operand1)); } @Nonnull public static NumberData shiftLeft(SoyValue operand0, SoyValue operand1) { - return IntegerData.forValue(toLongForBitwiseOp(operand0) << (int) toLongForBitwiseOp(operand1)); + return NumberData.forValue(toLongForBitwiseOp(operand0) << (int) toLongForBitwiseOp(operand1)); } @Nonnull public static NumberData bitwiseOr(SoyValue operand0, SoyValue operand1) { - return IntegerData.forValue(toLongForBitwiseOp(operand0) | toLongForBitwiseOp(operand1)); + return NumberData.forValue(toLongForBitwiseOp(operand0) | toLongForBitwiseOp(operand1)); } @Nonnull public static NumberData bitwiseXor(SoyValue operand0, SoyValue operand1) { - return IntegerData.forValue(toLongForBitwiseOp(operand0) ^ toLongForBitwiseOp(operand1)); + return NumberData.forValue(toLongForBitwiseOp(operand0) ^ toLongForBitwiseOp(operand1)); } @Nonnull public static NumberData bitwiseAnd(SoyValue operand0, SoyValue operand1) { - return IntegerData.forValue(toLongForBitwiseOp(operand0) & toLongForBitwiseOp(operand1)); + return NumberData.forValue(toLongForBitwiseOp(operand0) & toLongForBitwiseOp(operand1)); } /** Performs the {@code <} operator on the two values. */ public static boolean lessThan(SoyValue left, SoyValue right) { if (left instanceof StringData && right instanceof StringData) { return left.stringValue().compareTo(right.stringValue()) < 0; - } else if (left instanceof IntegerData && right instanceof IntegerData) { - return left.longValue() < right.longValue(); } else if (left instanceof UndefinedData || right instanceof UndefinedData) { return false; } else { @@ -203,19 +181,6 @@ private static double toDoubleForNumericOp(SoyValue value) { return value.numberValue(); } - private static long toLongForNumericOp(SoyValue value) { - if (value instanceof NullData) { - return 0; - } - if (value instanceof UndefinedData) { - throw new SoyDataException("'undefined' cannot be coerced to long"); - } - if (value instanceof FloatData) { - return (long) value.floatValue(); - } - return value.longValue(); - } - private static long toLongForBitwiseOp(SoyValue value) { if (value.isNullish()) { return 0; @@ -227,8 +192,6 @@ private static long toLongForBitwiseOp(SoyValue value) { public static boolean lessThanOrEqual(SoyValue left, SoyValue right) { if (left instanceof StringData && right instanceof StringData) { return left.stringValue().compareTo(right.stringValue()) <= 0; - } else if (left instanceof IntegerData && right instanceof IntegerData) { - return left.longValue() <= right.longValue(); } else if (left instanceof UndefinedData || right instanceof UndefinedData) { return false; } else { @@ -239,11 +202,7 @@ public static boolean lessThanOrEqual(SoyValue left, SoyValue right) { /** Performs the unary negation {@code -} operator on the value. */ @Nonnull public static NumberData negative(SoyValue node) { - if (node instanceof IntegerData) { - return IntegerData.forValue(node instanceof NullData ? 0 : -node.longValue()); - } else { - return FloatData.forValue(node instanceof NullData ? 0 : -node.floatValue()); - } + return NumberData.forValue(node instanceof NullData ? 0 : -node.floatValue()); } /** Determines if the operand's string form can be equality-compared with a string. */ diff --git a/java/src/com/google/template/soy/sharedpasses/opti/SimplifyExprVisitor.java b/java/src/com/google/template/soy/sharedpasses/opti/SimplifyExprVisitor.java index fa4b4dd36a..9ba240cd19 100644 --- a/java/src/com/google/template/soy/sharedpasses/opti/SimplifyExprVisitor.java +++ b/java/src/com/google/template/soy/sharedpasses/opti/SimplifyExprVisitor.java @@ -25,7 +25,6 @@ import com.google.template.soy.data.internalutils.InternalValueUtils; import com.google.template.soy.data.restricted.BooleanData; import com.google.template.soy.data.restricted.FloatData; -import com.google.template.soy.data.restricted.IntegerData; import com.google.template.soy.data.restricted.NullData; import com.google.template.soy.data.restricted.PrimitiveData; import com.google.template.soy.data.restricted.StringData; @@ -42,14 +41,13 @@ import com.google.template.soy.exprtree.ExprNode.PrimitiveNode; import com.google.template.soy.exprtree.ExprRootNode; import com.google.template.soy.exprtree.FieldAccessNode; -import com.google.template.soy.exprtree.FloatNode; import com.google.template.soy.exprtree.FunctionNode; -import com.google.template.soy.exprtree.IntegerNode; import com.google.template.soy.exprtree.ItemAccessNode; import com.google.template.soy.exprtree.ListLiteralNode; import com.google.template.soy.exprtree.MapLiteralNode; import com.google.template.soy.exprtree.MethodCallNode; import com.google.template.soy.exprtree.NullSafeAccessNode; +import com.google.template.soy.exprtree.NumberNode; import com.google.template.soy.exprtree.OperatorNodes.AmpAmpOpNode; import com.google.template.soy.exprtree.OperatorNodes.AsOpNode; import com.google.template.soy.exprtree.OperatorNodes.BarBarOpNode; @@ -66,8 +64,8 @@ import com.google.template.soy.sharedpasses.render.RenderException; import com.google.template.soy.types.AnyType; import com.google.template.soy.types.BoolType; +import com.google.template.soy.types.NumberType; import com.google.template.soy.types.SoyType; -import com.google.template.soy.types.SoyTypes; import com.google.template.soy.types.StringType; import java.util.function.BiFunction; import javax.annotation.Nullable; @@ -266,9 +264,9 @@ private static ExprNode visitItemAccessNode(ItemAccessNode node, ExprNode baseEx ExprNode keyExpr = node.getChild(1); if (baseExpr instanceof ListLiteralNode && !((ListLiteralNode) baseExpr).containsSpreads() - && keyExpr instanceof IntegerNode) { + && keyExpr instanceof NumberNode) { ListLiteralNode listLiteral = (ListLiteralNode) baseExpr; - long index = ((IntegerNode) keyExpr).getValue(); + long index = (long) ((NumberNode) keyExpr).getValue(); if (index >= 0 && index < listLiteral.numChildren()) { return listLiteral.getChild((int) index); } else { @@ -458,9 +456,8 @@ protected void visitInstanceOfOpNode(InstanceOfOpNode node) { case STRING_NODE: staticValue = rhs.equals(StringType.getInstance()); break; - case INTEGER_NODE: - case FLOAT_NODE: - staticValue = rhs.equals(SoyTypes.NUMBER_TYPE); + case NUMBER_NODE: + staticValue = rhs.equals(NumberType.getInstance()); break; case BOOLEAN_NODE: staticValue = rhs.equals(BoolType.getInstance()); @@ -582,10 +579,8 @@ static SoyValue getConstantOrNull(ExprNode expr) { return UndefinedData.INSTANCE; case BOOLEAN_NODE: return BooleanData.forValue(((BooleanNode) expr).getValue()); - case INTEGER_NODE: - return IntegerData.forValue(((IntegerNode) expr).getValue()); - case FLOAT_NODE: - return FloatData.forValue(((FloatNode) expr).getValue()); + case NUMBER_NODE: + return FloatData.forValue(((NumberNode) expr).getValue()); case STRING_NODE: return StringData.forValue(((StringNode) expr).getValue()); case PROTO_ENUM_VALUE_NODE: diff --git a/java/src/com/google/template/soy/sharedpasses/render/EvalVisitor.java b/java/src/com/google/template/soy/sharedpasses/render/EvalVisitor.java index a8a9676d45..5ce1500f6e 100644 --- a/java/src/com/google/template/soy/sharedpasses/render/EvalVisitor.java +++ b/java/src/com/google/template/soy/sharedpasses/render/EvalVisitor.java @@ -65,8 +65,8 @@ import com.google.template.soy.data.internal.SoyRecordImpl; import com.google.template.soy.data.restricted.BooleanData; import com.google.template.soy.data.restricted.FloatData; -import com.google.template.soy.data.restricted.IntegerData; import com.google.template.soy.data.restricted.NullData; +import com.google.template.soy.data.restricted.NumberData; import com.google.template.soy.data.restricted.StringData; import com.google.template.soy.data.restricted.UndefinedData; import com.google.template.soy.exprtree.AbstractReturningExprNodeVisitor; @@ -77,10 +77,8 @@ import com.google.template.soy.exprtree.ExprNode.Kind; import com.google.template.soy.exprtree.ExprRootNode; import com.google.template.soy.exprtree.FieldAccessNode; -import com.google.template.soy.exprtree.FloatNode; import com.google.template.soy.exprtree.FunctionNode; import com.google.template.soy.exprtree.FunctionNode.ExternRef; -import com.google.template.soy.exprtree.IntegerNode; import com.google.template.soy.exprtree.ItemAccessNode; import com.google.template.soy.exprtree.ListComprehensionNode; import com.google.template.soy.exprtree.ListComprehensionNode.ComprehensionVarDefn; @@ -90,6 +88,7 @@ import com.google.template.soy.exprtree.MethodCallNode; import com.google.template.soy.exprtree.NullNode; import com.google.template.soy.exprtree.NullSafeAccessNode; +import com.google.template.soy.exprtree.NumberNode; import com.google.template.soy.exprtree.OperatorNodes.AmpAmpOpNode; import com.google.template.soy.exprtree.OperatorNodes.AsOpNode; import com.google.template.soy.exprtree.OperatorNodes.AssertNonNullOpNode; @@ -323,12 +322,7 @@ protected SoyValue visitBooleanNode(BooleanNode node) { } @Override - protected SoyValue visitIntegerNode(IntegerNode node) { - return convertResult(node.getValue()); - } - - @Override - protected SoyValue visitFloatNode(FloatNode node) { + protected SoyValue visitNumberNode(NumberNode node) { return convertResult(node.getValue()); } @@ -573,7 +567,7 @@ private SoyValue visitFieldAccessNode( throw RenderException.create( String.format( "Expected value of type '%s', but actual type was '%s'.", - fieldAccess.getType(), value.getClass().getSimpleName())); + fieldAccess.getType(), value.getSoyTypeName())); } return value != null ? value : UndefinedData.INSTANCE; @@ -623,7 +617,7 @@ private SoyValue visitItemAccessNode(ItemAccessNode itemAccess, SoyValue base, b throw RenderException.create( String.format( "Expected value of type '%s', but actual type was '%s'.", - itemAccess.getType(), value.getClass().getSimpleName())); + itemAccess.getType(), value.getSoyTypeName())); } return value != null ? value : UndefinedData.INSTANCE; @@ -912,8 +906,6 @@ protected SoyValue visitFunctionNode(FunctionNode node) { "the " + nonpluginFn.getName() + " function can't be used in templates compiled to Java"); - case TO_FLOAT: - return visitToFloatFunction(node); case DEBUG_SOY_TEMPLATE_INFO: return BooleanData.forValue(debugSoyTemplateInfo); case VE_DATA: @@ -1146,19 +1138,14 @@ private SoyValue visitIsPrimaryMsgInUseFunction(FunctionNode node) { } // if the primary message id is available or the fallback message is not available, then we // are using the primary message. - long primaryMsgId = ((IntegerNode) node.getParam(1)).getValue(); + long primaryMsgId = Long.parseLong(((StringNode) node.getParam(1)).getValue()); if (msgBundle.hasMsg(primaryMsgId)) { return BooleanData.TRUE; } - long fallbackMsgId = ((IntegerNode) node.getParam(2)).getValue(); + long fallbackMsgId = Long.parseLong(((StringNode) node.getParam(2)).getValue()); return BooleanData.forValue(!msgBundle.hasMsg(fallbackMsgId)); } - private SoyValue visitToFloatFunction(FunctionNode node) { - IntegerData v = (IntegerData) visit(node.getParam(0)); - return FloatData.forValue((double) v.longValue()); - } - private SoyValue visitNewSetFunction(FunctionNode node) { return new SetImpl(visit(node.getParam(0))); } @@ -1186,7 +1173,7 @@ private SoyValue convertResult(boolean b) { * @param i The integer to convert. */ private SoyValue convertResult(long i) { - return IntegerData.forValue(i); + return NumberData.forValue(i); } /** diff --git a/java/src/com/google/template/soy/sharedpasses/render/RenderVisitor.java b/java/src/com/google/template/soy/sharedpasses/render/RenderVisitor.java index aa12d86d42..0c4ff2d038 100644 --- a/java/src/com/google/template/soy/sharedpasses/render/RenderVisitor.java +++ b/java/src/com/google/template/soy/sharedpasses/render/RenderVisitor.java @@ -42,7 +42,7 @@ import com.google.template.soy.data.UnsafeSanitizedContentOrdainer; import com.google.template.soy.data.internal.Converters; import com.google.template.soy.data.internal.ParamStore; -import com.google.template.soy.data.restricted.IntegerData; +import com.google.template.soy.data.restricted.NumberData; import com.google.template.soy.data.restricted.StringData; import com.google.template.soy.data.restricted.UndefinedData; import com.google.template.soy.exprtree.ExprNode; @@ -502,7 +502,7 @@ protected void visitCallBasicNode(CallBasicNode node) { protected String variantString(ExprNode variantExpr, CallNode node) { try { SoyValue variantData = eval(variantExpr, node); - if (variantData instanceof IntegerData) { + if (variantData instanceof NumberData) { // An integer constant is being used as variant. Use the value string representation as // variant. return String.valueOf(variantData.longValue()); diff --git a/java/src/com/google/template/soy/sharedpasses/render/TofuJavaValue.java b/java/src/com/google/template/soy/sharedpasses/render/TofuJavaValue.java index 3ffb6b3cad..caa45fae2f 100644 --- a/java/src/com/google/template/soy/sharedpasses/render/TofuJavaValue.java +++ b/java/src/com/google/template/soy/sharedpasses/render/TofuJavaValue.java @@ -23,14 +23,12 @@ import com.google.template.soy.base.SourceLocation; import com.google.template.soy.data.SoyValue; import com.google.template.soy.data.restricted.BooleanData; -import com.google.template.soy.data.restricted.IntegerData; import com.google.template.soy.data.restricted.NumberData; import com.google.template.soy.data.restricted.StringData; import com.google.template.soy.plugin.java.restricted.JavaValue; import com.google.template.soy.types.BoolType; -import com.google.template.soy.types.IntType; +import com.google.template.soy.types.NumberType; import com.google.template.soy.types.SoyType; -import com.google.template.soy.types.SoyTypes; import com.google.template.soy.types.StringType; import javax.annotation.Nullable; @@ -103,15 +101,15 @@ public TofuJavaValue asSoyFloat() { @CanIgnoreReturnValue @Override public TofuJavaValue asSoyInt() { - checkType(IntType.getInstance()); + checkType(NumberType.getInstance()); return this; } @Override public JavaValue coerceToJavaInt() { - checkType(SoyTypes.NUMBER_TYPE); + checkType(NumberType.getInstance()); return TofuJavaValue.forSoyValue( - IntegerData.forValue(((NumberData) soyValue).coerceToInt()), sourceLocation); + NumberData.forValue(((NumberData) soyValue).coerceToInt()), sourceLocation); } @CanIgnoreReturnValue diff --git a/java/src/com/google/template/soy/sharedpasses/render/TofuTypeChecks.java b/java/src/com/google/template/soy/sharedpasses/render/TofuTypeChecks.java index fa25f6a189..e1f7e6b1e9 100644 --- a/java/src/com/google/template/soy/sharedpasses/render/TofuTypeChecks.java +++ b/java/src/com/google/template/soy/sharedpasses/render/TofuTypeChecks.java @@ -30,8 +30,7 @@ import com.google.template.soy.data.SoyValue; import com.google.template.soy.data.TemplateValue; import com.google.template.soy.data.restricted.BooleanData; -import com.google.template.soy.data.restricted.FloatData; -import com.google.template.soy.data.restricted.IntegerData; +import com.google.template.soy.data.restricted.NumberData; import com.google.template.soy.data.restricted.StringData; import com.google.template.soy.types.SoyProtoType; import com.google.template.soy.types.SoyType; @@ -122,13 +121,11 @@ private static CheckResult doIsInstance(SoyType type, SoyValue value) { return isSanitizedofKind(value, ContentKind.CSS); case BOOL: return CheckResult.fromBool(value instanceof BooleanData); - case FLOAT: - return CheckResult.fromBool(value instanceof FloatData); + case NUMBER: + return CheckResult.fromBool(value instanceof NumberData); case HTML: case ELEMENT: return isSanitizedofKind(value, ContentKind.HTML); - case INT: - return CheckResult.fromBool(value instanceof IntegerData); case JS: return isSanitizedofKind(value, ContentKind.JS); case ITERABLE: @@ -155,7 +152,7 @@ private static CheckResult doIsInstance(SoyType type, SoyValue value) { == ((SoyProtoType) type).getDescriptor()); case PROTO_ENUM: // TODO(lukes): this should also assert that the value is in range - return CheckResult.fromBool(value instanceof IntegerData); + return CheckResult.fromBool(value instanceof NumberData); case RECORD: // This allows casting a map or proto to a record. return CheckResult.fromBool(value instanceof SoyRecord); diff --git a/java/src/com/google/template/soy/sharedpasses/render/TofuValueFactory.java b/java/src/com/google/template/soy/sharedpasses/render/TofuValueFactory.java index 090719348e..37f5c226f1 100644 --- a/java/src/com/google/template/soy/sharedpasses/render/TofuValueFactory.java +++ b/java/src/com/google/template/soy/sharedpasses/render/TofuValueFactory.java @@ -201,12 +201,12 @@ public TofuJavaValue constant(boolean value) { @Override public TofuJavaValue constant(double value) { - return TofuJavaValue.forSoyValue(FloatData.forValue(value), SourceLocation.UNKNOWN); + return TofuJavaValue.forSoyValue(NumberData.forValue(value), SourceLocation.UNKNOWN); } @Override public TofuJavaValue constant(long value) { - return TofuJavaValue.forSoyValue(IntegerData.forValue(value), SourceLocation.UNKNOWN); + return TofuJavaValue.forSoyValue(NumberData.forValue(value), SourceLocation.UNKNOWN); } @Override @@ -319,6 +319,10 @@ private Object adaptParam(TofuJavaValue tofuVal, Class type, Method method, i : value.numberValue(); } else if (type.isInstance(value)) { return value; + } else if (type == IntegerData.class) { + return IntegerData.forValue(value.longValue()); + } else if (type == FloatData.class) { + return FloatData.forValue(value.floatValue()); } Class primitiveType = Primitives.unwrap(type); @@ -418,9 +422,7 @@ private Object adaptParam(TofuJavaValue tofuVal, Class type, Method method, i private Object adaptParamItem(SoyValueProvider item, SoyType elmType) { SoyValue val = item.resolve(); switch (elmType.getKind()) { - case INT: - return val.longValue(); - case FLOAT: + case NUMBER: return val.floatValue(); case STRING: return val.coerceToString(); diff --git a/java/src/com/google/template/soy/soyparse/ParseErrors.java b/java/src/com/google/template/soy/soyparse/ParseErrors.java index 4d97c7fd64..0519029f06 100644 --- a/java/src/com/google/template/soy/soyparse/ParseErrors.java +++ b/java/src/com/google/template/soy/soyparse/ParseErrors.java @@ -25,8 +25,7 @@ import com.google.template.soy.error.SoyErrorKind; import com.google.template.soy.error.SoyErrorKind.StyleAllowance; import com.google.template.soy.exprtree.ExprNode; -import com.google.template.soy.exprtree.FloatNode; -import com.google.template.soy.exprtree.IntegerNode; +import com.google.template.soy.exprtree.NumberNode; import com.google.template.soy.exprtree.StringNode; import com.ibm.icu.text.MessagePattern; import java.util.List; @@ -290,7 +289,7 @@ static String validateSelectCaseLabel(ExprNode caseValue, ErrorReporter reporter isError = false; } } else { - isNumeric = caseValue instanceof FloatNode || caseValue instanceof IntegerNode; + isNumeric = caseValue instanceof NumberNode; isError = true; value = ""; } diff --git a/java/src/com/google/template/soy/soyparse/SoyFileParser.jj b/java/src/com/google/template/soy/soyparse/SoyFileParser.jj index 67ba1cda90..fac27b72fc 100644 --- a/java/src/com/google/template/soy/soyparse/SoyFileParser.jj +++ b/java/src/com/google/template/soy/soyparse/SoyFileParser.jj @@ -60,30 +60,25 @@ import com.google.template.soy.base.internal.BaseUtils; import com.google.template.soy.base.internal.IdGenerator; import com.google.template.soy.base.internal.Identifier; import com.google.template.soy.base.internal.QuoteStyle; -import com.google.template.soy.base.internal.SoyFileSupplier; import com.google.template.soy.basetree.Node; import com.google.template.soy.error.ErrorReporter.Checkpoint; import com.google.template.soy.error.ErrorReporter; import com.google.template.soy.error.SoyErrorKind.StyleAllowance; import com.google.template.soy.error.SoyErrorKind; -import com.google.template.soy.exprtree.AbstractParentExprNode; import com.google.template.soy.exprtree.BooleanNode; import com.google.template.soy.exprtree.CallableExprBuilder; import com.google.template.soy.exprtree.ExprNode.PrimitiveNode; import com.google.template.soy.exprtree.ExprNode; import com.google.template.soy.exprtree.ExprRootNode; import com.google.template.soy.exprtree.FieldAccessNode; -import com.google.template.soy.exprtree.FloatNode; -import com.google.template.soy.exprtree.FunctionNode; import com.google.template.soy.exprtree.GroupNode; -import com.google.template.soy.exprtree.IntegerNode; import com.google.template.soy.exprtree.ItemAccessNode; import com.google.template.soy.exprtree.ListComprehensionNode; import com.google.template.soy.exprtree.ListLiteralNode; import com.google.template.soy.exprtree.MapLiteralFromListNode; import com.google.template.soy.exprtree.MapLiteralNode; -import com.google.template.soy.exprtree.MethodCallNode; import com.google.template.soy.exprtree.NullNode; +import com.google.template.soy.exprtree.NumberNode; import com.google.template.soy.exprtree.Operator; import com.google.template.soy.exprtree.RecordLiteralNode; import com.google.template.soy.exprtree.SoyPrecedence; @@ -91,9 +86,7 @@ import com.google.template.soy.exprtree.StringNode; import com.google.template.soy.exprtree.TemplateLiteralNode; import com.google.template.soy.exprtree.TypeLiteralNode; import com.google.template.soy.exprtree.UndefinedNode; -import com.google.template.soy.exprtree.VarDefn; import com.google.template.soy.exprtree.VarRefNode; -import com.google.template.soy.soytree.AbstractSoyNode; import com.google.template.soy.soytree.AliasDeclaration; import com.google.template.soy.soytree.AssignmentNode; import com.google.template.soy.soytree.ByteOffsetIndex; @@ -160,7 +153,6 @@ import com.google.template.soy.soytree.defn.ImportedVar; import com.google.template.soy.soytree.defn.TemplateHeaderVarDefn; import com.google.template.soy.soytree.defn.TemplateParam; import com.google.template.soy.soytree.defn.TemplateStateVar; -import com.google.template.soy.types.FunctionType; import com.google.template.soy.types.TemplateType.ParameterKind; import com.google.template.soy.types.TemplateType; import com.google.template.soy.types.ast.FunctionTypeNode; @@ -172,14 +164,10 @@ import com.google.template.soy.types.ast.RecordTypeNode; import com.google.template.soy.types.ast.TemplateTypeNode; import com.google.template.soy.types.ast.TypeNode; import com.google.template.soy.types.ast.UnionTypeNode; -import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; import java.util.List; -import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.annotation.Nullable; @@ -2976,14 +2964,16 @@ private MsgPluralNode MsgPlural() : caseBeginEnd = { int value; - if (!(caseExpr instanceof IntegerNode)) { + if (!(caseExpr instanceof NumberNode)) { errorReporter.report(caseExpr.getSourceLocation(), PLURAL_CASE_MALFORMED); value = 0; } else { - value = (int) ((IntegerNode) caseExpr).getValue(); - if (value < 0) { - errorReporter.report(caseExpr.getSourceLocation(), PLURAL_CASE_OUT_OF_BOUNDS, value); + double valueAsDouble = ((NumberNode) caseExpr).getValue(); + if (valueAsDouble < 0 || (int) valueAsDouble != valueAsDouble) { + errorReporter.report(caseExpr.getSourceLocation(), PLURAL_CASE_OUT_OF_BOUNDS, valueAsDouble); value = 0; + } else { + value = (int) valueAsDouble; } } } @@ -4649,12 +4639,9 @@ private ExprNode PrecExpr12(ExprState state) : SourceLocation opLocation = createSrcLoc(op); SourceLocation location = opLocation.extend(expr.getSourceLocation()); - if (op.kind == MINUS && expr instanceof IntegerNode) { - long value = -1 * ((IntegerNode) expr).getValue(); - expr = new IntegerNode(value, location); - } else if (op.kind == MINUS && expr instanceof FloatNode) { - double value = -1 * ((FloatNode) expr).getValue(); - expr = new FloatNode(value, location); + if (op.kind == MINUS && expr instanceof NumberNode) { + double value = -1 * ((NumberNode) expr).getValue(); + expr = new NumberNode(value, location); } else { expr = createOperatorNode( location, @@ -5010,26 +4997,26 @@ private PrimitiveNode Primitive() : | tok = { SourceLocation loc = createSrcLoc(tok); - Long parsed = Longs.tryParse(tok.image, 10); - if (parsed == null || !IntegerNode.isInRange(parsed)) { - errorReporter.report(loc, INTEGER_OUT_OF_RANGE); - parsed = 0L; - } - primitive = new IntegerNode(parsed, loc); - } + Long parsed = Longs.tryParse(tok.image, 10); + if (parsed == null || !NumberNode.isInRange(parsed)) { + errorReporter.report(loc, INTEGER_OUT_OF_RANGE); + parsed = 0L; + } + primitive = new NumberNode(parsed, loc); +} | tok = { SourceLocation loc = createSrcLoc(tok); - Long parsed = Longs.tryParse(tok.image.substring(2), 16); - if (parsed == null || !IntegerNode.isInRange(parsed)) { - errorReporter.report(loc, INTEGER_OUT_OF_RANGE); - parsed = 0L; - } - primitive = new IntegerNode(parsed, loc); - } + Long parsed = Longs.tryParse(tok.image.substring(2), 16); + if (parsed == null || !NumberNode.isInRange(parsed)) { + errorReporter.report(loc, INTEGER_OUT_OF_RANGE); + parsed = 0L; + } + primitive = new NumberNode(parsed, loc); +} | tok = - { primitive = new FloatNode(Double.parseDouble(tok.image), createSrcLoc(tok)); } + { primitive = new NumberNode(Double.parseDouble(tok.image), createSrcLoc(tok)); } | primitive = StringLiteral() ) diff --git a/java/src/com/google/template/soy/soytree/MsgSubstUnitPlaceholderNameUtils.java b/java/src/com/google/template/soy/soytree/MsgSubstUnitPlaceholderNameUtils.java index 7ba3112bab..08e54d2b4e 100644 --- a/java/src/com/google/template/soy/soytree/MsgSubstUnitPlaceholderNameUtils.java +++ b/java/src/com/google/template/soy/soytree/MsgSubstUnitPlaceholderNameUtils.java @@ -29,9 +29,9 @@ import com.google.template.soy.exprtree.ExprNode; import com.google.template.soy.exprtree.FieldAccessNode; import com.google.template.soy.exprtree.GlobalNode; -import com.google.template.soy.exprtree.IntegerNode; import com.google.template.soy.exprtree.ItemAccessNode; import com.google.template.soy.exprtree.NullSafeAccessNode; +import com.google.template.soy.exprtree.NumberNode; import com.google.template.soy.exprtree.VarRefNode; import java.util.List; @@ -262,14 +262,14 @@ static ImmutableList genCandidateBaseNamesForExpr(ExprNode exprNode) { } else if (exprNode instanceof ItemAccessNode) { ItemAccessNode itemAccess = (ItemAccessNode) exprNode; exprNode = itemAccess.getBaseExprChild(); - if (itemAccess.getKeyExprChild() instanceof IntegerNode) { + if (itemAccess.getKeyExprChild() instanceof NumberNode) { // Prefix with index, but don't add to baseNames list since it's not a valid ident. - IntegerNode keyValue = (IntegerNode) itemAccess.getKeyExprChild(); + NumberNode keyValue = (NumberNode) itemAccess.getKeyExprChild(); if (keyValue.getValue() < 0) { // Stop if we encounter an invalid key. break; } - nameSegment = Long.toString(keyValue.getValue()); + nameSegment = Long.toString((long) keyValue.getValue()); baseName = BaseUtils.convertToUpperUnderscore(nameSegment) + ((baseName != null) ? "_" + baseName : ""); diff --git a/java/src/com/google/template/soy/soytree/TemplateBasicNode.java b/java/src/com/google/template/soy/soytree/TemplateBasicNode.java index 949eda3462..85431b6e32 100644 --- a/java/src/com/google/template/soy/soytree/TemplateBasicNode.java +++ b/java/src/com/google/template/soy/soytree/TemplateBasicNode.java @@ -22,9 +22,9 @@ import com.google.template.soy.error.SoyErrorKind; import com.google.template.soy.exprtree.ExprRootNode; import com.google.template.soy.exprtree.TemplateLiteralNode; +import com.google.template.soy.types.NumberType; import com.google.template.soy.types.SoyType; import com.google.template.soy.types.SoyTypeRegistry; -import com.google.template.soy.types.SoyTypes; import com.google.template.soy.types.StringType; import com.google.template.soy.types.TemplateType; import com.google.template.soy.types.UndefinedType; @@ -141,7 +141,7 @@ public ExprRootNode getVariantExpr() { } private static boolean isValidVariantType(SoyType type) { - return type.equals(SoyTypes.NUMBER_TYPE) + return type.equals(NumberType.getInstance()) || type.equals(StringType.getInstance()) || type.getKind().equals(SoyType.Kind.PROTO_ENUM); } diff --git a/java/src/com/google/template/soy/soytree/TemplateDelegateNode.java b/java/src/com/google/template/soy/soytree/TemplateDelegateNode.java index ecefe90bf4..9f23f61453 100644 --- a/java/src/com/google/template/soy/soytree/TemplateDelegateNode.java +++ b/java/src/com/google/template/soy/soytree/TemplateDelegateNode.java @@ -27,7 +27,7 @@ import com.google.template.soy.exprtree.ExprNode; import com.google.template.soy.exprtree.ExprRootNode; import com.google.template.soy.exprtree.GlobalNode; -import com.google.template.soy.exprtree.IntegerNode; +import com.google.template.soy.exprtree.NumberNode; import com.google.template.soy.exprtree.StringNode; import javax.annotation.Nullable; @@ -206,10 +206,11 @@ private static void validateVariantExpression(ExprNode primitiveNode, ErrorRepor reporter.report(sn.getSourceLocation(), INVALID_VARIANT_STRING, sn.getValue()); } break; - case INTEGER_NODE: - IntegerNode in = (IntegerNode) primitiveNode; - if (in.getValue() < 0) { - reporter.report(in.getSourceLocation(), INVALID_VARIANT_INTEGER, in.getValue()); + case NUMBER_NODE: + NumberNode in = (NumberNode) primitiveNode; + double val = in.getValue(); + if (val < 0 || (int) val != val) { + reporter.report(in.getSourceLocation(), INVALID_VARIANT_INTEGER, val); } break; case PROTO_ENUM_VALUE_NODE: diff --git a/java/src/com/google/template/soy/soytree/TemplateMetadataSerializer.java b/java/src/com/google/template/soy/soytree/TemplateMetadataSerializer.java index 6b3116c05d..9819bd6dcb 100644 --- a/java/src/com/google/template/soy/soytree/TemplateMetadataSerializer.java +++ b/java/src/com/google/template/soy/soytree/TemplateMetadataSerializer.java @@ -33,13 +33,12 @@ import com.google.template.soy.error.SoyErrorKind; import com.google.template.soy.types.AnyType; import com.google.template.soy.types.BoolType; -import com.google.template.soy.types.FloatType; import com.google.template.soy.types.FunctionType; import com.google.template.soy.types.GbigintType; import com.google.template.soy.types.IndexedType; -import com.google.template.soy.types.IntType; import com.google.template.soy.types.MessageType; import com.google.template.soy.types.NullType; +import com.google.template.soy.types.NumberType; import com.google.template.soy.types.RecordType; import com.google.template.soy.types.SanitizedType; import com.google.template.soy.types.SanitizedType.AttributesType; @@ -336,16 +335,14 @@ static SoyType fromProto( return AnyType.getInstance(); case UNKNOWN: return UnknownType.getInstance(); - case INT: - return IntType.getInstance(); case NULL: return NullType.getInstance(); case UNDEFINED: return UndefinedType.getInstance(); case BOOL: return BoolType.getInstance(); - case FLOAT: - return FloatType.getInstance(); + case NUMBER: + return NumberType.getInstance(); case STRING: return StringType.getInstance(); case GBIGINT: diff --git a/java/src/com/google/template/soy/soytree/TemplateNode.java b/java/src/com/google/template/soy/soytree/TemplateNode.java index 88911c40f0..2cd3beb5af 100644 --- a/java/src/com/google/template/soy/soytree/TemplateNode.java +++ b/java/src/com/google/template/soy/soytree/TemplateNode.java @@ -35,7 +35,7 @@ import com.google.template.soy.exprtree.AbstractVarDefn; import com.google.template.soy.exprtree.ExprNode; import com.google.template.soy.exprtree.ExprRootNode; -import com.google.template.soy.exprtree.IntegerNode; +import com.google.template.soy.exprtree.NumberNode; import com.google.template.soy.exprtree.ProtoEnumValueNode; import com.google.template.soy.exprtree.StringNode; import com.google.template.soy.exprtree.VarDefn; @@ -782,9 +782,9 @@ public boolean isInjected() { */ @Nullable protected static String variantExprToString(ExprNode exprNode) { - if (exprNode instanceof IntegerNode) { - long variantValue = ((IntegerNode) exprNode).getValue(); - return String.valueOf(variantValue); + if (exprNode instanceof NumberNode) { + double variantValue = ((NumberNode) exprNode).getValue(); + return String.valueOf((int) variantValue); } if (exprNode instanceof ProtoEnumValueNode) { return String.valueOf(((ProtoEnumValueNode) exprNode).getValue()); diff --git a/java/src/com/google/template/soy/treebuilder/ExprNodes.java b/java/src/com/google/template/soy/treebuilder/ExprNodes.java index 16d1be4543..7446bb3959 100644 --- a/java/src/com/google/template/soy/treebuilder/ExprNodes.java +++ b/java/src/com/google/template/soy/treebuilder/ExprNodes.java @@ -28,16 +28,15 @@ import com.google.template.soy.exprtree.ExprNode; import com.google.template.soy.exprtree.ExprNode.OperatorNode; import com.google.template.soy.exprtree.FieldAccessNode; -import com.google.template.soy.exprtree.FloatNode; import com.google.template.soy.exprtree.FunctionNode; import com.google.template.soy.exprtree.GlobalNode; import com.google.template.soy.exprtree.GroupNode; -import com.google.template.soy.exprtree.IntegerNode; import com.google.template.soy.exprtree.ItemAccessNode; import com.google.template.soy.exprtree.ListComprehensionNode; import com.google.template.soy.exprtree.ListLiteralNode; import com.google.template.soy.exprtree.MethodCallNode; import com.google.template.soy.exprtree.NullNode; +import com.google.template.soy.exprtree.NumberNode; import com.google.template.soy.exprtree.Operator; import com.google.template.soy.exprtree.RecordLiteralNode; import com.google.template.soy.exprtree.StringNode; @@ -76,8 +75,8 @@ public static FieldAccessNode fieldAccess(ExprNode base, String fieldName, boole return new FieldAccessNode(maybeCopyNode(base), fieldName, SourceLocation.UNKNOWN, isNullSafe); } - public static FloatNode floatLiteral(double value) { - return new FloatNode(value, SourceLocation.UNKNOWN); + public static NumberNode floatLiteral(double value) { + return new NumberNode(value, SourceLocation.UNKNOWN); } public static FunctionNode function(String name, ExprNode... params) { @@ -111,8 +110,8 @@ public static GroupNode group(ExprNode expr) { return new GroupNode(maybeCopyNode(expr), SourceLocation.UNKNOWN); } - public static IntegerNode integerLiteral(long value) { - return new IntegerNode(value, SourceLocation.UNKNOWN); + public static NumberNode integerLiteral(long value) { + return new NumberNode((double) value, SourceLocation.UNKNOWN); } public static ItemAccessNode itemAccess(ExprNode base, ExprNode key, boolean isNullSafe) { diff --git a/java/src/com/google/template/soy/types/FloatType.java b/java/src/com/google/template/soy/types/FloatType.java deleted file mode 100644 index e4d9e4a0dd..0000000000 --- a/java/src/com/google/template/soy/types/FloatType.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2013 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.template.soy.types; - -import com.google.template.soy.soytree.SoyTypeP; - -/** - * Soy floating-point type. - */ -public final class FloatType extends PrimitiveType { - - private static final FloatType INSTANCE = new FloatType(); - - // Not constructible - use getInstance(). - private FloatType() {} - - @Override - public Kind getKind() { - return Kind.FLOAT; - } - - @Override - public String toString() { - return "float"; - } - - @Override - void doToProto(SoyTypeP.Builder builder) { - builder.setPrimitive(SoyTypeP.PrimitiveTypeP.FLOAT); - } - - /** Return the single instance of this type. */ - public static FloatType getInstance() { - return INSTANCE; - } -} diff --git a/java/src/com/google/template/soy/types/MapType.java b/java/src/com/google/template/soy/types/MapType.java index 36e5ea786a..097bd436ef 100644 --- a/java/src/com/google/template/soy/types/MapType.java +++ b/java/src/com/google/template/soy/types/MapType.java @@ -58,7 +58,7 @@ public boolean isEmpty() { } private static final ImmutableSet ALLOWED_KINDS = - ImmutableSet.of(Kind.BOOL, Kind.INT, Kind.FLOAT, Kind.STRING, Kind.PROTO_ENUM, Kind.GBIGINT); + ImmutableSet.of(Kind.BOOL, Kind.NUMBER, Kind.STRING, Kind.PROTO_ENUM, Kind.GBIGINT); /** Whether the type is permissible as a key in a declared map type literal. */ // LINT.IfChange(allowed_soy_map_key_types) diff --git a/java/src/com/google/template/soy/types/IntType.java b/java/src/com/google/template/soy/types/NumberType.java similarity index 69% rename from java/src/com/google/template/soy/types/IntType.java rename to java/src/com/google/template/soy/types/NumberType.java index aacec11ee4..ce3e45f8ea 100644 --- a/java/src/com/google/template/soy/types/IntType.java +++ b/java/src/com/google/template/soy/types/NumberType.java @@ -18,40 +18,38 @@ import com.google.template.soy.soytree.SoyTypeP; -/** - * Soy integer type. - */ -public final class IntType extends PrimitiveType { +/** Soy floating-point type. */ +public final class NumberType extends PrimitiveType { - private static final IntType INSTANCE = new IntType(); + private static final NumberType INSTANCE = new NumberType(); // Not constructible - use getInstance(). - private IntType() {} + private NumberType() {} @Override public Kind getKind() { - return Kind.INT; + return Kind.NUMBER; } @Override + public String toString() { + return "number"; + } + boolean doIsAssignableFromNonUnionType(SoyType srcType) { Kind kind = srcType.getKind(); - // enums are implicitly assignable to ints since that is the runtime representation in all + // enums are implicitly assignable to number since that is the runtime representation in all // backends - return kind == Kind.INT || kind == Kind.PROTO_ENUM; - } - - @Override - public String toString() { - return "int"; + return kind == Kind.NUMBER || kind == Kind.PROTO_ENUM; } @Override void doToProto(SoyTypeP.Builder builder) { - builder.setPrimitive(SoyTypeP.PrimitiveTypeP.INT); + builder.setPrimitive(SoyTypeP.PrimitiveTypeP.NUMBER); } + /** Return the single instance of this type. */ - public static IntType getInstance() { + public static NumberType getInstance() { return INSTANCE; } } diff --git a/java/src/com/google/template/soy/types/SoyProtoType.java b/java/src/com/google/template/soy/types/SoyProtoType.java index 59dcefdb0e..0a1154ae71 100644 --- a/java/src/com/google/template/soy/types/SoyProtoType.java +++ b/java/src/com/google/template/soy/types/SoyProtoType.java @@ -18,7 +18,6 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.ImmutableSet.toImmutableSet; -import static com.google.template.soy.types.SoyTypes.NUMBER_TYPE; import com.google.common.base.Objects; import com.google.common.base.Preconditions; @@ -94,14 +93,14 @@ protected SoyType visitLongAsInt() { case FORCE_GBIGINT: return GbigintType.getInstance(); case FOLLOW_JS_TYPE: - return IntType.getInstance(); + return NumberType.getInstance(); } throw new AssertionError(); } @Override protected SoyType visitUnsignedInt() { - return setterField ? NUMBER_TYPE : IntType.getInstance(); + return NumberType.getInstance(); } @Override @@ -144,7 +143,7 @@ protected SoyType visitBool() { @Override protected SoyType visitInt() { - return setterField ? NUMBER_TYPE : IntType.getInstance(); + return NumberType.getInstance(); } @Override @@ -159,12 +158,12 @@ protected SoyType visitString() { @Override protected SoyType visitDoubleAsFloat() { - return setterField ? NUMBER_TYPE : FloatType.getInstance(); + return NumberType.getInstance(); } @Override protected SoyType visitFloat() { - return setterField ? NUMBER_TYPE : FloatType.getInstance(); + return NumberType.getInstance(); } @Override diff --git a/java/src/com/google/template/soy/types/SoyType.java b/java/src/com/google/template/soy/types/SoyType.java index 3507448be8..7f65666dd0 100644 --- a/java/src/com/google/template/soy/types/SoyType.java +++ b/java/src/com/google/template/soy/types/SoyType.java @@ -92,8 +92,7 @@ public enum Kind { NULL, UNDEFINED, BOOL, - INT, - FLOAT, + NUMBER, STRING, GBIGINT, // Sanitized types (subtypes of string) diff --git a/java/src/com/google/template/soy/types/SoyTypes.java b/java/src/com/google/template/soy/types/SoyTypes.java index e4170ba11e..44835e68be 100644 --- a/java/src/com/google/template/soy/types/SoyTypes.java +++ b/java/src/com/google/template/soy/types/SoyTypes.java @@ -49,10 +49,6 @@ public final class SoyTypes { private SoyTypes() {} - /** Shared constant for the 'number' type. */ - public static final SoyType NUMBER_TYPE = - UnionType.of(IntType.getInstance(), FloatType.getInstance()); - public static final SoyType GBIGINT = GbigintType.getInstance(); // TODO: b/319288438 - Remove these types once soy setters are migrated to gbigint. @@ -61,7 +57,7 @@ private SoyTypes() {} // setters in JS normally accept gbigint|number|string regardless of any jstype annotations. This // has not historically been the case in Soy, so we need to smooth over the difference. public static final SoyType GBIGINT_OR_NUMBER_FOR_MIGRATION = - UnionType.of(GbigintType.getInstance(), NUMBER_TYPE); + UnionType.of(GbigintType.getInstance(), NumberType.getInstance()); public static final SoyType GBIGINT_OR_STRING_FOR_MIGRATION = UnionType.of(GbigintType.getInstance(), StringType.getInstance()); @@ -96,8 +92,7 @@ private SoyTypes() {} private static final ImmutableSet ALWAYS_COMPARABLE_KINDS = Sets.immutableEnumSet(Kind.UNKNOWN, Kind.ANY, Kind.NULL, Kind.UNDEFINED); - public static final ImmutableSet ARITHMETIC_PRIMITIVES = - Sets.immutableEnumSet(Kind.INT, Kind.FLOAT); + public static final ImmutableSet ARITHMETIC_PRIMITIVES = Sets.immutableEnumSet(Kind.NUMBER); public static final ImmutableSet NULLISH_KINDS = Sets.immutableEnumSet(Kind.NULL, Kind.UNDEFINED); @@ -113,7 +108,7 @@ private SoyTypes() {} .build(); public static boolean isIntFloatOrNumber(SoyType type) { - return SoyTypes.NUMBER_TYPE.isAssignableFromStrict(type); + return NumberType.getInstance().isAssignableFromStrict(type); } /** @@ -242,7 +237,7 @@ public static boolean isNullOrUndefined(SoyType type) { } public static boolean isNumericOrUnknown(SoyType type) { - return type.getKind() == Kind.UNKNOWN || NUMBER_TYPE.isAssignableFromStrict(type); + return type.getKind() == Kind.UNKNOWN || NumberType.getInstance().isAssignableFromStrict(type); } public static Optional computeStricterType(SoyType t0, SoyType t1) { @@ -317,7 +312,7 @@ public static Optional computeLowestCommonTypeArithmetic(SoyType t0, So } else { // If we get here then we know that we have a mix of float and int. In this case arithmetic // ops always 'upgrade' to float. So just return that. - return Optional.of(FloatType.getInstance()); + return Optional.of(NumberType.getInstance()); } } @@ -784,6 +779,7 @@ public static boolean isValidInstanceOfOperand(SoyType type) { switch (type.getKind()) { case STRING: case BOOL: + case NUMBER: case HTML: case JS: case URI: @@ -802,8 +798,6 @@ public static boolean isValidInstanceOfOperand(SoyType type) { case MAP: return ((MapType) type).getKeyType().equals(AnyType.getInstance()) && ((MapType) type).getValueType().equals(AnyType.getInstance()); - case UNION: - return type.equals(NUMBER_TYPE); default: return false; } diff --git a/java/src/com/google/template/soy/types/TypeRegistries.java b/java/src/com/google/template/soy/types/TypeRegistries.java index c17fc57aa5..973570aa14 100644 --- a/java/src/com/google/template/soy/types/TypeRegistries.java +++ b/java/src/com/google/template/soy/types/TypeRegistries.java @@ -16,8 +16,6 @@ package com.google.template.soy.types; -import static com.google.common.base.Preconditions.checkState; -import static com.google.template.soy.types.SoyTypes.NUMBER_TYPE; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -102,8 +100,6 @@ private static final class TypeInternerImpl implements TypeInterner { private final Map protoImportTypes = new ConcurrentHashMap<>(); public TypeInternerImpl() { - // Register the special number type so == comparisons work - checkState(types.intern(NUMBER_TYPE) == NUMBER_TYPE); } @Override @@ -190,10 +186,10 @@ private static final class BuiltinTypeRegistry implements TypeRegistry { .put("null", NullType.getInstance()) .put("undefined", UndefinedType.getInstance()) .put("bool", BoolType.getInstance()) - .put("int", IntType.getInstance()) - .put("float", FloatType.getInstance()) + .put("int", NumberType.getInstance()) + .put("float", NumberType.getInstance()) .put("string", StringType.getInstance()) - .put("number", NUMBER_TYPE) + .put("number", NumberType.getInstance()) .put("gbigint", GbigintType.getInstance()) .put("html", HtmlType.getInstance()) .put("never", NeverType.getInstance()) diff --git a/java/tests/com/google/template/soy/basicfunctions/CeilingFunctionTest.java b/java/tests/com/google/template/soy/basicfunctions/CeilingFunctionTest.java index da4a7591df..14bc1758dd 100644 --- a/java/tests/com/google/template/soy/basicfunctions/CeilingFunctionTest.java +++ b/java/tests/com/google/template/soy/basicfunctions/CeilingFunctionTest.java @@ -36,13 +36,13 @@ public void testComputeForJavaSource() { SoyJavaSourceFunctionTester factory = new SoyJavaSourceFunctionTester(ceilingFunction); Object result = factory.callFunction(1L); - assertThat(result).isEqualTo(1L); + assertThat(result).isEqualTo(1D); result = factory.callFunction(2.5D); - assertThat(result).isEqualTo(3L); + assertThat(result).isEqualTo(3D); result = factory.callFunction(FloatData.forValue(2.5D)); - assertThat(result).isEqualTo(3L); + assertThat(result).isEqualTo(3D); } } diff --git a/java/tests/com/google/template/soy/basicfunctions/FloorFunctionTest.java b/java/tests/com/google/template/soy/basicfunctions/FloorFunctionTest.java index 4ff78f7789..bb27d8c6d7 100644 --- a/java/tests/com/google/template/soy/basicfunctions/FloorFunctionTest.java +++ b/java/tests/com/google/template/soy/basicfunctions/FloorFunctionTest.java @@ -36,12 +36,12 @@ public void testComputeForJavaSource() { SoyJavaSourceFunctionTester factory = new SoyJavaSourceFunctionTester(floorFunction); Object result = factory.callFunction(1L); - assertThat(result).isEqualTo(1L); + assertThat(result).isEqualTo(1D); result = factory.callFunction(2.5D); - assertThat(result).isEqualTo(2L); + assertThat(result).isEqualTo(2D); result = factory.callFunction(FloatData.forValue(2.5D)); - assertThat(result).isEqualTo(2L); + assertThat(result).isEqualTo(2D); } } diff --git a/java/tests/com/google/template/soy/basicfunctions/RandomIntFunctionTest.java b/java/tests/com/google/template/soy/basicfunctions/RandomIntFunctionTest.java index 4fb5c2f8e9..b5349a0404 100644 --- a/java/tests/com/google/template/soy/basicfunctions/RandomIntFunctionTest.java +++ b/java/tests/com/google/template/soy/basicfunctions/RandomIntFunctionTest.java @@ -44,7 +44,7 @@ public void testComputeForJavaSource() { arg = IntegerData.forValue(3); Set seenResults = Sets.newHashSetWithExpectedSize(3); for (int i = 0; i < 100; i++) { - int result = ((Long) tester.callFunction(arg)).intValue(); + int result = ((Number) tester.callFunction(arg)).intValue(); assertThat(result).isAtLeast(0); assertThat(result).isAtMost(2); seenResults.add(result); diff --git a/java/tests/com/google/template/soy/data/SoyListDataTest.java b/java/tests/com/google/template/soy/data/SoyListDataTest.java index aa7b5eaf03..554d5cc774 100644 --- a/java/tests/com/google/template/soy/data/SoyListDataTest.java +++ b/java/tests/com/google/template/soy/data/SoyListDataTest.java @@ -195,16 +195,4 @@ public void testIsEqualto() { new EqualsTester().addEqualityGroup(sld0).addEqualityGroup(sld1).testEquals(); assertThat(sld0.equals(new SoyListData())).isFalse(); } - - @Test - public void testLongHandling() { - // long value will loose precision if converted to double. - long l = 987654321987654321L; - SoyListData sld = new SoyListData(); - sld.add(l); - assertThat(sld.getLong(0)).isEqualTo(l); - - sld = new SoyListData(l); - assertThat(sld.getLong(0)).isEqualTo(l); - } } diff --git a/java/tests/com/google/template/soy/data/SoyMapDataTest.java b/java/tests/com/google/template/soy/data/SoyMapDataTest.java index 0b3bf765de..fc626f4f26 100644 --- a/java/tests/com/google/template/soy/data/SoyMapDataTest.java +++ b/java/tests/com/google/template/soy/data/SoyMapDataTest.java @@ -197,18 +197,6 @@ public void testIsEqualto() { assertThat(smd0.equals(new SoyMapData())).isFalse(); } - @Test - public void testLongHandling() { - // long value will loose precision if converted to double. - long l = 987654321987654321L; - SoyMapData smd = new SoyMapData(); - smd.put("long", l); - assertThat(smd.getLong("long")).isEqualTo(l); - - smd = new SoyMapData("long", l); - assertThat(smd.getLong("long")).isEqualTo(l); - } - @Test public void testRuntimeTypeTransitions() { ImmutableMap underlying = ImmutableMap.of("a", "b"); diff --git a/java/tests/com/google/template/soy/data/internalutils/InternalValueUtilsTest.java b/java/tests/com/google/template/soy/data/internalutils/InternalValueUtilsTest.java index 3ae53e65f3..933ff0649c 100644 --- a/java/tests/com/google/template/soy/data/internalutils/InternalValueUtilsTest.java +++ b/java/tests/com/google/template/soy/data/internalutils/InternalValueUtilsTest.java @@ -27,13 +27,13 @@ import com.google.template.soy.data.restricted.FloatData; import com.google.template.soy.data.restricted.IntegerData; import com.google.template.soy.data.restricted.NullData; +import com.google.template.soy.data.restricted.NumberData; import com.google.template.soy.data.restricted.PrimitiveData; import com.google.template.soy.data.restricted.StringData; import com.google.template.soy.data.restricted.UndefinedData; import com.google.template.soy.exprtree.BooleanNode; -import com.google.template.soy.exprtree.FloatNode; -import com.google.template.soy.exprtree.IntegerNode; import com.google.template.soy.exprtree.NullNode; +import com.google.template.soy.exprtree.NumberNode; import com.google.template.soy.exprtree.StringNode; import com.google.template.soy.exprtree.UndefinedNode; import java.util.Iterator; @@ -58,12 +58,14 @@ public void testConvertPrimitiveDataToExpr() { .getValue()); assertEquals( 26, - ((IntegerNode) - InternalValueUtils.convertPrimitiveDataToExpr(IntegerData.forValue(26), location)) - .getValue()); + (int) + ((NumberNode) + InternalValueUtils.convertPrimitiveDataToExpr( + NumberData.forValue(26), location)) + .getValue()); assertEquals( -3.14159, - ((FloatNode) + ((NumberNode) InternalValueUtils.convertPrimitiveDataToExpr( FloatData.forValue(-3.14159), location)) .getValue(), @@ -89,12 +91,12 @@ public void testConvertPrimitiveExprToData() { .booleanValue()); assertEquals( -1, - InternalValueUtils.convertPrimitiveExprToData(new IntegerNode(-1, SourceLocation.UNKNOWN)) + InternalValueUtils.convertPrimitiveExprToData(new NumberNode(-1, SourceLocation.UNKNOWN)) .integerValue()); assertEquals( 6.02e23, InternalValueUtils.convertPrimitiveExprToData( - new FloatNode(6.02e23, SourceLocation.UNKNOWN)) + new NumberNode(6.02e23, SourceLocation.UNKNOWN)) .floatValue(), 0.0); assertEquals( diff --git a/java/tests/com/google/template/soy/data/restricted/PrimitiveDataTest.java b/java/tests/com/google/template/soy/data/restricted/PrimitiveDataTest.java index 8ee9439c0d..2075b65969 100644 --- a/java/tests/com/google/template/soy/data/restricted/PrimitiveDataTest.java +++ b/java/tests/com/google/template/soy/data/restricted/PrimitiveDataTest.java @@ -20,6 +20,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import com.google.template.soy.base.internal.BaseUtils; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -108,17 +109,17 @@ public void testFloatData() { @Test public void testFloatDataToString() { // Tests that our toString is similar to Javascript's number toString. - assertEquals("0", FloatData.toString(0.0)); - assertEquals("0", FloatData.toString(-0.0)); - assertEquals("1", FloatData.toString(1.0)); - assertEquals("-1", FloatData.toString(-1.0)); - assertEquals("1000000000000000", FloatData.toString(1.0e15)); - assertEquals("-1000000000000000", FloatData.toString(-1.0e15)); - assertEquals("-1000000000000000", FloatData.toString(-1.0e15)); - assertEquals("1.51e32", FloatData.toString(1.51e32)); - assertEquals("NaN", FloatData.toString(Double.NaN)); - assertEquals("Infinity", FloatData.toString(Double.POSITIVE_INFINITY)); - assertEquals("-Infinity", FloatData.toString(Double.NEGATIVE_INFINITY)); + assertEquals("0", BaseUtils.formatDouble(0.0)); + assertEquals("0", BaseUtils.formatDouble(-0.0)); + assertEquals("1", BaseUtils.formatDouble(1.0)); + assertEquals("-1", BaseUtils.formatDouble(-1.0)); + assertEquals("1000000000000000", BaseUtils.formatDouble(1.0e15)); + assertEquals("-1000000000000000", BaseUtils.formatDouble(-1.0e15)); + assertEquals("-1000000000000000", BaseUtils.formatDouble(-1.0e15)); + assertEquals("1.51e32", BaseUtils.formatDouble(1.51e32)); + assertEquals("NaN", BaseUtils.formatDouble(Double.NaN)); + assertEquals("Infinity", BaseUtils.formatDouble(Double.POSITIVE_INFINITY)); + assertEquals("-Infinity", BaseUtils.formatDouble(Double.NEGATIVE_INFINITY)); } @Test diff --git a/java/tests/com/google/template/soy/exprtree/AbstractExprNodeVisitorTest.java b/java/tests/com/google/template/soy/exprtree/AbstractExprNodeVisitorTest.java index 6c54d43e95..ffcd014ea7 100644 --- a/java/tests/com/google/template/soy/exprtree/AbstractExprNodeVisitorTest.java +++ b/java/tests/com/google/template/soy/exprtree/AbstractExprNodeVisitorTest.java @@ -21,6 +21,7 @@ import com.google.common.collect.ImmutableMap; import com.google.template.soy.base.SourceLocation; +import com.google.template.soy.base.internal.QuoteStyle; import com.google.template.soy.exprtree.ExprNode.OperatorNode; import com.google.template.soy.exprtree.OperatorNodes.MinusOpNode; import java.util.ArrayDeque; @@ -30,9 +31,7 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -/** - * Unit tests for AbstractExprNodeVisitor. - */ +/** Unit tests for AbstractExprNodeVisitor. */ @RunWith(JUnit4.class) public final class AbstractExprNodeVisitorTest { @@ -41,7 +40,7 @@ public final class AbstractExprNodeVisitorTest { @Test public void testConcreteImplementation() { - IntegerNode expr = new IntegerNode(17, LOC); + NumberNode expr = new NumberNode(17, LOC); IncompleteEvalVisitor iev = new IncompleteEvalVisitor(null); assertThat(iev.exec(expr)).isEqualTo(17.0); @@ -51,7 +50,7 @@ public void testConcreteImplementation() { public void testInterfaceImplementation() { MinusOpNode expr = new MinusOpNode(LOC, LOC); - expr.addChild(new IntegerNode(17, LOC)); + expr.addChild(new NumberNode(17, LOC)); VarRefNode dataRef = new VarRefNode("$boo", LOC, null); expr.addChild(dataRef); @@ -59,7 +58,7 @@ public void testInterfaceImplementation() { IncompleteEvalVisitor iev = new IncompleteEvalVisitor(ImmutableMap.of("$boo", 13.0)); assertThat(iev.exec(expr)).isEqualTo(4.0); - expr.replaceChild(0, new IntegerNode(34, LOC)); + expr.replaceChild(0, new NumberNode(34, LOC)); assertThat(iev.exec(expr)).isEqualTo(21.0); } @@ -68,7 +67,7 @@ public void testInterfaceImplementation() { public void testNotImplemented() { MinusOpNode expr = new MinusOpNode(LOC, LOC); - expr.addChild(new FloatNode(17.0, LOC)); + expr.addChild(new StringNode("17.0", QuoteStyle.SINGLE, LOC)); VarRefNode dataRef = new VarRefNode("$boo", LOC, null); expr.addChild(dataRef); @@ -101,8 +100,8 @@ public Double exec(ExprNode node) { } @Override - protected void visitIntegerNode(IntegerNode node) { - resultStack.push((double) node.getValue()); + protected void visitNumberNode(NumberNode node) { + resultStack.push(node.getValue()); } @Override diff --git a/java/tests/com/google/template/soy/exprtree/FunctionNodeTest.java b/java/tests/com/google/template/soy/exprtree/FunctionNodeTest.java index 229da9cc42..5ad27a4a26 100644 --- a/java/tests/com/google/template/soy/exprtree/FunctionNodeTest.java +++ b/java/tests/com/google/template/soy/exprtree/FunctionNodeTest.java @@ -45,8 +45,8 @@ public void testToSourceString() { Identifier.create("round", SourceLocation.UNKNOWN), new SoySourceFunction() {}, SourceLocation.UNKNOWN); - fn.addChild(new FloatNode(3.14159, SourceLocation.UNKNOWN)); - fn.addChild(new IntegerNode(2, SourceLocation.UNKNOWN)); + fn.addChild(new NumberNode(3.14159, SourceLocation.UNKNOWN)); + fn.addChild(new NumberNode(2, SourceLocation.UNKNOWN)); assertThat(fn.toSourceString()).isEqualTo("round(3.14159, 2)"); } @@ -105,8 +105,8 @@ public void testToSourceStringNamed() { Identifier.create("i", SourceLocation.UNKNOWN), Identifier.create("s", SourceLocation.UNKNOWN)), SourceLocation.UNKNOWN); - fn.addChild(new FloatNode(3.14159, SourceLocation.UNKNOWN)); - fn.addChild(new IntegerNode(2, SourceLocation.UNKNOWN)); + fn.addChild(new NumberNode(3.14159, SourceLocation.UNKNOWN)); + fn.addChild(new NumberNode(2, SourceLocation.UNKNOWN)); fn.addChild(new StringNode("str", QuoteStyle.SINGLE, SourceLocation.UNKNOWN)); assertThat(fn.toSourceString()).isEqualTo("my.awesome.Proto(f: 3.14159, i: 2, s: 'str')"); } diff --git a/java/tests/com/google/template/soy/exprtree/ListLiteralNodeTest.java b/java/tests/com/google/template/soy/exprtree/ListLiteralNodeTest.java index 75f49e866d..b16e4934e7 100644 --- a/java/tests/com/google/template/soy/exprtree/ListLiteralNodeTest.java +++ b/java/tests/com/google/template/soy/exprtree/ListLiteralNodeTest.java @@ -41,7 +41,7 @@ public void testToSourceString() { ListLiteralNode listLit = new ListLiteralNode( ImmutableList.of( - new StringNode("blah", QuoteStyle.SINGLE, X), new IntegerNode(123, X), dataRef), + new StringNode("blah", QuoteStyle.SINGLE, X), new NumberNode(123, X), dataRef), X); assertThat(listLit.toSourceString()).isEqualTo("['blah', 123, $foo]"); diff --git a/java/tests/com/google/template/soy/exprtree/MethodCallNodeTest.java b/java/tests/com/google/template/soy/exprtree/MethodCallNodeTest.java index f662e987ae..4a4fe9e89d 100644 --- a/java/tests/com/google/template/soy/exprtree/MethodCallNodeTest.java +++ b/java/tests/com/google/template/soy/exprtree/MethodCallNodeTest.java @@ -52,7 +52,7 @@ public void testToSourceString() { MethodCallNode.newWithPositionalArgs( baseExpr, ImmutableList.of( - new IntegerNode(2, LOCATION), new StringNode("str", QuoteStyle.SINGLE, LOCATION)), + new NumberNode(2, LOCATION), new StringNode("str", QuoteStyle.SINGLE, LOCATION)), Identifier.create("myMethod", LOCATION), LOCATION, /* isNullSafe= */ true); diff --git a/java/tests/com/google/template/soy/exprtree/RecordLiteralNodeTest.java b/java/tests/com/google/template/soy/exprtree/RecordLiteralNodeTest.java index 351ff2153a..4ac7f9616a 100644 --- a/java/tests/com/google/template/soy/exprtree/RecordLiteralNodeTest.java +++ b/java/tests/com/google/template/soy/exprtree/RecordLiteralNodeTest.java @@ -46,7 +46,7 @@ public void testToSourceString() { X); recordLit.addChildren( ImmutableList.of( - new StringNode("blah", QuoteStyle.SINGLE, X), new IntegerNode(123, X), fooDataRef)); + new StringNode("blah", QuoteStyle.SINGLE, X), new NumberNode(123, X), fooDataRef)); assertThat(recordLit.toSourceString()).isEqualTo("record(aaa: 'blah', bbb: 123, boo: $foo)"); } } diff --git a/java/tests/com/google/template/soy/jbcsrc/ExpressionCompilerTest.java b/java/tests/com/google/template/soy/jbcsrc/ExpressionCompilerTest.java index 842f6a9863..c842f4660f 100644 --- a/java/tests/com/google/template/soy/jbcsrc/ExpressionCompilerTest.java +++ b/java/tests/com/google/template/soy/jbcsrc/ExpressionCompilerTest.java @@ -33,6 +33,7 @@ import com.google.template.soy.data.restricted.BooleanData; import com.google.template.soy.data.restricted.FloatData; import com.google.template.soy.data.restricted.IntegerData; +import com.google.template.soy.data.restricted.NumberData; import com.google.template.soy.data.restricted.StringData; import com.google.template.soy.data.restricted.UndefinedData; import com.google.template.soy.error.ErrorReporter; @@ -54,10 +55,9 @@ import com.google.template.soy.testing.SharedTestUtils; import com.google.template.soy.testing.SoyFileSetParserBuilder; import com.google.template.soy.types.BoolType; -import com.google.template.soy.types.FloatType; -import com.google.template.soy.types.IntType; import com.google.template.soy.types.LegacyObjectMapType; import com.google.template.soy.types.ListType; +import com.google.template.soy.types.NumberType; import com.google.template.soy.types.RecordType; import com.google.template.soy.types.SanitizedType; import com.google.template.soy.types.SoyType; @@ -86,7 +86,7 @@ public void setUp() { @Test public void testConstants() { - assertExpression("1").evaluatesTo(1L); + assertExpression("1").evaluatesTo(1D); assertExpression("1.0").evaluatesTo(1D); assertExpression("false").evaluatesTo(false); assertExpression("true").evaluatesTo(true); @@ -126,26 +126,19 @@ public void testCollectionLiterals_record() throws Exception { @Test public void testNegativeOpNode() { - assertExpression("-1").evaluatesTo(-1L); + assertExpression("-1").evaluatesTo(-1D); assertExpression("-1.0").evaluatesTo(-1.0); - variables.put("foo", untypedBoxedSoyExpression(SoyExpression.forInt(constant(1L)))); - assertExpression("-$foo").evaluatesTo(IntegerData.forValue(-1)); - variables.put("foo", untypedBoxedSoyExpression(SoyExpression.forFloat(constant(1D)))); assertExpression("-$foo").evaluatesTo(FloatData.forValue(-1.0)); } @Test public void testModOpNode() { - assertExpression("3 % 2").evaluatesTo(1L); - assertExpression("5 % 3").evaluatesTo(2L); + assertExpression("3 % 2").evaluatesTo(1D); + assertExpression("5 % 3").evaluatesTo(2D); assertExpression("5.0 % 3.0").evaluatesTo(2D); - variables.put("foo", untypedBoxedSoyExpression(SoyExpression.forInt(constant(3L)))); - variables.put("bar", untypedBoxedSoyExpression(SoyExpression.forInt(constant(2L)))); - assertExpression("$foo % $bar").evaluatesTo(IntegerData.forValue(1)); - variables.put("foo", untypedBoxedSoyExpression(SoyExpression.forFloat(constant(3.0)))); variables.put("bar", untypedBoxedSoyExpression(SoyExpression.forFloat(constant(2.0)))); assertExpression("$foo % $bar").evaluatesTo(FloatData.forValue(1)); @@ -168,7 +161,7 @@ public void testDivideByOpNode() { @Test public void testTimesOpNode() { assertExpression("4.2 * 2").evaluatesTo(8.4); - assertExpression("4 * 2").evaluatesTo(8L); + assertExpression("4 * 2").evaluatesTo(8D); variables.put("foo", untypedBoxedSoyExpression(SoyExpression.forFloat(constant(3.0)))); variables.put("bar", untypedBoxedSoyExpression(SoyExpression.forFloat(constant(2.0)))); @@ -178,7 +171,7 @@ public void testTimesOpNode() { @Test public void testMinusOpNode() { assertExpression("4.2 - 2").evaluatesTo(2.2); - assertExpression("4 - 2").evaluatesTo(2L); + assertExpression("4 - 2").evaluatesTo(2D); variables.put("foo", untypedBoxedSoyExpression(SoyExpression.forFloat(constant(3.0)))); variables.put("bar", untypedBoxedSoyExpression(SoyExpression.forFloat(constant(2.0)))); @@ -188,7 +181,7 @@ public void testMinusOpNode() { @Test public void testPlusOpNode() { assertExpression("4.2 + 2").evaluatesTo(6.2); - assertExpression("4 + 2").evaluatesTo(6L); + assertExpression("4 + 2").evaluatesTo(6D); assertExpression("4 + '2'").evaluatesTo("42"); assertExpression("'4' + 2").evaluatesTo("42"); @@ -196,9 +189,8 @@ public void testPlusOpNode() { assertExpression("$foo + 2").evaluatesTo(StringData.forValue("foo2")); assertExpression("$foo + '2'").evaluatesTo("foo2"); // Note, not boxed - variables.put("foo", untypedBoxedSoyExpression(SoyExpression.forInt(constant(1L)))); - assertExpression("$foo + 2").evaluatesTo(IntegerData.forValue(3)); - assertExpression("$foo + '2'").evaluatesTo("12"); + variables.put("foo", untypedBoxedSoyExpression(SoyExpression.forFloat(constant(1D)))); + assertExpression("$foo + 2").evaluatesTo(NumberData.forValue(3)); } @Test @@ -215,9 +207,9 @@ public void testNotOpNode() { @Test public void testComparisonOperators() { - variables.put("oneInt", untypedBoxedSoyExpression(SoyExpression.forInt(constant(1L)))); + variables.put("oneInt", untypedBoxedSoyExpression(SoyExpression.forFloat(constant(1D)))); variables.put("oneFloat", untypedBoxedSoyExpression(SoyExpression.forFloat(constant(1.0)))); - variables.put("twoInt", untypedBoxedSoyExpression(SoyExpression.forInt(constant(2L)))); + variables.put("twoInt", untypedBoxedSoyExpression(SoyExpression.forFloat(constant(2D)))); variables.put("twoFloat", untypedBoxedSoyExpression(SoyExpression.forFloat(constant(2.0)))); variables.put("oneStr", untypedBoxedSoyExpression(SoyExpression.forString(constant("kill")))); variables.put("twoStr", untypedBoxedSoyExpression(SoyExpression.forString(constant("zoo")))); @@ -330,18 +322,18 @@ public void testEqualOpNode() { @Test public void testConditionalOpNode() { - assertExpression("false ? 1 : 2").evaluatesTo(2L); - assertExpression("true ? 1 : 2").evaluatesTo(1L); + assertExpression("false ? 1 : 2").evaluatesTo(2D); + assertExpression("true ? 1 : 2").evaluatesTo(1D); - assertExpression("false ? 1.0 : 2").evaluatesTo(IntegerData.forValue(2)); - assertExpression("true ? 1 : 2.0").evaluatesTo(IntegerData.forValue(1)); + assertExpression("false ? 1.0 : 2").evaluatesTo(2D); + assertExpression("true ? 1 : 2.0").evaluatesTo(1D); assertExpression("false ? 'a' : 'b'").evaluatesTo("b"); assertExpression("true ? 'a' : 'b'").evaluatesTo("a"); // note the boxing - assertExpression("false ? 'a' : 2").evaluatesTo(IntegerData.forValue(2)); - assertExpression("true ? 1 : 'b'").evaluatesTo(IntegerData.forValue(1)); + assertExpression("false ? 'a' : 2").evaluatesTo(NumberData.forValue(2)); + assertExpression("true ? 1 : 'b'").evaluatesTo(NumberData.forValue(1)); assertExpression("false ? 1 : 'b'").evaluatesTo(StringData.forValue("b")); assertExpression("true ? 'a' : 2").evaluatesTo(StringData.forValue("a")); } @@ -375,7 +367,7 @@ public void testConditionalOpNode_advanced() { @Test public void testNullCoalescingOpNode() { - assertExpression("1 ?? 2").evaluatesTo(1L); + assertExpression("1 ?? 2").evaluatesTo(1D); // force the type checker to interpret the left hand side as a nullable string, the literal null // is rejected by the type checker. assertExpression("(true ? null : 'a') ?? 2").evaluatesTo(IntegerData.forValue(2)); @@ -432,9 +424,9 @@ public void testItemAccess_lists() { assertExpression("$list[3] + 1").throwsException(SoyDataException.class); // even if the index type is not known, it still works - variables.put("anInt", untypedBoxedSoyExpression(SoyExpression.forInt(constant(1L)))); + variables.put("anInt", untypedBoxedSoyExpression(SoyExpression.forFloat(constant(1D)))); assertExpression("$list[$anInt]").evaluatesTo(IntegerData.forValue(1)); - variables.put("anInt", untypedBoxedSoyExpression(SoyExpression.forInt(constant(3L)))); + variables.put("anInt", untypedBoxedSoyExpression(SoyExpression.forFloat(constant(3D)))); assertExpression("$list[$anInt]").evaluatesTo(UndefinedData.INSTANCE); } @@ -456,7 +448,7 @@ public void testNullSafeItemAccess_map() { variables.put( "nullMap", SoyExpression.forSoyValue( - LegacyObjectMapType.of(StringType.getInstance(), IntType.getInstance()), soyNull())); + LegacyObjectMapType.of(StringType.getInstance(), NumberType.getInstance()), soyNull())); assertExpression("$nullMap['a']").throwsException(NullPointerException.class); assertExpression("$nullMap?['a']").evaluatesTo(UndefinedData.INSTANCE); } @@ -496,7 +488,7 @@ public void testNullSafeFieldAccess() { public void testBuiltinFunctions() { variables.put("x", compileExpression("record(a: 1)").box()); variables.put( - "y", SoyExpression.forSoyValue(SoyTypes.makeNullable(FloatType.getInstance()), soyNull())); + "y", SoyExpression.forSoyValue(SoyTypes.makeNullable(NumberType.getInstance()), soyNull())); assertExpression("checkNotNull($x.a)").evaluatesTo(IntegerData.forValue(1)); assertExpression("checkNotNull($y)").throwsException(NullPointerException.class); } diff --git a/java/tests/com/google/template/soy/jbcsrc/LazyClosureCompilerTest.java b/java/tests/com/google/template/soy/jbcsrc/LazyClosureCompilerTest.java index a5dc6914c7..cdb2c40d77 100644 --- a/java/tests/com/google/template/soy/jbcsrc/LazyClosureCompilerTest.java +++ b/java/tests/com/google/template/soy/jbcsrc/LazyClosureCompilerTest.java @@ -33,7 +33,7 @@ import com.google.template.soy.data.LoggingAdvisingAppendable.BufferingAppendable; import com.google.template.soy.data.SoyList; import com.google.template.soy.data.SoyValue; -import com.google.template.soy.data.restricted.IntegerData; +import com.google.template.soy.data.restricted.NumberData; import com.google.template.soy.jbcsrc.TemplateTester.CompiledTemplateSubject; import com.google.template.soy.jbcsrc.shared.CompiledTemplate; import com.google.template.soy.jbcsrc.shared.CompiledTemplates; @@ -235,7 +235,7 @@ public void testLetValueNodeStructure() throws NoSuchMethodException { // 3 for the template and one for the let assertThat(fileClass.getDeclaredMethods()).hasLength(4); Method letMethod = - fileClass.getDeclaredMethod("foo$let_foo", RenderContext.class, IntegerData.class); + fileClass.getDeclaredMethod("foo$let_foo", RenderContext.class, NumberData.class); assertThat(Modifier.toString(letMethod.getModifiers())).isEqualTo("static"); assertThat(letMethod.getDeclaringClass()).isEqualTo(fileClass); } diff --git a/java/tests/com/google/template/soy/jbcsrc/StreamingPrintDirectivesTest.java b/java/tests/com/google/template/soy/jbcsrc/StreamingPrintDirectivesTest.java index 97cfae5155..74ec3609ab 100644 --- a/java/tests/com/google/template/soy/jbcsrc/StreamingPrintDirectivesTest.java +++ b/java/tests/com/google/template/soy/jbcsrc/StreamingPrintDirectivesTest.java @@ -188,7 +188,7 @@ public void testStreamingDisablesRuntimeTypeChecks() throws Exception { ClassCastException.class, () -> templates.getTemplate("ns.nonstreamable").render(null, badParam, output, context)); - assertThat(cce).hasMessageThat().contains("expected int, got string"); + assertThat(cce).hasMessageThat().contains("expected number, got string"); } @Test diff --git a/java/tests/com/google/template/soy/jbcsrc/restricted/SoyExpressionTest.java b/java/tests/com/google/template/soy/jbcsrc/restricted/SoyExpressionTest.java index adbc495a17..ae4a4033c4 100644 --- a/java/tests/com/google/template/soy/jbcsrc/restricted/SoyExpressionTest.java +++ b/java/tests/com/google/template/soy/jbcsrc/restricted/SoyExpressionTest.java @@ -27,14 +27,11 @@ import com.google.template.soy.data.internal.ListImpl; import com.google.template.soy.data.restricted.BooleanData; import com.google.template.soy.data.restricted.FloatData; -import com.google.template.soy.data.restricted.IntegerData; import com.google.template.soy.data.restricted.NullData; import com.google.template.soy.data.restricted.StringData; import com.google.template.soy.data.restricted.UndefinedData; -import com.google.template.soy.types.AnyType; -import com.google.template.soy.types.FloatType; -import com.google.template.soy.types.IntType; import com.google.template.soy.types.ListType; +import com.google.template.soy.types.NumberType; import com.google.template.soy.types.StringType; import com.google.template.soy.types.UnknownType; import org.junit.Test; @@ -46,34 +43,13 @@ public class SoyExpressionTest { @Test - public void testIntExpressions() { - SoyExpression expr = SoyExpression.forInt(constant(12L)); - assertThat(expr.isNonJavaNullable()).isTrue(); - assertThat(expr.isNonSoyNullish()).isTrue(); - assertThat(expr.box().isNonJavaNullable()).isTrue(); - assertThat(expr.box().isNonSoyNullish()).isTrue(); - - assertThatExpression(expr).evaluatesTo(12L); - assertThatExpression(expr.box()).evaluatesTo(IntegerData.forValue(12)); - assertThatExpression(expr.box().unboxAsLong()).evaluatesTo(12L); - assertThatExpression(expr.coerceToDouble()).evaluatesTo(12D); - assertThatExpression( - SoyExpression.forSoyValue(AnyType.getInstance(), expr.box()).coerceToDouble()) - .evaluatesTo(12D); - - assertThatExpression(expr.coerceToBoolean()).evaluatesTo(true); - assertThatExpression(SoyExpression.forInt(constant(0L)).coerceToBoolean()).evaluatesTo(false); - assertThatExpression(expr.coerceToString()).evaluatesTo("12"); - } - - @Test - public void testFloatExpressions() { + public void testNumberExpressions() { SoyExpression expr = SoyExpression.forFloat(constant(12.34D)); assertThatExpression(expr).evaluatesTo(12.34D); assertThatExpression(expr.box()).evaluatesTo(FloatData.forValue(12.34D)); assertThatExpression(expr.box().coerceToDouble()).evaluatesTo(12.34D); assertThatExpression( - SoyExpression.forSoyValue(FloatType.getInstance(), expr.box()).coerceToString()) + SoyExpression.forSoyValue(NumberType.getInstance(), expr.box()).coerceToString()) .evaluatesTo("12.34"); assertThatExpression(expr.coerceToBoolean()).evaluatesTo(true); @@ -137,7 +113,7 @@ public void testStringExpression() { @Test public void testListExpression() { - ListType list = ListType.of(IntType.getInstance()); + ListType list = ListType.of(NumberType.getInstance()); assertThatExpression(forSoyValue(list, soyNull()).coerceToBoolean()).evaluatesTo(false); assertThatExpression(forSoyValue(list, soyNull())).evaluatesTo(NullData.INSTANCE); assertThatExpression( @@ -234,12 +210,6 @@ public void testCoerceToBoolean_nullableBoxed() { .evaluatesTo(false); } - @Test - public void testCoerceToBoolean_primitives_int() { - assertThatExpression(SoyExpression.forInt(constant(1L)).coerceToBoolean()).evaluatesTo(true); - assertThatExpression(SoyExpression.forInt(constant(0L)).coerceToBoolean()).evaluatesTo(false); - } - @Test public void testCoerceToBoolean_primitives_float() { assertThatExpression(SoyExpression.forFloat(constant(1D)).coerceToBoolean()).evaluatesTo(true); diff --git a/java/tests/com/google/template/soy/jbcsrc/restricted/SoyRuntimeTypeTest.java b/java/tests/com/google/template/soy/jbcsrc/restricted/SoyRuntimeTypeTest.java index d551fe3d98..3cb7c526b3 100644 --- a/java/tests/com/google/template/soy/jbcsrc/restricted/SoyRuntimeTypeTest.java +++ b/java/tests/com/google/template/soy/jbcsrc/restricted/SoyRuntimeTypeTest.java @@ -29,10 +29,9 @@ import com.google.template.soy.testing3.Proto3Message; import com.google.template.soy.types.AnyType; import com.google.template.soy.types.BoolType; -import com.google.template.soy.types.FloatType; -import com.google.template.soy.types.IntType; import com.google.template.soy.types.ListType; import com.google.template.soy.types.NullType; +import com.google.template.soy.types.NumberType; import com.google.template.soy.types.SanitizedType; import com.google.template.soy.types.SoyProtoEnumType; import com.google.template.soy.types.SoyProtoType; @@ -54,8 +53,9 @@ public class SoyRuntimeTypeTest { public void testPrimitiveTypes() { assertThat(NullType.getInstance()).isBoxedAs(SoyValue.class).isNotUnboxable(); - assertThat(IntType.getInstance()).isBoxedAs(SoyValue.class).isUnboxedAs(long.class); - Truth.assertThat(SoyRuntimeType.getUnboxedType(IntType.getInstance()).get().box().runtimeType()) + assertThat(NumberType.getInstance()).isBoxedAs(SoyValue.class).isUnboxedAs(double.class); + Truth.assertThat( + SoyRuntimeType.getUnboxedType(NumberType.getInstance()).get().box().runtimeType()) .isEqualTo(BytecodeUtils.SOY_VALUE_TYPE); assertThat(BoolType.getInstance()).isBoxedAs(SoyValue.class).isUnboxedAs(boolean.class); @@ -68,21 +68,21 @@ public void testPrimitiveTypes() { SoyRuntimeType.getUnboxedType(StringType.getInstance()).get().box().runtimeType()) .isEqualTo(BytecodeUtils.SOY_VALUE_TYPE); - assertThat(FloatType.getInstance()).isBoxedAs(SoyValue.class).isUnboxedAs(double.class); + assertThat(NumberType.getInstance()).isBoxedAs(SoyValue.class).isUnboxedAs(double.class); Truth.assertThat( - SoyRuntimeType.getUnboxedType(FloatType.getInstance()).get().box().runtimeType()) + SoyRuntimeType.getUnboxedType(NumberType.getInstance()).get().box().runtimeType()) .isEqualTo(BytecodeUtils.SOY_VALUE_TYPE); assertThat(new SoyProtoEnumType(Proto3Message.AnEnum.getDescriptor())) .isBoxedAs(SoyValue.class) - .isUnboxedAs(long.class); + .isUnboxedAs(double.class); assertThat( UnionType.of( new SoyProtoEnumType(Proto3Message.AnEnum.getDescriptor()), new SoyProtoEnumType(Foo3.AnotherEnum.getDescriptor()))) .isBoxedAs(SoyValue.class) - .isUnboxedAs(long.class); + .isUnboxedAs(double.class); for (SanitizedContentKind kind : SanitizedContentKind.values()) { if (kind == SanitizedContentKind.TEXT) { @@ -95,7 +95,7 @@ public void testPrimitiveTypes() { assertThat(SoyProtoType.newForTest(Proto3Message.getDescriptor())) .isBoxedAs(SoyValue.class) .isUnboxedAs(Proto3Message.class); - assertThat(ListType.of(IntType.getInstance())) + assertThat(ListType.of(NumberType.getInstance())) .isBoxedAs(SoyValue.class) .isUnboxedAs(List.class); @@ -106,13 +106,14 @@ public void testPrimitiveTypes() { @Test public void testUnionTypes() { // no unboxed representation for this one - assertThat(UnionType.of(IntType.getInstance(), StringType.getInstance())) + assertThat(UnionType.of(NumberType.getInstance(), StringType.getInstance())) .isBoxedAs(SoyValue.class) .isNotUnboxable(); // But unions of lists do work assertThat( - UnionType.of(ListType.of(IntType.getInstance()), ListType.of(StringType.getInstance()))) + UnionType.of( + ListType.of(NumberType.getInstance()), ListType.of(StringType.getInstance()))) .isBoxedAs(SoyValue.class) .isUnboxedAs(List.class); // as do union of sanitized diff --git a/java/tests/com/google/template/soy/jssrc/internal/JsTypeTest.java b/java/tests/com/google/template/soy/jssrc/internal/JsTypeTest.java index 6f46cd5be8..933c9ebdba 100644 --- a/java/tests/com/google/template/soy/jssrc/internal/JsTypeTest.java +++ b/java/tests/com/google/template/soy/jssrc/internal/JsTypeTest.java @@ -28,9 +28,9 @@ import com.google.template.soy.testing3.Proto3Message; import com.google.template.soy.types.AnyType; import com.google.template.soy.types.BoolType; -import com.google.template.soy.types.IntType; import com.google.template.soy.types.ListType; import com.google.template.soy.types.MapType; +import com.google.template.soy.types.NumberType; import com.google.template.soy.types.RecordType; import com.google.template.soy.types.SanitizedType.HtmlType; import com.google.template.soy.types.SanitizedType.UriType; @@ -51,7 +51,7 @@ public final class JsTypeTest { private static final ListType LIST_OF_HTML = ListType.of(HtmlType.getInstance()); private static final SoyType NULLABLE_LIST_OF_HTML = makeNullable(LIST_OF_HTML); private static final SoyType UNION_OF_STRING_OR_INT = - UnionType.of(StringType.getInstance(), IntType.getInstance()); + UnionType.of(StringType.getInstance(), NumberType.getInstance()); private static final SoyType NULLABLE_STRING = makeNullable(StringType.getInstance()); @Test @@ -61,7 +61,7 @@ public void testGetJsTypeName() { assertThatTypeExprForRecordMember(UnknownType.getInstance()).isEqualTo("?"); assertThatTypeExprForOptionalRecordMember(UnknownType.getInstance()).isEqualTo("(?|undefined)"); - assertThatTypeExpr(IntType.getInstance()).isEqualTo("number"); + assertThatTypeExpr(NumberType.getInstance()).isEqualTo("number"); // Basic unions assertThatTypeExpr(UNION_OF_STRING_OR_INT).isEqualTo("number|string"); @@ -109,7 +109,7 @@ public void testGetJsTypeName() { // Records assertThatTypeExpr( - RecordType.of(ImmutableMap.of("foo", IntType.getInstance(), "bar", LIST_OF_HTML))) + RecordType.of(ImmutableMap.of("foo", NumberType.getInstance(), "bar", LIST_OF_HTML))) .isEqualTo( "{foo: number, bar:" + " (!Array),}"); assertThatTypeExpr( - RecordType.of(ImmutableMap.of("foo", IntType.getInstance(), "bar", LIST_OF_HTML))) + RecordType.of(ImmutableMap.of("foo", NumberType.getInstance(), "bar", LIST_OF_HTML))) .isEqualTo( "{foo: number, bar:" + " (!Array}", "{assertType('bool|undefined', $pa)}", - "{assertType('list|undefined', $pb)}"); + "{assertType('list|undefined', $pb)}"); } @Test @@ -106,9 +106,9 @@ public void testState() { "{@param pd: list|null = null}", "
", "{assertType('bool', $pa)}", - "{assertType('list', $pb)}", + "{assertType('list', $pb)}", "{assertType('bool|null', $pc)}", - "{assertType('list|null', $pd)}", + "{assertType('list|null', $pd)}", "
")) .addSoyFunction(ASSERT_TYPE_FUNCTION) .desugarHtmlNodes(false) @@ -121,7 +121,7 @@ public void testState() { List stateVars = node.getStateVars(); assertThat(stateVars.get(0).defaultValue().getType()).isEqualTo(BoolType.getInstance()); assertThat(stateVars.get(1).defaultValue().getType()) - .isEqualTo(ListType.of(IntType.getInstance())); + .isEqualTo(ListType.of(NumberType.getInstance())); List params = node.getParams(); assertThat(params.get(0).defaultValue().getType()).isEqualTo(NullType.getInstance()); @@ -160,7 +160,7 @@ public void testStateTypeInference() { "{@state proto:= ExampleExtendable()}", "
", "{assertType('bool', $pa)}", - "{assertType('list', $pb)}", + "{assertType('list', $pb)}", "{assertType('example.ExampleExtendable', $proto)}", "
"), ExampleExtendable.getDescriptor()) @@ -178,7 +178,7 @@ public void testStateTypeInference() { assertThat(stateVars.get(0).type()).isEqualTo(BoolType.getInstance()); assertThat(stateVars.get(1).name()).isEqualTo("pb"); - assertThat(stateVars.get(1).type()).isEqualTo(ListType.of(IntType.getInstance())); + assertThat(stateVars.get(1).type()).isEqualTo(ListType.of(NumberType.getInstance())); assertThat(stateVars.get(2).name()).isEqualTo("proto"); assertThat(stateVars.get(2).type().toString()) @@ -192,10 +192,10 @@ public void testDataRefTypes() { "{@param pb: list}", "{@param pe: map>}", "{assertType('bool', $pa)}", - "{assertType('list', $pb)}", - "{assertType('int', $pb[0])}", - "{assertType('map>', $pe)}", - "{assertType('map', $pe.get(0)!)}", + "{assertType('list', $pb)}", + "{assertType('number', $pb[0])}", + "{assertType('map>', $pe)}", + "{assertType('map', $pe.get(0)!)}", "{assertType('string', $pe.get(1 + 1)!.get(2)!)}"); } @@ -203,7 +203,7 @@ public void testDataRefTypes() { public void testRecordTypes() { assertTypes( "{@param pa: [a:int, b:string]}", - "{assertType('int', $pa.a)}", + "{assertType('number', $pa.a)}", "{assertType('string', $pa.b)}"); } @@ -217,21 +217,21 @@ public void testDataRefTypesWithUnknown() { "{assertType('?', $pa[0])}", "{assertType('?', $pa.xxx)}", "{assertType('?', $pa.xxx.yyy)}", - "{assertType('float', $pb.get($pa)!)}", + "{assertType('number', $pb.get($pa)!)}", "{assertType('string', $pc.get($pa)!)}"); } @Test public void testDataRefTypesError() { assertResolveExpressionTypesFails( - "Method 'get' called with parameter types (int) but expected (string).", + "Method 'get' called with parameter types (number) but expected (string).", constructFileSource("{@param pa: map}", "{$pa.get(0)}")); } @Test public void testRecordTypesError() { assertResolveExpressionTypesFails( - "Undefined field 'c' for record type [a: int, bb: float]. Did you mean 'a'?", + "Undefined field 'c' for record type [a: number, bb: number]. Did you mean 'a'?", constructFileSource("{@param pa: [a:int, bb:float]}", "{$pa.c}")); } @@ -242,7 +242,7 @@ public void testGetExtensionMethodTyping() { constructTemplateSource( "{@param proto: ExampleExtendable}", "{assertType('bool', $proto.getExtension(someBoolExtension))}", - "{assertType('int|undefined', $proto.getExtension(" + "{assertType('number|undefined', $proto.getExtension(" + "SomeExtension.someExtensionField).getSomeExtensionNumOrUndefined())}"), ExampleExtendable.getDescriptor(), SomeExtension.getDescriptor(), @@ -260,23 +260,23 @@ public void testArithmeticOps() { "{@param pi: int}", "{@param pf: float}", "{assertType('?', $pa + $pa)}", - "{assertType('int', $pi + $pi)}", - "{assertType('float', $pf + $pf)}", + "{assertType('number', $pi + $pi)}", + "{assertType('number', $pf + $pf)}", "{assertType('?', $pa - $pa)}", - "{assertType('int', $pi - $pi)}", - "{assertType('float', $pf - $pf)}", + "{assertType('number', $pi - $pi)}", + "{assertType('number', $pf - $pf)}", "{assertType('?', $pa * $pa)}", - "{assertType('int', $pi * $pi)}", - "{assertType('float', $pf * $pf)}", - "{assertType('float', $pa / $pa)}", - "{assertType('float', $pi / $pi)}", - "{assertType('float', $pf / $pf)}", + "{assertType('number', $pi * $pi)}", + "{assertType('number', $pf * $pf)}", + "{assertType('number', $pa / $pa)}", + "{assertType('number', $pi / $pi)}", + "{assertType('number', $pf / $pf)}", "{assertType('?', $pa % $pa)}", - "{assertType('int', $pi % $pi)}", - "{assertType('float', $pf % $pf)}", + "{assertType('number', $pi % $pi)}", + "{assertType('number', $pf % $pf)}", "{assertType('?', -$pa)}", - "{assertType('int', -$pi)}", - "{assertType('float', -$pf)}"); + "{assertType('number', -$pi)}", + "{assertType('number', -$pf)}"); } @Test @@ -306,11 +306,11 @@ public void testLogicalOps() { "{@param pi: int}", "{@param pf: float}", "{assertType('?', $pa && $pa)}", - "{assertType('int', $pi && $pi)}", - "{assertType('float', $pf && $pf)}", + "{assertType('number', $pi && $pi)}", + "{assertType('number', $pf && $pf)}", "{assertType('?', $pa || $pa)}", - "{assertType('int', $pi || $pi)}", - "{assertType('float', $pf || $pf)}", + "{assertType('number', $pi || $pi)}", + "{assertType('number', $pf || $pf)}", "{assertType('bool', !$pa)}", "{assertType('bool', !$pi)}", "{assertType('bool', !$pf)}"); @@ -350,9 +350,9 @@ public void testNullCoalescingAndConditionalOps() { "{@param pf: float}", "{@param? ni: int|null}", "{assertType('?', $pa ?? $pi)}", - "{assertType('float|int', $pi ?? $pf)}", - "{assertType('float|int', $pa ? $pi : $pf)}", - "{assertType('int', $ni ?? 0)}"); + "{assertType('number', $pi ?? $pf)}", + "{assertType('number', $pa ? $pi : $pf)}", + "{assertType('number', $ni ?? 0)}"); } @Test @@ -360,7 +360,7 @@ public void testNullCoalescingAndConditionalOps_complexCondition() { SoyFileSetNode soyTree = SoyFileSetParserBuilder.forFileContents( constructFileSource( - "{@param? l: [a :int]|null}", "{assertType('int', $l?.a ?? 0)}")) + "{@param? l: [a :int]|null}", "{assertType('number', $l?.a ?? 0)}")) .typeRegistry(TYPE_REGISTRY) .addSoyFunction(ASSERT_TYPE_FUNCTION) .parse() @@ -374,10 +374,10 @@ public void testListLiteral() { "{@param pi: int}", "{@param pf: float}", "{let $list: [$pi, $pf]/}", - "{assertType('list', $list)}", - "{assertType('int', length($list))}", - "{assertType('list', [1, 2, 3, ...$list])}", - "{assertType('list', [1, ...$list, 's'])}", + "{assertType('list', $list)}", + "{assertType('number', length($list))}", + "{assertType('list', [1, 2, 3, ...$list])}", + "{assertType('list', [1, ...$list, 's'])}", ""); } @@ -387,7 +387,7 @@ public void testMapLiteral() { "{@param pi: int}", "{@param pf: float}", "{let $map: map(1: $pi, 2:$pf)/}", - "{assertType('map', $map)}"); + "{assertType('map', $map)}"); } @Test @@ -397,7 +397,7 @@ public void testMapLiteralWithStringKeysAsMap() { "{@param v2: string}", "{@param k1: string}", "{let $map: map($k1: $v1, 'b': $v2) /}", - "{assertType('map', $map)}"); + "{assertType('map', $map)}"); } @Test @@ -407,7 +407,7 @@ public void testMapLiteralWithStringLiteralKeysDoesNotCreateRecord() { "{@param pf: float}", // With the old map syntax, this would create a record type (see next test) "{let $map: map('a': $pi, 'b':$pf)/}", - "{assertType('map', $map)}"); + "{assertType('map', $map)}"); } @Test @@ -416,11 +416,11 @@ public void testRecordLiteralAsRecord() { "{@param pi: int}", "{@param pf: float}", "{let $record: record(a: $pi, b: $pf)/}", - "{assertType('[a: int, b: float]', $record)}", - "{assertType('[a: int, b: float]', record(...$record))}", - "{assertType('[a: int, b: float]', record(a: $pi, ...$record))}", - "{assertType('[a: int, b: float]', record(a: $pf, ...$record))}", - "{assertType('[a: float, b: float]', record(...$record, a: $pf))}", + "{assertType('[a: number, b: number]', $record)}", + "{assertType('[a: number, b: number]', record(...$record))}", + "{assertType('[a: number, b: number]', record(a: $pi, ...$record))}", + "{assertType('[a: number, b: number]', record(a: $pf, ...$record))}", + "{assertType('[a: number, b: number]', record(...$record, a: $pf))}", ""); } @@ -581,8 +581,8 @@ public void testDataFlowTypeNarrowing() { assertTypes( "{@param pa: string|null}", "{@param pb: int|null}", - "{assertType('int|null|string', $pa && $pb)}", - "{assertType('int|null|string', $pa || $pb)}"); + "{assertType('null|number|string', $pa && $pb)}", + "{assertType('null|number|string', $pa || $pb)}"); } @Test @@ -771,10 +771,10 @@ public void testDataFlowTypeNarrowing_complexExpressions() { "{@param map: map}", "{@param record: " + "[a : [nullableInt : int|null, nullableBool : bool|null]|null]}", "{if $map.get('a')}", - " {assertType('int', $map.get('a'))}", + " {assertType('number', $map.get('a'))}", "{/if}", "{if $record.a?.nullableInt}", - " {assertType('int', $record.a?.nullableInt)}", + " {assertType('number', $record.a?.nullableInt)}", "{/if}", ""); // Don't add |null to types for checks like this. @@ -903,9 +903,9 @@ public void testDataFlowTypeNarrowing_switch() { " {case 'str', 'str2'}", " {assertType('string', $p)}", " {case 'str', 8675309}", - " {assertType('int|string', $p)}", + " {assertType('number|string', $p)}", " {default}", - " {assertType('bool|int|string|undefined', $p)}", + " {assertType('bool|number|string|undefined', $p)}", "{/switch}"); assertTypes( "{@param? p: string|bool|int|null}", @@ -913,7 +913,7 @@ public void testDataFlowTypeNarrowing_switch() { " {case null}", " {assertType('null', $p)}", " {default}", - " {assertType('bool|int|string|undefined', $p)}", + " {assertType('bool|number|string|undefined', $p)}", "{/switch}"); assertTypes( "{@param? p: string|bool|int|null}", @@ -923,7 +923,7 @@ public void testDataFlowTypeNarrowing_switch() { " {case undefined}", " {assertType('undefined', $p)}", " {default}", - " {assertType('bool|int|string', $p)}", + " {assertType('bool|number|string', $p)}", "{/switch}"); } @@ -937,8 +937,8 @@ public void testConditionalOperatorDataFlowTypeNarrowing() { "{assertType('bool', $pa != null ?? $pb)}", // #1 must be non-null "{assertType('bool', $pa ?? $pb)}", // #2 must be non-null (re-written to ($pa != null ? $pa // : $pb)) - "{assertType('int', $pc.a ? $pc.a : 0)}", - "{if !$pc.a}{assertType('int|null', $pc.a)}{/if}"); + "{assertType('number', $pc.a ? $pc.a : 0)}", + "{if !$pc.a}{assertType('null|number', $pc.a)}{/if}"); } @Test @@ -946,8 +946,8 @@ public void testBuiltinFunctionTyping() { assertTypes( "{@inject list: list}", "{for $item in $list}", - " {assertType('int|null', $item)}", - " {assertType('int', checkNotNull($item))}", + " {assertType('null|number', $item)}", + " {assertType('number', checkNotNull($item))}", " {assertType('string', css('foo'))}", " {assertType('string', xid('bar'))}", "{/for}"); @@ -958,11 +958,11 @@ public void testAsOperator() { assertTypes( "{@param union: number|string}", "{@param b: bool}", - "{assertType('float|int', $union as number)}", + "{assertType('number', $union as number)}", "{assertType('string', $union as string)}", "{assertType('bool', $union as any as bool)}", // precedence: - "{assertType('int|string', $b ? 1 : $union as string)}", + "{assertType('number|string', $b ? 1 : $union as string)}", "{assertType('string', ($b ? 1 : $union) as string)}", ""); } @@ -982,7 +982,7 @@ public void testInstanceOfOperator() { "{if $union instanceof string}", " {assertType('string', $union)}", "{elseif $union instanceof number}", - " {assertType('float|int', $union)}", + " {assertType('number', $union)}", "{else}", " {assertType('never', $union)}", "{/if}", @@ -998,9 +998,9 @@ public void testInstanceOfOperator() { assertTypes( "{@param union: map|map|list}", "{if $union instanceof map}", - " {assertType('map|map', $union)}", + " {assertType('map|map', $union)}", "{else}", - " {assertType('list', $union)}", + " {assertType('list', $union)}", "{/if}", ""); @@ -1078,7 +1078,7 @@ public void testProtoTyping() { "{assertType('example.ExampleExtendable', $proto)}", "{assertType('string', $proto.getSomeString())}", "{assertType('string|undefined', $proto.getSomeStringOrUndefined())}", - "{assertType('int|undefined', $proto.getSomeNumNoDefaultOrUndefined())}", + "{assertType('number|undefined', $proto.getSomeNumNoDefaultOrUndefined())}", "{assertType('example.SomeEmbeddedMessage|undefined'," + " $proto.getSomeEmbeddedMessage())}", "", @@ -1115,10 +1115,10 @@ public void testProto64BitIntTyping() { @Test public void testBadForEach() { assertResolveExpressionTypesFails( - "Cannot iterate over $p of type int.", + "Cannot iterate over $p of type number.", constructFileSource("{@param p: int}", "{for $item in $p}{/for}")); assertResolveExpressionTypesFails( - "Cannot iterate over $p of type int|string.", + "Cannot iterate over $p of type number|string.", constructFileSource("{@param p: int|string}", "{for $item in $p}{/for}")); assertResolveExpressionTypesFails( "Cannot iterate over $p of type list|string|uri.", @@ -1131,32 +1131,32 @@ public void testInjectedParamTypes() { "{@inject pa: bool}", "{@inject? pb: list}", "{assertType('bool', $pa)}", - "{assertType('list|undefined', $pb)}"); + "{assertType('list|undefined', $pb)}"); } @Test public void testConcatLists() { assertTypes( "{assertType('list', ['1'].concat(['2']))}", - "{assertType('list', [1].concat([2]))}", - "{assertType('list', [1].concat([]))}", - "{assertType('list', [].concat([1]))}", - "{assertType('list', (true ? [] : [1]).concat([2]))}", + "{assertType('list', [1].concat([2]))}", + "{assertType('list', [1].concat([]))}", + "{assertType('list', [].concat([1]))}", + "{assertType('list', (true ? [] : [1]).concat([2]))}", "{assertType('list', [].concat([]))}", - "{assertType('list', [1].concat([\"2\"]))}"); + "{assertType('list', [1].concat([\"2\"]))}"); } @Test public void testConcatMaps() { assertTypes( "{assertType('map', map('1' : '2').concat(map('3':'4')))}", - "{assertType('map', map(1: 2).concat(map(3: 4)))}", - "{assertType('map', map(1: 2).concat(map()))}", - "{assertType('map', map().concat(map(3: 4)))}", - "{assertType('map', map().concat(true ? map() : map(3: 4)))}", - "{assertType('map', (true ? map() : map(1:2)).concat(map()))}", + "{assertType('map', map(1: 2).concat(map(3: 4)))}", + "{assertType('map', map(1: 2).concat(map()))}", + "{assertType('map', map().concat(map(3: 4)))}", + "{assertType('map', map().concat(true ? map() : map(3: 4)))}", + "{assertType('map', (true ? map() : map(1:2)).concat(map()))}", "{assertType('map', map().concat(map()))}", - "{assertType('map', map(1: '2').concat(map(3: 4)))}"); + "{assertType('map', map(1: '2').concat(map(3: 4)))}"); } @Test @@ -1172,7 +1172,7 @@ public void testMapKeys() { public void testMapToLegacyObjectMap() { assertTypes( "{@param m: map}", - "{assertType('legacy_object_map', mapToLegacyObjectMap($m))}", + "{assertType('legacy_object_map', mapToLegacyObjectMap($m))}", "{assertType('legacy_object_map', mapToLegacyObjectMap(map()))}", ""); } @@ -1196,11 +1196,11 @@ public void testVeDataLiteral() { @Test public void testErrorMessagesInUnionTypes() { assertResolveExpressionTypesFails( - "Type float does not support bracket access.", + "Type number does not support bracket access.", constructFileSource("{@param p: float|int}", "{$p[1]}")); assertResolveExpressionTypesFails( - "Field 'a' does not exist on type float.", + "Field 'a' does not exist on type number.", constructFileSource("{@param p: float|int}", "{$p.a}")); } @@ -1222,7 +1222,7 @@ public void testTypeParser() { SoyType type = parseSoyType("string"); assertThat(type).isEqualTo(StringType.getInstance()); type = parseSoyType("int"); - assertThat(type).isEqualTo(IntType.getInstance()); + assertThat(type).isEqualTo(NumberType.getInstance()); type = parseSoyType("list"); assertThat(type.getKind()).isEqualTo(SoyType.Kind.LIST); assertThat(((ListType) type).getElementType()).isEqualTo(AnyType.getInstance()); @@ -1239,13 +1239,13 @@ public void testNonNullAssertion() { "{@param? n: int|null}", "{@param b: bool}", "{@param r: [a: null|[b: null|[c: null|string]]]}", - "{assertType('int', $i!)}", - "{assertType('int|null', $i != null ? $i! : null)}", + "{assertType('number', $i!)}", + "{assertType('null|number', $i != null ? $i! : null)}", "{assertType('string', $r.a.b.c!)}", "{assertType('[c: null|string]', $r.a.b!)}", - "{assertType('int', $i ?? $n!)}", - "{assertType('int', ($b ? $i : $n)!)}", - "{assertType('int|undefined', $b ? $i : $n!)}", + "{assertType('number', $i ?? $n!)}", + "{assertType('number', ($b ? $i : $n)!)}", + "{assertType('number|undefined', $b ? $i : $n!)}", "{assertType('[c: null|string]|null|undefined', $r!.a?.b)}", "{assertType('[c: null|string]|null', $r?.a!.b)}", "{assertType('string', $r?.a.b.c!)}", diff --git a/java/tests/com/google/template/soy/plugin/java/restricted/testing/SoyJavaSourceFunctionTester.java b/java/tests/com/google/template/soy/plugin/java/restricted/testing/SoyJavaSourceFunctionTester.java index 119c2bb492..2025c2e0e7 100644 --- a/java/tests/com/google/template/soy/plugin/java/restricted/testing/SoyJavaSourceFunctionTester.java +++ b/java/tests/com/google/template/soy/plugin/java/restricted/testing/SoyJavaSourceFunctionTester.java @@ -230,11 +230,12 @@ private SoyExpression transform(Object value) { return SoyExpression.forSoyValue( NullType.getInstance(), BytecodeUtils.constantNull(BytecodeUtils.NULL_DATA_TYPE)); } else if (value instanceof Integer) { - return SoyExpression.forInt(BytecodeUtils.constant(((Integer) value).longValue())); + return SoyExpression.forFloat(BytecodeUtils.constant(((Integer) value).doubleValue())); } else if (value instanceof Long) { - return SoyExpression.forInt(BytecodeUtils.constant(((Long) value).longValue())); + return SoyExpression.forFloat(BytecodeUtils.constant(((Long) value).doubleValue())); } else if (value instanceof IntegerData) { - return SoyExpression.forInt(BytecodeUtils.constant(((IntegerData) value).longValue())).box(); + return SoyExpression.forFloat(BytecodeUtils.constant(((IntegerData) value).floatValue())) + .box(); } else if (value instanceof Double) { return SoyExpression.forFloat(BytecodeUtils.constant(((Double) value).doubleValue())); } else if (value instanceof FloatData) { diff --git a/java/tests/com/google/template/soy/sharedpasses/opti/SimplifyExprVisitorTest.java b/java/tests/com/google/template/soy/sharedpasses/opti/SimplifyExprVisitorTest.java index 4a1fe6e337..4c1a8cd11c 100644 --- a/java/tests/com/google/template/soy/sharedpasses/opti/SimplifyExprVisitorTest.java +++ b/java/tests/com/google/template/soy/sharedpasses/opti/SimplifyExprVisitorTest.java @@ -99,7 +99,7 @@ public void testSimplifyPartiallySimplifiableExpr() { new ExpressionParser("floor($boo / (1.0 + 2))") .withParam("boo", "float") .parseForParentNode()) - .simplifiesTo("floor($boo / 3.0)"); + .simplifiesTo("floor($boo / 3)"); } @Test diff --git a/java/tests/com/google/template/soy/sharedpasses/render/EvalVisitorTest.java b/java/tests/com/google/template/soy/sharedpasses/render/EvalVisitorTest.java index 73499f685b..673ef12bbe 100644 --- a/java/tests/com/google/template/soy/sharedpasses/render/EvalVisitorTest.java +++ b/java/tests/com/google/template/soy/sharedpasses/render/EvalVisitorTest.java @@ -90,8 +90,6 @@ protected SoyRecord createTestData() { SoyValueConverterUtility.newDict(), "list0", SoyValueConverterUtility.newList(), - "longNumber", - 1000000000000000001L, "floatNumber", 1.5, "aNull", @@ -305,10 +303,7 @@ public void testEvalDataRefBasic() throws Exception { assertRenderException("$boo?[2]", "encountered non-map/list just before accessing \"[2]\""); assertRenderException( "$boo?['xyz']", "encountered non-map/list just before accessing \"['xyz']\""); - assertDataException( - "$foo[2]", - "SoyDict accessed with non-string key (got key type" - + " com.google.template.soy.data.restricted.IntegerData)."); + assertDataException("$foo[2]", "SoyDict accessed with non-string key (got key type number)."); assertRenderException("$moo.too", "Attempted to access field \"too\" of non-record type"); // assertRenderException( // "$roo.too", "encountered undefined LHS just before accessing \".too\""); @@ -326,10 +321,7 @@ public void testEvalDataRefWithNullSafeAccess() throws Exception { "$foo?.bar?.moo.tar", "encountered non-record just before accessing \".moo\""); assertThat(eval("$foo?.baz?.moo.tar")).isInstanceOf(UndefinedData.class); assertThat(eval("$aNull?.baz?.moo.tar")).isInstanceOf(UndefinedData.class); - assertDataException( - "$foo[2]", - "SoyDict accessed with non-string key (got key type" - + " com.google.template.soy.data.restricted.IntegerData)."); + assertDataException("$foo[2]", "SoyDict accessed with non-string key (got key type number)."); assertRenderException("$moo?.too", "encountered non-record just before accessing \".too\""); assertThat(eval("$roo?.too")).isInstanceOf(UndefinedData.class); assertThat(eval("$roo?[2]")).isInstanceOf(UndefinedData.class); @@ -355,13 +347,6 @@ public void testEvalNumericalOperators() throws Exception { assertEval("$goo[4] - $boo", 7); assertEval("1.002- $woo", 2.62); - - // Ensure longs work. - assertEval("$longNumber + $longNumber", 2000000000000000002L); - assertEval("$longNumber * 4 - $longNumber", 3000000000000000003L); - assertEval("$longNumber / $longNumber", 1.0); // NOTE: Division is on floats. - assertEval("$longNumber < ($longNumber + 1)", true); - assertEval("$longNumber < ($longNumber - 1)", false); } @Test @@ -437,22 +422,6 @@ public void testEvalComparisonOperators() throws Exception { assertEval("22 != '22'", false); assertEval("'' + 22 != '22'", false); - assertEval("$longNumber < $longNumber", false); - assertEval("$longNumber < ($longNumber - 1)", false); - assertEval("($longNumber - 1) < $longNumber", true); - - assertEval("$longNumber <= $longNumber", true); - assertEval("$longNumber <= ($longNumber - 1)", false); - assertEval("($longNumber - 1) <= $longNumber", true); - - assertEval("$longNumber > $longNumber", false); - assertEval("$longNumber > ($longNumber - 1)", true); - assertEval("($longNumber - 1) > $longNumber", false); - - assertEval("$longNumber >= $longNumber", true); - assertEval("$longNumber >= ($longNumber - 1)", true); - assertEval("($longNumber - 1) >= $longNumber", false); - assertEval("$floatNumber < $floatNumber", false); assertEval("$floatNumber < ($floatNumber - 1)", false); assertEval("($floatNumber - 1) < $floatNumber", true); diff --git a/java/tests/com/google/template/soy/sharedpasses/render/RenderVisitorTest.java b/java/tests/com/google/template/soy/sharedpasses/render/RenderVisitorTest.java index 6bffc005ed..4fbd2a4baa 100644 --- a/java/tests/com/google/template/soy/sharedpasses/render/RenderVisitorTest.java +++ b/java/tests/com/google/template/soy/sharedpasses/render/RenderVisitorTest.java @@ -1446,7 +1446,7 @@ public void testDelayedCheckingOfCachingProviders_typeCheckFailure() { .isEqualTo( "When evaluating \"$foo\": Parameter type mismatch: attempt to bind value " + "'hello world' (a StringData) to parameter " - + "'foo' which has a declared type of 'int'."); + + "'foo' which has a declared type of 'number'."); } } diff --git a/java/tests/com/google/template/soy/sharedpasses/render/TofuTypeChecksTest.java b/java/tests/com/google/template/soy/sharedpasses/render/TofuTypeChecksTest.java index fb4b919bff..d4765df310 100644 --- a/java/tests/com/google/template/soy/sharedpasses/render/TofuTypeChecksTest.java +++ b/java/tests/com/google/template/soy/sharedpasses/render/TofuTypeChecksTest.java @@ -32,11 +32,10 @@ import com.google.template.soy.data.restricted.StringData; import com.google.template.soy.types.AnyType; import com.google.template.soy.types.BoolType; -import com.google.template.soy.types.FloatType; -import com.google.template.soy.types.IntType; import com.google.template.soy.types.LegacyObjectMapType; import com.google.template.soy.types.ListType; import com.google.template.soy.types.NullType; +import com.google.template.soy.types.NumberType; import com.google.template.soy.types.SanitizedType; import com.google.template.soy.types.SanitizedType.HtmlType; import com.google.template.soy.types.SoyType; @@ -174,13 +173,12 @@ public void testStringTypeIsInstanceNewBehavior() { @Test public void testIntTypeIsInstance() { - assertIsInstance(IntType.getInstance(), INTEGER_DATA); + assertIsInstance(NumberType.getInstance(), FLOAT_DATA, INTEGER_DATA); assertIsNotInstance( - IntType.getInstance(), + NumberType.getInstance(), NULL_DATA, BOOLEAN_DATA, STRING_DATA, - FLOAT_DATA, HTML_DATA, ATTRIBUTES_DATA, CSS_DATA, @@ -194,13 +192,12 @@ public void testIntTypeIsInstance() { @Test public void testFloatTypeIsInstance() { - assertIsInstance(FloatType.getInstance(), FLOAT_DATA); + assertIsInstance(NumberType.getInstance(), INTEGER_DATA, FLOAT_DATA); assertIsNotInstance( - FloatType.getInstance(), + NumberType.getInstance(), NULL_DATA, BOOLEAN_DATA, STRING_DATA, - INTEGER_DATA, HTML_DATA, ATTRIBUTES_DATA, CSS_DATA, @@ -379,8 +376,8 @@ public void testRecordTypeIsInstance() { @Test public void testUnionTypeIsInstanceNewBehavior() { - SoyType utype = UnionType.of(IntType.getInstance(), StringType.getInstance()); - assertIsInstance(utype, INTEGER_DATA, STRING_DATA); + SoyType utype = UnionType.of(NumberType.getInstance(), StringType.getInstance()); + assertIsInstance(utype, INTEGER_DATA, FLOAT_DATA, STRING_DATA); assertIsNotInstance( utype, HTML_DATA, @@ -391,7 +388,6 @@ public void testUnionTypeIsInstanceNewBehavior() { JS_DATA, NULL_DATA, BOOLEAN_DATA, - FLOAT_DATA, LIST_DATA, MAP_DATA, DICT_DATA); @@ -424,13 +420,12 @@ public void testStringUnionTypeIsInstance() { @Test public void testUnionTypeIsInstance() { - SoyType utype = UnionType.of(IntType.getInstance(), StringType.getInstance()); - assertIsInstance(utype, INTEGER_DATA, STRING_DATA); + SoyType utype = UnionType.of(NumberType.getInstance(), StringType.getInstance()); + assertIsInstance(utype, INTEGER_DATA, FLOAT_DATA, STRING_DATA); assertIsNotInstance( utype, NULL_DATA, BOOLEAN_DATA, - FLOAT_DATA, LIST_DATA, MAP_DATA, DICT_DATA, diff --git a/java/tests/com/google/template/soy/soyparse/ParseExpressionTest.java b/java/tests/com/google/template/soy/soyparse/ParseExpressionTest.java index e3a6d1acc1..d553bb6161 100644 --- a/java/tests/com/google/template/soy/soyparse/ParseExpressionTest.java +++ b/java/tests/com/google/template/soy/soyparse/ParseExpressionTest.java @@ -29,13 +29,12 @@ import com.google.template.soy.exprtree.ExprNode; import com.google.template.soy.exprtree.ExprNode.OperatorNode; import com.google.template.soy.exprtree.FieldAccessNode; -import com.google.template.soy.exprtree.FloatNode; import com.google.template.soy.exprtree.FunctionNode; -import com.google.template.soy.exprtree.IntegerNode; import com.google.template.soy.exprtree.ItemAccessNode; import com.google.template.soy.exprtree.ListLiteralNode; import com.google.template.soy.exprtree.MethodCallNode; import com.google.template.soy.exprtree.NullNode; +import com.google.template.soy.exprtree.NumberNode; import com.google.template.soy.exprtree.Operator; import com.google.template.soy.exprtree.OperatorNodes.AssertNonNullOpNode; import com.google.template.soy.exprtree.OperatorNodes.BarBarOpNode; @@ -330,7 +329,7 @@ public void testParseDataReference() throws Exception { assertNodeEquals( new ItemAccessNode( new ItemAccessNode( - new VarRefNode("$boo", loc, null), new IntegerNode(0, loc), loc, false), + new VarRefNode("$boo", loc, null), new NumberNode(0, loc), loc, false), new VarRefNode("$foo", loc, null), loc, false), @@ -340,7 +339,7 @@ public void testParseDataReference() throws Exception { assertNodeEquals( new ItemAccessNode( new ItemAccessNode( - new VarRefNode("$boo", loc, null), new IntegerNode(0, loc), loc, true), + new VarRefNode("$boo", loc, null), new NumberNode(0, loc), loc, true), new VarRefNode("$foo", loc, null), loc, true), @@ -360,16 +359,16 @@ public void testParsePrimitives() throws Exception { assertThat(((BooleanNode) expr).getValue()).isFalse(); expr = assertThatExpression("26").isValidExpression(); - assertThat(((IntegerNode) expr).getValue()).isEqualTo(26); + assertThat(((NumberNode) expr).getValue()).isEqualTo(26); expr = assertThatExpression("0xCAFE").isValidExpression(); - assertThat(((IntegerNode) expr).getValue()).isEqualTo(0xCAFE); + assertThat(((NumberNode) expr).getValue()).isEqualTo(0xCAFE); expr = assertThatExpression("3.14").isValidExpression(); - assertThat(((FloatNode) expr).getValue()).isEqualTo(3.14); + assertThat(((NumberNode) expr).getValue()).isEqualTo(3.14); expr = assertThatExpression("3e-3").isValidExpression(); - assertThat(((FloatNode) expr).getValue()).isEqualTo(3e-3); + assertThat(((NumberNode) expr).getValue()).isEqualTo(3e-3); expr = assertThatExpression("'Aa`! \\n \\r \\t \\\\ \\\' \"'").isValidExpression(); assertThat(((StringNode) expr).getValue()).isEqualTo("Aa`! \n \r \t \\ \' \""); @@ -418,8 +417,8 @@ public void testParseFunctionCall() throws Exception { FunctionNode roundFn = (FunctionNode) expr; assertThat(roundFn.getFunctionName()).isEqualTo("round"); assertThat(roundFn.numChildren()).isEqualTo(2); - assertThat(((FloatNode) roundFn.getChild(0)).getValue()).isEqualTo(3.14159); - assertThat(((IntegerNode) roundFn.getChild(1)).getValue()).isEqualTo(2); + assertThat(((NumberNode) roundFn.getChild(0)).getValue()).isEqualTo(3.14159); + assertThat(((NumberNode) roundFn.getChild(1)).getValue()).isEqualTo(2); } @Test @@ -437,7 +436,7 @@ public void testParseProtoInitCall() throws Exception { .inOrder(); assertThat(protoFn.numChildren()).isEqualTo(4); assertThat(((VarRefNode) protoFn.getChild(0)).getName()).isEqualTo("my"); - assertThat(((IntegerNode) protoFn.getChild(1)).getValue()).isEqualTo(1); + assertThat(((NumberNode) protoFn.getChild(1)).getValue()).isEqualTo(1); assertThat(((StringNode) ((FunctionNode) protoFn.getChild(2)).getChild(0)).getValue()) .isEqualTo("str"); assertThat(((StringNode) protoFn.getChild(3)).getValue()).isEqualTo("str"); @@ -449,7 +448,7 @@ public void testParseProtoInitCall() throws Exception { @Test public void testParseOperators() throws Exception { ExprNode expr = assertThatExpression("-11").isValidExpression(); - IntegerNode negInt = (IntegerNode) expr; + NumberNode negInt = (NumberNode) expr; assertThat(negInt.getValue()).isEqualTo(-11); expr = assertThatExpression("!false").isValidExpression(); @@ -458,8 +457,8 @@ public void testParseOperators() throws Exception { expr = assertThatExpression("90 -14.75").isValidExpression(); MinusOpNode minusOp = (MinusOpNode) expr; - assertThat(((IntegerNode) minusOp.getChild(0)).getValue()).isEqualTo(90); - assertThat(((FloatNode) minusOp.getChild(1)).getValue()).isEqualTo(14.75); + assertThat(((NumberNode) minusOp.getChild(0)).getValue()).isEqualTo(90); + assertThat(((NumberNode) minusOp.getChild(1)).getValue()).isEqualTo(14.75); expr = assertThatExpression("$a || true").isValidExpression(); BarBarOpNode orOp = (BarBarOpNode) expr; @@ -477,7 +476,7 @@ public void testParseOperators() throws Exception { ConditionalOpNode condOp = (ConditionalOpNode) expr; assertThat(condOp.getChild(0)).isInstanceOf(NullCoalescingOpNode.class); assertThat(condOp.getChild(1)).isInstanceOf(TimesOpNode.class); - assertThat(condOp.getChild(2)).isInstanceOf(IntegerNode.class); + assertThat(condOp.getChild(2)).isInstanceOf(NumberNode.class); } @Test @@ -509,7 +508,7 @@ public void testOperatorPrecedence() throws Exception { public void testNonNullAssertion() { ExprNode expr = assertThatExpression("1!").isValidExpression(); AssertNonNullOpNode nonNullOp = (AssertNonNullOpNode) expr; - assertThat(nonNullOp.getChild(0)).isInstanceOf(IntegerNode.class); + assertThat(nonNullOp.getChild(0)).isInstanceOf(NumberNode.class); expr = assertThatExpression("record(a: 1)!.a").isValidExpression(); FieldAccessNode fieldAccess = (FieldAccessNode) expr; diff --git a/java/tests/com/google/template/soy/soyparse/SourceLocationTest.java b/java/tests/com/google/template/soy/soyparse/SourceLocationTest.java index a0d952ddbf..95ddc212f9 100644 --- a/java/tests/com/google/template/soy/soyparse/SourceLocationTest.java +++ b/java/tests/com/google/template/soy/soyparse/SourceLocationTest.java @@ -109,13 +109,13 @@ public void testTemplateCall() throws Exception { " VarRefNode planet", " CallParamValueNode {param index: 5 /}", " ExprRootNode 5", - " IntegerNode 5", + " NumberNode 5", " CallParamContentNode {param name kind='text'}Jupiter{/param}", " RawTextNode Jupiter", " CallDelegateNode {delcall ns.maybePlanet}{[...]}Pluto{/param}{/delcall}", " CallParamValueNode {param index: 9 /}", " ExprRootNode 9", - " IntegerNode 9", + " NumberNode 9", " CallParamContentNode {param name kind='text'}Pluto{/param}", " RawTextNode Pluto", " TemplateBasicNode {template planet}{@param [...]ex}: {$name}.{/template}", @@ -165,15 +165,15 @@ public void testSwitches() throws Exception { " VarRefNode $i", " SwitchCaseNode {case 0}Mercury", " ExprRootNode 0", - " IntegerNode 0", + " NumberNode 0", " RawTextNode Mercury", " SwitchCaseNode {case 1}Venus", " ExprRootNode 1", - " IntegerNode 1", + " NumberNode 1", " RawTextNode Venus", " SwitchCaseNode {case 2}Mars", " ExprRootNode 2", - " IntegerNode 2", + " NumberNode 2", " RawTextNode Mars", " SwitchDefaultNode {default}Gassy", " RawTextNode Gassy", @@ -306,7 +306,7 @@ public void testLet() throws Exception { " ExprRootNode round($distance, 2)", " FunctionNode round($distance, 2)", " VarRefNode $distance", - " IntegerNode 2", + " NumberNode 2", " LetContentNode {let $formatted kind='tex[...]pprox} light years{/let}", " PrintNode {$approx}", " ExprRootNode $approx", @@ -434,11 +434,11 @@ public void testExpressions() throws Exception { " TemplateBasicNode {template math}{@param a:[...]r.a.b?.c?[0]}{/template}", " ExprRootNode [1, 2, 3*4]", " ListLiteralNode [1, 2, 3*4]", - " IntegerNode 1", - " IntegerNode 2", + " NumberNode 1", + " NumberNode 2", " TimesOpNode 3*4", - " IntegerNode 3", - " IntegerNode 4", + " NumberNode 3", + " NumberNode 4", " PrintNode {$a + $b + $c}", " ExprRootNode $a + $b + $c", " PlusOpNode $a + $b + $c", @@ -527,7 +527,7 @@ public void testExpressions() throws Exception { " FieldAccessNode $r.a.b", " FieldAccessNode $r.a", " VarRefNode $r", - " IntegerNode 0", + " NumberNode 0", " PrintNode {$r.a.b?.c?[0]}", " ExprRootNode $r.a.b?.c?[0]", " ItemAccessNode $r.a.b?.c?[0]", @@ -535,7 +535,7 @@ public void testExpressions() throws Exception { " FieldAccessNode $r.a.b", " FieldAccessNode $r.a", " VarRefNode $r", - " IntegerNode 0", + " NumberNode 0", ""), JOINER.join( "{namespace ns}", @@ -635,7 +635,7 @@ public void testTrailingCommentsInNonClosingNodes() { " ExprRootNode $foolist[0]", " ItemAccessNode $foolist[0]", " VarRefNode $foolist", - " IntegerNode 0", + " NumberNode 0", " MsgSelectCaseNode {case 'foo'}foo", " RawTextNode foo", " MsgSelectDefaultNode {default}baz", diff --git a/java/tests/com/google/template/soy/soyparse/TemplateParserTest.java b/java/tests/com/google/template/soy/soyparse/TemplateParserTest.java index b3954e135b..4e3f96807b 100644 --- a/java/tests/com/google/template/soy/soyparse/TemplateParserTest.java +++ b/java/tests/com/google/template/soy/soyparse/TemplateParserTest.java @@ -31,7 +31,7 @@ import com.google.template.soy.error.SoyError; import com.google.template.soy.exprtree.ExprEquivalence; import com.google.template.soy.exprtree.FieldAccessNode; -import com.google.template.soy.exprtree.IntegerNode; +import com.google.template.soy.exprtree.NumberNode; import com.google.template.soy.exprtree.OperatorNodes.GreaterThanOpNode; import com.google.template.soy.exprtree.OperatorNodes.PlusOpNode; import com.google.template.soy.exprtree.StringNode; @@ -866,7 +866,7 @@ public void testParseHeaderDecls() throws Exception { assertThat(params.get(0).desc()).isNull(); assertThat(params.get(1).name()).isEqualTo("foo"); - assertEquals("list", params.get(1).type().toString()); + assertEquals("list", params.get(1).type().toString()); assertThat(params.get(1).desc()).isEqualTo("Something random."); assertThat(params.get(2).name()).isEqualTo("goo"); @@ -919,7 +919,7 @@ public void testParsePrintStmt() throws Exception { assertEquals(1, pn3.numChildren()); PrintDirectiveNode pn3d0 = pn3.getChild(0); assertEquals("|insertWordBreaks", pn3d0.getName()); - assertEquals(8, ((IntegerNode) pn3d0.getArgs().get(0).getRoot()).getValue()); + assertEquals(8, (int) ((NumberNode) pn3d0.getArgs().get(0).getRoot()).getValue()); assertEquals(MessagePlaceholder.create("XXX"), pn3.getPlaceholder()); assertTrue(pn3.getExpr().getRoot() instanceof StringNode); @@ -1274,8 +1274,8 @@ public void testParseSwitchStmt() throws Exception { SwitchCaseNode scn0 = (SwitchCaseNode) sn.getChild(0); assertEquals(1, scn0.getExprList().size()); - assertTrue(scn0.getExprList().get(0).getRoot() instanceof IntegerNode); - assertEquals(0, ((IntegerNode) scn0.getExprList().get(0).getRoot()).getValue()); + assertTrue(scn0.getExprList().get(0).getRoot() instanceof NumberNode); + assertEquals(0, (int) ((NumberNode) scn0.getExprList().get(0).getRoot()).getValue()); SwitchCaseNode scn1 = (SwitchCaseNode) sn.getChild(1); assertEquals(1, scn1.getExprList().size()); @@ -1284,8 +1284,8 @@ public void testParseSwitchStmt() throws Exception { SwitchCaseNode scn2 = (SwitchCaseNode) sn.getChild(2); assertEquals(3, scn2.getExprList().size()); - assertTrue(scn2.getExprList().get(0).getRoot() instanceof IntegerNode); - assertTrue(scn2.getExprList().get(1).getRoot() instanceof IntegerNode); + assertTrue(scn2.getExprList().get(0).getRoot() instanceof NumberNode); + assertTrue(scn2.getExprList().get(1).getRoot() instanceof NumberNode); assertTrue(scn2.getExprList().get(2).getRoot() instanceof VarRefNode); assertEquals("-1", scn2.getExprList().get(0).getRoot().toSourceString()); assertEquals("1", scn2.getExprList().get(1).getRoot().toSourceString()); diff --git a/java/tests/com/google/template/soy/soytree/SoyTreeUtilsTest.java b/java/tests/com/google/template/soy/soytree/SoyTreeUtilsTest.java index 9deadedb28..dd8fe7d0c1 100644 --- a/java/tests/com/google/template/soy/soytree/SoyTreeUtilsTest.java +++ b/java/tests/com/google/template/soy/soytree/SoyTreeUtilsTest.java @@ -36,7 +36,7 @@ import com.google.template.soy.exprtree.ExprNode.OperatorNode; import com.google.template.soy.exprtree.ExprNode.ParentExprNode; import com.google.template.soy.exprtree.FunctionNode; -import com.google.template.soy.exprtree.IntegerNode; +import com.google.template.soy.exprtree.NumberNode; import com.google.template.soy.exprtree.VarDefn; import com.google.template.soy.exprtree.VarRefNode; import com.google.template.soy.shared.restricted.SoyFunction; @@ -461,8 +461,8 @@ public void testIsDescendant() { assertIsDescendent(ifNode, template); assertIsNotDescendent(template, ifNode); - IntegerNode number = - Iterables.getOnlyElement(SoyTreeUtils.getAllNodesOfType(template, IntegerNode.class)); + NumberNode number = + Iterables.getOnlyElement(SoyTreeUtils.getAllNodesOfType(template, NumberNode.class)); OperatorNode plusOp = Iterables.getOnlyElement(SoyTreeUtils.getAllNodesOfType(template, OperatorNode.class)); diff --git a/java/tests/com/google/template/soy/tofu/internal/TofuExceptionsTest.java b/java/tests/com/google/template/soy/tofu/internal/TofuExceptionsTest.java index cb4bb50583..da9613763c 100644 --- a/java/tests/com/google/template/soy/tofu/internal/TofuExceptionsTest.java +++ b/java/tests/com/google/template/soy/tofu/internal/TofuExceptionsTest.java @@ -90,7 +90,7 @@ public void testExceptions_badType() throws Exception { .hasMessageThat() .isEqualTo( "Parameter type mismatch: attempt to bind value 'not a record' (a StringData) to " - + "parameter 'foo' which has a declared type of '[boo: int, bad: string]'."); + + "parameter 'foo' which has a declared type of '[boo: number, bad: string]'."); assertThat(ste.getStackTrace()[0].toString()).isEqualTo("ns.calleeTemplate(no-path:8)"); assertThat(ste.getStackTrace()[1].toString()).isEqualTo("ns.callerTemplate(no-path:5)"); } @@ -130,7 +130,7 @@ public void testExceptions_wrongTypeFuture() { .isEqualTo( "When evaluating \"$foo.boo\": Parameter type mismatch: attempt to bind value " + "'not a record' (a StringData) to parameter 'foo' which has a declared type " - + "of '[boo: int, bad: string]'."); + + "of '[boo: number, bad: string]'."); assertThat(ste.getStackTrace()[0].toString()).isEqualTo("ns.calleeTemplate(no-path:8)"); assertThat(ste.getStackTrace()[1].toString()).isEqualTo("ns.calleeTemplate(no-path:10)"); assertThat(ste.getStackTrace()[2].toString()).isEqualTo("ns.callerTemplate(no-path:5)"); @@ -150,7 +150,7 @@ public void testExceptions_transclusion_wrongTypeFuture() { .isEqualTo( "When evaluating \"$foo\": Parameter type mismatch: " + "attempt to bind value 'not an int' (a StringData) to parameter 'foo' which " - + "has a declared type of 'int'."); + + "has a declared type of 'number'."); assertThat(ste.getStackTrace()[0].toString()).isEqualTo("ns.transclusionCallee(no-path:21)"); } } diff --git a/java/tests/com/google/template/soy/types/SoyTypeRegistryTest.java b/java/tests/com/google/template/soy/types/SoyTypeRegistryTest.java index ddb7b1bbd5..03ffc09389 100644 --- a/java/tests/com/google/template/soy/types/SoyTypeRegistryTest.java +++ b/java/tests/com/google/template/soy/types/SoyTypeRegistryTest.java @@ -43,37 +43,33 @@ public void testPrimitiveTypes() { assertThat(typeRegistry.getType("any").getKind()).isEqualTo(SoyType.Kind.ANY); assertThat(typeRegistry.getType("null").getKind()).isEqualTo(SoyType.Kind.NULL); assertThat(typeRegistry.getType("bool").getKind()).isEqualTo(SoyType.Kind.BOOL); - assertThat(typeRegistry.getType("int").getKind()).isEqualTo(SoyType.Kind.INT); - assertThat(typeRegistry.getType("float").getKind()).isEqualTo(SoyType.Kind.FLOAT); + assertThat(typeRegistry.getType("int").getKind()).isEqualTo(SoyType.Kind.NUMBER); + assertThat(typeRegistry.getType("float").getKind()).isEqualTo(SoyType.Kind.NUMBER); + assertThat(typeRegistry.getType("number").getKind()).isEqualTo(SoyType.Kind.NUMBER); assertThat(typeRegistry.getType("string").getKind()).isEqualTo(SoyType.Kind.STRING); - - // Check that 'number' type is assignable from both float and int. - assertThat(typeRegistry.getType("number").isAssignableFromStrict(IntType.getInstance())) - .isTrue(); - assertThat(typeRegistry.getType("number").isAssignableFromStrict(FloatType.getInstance())) - .isTrue(); } @Test public void testCreateListType() { - ListType listOfInt = typeRegistry.getOrCreateListType(IntType.getInstance()); - ListType listOfInt2 = typeRegistry.getOrCreateListType(IntType.getInstance()); - ListType listOfFloat = typeRegistry.getOrCreateListType(FloatType.getInstance()); + ListType listOfInt = typeRegistry.getOrCreateListType(NumberType.getInstance()); + ListType listOfInt2 = typeRegistry.getOrCreateListType(NumberType.getInstance()); + ListType listOfString = typeRegistry.getOrCreateListType(StringType.getInstance()); assertThat(listOfInt2).isSameInstanceAs(listOfInt); - assertThat(listOfFloat).isNotSameInstanceAs(listOfInt); + assertThat(listOfString).isNotSameInstanceAs(listOfInt); } @Test public void testCreateLegacyObjectMapType() { LegacyObjectMapType mapOfIntToString = typeRegistry.getOrCreateLegacyObjectMapType( - IntType.getInstance(), StringType.getInstance()); + NumberType.getInstance(), StringType.getInstance()); LegacyObjectMapType mapOfIntToString2 = typeRegistry.getOrCreateLegacyObjectMapType( - IntType.getInstance(), StringType.getInstance()); + NumberType.getInstance(), StringType.getInstance()); LegacyObjectMapType mapOfIntToInt = - typeRegistry.getOrCreateLegacyObjectMapType(IntType.getInstance(), IntType.getInstance()); + typeRegistry.getOrCreateLegacyObjectMapType( + NumberType.getInstance(), NumberType.getInstance()); LegacyObjectMapType mapOfStringToString = typeRegistry.getOrCreateLegacyObjectMapType( StringType.getInstance(), StringType.getInstance()); @@ -85,9 +81,12 @@ public void testCreateLegacyObjectMapType() { @Test public void testCreateUnionType() { - SoyType u1 = typeRegistry.getOrCreateUnionType(IntType.getInstance(), FloatType.getInstance()); - SoyType u2 = typeRegistry.getOrCreateUnionType(IntType.getInstance(), FloatType.getInstance()); - SoyType u3 = typeRegistry.getOrCreateUnionType(IntType.getInstance(), StringType.getInstance()); + SoyType u1 = + typeRegistry.getOrCreateUnionType(NumberType.getInstance(), NumberType.getInstance()); + SoyType u2 = + typeRegistry.getOrCreateUnionType(NumberType.getInstance(), NumberType.getInstance()); + SoyType u3 = + typeRegistry.getOrCreateUnionType(NumberType.getInstance(), StringType.getInstance()); assertThat(u2).isSameInstanceAs(u1); assertThat(u3).isNotSameInstanceAs(u1); @@ -98,28 +97,28 @@ public void testCreateRecordType() { RecordType r1 = typeRegistry.getOrCreateRecordType( ImmutableList.of( - RecordType.memberOf("a", false, IntType.getInstance()), - RecordType.memberOf("b", false, FloatType.getInstance()))); + RecordType.memberOf("a", false, NumberType.getInstance()), + RecordType.memberOf("b", false, NumberType.getInstance()))); RecordType r2 = typeRegistry.getOrCreateRecordType( ImmutableList.of( - RecordType.memberOf("a", false, IntType.getInstance()), - RecordType.memberOf("b", false, FloatType.getInstance()))); + RecordType.memberOf("a", false, NumberType.getInstance()), + RecordType.memberOf("b", false, NumberType.getInstance()))); RecordType r3 = typeRegistry.getOrCreateRecordType( ImmutableList.of( - RecordType.memberOf("a", false, IntType.getInstance()), + RecordType.memberOf("a", false, NumberType.getInstance()), RecordType.memberOf("b", false, StringType.getInstance()))); RecordType r4 = typeRegistry.getOrCreateRecordType( ImmutableList.of( - RecordType.memberOf("a", false, IntType.getInstance()), - RecordType.memberOf("c", false, FloatType.getInstance()))); + RecordType.memberOf("a", false, NumberType.getInstance()), + RecordType.memberOf("c", false, NumberType.getInstance()))); RecordType r5 = typeRegistry.getOrCreateRecordType( ImmutableList.of( - RecordType.memberOf("a", false, IntType.getInstance()), - RecordType.memberOf("c", true, FloatType.getInstance()))); + RecordType.memberOf("a", false, NumberType.getInstance()), + RecordType.memberOf("c", true, NumberType.getInstance()))); assertThat(r2).isSameInstanceAs(r1); assertThat(r3).isNotSameInstanceAs(r1); @@ -127,16 +126,6 @@ public void testCreateRecordType() { assertThat(r4).isNotSameInstanceAs(r5); } - @Test - public void testNumberType() { - // Make sure the type registry knows about the special number type - assertThat(SoyTypes.NUMBER_TYPE) - .isSameInstanceAs( - typeRegistry.getOrCreateUnionType(FloatType.getInstance(), IntType.getInstance())); - assertThat(SoyTypes.NUMBER_TYPE) - .isSameInstanceAs(typeRegistry.getOrCreateUnionType(SoyTypes.NUMBER_TYPE)); - } - @Test public void testProtoFqnCollision() { SoyTypeRegistryBuilder builder = diff --git a/java/tests/com/google/template/soy/types/SoyTypesTest.java b/java/tests/com/google/template/soy/types/SoyTypesTest.java index 89110d120f..ac9da8505c 100644 --- a/java/tests/com/google/template/soy/types/SoyTypesTest.java +++ b/java/tests/com/google/template/soy/types/SoyTypesTest.java @@ -19,7 +19,6 @@ import static com.google.common.base.Strings.lenientFormat; import static com.google.common.truth.Fact.simpleFact; import static com.google.common.truth.Truth.assertThat; -import static com.google.template.soy.types.SoyTypes.NUMBER_TYPE; import static com.google.template.soy.types.SoyTypes.makeNullable; import com.google.common.collect.ImmutableMap; @@ -48,8 +47,8 @@ @RunWith(JUnit4.class) public class SoyTypesTest { private static final BoolType BOOL_TYPE = BoolType.getInstance(); - private static final IntType INT_TYPE = IntType.getInstance(); - private static final FloatType FLOAT_TYPE = FloatType.getInstance(); + private static final NumberType NUMBER_TYPE = NumberType.getInstance(); + private static final NumberType FLOAT_TYPE = NumberType.getInstance(); private static final StringType STRING_TYPE = StringType.getInstance(); private static final HtmlType HTML_TYPE = HtmlType.getInstance(); private static final NullType NULL_TYPE = NullType.getInstance(); @@ -170,11 +169,11 @@ public void testSanitizedType() { @Test public void testUnionType() { // Test that it flattens properly - SoyType utype = UnionType.of(INT_TYPE, UnionType.of(INT_TYPE, NULL_TYPE)); - assertThat(utype.toString()).isEqualTo("int|null"); + SoyType utype = UnionType.of(NUMBER_TYPE, UnionType.of(NUMBER_TYPE, NULL_TYPE)); + assertThat(utype.toString()).isEqualTo("null|number"); assertThatSoyType("int|null").isAssignableFromStrict("int"); assertThatSoyType("int|null").isAssignableFromStrict("null"); - assertThatSoyType("int|null").isNotAssignableFromStrict("float"); + assertThatSoyType("int|null").isAssignableFromStrict("float"); assertThatSoyType("int|null").isNotAssignableFromStrict("string"); assertThatSoyType("int|null").isNotAssignableFromStrict("any"); assertThatSoyType("int|null").isNotAssignableFromStrict("?"); @@ -326,7 +325,7 @@ public void testMapValueCovariance() { public void testNamed() { SoyTypeRegistry baseRegistry = SoyTypeRegistryBuilder.create(); SoyType stringAlias = NamedType.create("StringAlias", "-", StringType.getInstance()); - SoyType intAlias = NamedType.create("IntAlias", "-", IntType.getInstance()); + SoyType intAlias = NamedType.create("IntAlias", "-", NumberType.getInstance()); SoyType unionOfAliases = NamedType.create("UnionOfAliases", "-", UnionType.of(stringAlias, intAlias)); SoyType anotherUnion = @@ -563,120 +562,138 @@ public void testAllContentKindsCovered() { @Test public void testBothOfKind() { - assertThat(SoyTypes.bothOfKind(INT_TYPE, INT_TYPE, INT_TYPE.getKind())).isTrue(); - assertThat(SoyTypes.bothOfKind(INT_TYPE, INT_TYPE, SoyTypes.ARITHMETIC_PRIMITIVES)).isTrue(); + assertThat(SoyTypes.bothOfKind(NUMBER_TYPE, NUMBER_TYPE, NUMBER_TYPE.getKind())).isTrue(); + assertThat(SoyTypes.bothOfKind(NUMBER_TYPE, NUMBER_TYPE, SoyTypes.ARITHMETIC_PRIMITIVES)) + .isTrue(); assertThat( SoyTypes.bothOfKind( - INT_TYPE, FLOAT_TYPE, ImmutableSet.of(INT_TYPE.getKind(), FLOAT_TYPE.getKind()))) + NUMBER_TYPE, + FLOAT_TYPE, + ImmutableSet.of(NUMBER_TYPE.getKind(), FLOAT_TYPE.getKind()))) + .isTrue(); + assertThat(SoyTypes.bothOfKind(NUMBER_TYPE, FLOAT_TYPE, SoyTypes.ARITHMETIC_PRIMITIVES)) .isTrue(); - assertThat(SoyTypes.bothOfKind(INT_TYPE, FLOAT_TYPE, SoyTypes.ARITHMETIC_PRIMITIVES)).isTrue(); assertThat( SoyTypes.bothOfKind( - NUMBER_TYPE, FLOAT_TYPE, ImmutableSet.of(INT_TYPE.getKind(), FLOAT_TYPE.getKind()))) + NumberType.getInstance(), + FLOAT_TYPE, + ImmutableSet.of(NUMBER_TYPE.getKind(), FLOAT_TYPE.getKind()))) .isTrue(); - assertThat(SoyTypes.bothOfKind(NUMBER_TYPE, FLOAT_TYPE, SoyTypes.ARITHMETIC_PRIMITIVES)) + assertThat( + SoyTypes.bothOfKind( + NumberType.getInstance(), FLOAT_TYPE, SoyTypes.ARITHMETIC_PRIMITIVES)) .isTrue(); - assertThat(SoyTypes.bothOfKind(INT_TYPE, INT_TYPE, ImmutableSet.of())).isFalse(); - assertThat(SoyTypes.bothOfKind(INT_TYPE, FLOAT_TYPE, INT_TYPE.getKind())).isFalse(); - assertThat(SoyTypes.bothOfKind(NUMBER_TYPE, INT_TYPE, INT_TYPE.getKind())).isFalse(); - assertThat(SoyTypes.bothOfKind(STRING_TYPE, NUMBER_TYPE, SoyTypes.ARITHMETIC_PRIMITIVES)) + assertThat(SoyTypes.bothOfKind(NUMBER_TYPE, NUMBER_TYPE, ImmutableSet.of())).isFalse(); + assertThat(SoyTypes.bothOfKind(NUMBER_TYPE, FLOAT_TYPE, NUMBER_TYPE.getKind())).isTrue(); + assertThat(SoyTypes.bothOfKind(NumberType.getInstance(), NUMBER_TYPE, NUMBER_TYPE.getKind())) + .isTrue(); + assertThat( + SoyTypes.bothOfKind( + STRING_TYPE, NumberType.getInstance(), SoyTypes.ARITHMETIC_PRIMITIVES)) .isFalse(); } @Test public void testEitherOfKind() { - assertThat(SoyTypes.eitherOfKind(INT_TYPE, INT_TYPE, INT_TYPE.getKind())).isTrue(); - assertThat(SoyTypes.eitherOfKind(STRING_TYPE, INT_TYPE, INT_TYPE.getKind())).isTrue(); - assertThat(SoyTypes.eitherOfKind(INT_TYPE, STRING_TYPE, INT_TYPE.getKind())).isTrue(); - assertThat(SoyTypes.eitherOfKind(INT_TYPE, STRING_TYPE, SoyTypes.ARITHMETIC_PRIMITIVES)) + assertThat(SoyTypes.eitherOfKind(NUMBER_TYPE, NUMBER_TYPE, NUMBER_TYPE.getKind())).isTrue(); + assertThat(SoyTypes.eitherOfKind(STRING_TYPE, NUMBER_TYPE, NUMBER_TYPE.getKind())).isTrue(); + assertThat(SoyTypes.eitherOfKind(NUMBER_TYPE, STRING_TYPE, NUMBER_TYPE.getKind())).isTrue(); + assertThat(SoyTypes.eitherOfKind(NUMBER_TYPE, STRING_TYPE, SoyTypes.ARITHMETIC_PRIMITIVES)) .isTrue(); - assertThat(SoyTypes.eitherOfKind(FLOAT_TYPE, NUMBER_TYPE, ImmutableSet.of())).isFalse(); - assertThat(SoyTypes.eitherOfKind(FLOAT_TYPE, NUMBER_TYPE, INT_TYPE.getKind())).isFalse(); + assertThat(SoyTypes.eitherOfKind(FLOAT_TYPE, NumberType.getInstance(), ImmutableSet.of())) + .isFalse(); + assertThat(SoyTypes.eitherOfKind(FLOAT_TYPE, NumberType.getInstance(), NUMBER_TYPE.getKind())) + .isTrue(); } @Test public void testComputeStricterType() { - assertThat(SoyTypes.computeStricterType(INT_TYPE, ANY_TYPE)).hasValue(INT_TYPE); - assertThat(SoyTypes.computeStricterType(NUMBER_TYPE, INT_TYPE)).hasValue(INT_TYPE); + assertThat(SoyTypes.computeStricterType(NUMBER_TYPE, ANY_TYPE)).hasValue(NUMBER_TYPE); + assertThat(SoyTypes.computeStricterType(NumberType.getInstance(), NUMBER_TYPE)) + .hasValue(NUMBER_TYPE); assertThat(SoyTypes.computeStricterType(makeNullable(STRING_TYPE), STRING_TYPE)) .hasValue(STRING_TYPE); - assertThat(SoyTypes.computeStricterType(INT_TYPE, STRING_TYPE)).isEmpty(); + assertThat(SoyTypes.computeStricterType(NUMBER_TYPE, STRING_TYPE)).isEmpty(); } @Test public void testLowestCommonType() { SoyTypeRegistry typeRegistry = SoyTypeRegistryBuilder.create(); - assertThat(SoyTypes.computeLowestCommonType(typeRegistry, INT_TYPE, ANY_TYPE)) + assertThat(SoyTypes.computeLowestCommonType(typeRegistry, NUMBER_TYPE, ANY_TYPE)) .isEqualTo(ANY_TYPE); - assertThat(SoyTypes.computeLowestCommonType(typeRegistry, INT_TYPE, UNKNOWN_TYPE)) + assertThat(SoyTypes.computeLowestCommonType(typeRegistry, NUMBER_TYPE, UNKNOWN_TYPE)) .isEqualTo(UNKNOWN_TYPE); - assertThat(SoyTypes.computeLowestCommonType(typeRegistry, UNKNOWN_TYPE, INT_TYPE)) + assertThat(SoyTypes.computeLowestCommonType(typeRegistry, UNKNOWN_TYPE, NUMBER_TYPE)) .isEqualTo(UNKNOWN_TYPE); - assertThat(SoyTypes.computeLowestCommonType(typeRegistry, ANY_TYPE, INT_TYPE)) + assertThat(SoyTypes.computeLowestCommonType(typeRegistry, ANY_TYPE, NUMBER_TYPE)) .isEqualTo(ANY_TYPE); assertThat(SoyTypes.computeLowestCommonType(typeRegistry, STRING_TYPE, HTML_TYPE)) .isEqualTo(UnionType.of(STRING_TYPE, HTML_TYPE)); - assertThat(SoyTypes.computeLowestCommonType(typeRegistry, INT_TYPE, FLOAT_TYPE)) - .isEqualTo(NUMBER_TYPE); - assertThat(SoyTypes.computeLowestCommonType(typeRegistry, FLOAT_TYPE, INT_TYPE)) - .isEqualTo(NUMBER_TYPE); - assertThat(SoyTypes.computeLowestCommonType(typeRegistry, INT_TYPE, UNDEFINED_TYPE)) - .isEqualTo(UnionType.of(INT_TYPE, UNDEFINED_TYPE)); + assertThat(SoyTypes.computeLowestCommonType(typeRegistry, NUMBER_TYPE, FLOAT_TYPE)) + .isEqualTo(NumberType.getInstance()); + assertThat(SoyTypes.computeLowestCommonType(typeRegistry, FLOAT_TYPE, NUMBER_TYPE)) + .isEqualTo(NumberType.getInstance()); + assertThat(SoyTypes.computeLowestCommonType(typeRegistry, NUMBER_TYPE, UNDEFINED_TYPE)) + .isEqualTo(UnionType.of(NUMBER_TYPE, UNDEFINED_TYPE)); } @Test public void testLowestCommonTypeArithmetic() { - assertThat(SoyTypes.computeLowestCommonTypeArithmetic(INT_TYPE, ANY_TYPE)).isEmpty(); - assertThat(SoyTypes.computeLowestCommonTypeArithmetic(ANY_TYPE, INT_TYPE)).isEmpty(); + assertThat(SoyTypes.computeLowestCommonTypeArithmetic(NUMBER_TYPE, ANY_TYPE)).isEmpty(); + assertThat(SoyTypes.computeLowestCommonTypeArithmetic(ANY_TYPE, NUMBER_TYPE)).isEmpty(); assertThat(SoyTypes.computeLowestCommonTypeArithmetic(STRING_TYPE, HTML_TYPE)).isEmpty(); assertThat(SoyTypes.computeLowestCommonTypeArithmetic(HTML_TYPE, STRING_TYPE)).isEmpty(); - assertThat(SoyTypes.computeLowestCommonTypeArithmetic(ListType.of(INT_TYPE), INT_TYPE)) + assertThat(SoyTypes.computeLowestCommonTypeArithmetic(ListType.of(NUMBER_TYPE), NUMBER_TYPE)) .isEmpty(); assertThat( SoyTypes.computeLowestCommonTypeArithmetic( - LegacyObjectMapType.of(INT_TYPE, STRING_TYPE), INT_TYPE)) + LegacyObjectMapType.of(NUMBER_TYPE, STRING_TYPE), NUMBER_TYPE)) .isEmpty(); assertThat( SoyTypes.computeLowestCommonTypeArithmetic( - RecordType.of(ImmutableMap.of("a", INT_TYPE, "b", FLOAT_TYPE)), INT_TYPE)) + RecordType.of(ImmutableMap.of("a", NUMBER_TYPE, "b", FLOAT_TYPE)), NUMBER_TYPE)) .isEmpty(); assertThat( SoyTypes.computeLowestCommonTypeArithmetic( - UnionType.of(LegacyObjectMapType.of(FLOAT_TYPE, STRING_TYPE), INT_TYPE), + UnionType.of(LegacyObjectMapType.of(FLOAT_TYPE, STRING_TYPE), NUMBER_TYPE), FLOAT_TYPE)) .isEmpty(); assertThat( SoyTypes.computeLowestCommonTypeArithmetic( - UnionType.of(BOOL_TYPE, INT_TYPE, STRING_TYPE), NUMBER_TYPE)) + UnionType.of(BOOL_TYPE, NUMBER_TYPE, STRING_TYPE), NumberType.getInstance())) .isEmpty(); assertThat( - SoyTypes.computeLowestCommonTypeArithmetic(UnionType.of(BOOL_TYPE, INT_TYPE), INT_TYPE)) + SoyTypes.computeLowestCommonTypeArithmetic( + UnionType.of(BOOL_TYPE, NUMBER_TYPE), NUMBER_TYPE)) .isEmpty(); assertThat( SoyTypes.computeLowestCommonTypeArithmetic( - UnionType.of(STRING_TYPE, FLOAT_TYPE), INT_TYPE)) + UnionType.of(STRING_TYPE, FLOAT_TYPE), NUMBER_TYPE)) .isEmpty(); - assertThat(SoyTypes.computeLowestCommonTypeArithmetic(INT_TYPE, FLOAT_TYPE)) + assertThat(SoyTypes.computeLowestCommonTypeArithmetic(NUMBER_TYPE, FLOAT_TYPE)) .hasValue(FLOAT_TYPE); - assertThat(SoyTypes.computeLowestCommonTypeArithmetic(FLOAT_TYPE, INT_TYPE)) + assertThat(SoyTypes.computeLowestCommonTypeArithmetic(FLOAT_TYPE, NUMBER_TYPE)) .hasValue(FLOAT_TYPE); assertThat(SoyTypes.computeLowestCommonTypeArithmetic(FLOAT_TYPE, UNKNOWN_TYPE)) .hasValue(UNKNOWN_TYPE); assertThat(SoyTypes.computeLowestCommonTypeArithmetic(UNKNOWN_TYPE, FLOAT_TYPE)) .hasValue(UNKNOWN_TYPE); - assertThat(SoyTypes.computeLowestCommonTypeArithmetic(INT_TYPE, INT_TYPE)).hasValue(INT_TYPE); - assertThat(SoyTypes.computeLowestCommonTypeArithmetic(FLOAT_TYPE, FLOAT_TYPE)) - .hasValue(FLOAT_TYPE); - assertThat(SoyTypes.computeLowestCommonTypeArithmetic(FLOAT_TYPE, NUMBER_TYPE)) - .hasValue(NUMBER_TYPE); - assertThat(SoyTypes.computeLowestCommonTypeArithmetic(INT_TYPE, NUMBER_TYPE)) - .hasValue(NUMBER_TYPE); assertThat(SoyTypes.computeLowestCommonTypeArithmetic(NUMBER_TYPE, NUMBER_TYPE)) .hasValue(NUMBER_TYPE); + assertThat(SoyTypes.computeLowestCommonTypeArithmetic(FLOAT_TYPE, FLOAT_TYPE)) + .hasValue(FLOAT_TYPE); + assertThat(SoyTypes.computeLowestCommonTypeArithmetic(FLOAT_TYPE, NumberType.getInstance())) + .hasValue(NumberType.getInstance()); + assertThat(SoyTypes.computeLowestCommonTypeArithmetic(NUMBER_TYPE, NumberType.getInstance())) + .hasValue(NumberType.getInstance()); + assertThat( + SoyTypes.computeLowestCommonTypeArithmetic( + NumberType.getInstance(), NumberType.getInstance())) + .hasValue(NumberType.getInstance()); } @Test @@ -690,20 +707,22 @@ public void testGetSoyTypeForBinaryOperatorPlusOp() { * return number. */ // All number types - assertThat(SoyTypes.getSoyTypeForBinaryOperator(INT_TYPE, FLOAT_TYPE, plusOp)) - .isEqualTo(FLOAT_TYPE); - assertThat(SoyTypes.getSoyTypeForBinaryOperator(FLOAT_TYPE, INT_TYPE, plusOp)) - .isEqualTo(FLOAT_TYPE); - assertThat(SoyTypes.getSoyTypeForBinaryOperator(INT_TYPE, INT_TYPE, plusOp)) - .isEqualTo(INT_TYPE); - assertThat(SoyTypes.getSoyTypeForBinaryOperator(FLOAT_TYPE, FLOAT_TYPE, plusOp)) + assertThat(SoyTypes.getSoyTypeForBinaryOperator(NUMBER_TYPE, FLOAT_TYPE, plusOp)) .isEqualTo(FLOAT_TYPE); assertThat(SoyTypes.getSoyTypeForBinaryOperator(FLOAT_TYPE, NUMBER_TYPE, plusOp)) .isEqualTo(FLOAT_TYPE); - assertThat(SoyTypes.getSoyTypeForBinaryOperator(INT_TYPE, NUMBER_TYPE, plusOp)) - .isEqualTo(NUMBER_TYPE); assertThat(SoyTypes.getSoyTypeForBinaryOperator(NUMBER_TYPE, NUMBER_TYPE, plusOp)) .isEqualTo(NUMBER_TYPE); + assertThat(SoyTypes.getSoyTypeForBinaryOperator(FLOAT_TYPE, FLOAT_TYPE, plusOp)) + .isEqualTo(FLOAT_TYPE); + assertThat(SoyTypes.getSoyTypeForBinaryOperator(FLOAT_TYPE, NumberType.getInstance(), plusOp)) + .isEqualTo(FLOAT_TYPE); + assertThat(SoyTypes.getSoyTypeForBinaryOperator(NUMBER_TYPE, NumberType.getInstance(), plusOp)) + .isEqualTo(NumberType.getInstance()); + assertThat( + SoyTypes.getSoyTypeForBinaryOperator( + NumberType.getInstance(), NumberType.getInstance(), plusOp)) + .isEqualTo(NumberType.getInstance()); // Unknown types are fine assertThat(SoyTypes.getSoyTypeForBinaryOperator(FLOAT_TYPE, UNKNOWN_TYPE, plusOp)) @@ -714,17 +733,17 @@ public void testGetSoyTypeForBinaryOperatorPlusOp() { .isEqualTo(UNKNOWN_TYPE); // Any string types should be resolved to string - assertThat(SoyTypes.getSoyTypeForBinaryOperator(INT_TYPE, STRING_TYPE, plusOp)) + assertThat(SoyTypes.getSoyTypeForBinaryOperator(NUMBER_TYPE, STRING_TYPE, plusOp)) .isEqualTo(STRING_TYPE); assertThat(SoyTypes.getSoyTypeForBinaryOperator(STRING_TYPE, BOOL_TYPE, plusOp)) .isEqualTo(STRING_TYPE); assertThat(SoyTypes.getSoyTypeForBinaryOperator(STRING_TYPE, FLOAT_TYPE, plusOp)) .isEqualTo(STRING_TYPE); - assertThat(SoyTypes.getSoyTypeForBinaryOperator(STRING_TYPE, NUMBER_TYPE, plusOp)) + assertThat(SoyTypes.getSoyTypeForBinaryOperator(STRING_TYPE, NumberType.getInstance(), plusOp)) .isEqualTo(STRING_TYPE); assertThat(SoyTypes.getSoyTypeForBinaryOperator(STRING_TYPE, STRING_TYPE, plusOp)) .isEqualTo(STRING_TYPE); - assertThat(SoyTypes.getSoyTypeForBinaryOperator(INT_TYPE, URI_TYPE, plusOp)) + assertThat(SoyTypes.getSoyTypeForBinaryOperator(NUMBER_TYPE, URI_TYPE, plusOp)) .isEqualTo(STRING_TYPE); assertThat(SoyTypes.getSoyTypeForBinaryOperator(HTML_TYPE, STRING_TYPE, plusOp)) .isEqualTo(STRING_TYPE); @@ -734,56 +753,64 @@ public void testGetSoyTypeForBinaryOperatorPlusOp() { .isNull(); assertThat( SoyTypes.getSoyTypeForBinaryOperator( - INT_TYPE, LegacyObjectMapType.of(INT_TYPE, STRING_TYPE), plusOp)) + NUMBER_TYPE, LegacyObjectMapType.of(NUMBER_TYPE, STRING_TYPE), plusOp)) .isNull(); assertThat( SoyTypes.getSoyTypeForBinaryOperator( - INT_TYPE, RecordType.of(ImmutableMap.of("a", INT_TYPE, "b", FLOAT_TYPE)), plusOp)) + NUMBER_TYPE, + RecordType.of(ImmutableMap.of("a", NUMBER_TYPE, "b", FLOAT_TYPE)), + plusOp)) .isNull(); // Union types should list all possible combinations assertThat( SoyTypes.getSoyTypeForBinaryOperator( - INT_TYPE, UnionType.of(INT_TYPE, STRING_TYPE), plusOp)) - .isEqualTo(UnionType.of(INT_TYPE, STRING_TYPE)); + NUMBER_TYPE, UnionType.of(NUMBER_TYPE, STRING_TYPE), plusOp)) + .isEqualTo(UnionType.of(NUMBER_TYPE, STRING_TYPE)); assertThat( SoyTypes.getSoyTypeForBinaryOperator( - UnionType.of(STRING_TYPE, FLOAT_TYPE), UnionType.of(INT_TYPE, STRING_TYPE), plusOp)) + UnionType.of(STRING_TYPE, FLOAT_TYPE), + UnionType.of(NUMBER_TYPE, STRING_TYPE), + plusOp)) .isEqualTo(UnionType.of(STRING_TYPE, FLOAT_TYPE)); assertThat( SoyTypes.getSoyTypeForBinaryOperator( - UnionType.of(STRING_TYPE, INT_TYPE), UnionType.of(INT_TYPE, STRING_TYPE), plusOp)) - .isEqualTo(UnionType.of(STRING_TYPE, INT_TYPE)); + UnionType.of(STRING_TYPE, NUMBER_TYPE), + UnionType.of(NUMBER_TYPE, STRING_TYPE), + plusOp)) + .isEqualTo(UnionType.of(STRING_TYPE, NUMBER_TYPE)); assertThat( SoyTypes.getSoyTypeForBinaryOperator( - UnionType.of(STRING_TYPE, HTML_TYPE), UnionType.of(INT_TYPE, STRING_TYPE), plusOp)) + UnionType.of(STRING_TYPE, HTML_TYPE), + UnionType.of(NUMBER_TYPE, STRING_TYPE), + plusOp)) .isEqualTo(STRING_TYPE); // If any of these combinations are incompatible, we should return null. assertThat( SoyTypes.getSoyTypeForBinaryOperator( - INT_TYPE, UnionType.of(BOOL_TYPE, FLOAT_TYPE, INT_TYPE), plusOp)) + NUMBER_TYPE, UnionType.of(BOOL_TYPE, FLOAT_TYPE, NUMBER_TYPE), plusOp)) .isNull(); assertThat( SoyTypes.getSoyTypeForBinaryOperator( UnionType.of(STRING_TYPE, HTML_TYPE), - UnionType.of(INT_TYPE, STRING_TYPE, ListType.of(INT_TYPE)), + UnionType.of(NUMBER_TYPE, STRING_TYPE, ListType.of(NUMBER_TYPE)), plusOp)) .isNull(); // Nullable types should be fine. However, null type itself is not allowed. - assertThat(SoyTypes.getSoyTypeForBinaryOperator(INT_TYPE, NULL_TYPE, plusOp)).isNull(); + assertThat(SoyTypes.getSoyTypeForBinaryOperator(NUMBER_TYPE, NULL_TYPE, plusOp)).isNull(); assertThat( SoyTypes.getSoyTypeForBinaryOperator( - INT_TYPE, UnionType.of(NULL_TYPE, INT_TYPE), plusOp)) - .isEqualTo(INT_TYPE); + NUMBER_TYPE, UnionType.of(NULL_TYPE, NUMBER_TYPE), plusOp)) + .isEqualTo(NUMBER_TYPE); assertThat( SoyTypes.getSoyTypeForBinaryOperator( - UnionType.of(NULL_TYPE, FLOAT_TYPE), UnionType.of(NULL_TYPE, INT_TYPE), plusOp)) + UnionType.of(NULL_TYPE, FLOAT_TYPE), UnionType.of(NULL_TYPE, NUMBER_TYPE), plusOp)) .isEqualTo(FLOAT_TYPE); assertThat( SoyTypes.getSoyTypeForBinaryOperator( - UnionType.of(NULL_TYPE, STRING_TYPE), UnionType.of(NULL_TYPE, INT_TYPE), plusOp)) + UnionType.of(NULL_TYPE, STRING_TYPE), UnionType.of(NULL_TYPE, NUMBER_TYPE), plusOp)) .isEqualTo(STRING_TYPE); } @@ -791,20 +818,22 @@ public void testGetSoyTypeForBinaryOperatorPlusOp() { public void testGetSoyTypeForBinaryOperatorArithmeticOp() { SoyTypes.SoyTypeBinaryOperator plusOp = new SoyTypes.SoyTypeArithmeticOperator(); // All number types - assertThat(SoyTypes.getSoyTypeForBinaryOperator(INT_TYPE, FLOAT_TYPE, plusOp)) - .isEqualTo(FLOAT_TYPE); - assertThat(SoyTypes.getSoyTypeForBinaryOperator(FLOAT_TYPE, INT_TYPE, plusOp)) - .isEqualTo(FLOAT_TYPE); - assertThat(SoyTypes.getSoyTypeForBinaryOperator(INT_TYPE, INT_TYPE, plusOp)) - .isEqualTo(INT_TYPE); - assertThat(SoyTypes.getSoyTypeForBinaryOperator(FLOAT_TYPE, FLOAT_TYPE, plusOp)) + assertThat(SoyTypes.getSoyTypeForBinaryOperator(NUMBER_TYPE, FLOAT_TYPE, plusOp)) .isEqualTo(FLOAT_TYPE); assertThat(SoyTypes.getSoyTypeForBinaryOperator(FLOAT_TYPE, NUMBER_TYPE, plusOp)) .isEqualTo(FLOAT_TYPE); - assertThat(SoyTypes.getSoyTypeForBinaryOperator(INT_TYPE, NUMBER_TYPE, plusOp)) - .isEqualTo(NUMBER_TYPE); assertThat(SoyTypes.getSoyTypeForBinaryOperator(NUMBER_TYPE, NUMBER_TYPE, plusOp)) .isEqualTo(NUMBER_TYPE); + assertThat(SoyTypes.getSoyTypeForBinaryOperator(FLOAT_TYPE, FLOAT_TYPE, plusOp)) + .isEqualTo(FLOAT_TYPE); + assertThat(SoyTypes.getSoyTypeForBinaryOperator(FLOAT_TYPE, NumberType.getInstance(), plusOp)) + .isEqualTo(FLOAT_TYPE); + assertThat(SoyTypes.getSoyTypeForBinaryOperator(NUMBER_TYPE, NumberType.getInstance(), plusOp)) + .isEqualTo(NumberType.getInstance()); + assertThat( + SoyTypes.getSoyTypeForBinaryOperator( + NumberType.getInstance(), NumberType.getInstance(), plusOp)) + .isEqualTo(NumberType.getInstance()); // Unknown types are fine assertThat(SoyTypes.getSoyTypeForBinaryOperator(FLOAT_TYPE, UNKNOWN_TYPE, plusOp)) @@ -815,11 +844,12 @@ public void testGetSoyTypeForBinaryOperatorArithmeticOp() { .isEqualTo(UNKNOWN_TYPE); // Any string types should be rejected - assertThat(SoyTypes.getSoyTypeForBinaryOperator(INT_TYPE, STRING_TYPE, plusOp)).isNull(); + assertThat(SoyTypes.getSoyTypeForBinaryOperator(NUMBER_TYPE, STRING_TYPE, plusOp)).isNull(); assertThat(SoyTypes.getSoyTypeForBinaryOperator(STRING_TYPE, FLOAT_TYPE, plusOp)).isNull(); - assertThat(SoyTypes.getSoyTypeForBinaryOperator(STRING_TYPE, NUMBER_TYPE, plusOp)).isNull(); + assertThat(SoyTypes.getSoyTypeForBinaryOperator(STRING_TYPE, NumberType.getInstance(), plusOp)) + .isNull(); assertThat(SoyTypes.getSoyTypeForBinaryOperator(STRING_TYPE, STRING_TYPE, plusOp)).isNull(); - assertThat(SoyTypes.getSoyTypeForBinaryOperator(INT_TYPE, URI_TYPE, plusOp)).isNull(); + assertThat(SoyTypes.getSoyTypeForBinaryOperator(NUMBER_TYPE, URI_TYPE, plusOp)).isNull(); assertThat(SoyTypes.getSoyTypeForBinaryOperator(HTML_TYPE, STRING_TYPE, plusOp)).isNull(); // Arbitrary types are banned @@ -827,51 +857,58 @@ public void testGetSoyTypeForBinaryOperatorArithmeticOp() { .isNull(); assertThat( SoyTypes.getSoyTypeForBinaryOperator( - INT_TYPE, LegacyObjectMapType.of(INT_TYPE, STRING_TYPE), plusOp)) + NUMBER_TYPE, LegacyObjectMapType.of(NUMBER_TYPE, STRING_TYPE), plusOp)) .isNull(); assertThat( SoyTypes.getSoyTypeForBinaryOperator( - INT_TYPE, RecordType.of(ImmutableMap.of("a", INT_TYPE, "b", FLOAT_TYPE)), plusOp)) + NUMBER_TYPE, + RecordType.of(ImmutableMap.of("a", NUMBER_TYPE, "b", FLOAT_TYPE)), + plusOp)) .isNull(); // If any of these combinations are incompatible, we should return null. assertThat( SoyTypes.getSoyTypeForBinaryOperator( - INT_TYPE, UnionType.of(BOOL_TYPE, FLOAT_TYPE, INT_TYPE), plusOp)) + NUMBER_TYPE, UnionType.of(BOOL_TYPE, FLOAT_TYPE, NUMBER_TYPE), plusOp)) .isNull(); assertThat( SoyTypes.getSoyTypeForBinaryOperator( - UnionType.of(STRING_TYPE, INT_TYPE), - UnionType.of(INT_TYPE, STRING_TYPE, ListType.of(INT_TYPE)), + UnionType.of(STRING_TYPE, NUMBER_TYPE), + UnionType.of(NUMBER_TYPE, STRING_TYPE, ListType.of(NUMBER_TYPE)), plusOp)) .isNull(); assertThat( SoyTypes.getSoyTypeForBinaryOperator( - UnionType.of(NULL_TYPE, STRING_TYPE), UnionType.of(NULL_TYPE, INT_TYPE), plusOp)) + UnionType.of(NULL_TYPE, STRING_TYPE), UnionType.of(NULL_TYPE, NUMBER_TYPE), plusOp)) .isNull(); // Nullable types should be fine. However, null type itself is not allowed. - assertThat(SoyTypes.getSoyTypeForBinaryOperator(INT_TYPE, NULL_TYPE, plusOp)).isNull(); + assertThat(SoyTypes.getSoyTypeForBinaryOperator(NUMBER_TYPE, NULL_TYPE, plusOp)).isNull(); assertThat( SoyTypes.getSoyTypeForBinaryOperator( - INT_TYPE, UnionType.of(NULL_TYPE, INT_TYPE), plusOp)) - .isEqualTo(INT_TYPE); + NUMBER_TYPE, UnionType.of(NULL_TYPE, NUMBER_TYPE), plusOp)) + .isEqualTo(NUMBER_TYPE); assertThat( SoyTypes.getSoyTypeForBinaryOperator( - UnionType.of(NULL_TYPE, FLOAT_TYPE), UnionType.of(NULL_TYPE, INT_TYPE), plusOp)) + UnionType.of(NULL_TYPE, FLOAT_TYPE), UnionType.of(NULL_TYPE, NUMBER_TYPE), plusOp)) .isEqualTo(FLOAT_TYPE); } @Test public void testGetSoyTypeForBinaryOperatorEqualOp() { SoyTypes.SoyTypeBinaryOperator equalOp = new SoyTypes.SoyTypeEqualComparisonOp(); - assertThat(SoyTypes.getSoyTypeForBinaryOperator(INT_TYPE, FLOAT_TYPE, equalOp)).isNotNull(); - assertThat(SoyTypes.getSoyTypeForBinaryOperator(FLOAT_TYPE, INT_TYPE, equalOp)).isNotNull(); - assertThat(SoyTypes.getSoyTypeForBinaryOperator(INT_TYPE, INT_TYPE, equalOp)).isNotNull(); - assertThat(SoyTypes.getSoyTypeForBinaryOperator(FLOAT_TYPE, FLOAT_TYPE, equalOp)).isNotNull(); + assertThat(SoyTypes.getSoyTypeForBinaryOperator(NUMBER_TYPE, FLOAT_TYPE, equalOp)).isNotNull(); assertThat(SoyTypes.getSoyTypeForBinaryOperator(FLOAT_TYPE, NUMBER_TYPE, equalOp)).isNotNull(); - assertThat(SoyTypes.getSoyTypeForBinaryOperator(INT_TYPE, NUMBER_TYPE, equalOp)).isNotNull(); assertThat(SoyTypes.getSoyTypeForBinaryOperator(NUMBER_TYPE, NUMBER_TYPE, equalOp)).isNotNull(); + assertThat(SoyTypes.getSoyTypeForBinaryOperator(FLOAT_TYPE, FLOAT_TYPE, equalOp)).isNotNull(); + assertThat(SoyTypes.getSoyTypeForBinaryOperator(FLOAT_TYPE, NumberType.getInstance(), equalOp)) + .isNotNull(); + assertThat(SoyTypes.getSoyTypeForBinaryOperator(NUMBER_TYPE, NumberType.getInstance(), equalOp)) + .isNotNull(); + assertThat( + SoyTypes.getSoyTypeForBinaryOperator( + NumberType.getInstance(), NumberType.getInstance(), equalOp)) + .isNotNull(); // Unknown types are fine assertThat(SoyTypes.getSoyTypeForBinaryOperator(FLOAT_TYPE, UNKNOWN_TYPE, equalOp)).isNotNull(); @@ -882,10 +919,11 @@ public void testGetSoyTypeForBinaryOperatorEqualOp() { assertThat(SoyTypes.getSoyTypeForBinaryOperator(ANY_TYPE, NULL_TYPE, equalOp)).isNotNull(); // String-number comparisons are okay. - assertThat(SoyTypes.getSoyTypeForBinaryOperator(INT_TYPE, STRING_TYPE, equalOp)).isNotNull(); + assertThat(SoyTypes.getSoyTypeForBinaryOperator(NUMBER_TYPE, STRING_TYPE, equalOp)).isNotNull(); assertThat(SoyTypes.getSoyTypeForBinaryOperator(STRING_TYPE, FLOAT_TYPE, equalOp)).isNotNull(); - assertThat(SoyTypes.getSoyTypeForBinaryOperator(STRING_TYPE, NUMBER_TYPE, equalOp)).isNotNull(); - assertThat(SoyTypes.getSoyTypeForBinaryOperator(INT_TYPE, URI_TYPE, equalOp)).isNotNull(); + assertThat(SoyTypes.getSoyTypeForBinaryOperator(STRING_TYPE, NumberType.getInstance(), equalOp)) + .isNotNull(); + assertThat(SoyTypes.getSoyTypeForBinaryOperator(NUMBER_TYPE, URI_TYPE, equalOp)).isNotNull(); // String-string comparisons are okay. assertThat(SoyTypes.getSoyTypeForBinaryOperator(STRING_TYPE, STRING_TYPE, equalOp)).isNotNull(); @@ -896,60 +934,68 @@ public void testGetSoyTypeForBinaryOperatorEqualOp() { .isNull(); assertThat( SoyTypes.getSoyTypeForBinaryOperator( - INT_TYPE, LegacyObjectMapType.of(INT_TYPE, STRING_TYPE), equalOp)) + NUMBER_TYPE, LegacyObjectMapType.of(NUMBER_TYPE, STRING_TYPE), equalOp)) .isNull(); assertThat( SoyTypes.getSoyTypeForBinaryOperator( - INT_TYPE, RecordType.of(ImmutableMap.of("a", INT_TYPE, "b", FLOAT_TYPE)), equalOp)) + NUMBER_TYPE, + RecordType.of(ImmutableMap.of("a", NUMBER_TYPE, "b", FLOAT_TYPE)), + equalOp)) .isNull(); // Union types might be okay if all combinations are okay. assertThat( SoyTypes.getSoyTypeForBinaryOperator( - INT_TYPE, UnionType.of(INT_TYPE, STRING_TYPE), equalOp)) + NUMBER_TYPE, UnionType.of(NUMBER_TYPE, STRING_TYPE), equalOp)) .isNotNull(); assertThat( SoyTypes.getSoyTypeForBinaryOperator( UnionType.of(STRING_TYPE, FLOAT_TYPE), - UnionType.of(INT_TYPE, STRING_TYPE), + UnionType.of(NUMBER_TYPE, STRING_TYPE), equalOp)) .isNotNull(); assertThat( SoyTypes.getSoyTypeForBinaryOperator( - UnionType.of(STRING_TYPE, INT_TYPE), UnionType.of(INT_TYPE, STRING_TYPE), equalOp)) + UnionType.of(STRING_TYPE, NUMBER_TYPE), + UnionType.of(NUMBER_TYPE, STRING_TYPE), + equalOp)) .isNotNull(); assertThat( SoyTypes.getSoyTypeForBinaryOperator( - UnionType.of(STRING_TYPE, HTML_TYPE), UnionType.of(INT_TYPE, STRING_TYPE), equalOp)) + UnionType.of(STRING_TYPE, HTML_TYPE), + UnionType.of(NUMBER_TYPE, STRING_TYPE), + equalOp)) .isNotNull(); // If any of these combinations are incompatible, we should return null. assertThat( SoyTypes.getSoyTypeForBinaryOperator( - LegacyObjectMapType.of(INT_TYPE, STRING_TYPE), - UnionType.of(BOOL_TYPE, FLOAT_TYPE, INT_TYPE), + LegacyObjectMapType.of(NUMBER_TYPE, STRING_TYPE), + UnionType.of(BOOL_TYPE, FLOAT_TYPE, NUMBER_TYPE), equalOp)) .isNull(); assertThat( SoyTypes.getSoyTypeForBinaryOperator( UnionType.of(STRING_TYPE, HTML_TYPE), - UnionType.of(INT_TYPE, STRING_TYPE, ListType.of(INT_TYPE)), + UnionType.of(NUMBER_TYPE, STRING_TYPE, ListType.of(NUMBER_TYPE)), equalOp)) .isNull(); // Nullable types and null type itself are okay. - assertThat(SoyTypes.getSoyTypeForBinaryOperator(INT_TYPE, NULL_TYPE, equalOp)).isNotNull(); + assertThat(SoyTypes.getSoyTypeForBinaryOperator(NUMBER_TYPE, NULL_TYPE, equalOp)).isNotNull(); assertThat( SoyTypes.getSoyTypeForBinaryOperator( - INT_TYPE, UnionType.of(NULL_TYPE, INT_TYPE), equalOp)) + NUMBER_TYPE, UnionType.of(NULL_TYPE, NUMBER_TYPE), equalOp)) .isNotNull(); assertThat( SoyTypes.getSoyTypeForBinaryOperator( - UnionType.of(NULL_TYPE, FLOAT_TYPE), UnionType.of(NULL_TYPE, INT_TYPE), equalOp)) + UnionType.of(NULL_TYPE, FLOAT_TYPE), UnionType.of(NULL_TYPE, NUMBER_TYPE), equalOp)) .isNotNull(); assertThat( SoyTypes.getSoyTypeForBinaryOperator( - UnionType.of(NULL_TYPE, STRING_TYPE), UnionType.of(NULL_TYPE, INT_TYPE), equalOp)) + UnionType.of(NULL_TYPE, STRING_TYPE), + UnionType.of(NULL_TYPE, NUMBER_TYPE), + equalOp)) .isNotNull(); } @@ -971,7 +1017,7 @@ public void testIsKindOrUnionOfKind() { VeType.of("my.Proto"), VeType.of("my.OtherProto"), NullType.getInstance()), Kind.VE)) .isFalse(); - assertThat(SoyTypes.isKindOrUnionOfKind(IntType.getInstance(), Kind.BOOL)).isFalse(); + assertThat(SoyTypes.isKindOrUnionOfKind(NumberType.getInstance(), Kind.BOOL)).isFalse(); } @Test @@ -999,7 +1045,7 @@ public void testContainsKinds_nonMatchingUnion() { SoyTypes.containsKinds( UnionType.of( VeType.of("my.Proto"), VeType.of("my.OtherProto"), NullType.getInstance()), - Sets.immutableEnumSet(Kind.INT))) + Sets.immutableEnumSet(Kind.NUMBER))) .isFalse(); } @@ -1007,7 +1053,7 @@ public void testContainsKinds_nonMatchingUnion() { public void testContainsKinds_multipleKinds() { assertThat( SoyTypes.containsKinds( - IntType.getInstance(), Sets.immutableEnumSet(Kind.BOOL, Kind.STRING))) + NumberType.getInstance(), Sets.immutableEnumSet(Kind.BOOL, Kind.STRING))) .isFalse(); } @@ -1015,7 +1061,7 @@ public void testContainsKinds_multipleKinds() { public void testContainsKinds_multipleNonMatchingKinds() { assertThat( SoyTypes.containsKinds( - IntType.getInstance(), Sets.immutableEnumSet(Kind.BOOL, Kind.INT))) + NumberType.getInstance(), Sets.immutableEnumSet(Kind.BOOL, Kind.NUMBER))) .isTrue(); } @@ -1033,7 +1079,7 @@ public void testLooseAssignability() { assertThatSoyType("[foo: string, bar: int]").isAssignableFromLoose("[foo: ?, bar: ?, baz: ?]"); assertThatSoyType("[foo: string, bar: int]") - .isNotAssignableFromLoose("[foo: string, bar: number]"); + .isAssignableFromLoose("[foo: string, bar: number]"); } @Test diff --git a/java/tests/com/google/template/soy/types/TypeParserTest.java b/java/tests/com/google/template/soy/types/TypeParserTest.java index 2fee1c45eb..12e52fb6fa 100644 --- a/java/tests/com/google/template/soy/types/TypeParserTest.java +++ b/java/tests/com/google/template/soy/types/TypeParserTest.java @@ -42,7 +42,7 @@ public void setUp() throws Exception { public void testParseTypeNames() { assertTypeEquals(AnyType.getInstance(), "any"); assertTypeEquals(AnyType.getInstance(), " any "); - assertTypeEquals(IntType.getInstance(), "int"); + assertTypeEquals(NumberType.getInstance(), "int"); assertTypeEquals(BoolType.getInstance(), "bool"); assertTypeEquals(UnknownType.getInstance(), "?"); assertTypeEquals(MessageType.getInstance(), "Message"); @@ -50,24 +50,26 @@ public void testParseTypeNames() { @Test public void testParseUnionTypes() { - assertTypeEquals(UnionType.of(IntType.getInstance(), BoolType.getInstance()), "int|bool"); + assertTypeEquals(UnionType.of(NumberType.getInstance(), BoolType.getInstance()), "int|bool"); assertTypeEquals( - UnionType.of(IntType.getInstance(), BoolType.getInstance(), StringType.getInstance()), + UnionType.of(NumberType.getInstance(), BoolType.getInstance(), StringType.getInstance()), "int|bool|string"); - assertTypeEquals(UnionType.of(IntType.getInstance(), BoolType.getInstance()), " int | bool "); + assertTypeEquals( + UnionType.of(NumberType.getInstance(), BoolType.getInstance()), " int | bool "); } @Test public void testParseRecordTypes() { - assertTypeEquals(RecordType.of(ImmutableMap.of("a", IntType.getInstance())), "[a:int]"); - assertTypeEquals(RecordType.of(ImmutableMap.of("a", IntType.getInstance())), "[a:int]"); + assertTypeEquals(RecordType.of(ImmutableMap.of("a", NumberType.getInstance())), "[a:int]"); + assertTypeEquals(RecordType.of(ImmutableMap.of("a", NumberType.getInstance())), "[a:int]"); assertTypeEquals( - RecordType.of(ImmutableMap.of("a", IntType.getInstance(), "b", FloatType.getInstance())), + RecordType.of( + ImmutableMap.of("a", NumberType.getInstance(), "b", NumberType.getInstance())), "[a:int, b:float]"); assertTypeEquals( RecordType.of( ImmutableMap.of( - "a", IntType.getInstance(), "b", ListType.of(StringType.getInstance()))), + "a", NumberType.getInstance(), "b", ListType.of(StringType.getInstance()))), "[a:int, b:list]"); } @@ -76,9 +78,10 @@ public void testParameterizedTypes() { assertTypeEquals(ListType.of(StringType.getInstance()), "list"); assertTypeEquals(ListType.of(StringType.getInstance()), "list < string > "); assertTypeEquals( - LegacyObjectMapType.of(IntType.getInstance(), BoolType.getInstance()), + LegacyObjectMapType.of(NumberType.getInstance(), BoolType.getInstance()), "legacy_object_map"); - assertTypeEquals(MapType.of(IntType.getInstance(), BoolType.getInstance()), "map"); + assertTypeEquals( + MapType.of(NumberType.getInstance(), BoolType.getInstance()), "map"); } // ----------------------------------------------------------------------------------------------- diff --git a/src/main/protobuf/template_metadata.proto b/src/main/protobuf/template_metadata.proto index 48f1df2a83..4049103ca2 100644 --- a/src/main/protobuf/template_metadata.proto +++ b/src/main/protobuf/template_metadata.proto @@ -54,8 +54,7 @@ message SoyTypeP { NULL = 3; UNDEFINED = 15; BOOL = 4; - INT = 5; - FLOAT = 6; + NUMBER = 6; STRING = 7; ATTRIBUTES = 9; JS = 10;