Skip to content

Commit

Permalink
internal changes
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 671032832
  • Loading branch information
Soy Authors authored and copybara-github committed Nov 27, 2024
1 parent 5f30e7a commit f03beb5
Show file tree
Hide file tree
Showing 42 changed files with 821 additions and 173 deletions.
17 changes: 17 additions & 0 deletions documentation/reference/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,23 @@ Explicitly
[coerces](http://go/soy/reference/coercions?polyglot=call-command#boolean-coercions)
the argument to a boolean.

Warning: `Boolean()` does not work correctly for gbigint 0 values. Please use
`gbigintToBoolean()` instead.

### `gbigintToBoolean(gbigint)` {#gbigintToBoolean}

This mirrors the
[TS/JS platform function](http:go/bigint#converting-gbigint-values-to-boolean)
of the same name. Safely coerces a `gbigint` to a `boolean` when the underlying
browser may not natively support `bigint`.

### `isSafeInt52(gbigint)` {#isSafeInt52}

This mirrors the
[TS/JS platform function](go/bigint#converting-gbigint-values-to-number) of the
same name. Returns true if the `gbigint` value is within the safe JavaScript
integer range and can be coerced to `number` type without loss of precision.

### `hasContent(value)` {#hasContent}

Value must be one of the sanitized content types (`html`, `attributes`, `uri`,
Expand Down
6 changes: 3 additions & 3 deletions documentation/reference/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -511,12 +511,12 @@ See the [dev guide](../dev/protos.md) for more information on how protos work.
Protocol buffers are supported in Soy. They can be accessed as though they were
`record` types with the `.` operator.

Protocol Buffers in Soy have the same semantics as `protocolbuffers-javascript`,
not `Java` protos.
Protocol Buffers in Soy have the same semantics as
[Apps JSPB JS](http://go/jspb), not `Java` protos.

See the [dev guide](../dev/protos.md) for more information on how protos work.

NOTE: currently, protos are _not supported_ in the Python backend.
NOTE: currently, protos are *not supported* in the Python backend.

<br>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,6 @@ protected static StringData asString(String s) {
return StringData.forValue(s);
}


protected static SoyValue asNullableString(@Nullable String s) {
return s == null ? NullData.INSTANCE : asString(s);
}
Expand Down
103 changes: 93 additions & 10 deletions java/src/com/google/template/soy/data/ProtoFieldInterpreter.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,12 @@
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.StringData;
import com.google.template.soy.internal.proto.FieldVisitor;
import com.google.template.soy.internal.proto.Int64ConversionMode;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
Expand All @@ -55,36 +58,60 @@
*/
public abstract class ProtoFieldInterpreter {
private static final FieldVisitor<ProtoFieldInterpreter> VISITOR =
new Visitor(/* forceStringConversion= */ false);
new Visitor(Int64ConversionMode.FOLLOW_JS_TYPE);

private static final FieldVisitor<ProtoFieldInterpreter> GBIGINT_VISITOR =
new Visitor(Int64ConversionMode.FORCE_GBIGINT);

private static final FieldVisitor<ProtoFieldInterpreter> FORCE_STRING_VISITOR =
new Visitor(/* forceStringConversion= */ true);
new Visitor(Int64ConversionMode.FORCE_STRING);

private static final class Visitor extends FieldVisitor<ProtoFieldInterpreter> {
private final boolean forceStringConversion;
private final Int64ConversionMode int64Mode;

Visitor(boolean forceStringConversion) {
Visitor(Int64ConversionMode int64Mode) {
super();
this.forceStringConversion = forceStringConversion;
this.int64Mode = int64Mode;
}

@Override
protected ProtoFieldInterpreter visitLongAsInt() {
return forceStringConversion ? LONG_AS_STRING : LONG_AS_INT;
switch (int64Mode) {
case FORCE_STRING:
return LONG_AS_STRING;
case FORCE_GBIGINT:
return LONG_AS_GBIGINT;
case FOLLOW_JS_TYPE:
return LONG_AS_INT;
}

throw new AssertionError();
}

@Override
protected ProtoFieldInterpreter visitUnsignedInt() {
return forceStringConversion ? UNSIGNEDLONG_AS_STRING : UNSIGNED_INT;
if (int64Mode == Int64ConversionMode.FORCE_STRING) {
return UNSIGNEDLONG_AS_STRING;
}

return UNSIGNED_INT;
}

@Override
protected ProtoFieldInterpreter visitUnsignedLongAsString() {
if (int64Mode == Int64ConversionMode.FORCE_GBIGINT) {
return UNSIGNEDLONG_AS_GBIGINT;
}

return UNSIGNEDLONG_AS_STRING;
}

@Override
protected ProtoFieldInterpreter visitLongAsString() {
if (int64Mode == Int64ConversionMode.FORCE_GBIGINT) {
return LONG_AS_GBIGINT;
}

return LONG_AS_STRING;
}

Expand Down Expand Up @@ -174,9 +201,18 @@ protected ProtoFieldInterpreter visitRepeated(ProtoFieldInterpreter value) {

/** Creates a {@link ProtoFieldInterpreter} for the given field. */
static ProtoFieldInterpreter create(
FieldDescriptor fieldDescriptor, boolean forceStringConversion) {
return FieldVisitor.visitField(
fieldDescriptor, forceStringConversion ? FORCE_STRING_VISITOR : VISITOR);
FieldDescriptor fieldDescriptor, Int64ConversionMode int64Mode) {

switch (int64Mode) {
case FORCE_STRING:
return FieldVisitor.visitField(fieldDescriptor, FORCE_STRING_VISITOR);
case FORCE_GBIGINT:
return FieldVisitor.visitField(fieldDescriptor, GBIGINT_VISITOR);
case FOLLOW_JS_TYPE:
return FieldVisitor.visitField(fieldDescriptor, VISITOR);
}

throw new AssertionError();
}

private static ProtoFieldInterpreter getListType(ProtoFieldInterpreter local) {
Expand Down Expand Up @@ -310,6 +346,9 @@ public SoyValue soyFromProto(Object field) {

@Override
public Object protoFromSoy(SoyValue field) {
if (field instanceof GbigintData) {
return ((GbigintData) field).longValue();
}
return field.coerceToLong();
}
};
Expand All @@ -324,10 +363,30 @@ public SoyValue soyFromProto(Object field) {

@Override
public Object protoFromSoy(SoyValue field) {
if (field instanceof GbigintData) {
return ((GbigintData) field).longValue();
}
return Long.parseLong(field.stringValue());
}
};

/** A {@link ProtoFieldInterpreter} for int64 typed fields interpreted as soy gbigint. */
public static final ProtoFieldInterpreter LONG_AS_GBIGINT =
new ProtoFieldInterpreter() {
@Override
public SoyValue soyFromProto(Object field) {
return GbigintData.forValue(BigInteger.valueOf((Long) field));
}

@Override
public Object protoFromSoy(SoyValue field) {
if (field instanceof StringData) {
return GbigintData.forStringValue(field.stringValue());
}
return field.longValue();
}
};

/**
* A {@link ProtoFieldInterpreter} for uint64 typed fields interpreted as soy strings.
*
Expand All @@ -342,6 +401,30 @@ public SoyValue soyFromProto(Object field) {

@Override
public Object protoFromSoy(SoyValue field) {
if (field instanceof GbigintData) {
return ((GbigintData) field).unsignedLongValue();
}
return UnsignedLongs.parseUnsignedLong(field.stringValue());
}
};

/**
* A {@link ProtoFieldInterpreter} for uint64 typed fields interpreted as soy gbigint.
*
* <p>TODO(lukes): when soy fully switches to java8 use the methods on java.lang.Long
*/
public static final ProtoFieldInterpreter UNSIGNEDLONG_AS_GBIGINT =
new ProtoFieldInterpreter() {
@Override
public SoyValue soyFromProto(Object field) {
return GbigintData.forValue(BigInteger.valueOf((Long) field));
}

@Override
public Object protoFromSoy(SoyValue field) {
if (field instanceof GbigintData) {
return ((GbigintData) field).unsignedLongValue();
}
return UnsignedLongs.parseUnsignedLong(field.stringValue());
}
};
Expand Down
48 changes: 29 additions & 19 deletions java/src/com/google/template/soy/data/SoyProtoValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import com.google.protobuf.TextFormat;
import com.google.template.soy.data.restricted.UndefinedData;
import com.google.template.soy.internal.proto.Field;
import com.google.template.soy.internal.proto.Int64ConversionMode;
import com.google.template.soy.internal.proto.JavaQualifiedNames;
import com.google.template.soy.internal.proto.ProtoUtils;
import com.google.template.soy.jbcsrc.shared.Names;
Expand Down Expand Up @@ -79,30 +80,41 @@ private static final class ProtoClass {

private static final class FieldWithInterpreter extends Field {
@LazyInit ProtoFieldInterpreter interpreter;
@LazyInit ProtoFieldInterpreter asGbigintInterpreter;
@LazyInit ProtoFieldInterpreter asStringInterpreter;

FieldWithInterpreter(FieldDescriptor fieldDesc) {
super(fieldDesc);
}

private ProtoFieldInterpreter impl(boolean forceStringConversion) {
ProtoFieldInterpreter local = interpreter;
private ProtoFieldInterpreter impl(Int64ConversionMode int64Mode) {
ProtoFieldInterpreter local;
switch (int64Mode) {
case FOLLOW_JS_TYPE:
local = interpreter;
break;
case FORCE_GBIGINT:
local = asGbigintInterpreter;
break;
case FORCE_STRING:
local = asStringInterpreter;
break;
default:
throw new AssertionError();
}
if (local == null) {
local = ProtoFieldInterpreter.create(getDescriptor(), forceStringConversion);
local = ProtoFieldInterpreter.create(getDescriptor(), int64Mode);
}
return local;
}

public SoyValue interpretField(Message message) {
return interpretField(message, /* forceStringConversion= */ false);
}

private SoyValue interpretField(Message message, boolean forceStringConversion) {
return impl(forceStringConversion).soyFromProto(message.getField(getDescriptor()));
private SoyValue interpretField(Message message, Int64ConversionMode int64Mode) {
return impl(int64Mode).soyFromProto(message.getField(getDescriptor()));
}

public void assignField(Message.Builder builder, SoyValue value) {
builder.setField(
getDescriptor(), impl(/* forceStringConversion= */ false).protoFromSoy(value));
getDescriptor(), impl(Int64ConversionMode.FOLLOW_JS_TYPE).protoFromSoy(value));
}
}

Expand Down Expand Up @@ -170,10 +182,10 @@ public Message getProto() {
}

public SoyValue getProtoField(String name) {
return getProtoField(name, /* forceStringConversion= */ false);
return getProtoField(name, Int64ConversionMode.FOLLOW_JS_TYPE);
}

public SoyValue getProtoField(String name, boolean forceStringConversion) {
public SoyValue getProtoField(String name, Int64ConversionMode int64Mode) {
FieldWithInterpreter field = clazz().fields.get(name);
if (field == null) {
throw new IllegalArgumentException(
Expand All @@ -184,7 +196,7 @@ public SoyValue getProtoField(String name, boolean forceStringConversion) {
// Unset singular message fields are always null to match JSPB semantics.
return UndefinedData.INSTANCE;
}
return field.interpretField(proto, forceStringConversion);
return field.interpretField(proto, int64Mode);
}

public SoyValue getReadonlyProtoField(String name) {
Expand All @@ -197,7 +209,7 @@ public SoyValue getReadonlyProtoField(String name) {
if (fd.isRepeated() || fd.getJavaType() != JavaType.MESSAGE) {
throw new AssertionError("impossible");
}
return field.interpretField(proto);
return field.interpretField(proto, Int64ConversionMode.FOLLOW_JS_TYPE);
}

/**
Expand All @@ -206,15 +218,15 @@ public SoyValue getReadonlyProtoField(String name) {
* null.
*/
public SoyValue getProtoFieldOrNull(String name) {
return getProtoFieldOrNull(name, /* forceStringConversion= */ false);
return getProtoFieldOrNull(name, Int64ConversionMode.FOLLOW_JS_TYPE);
}

/**
* Returns the value of the field, or null only if the field has presence semantics and is unset.
* For fields with no presence semantics (i.e., there's no hasser method), the value is never
* null.
*/
public SoyValue getProtoFieldOrNull(String name, boolean forceStringConversion) {
public SoyValue getProtoFieldOrNull(String name, Int64ConversionMode int64Mode) {
FieldWithInterpreter field = clazz().fields.get(name);
if (field == null) {
throw new IllegalArgumentException(
Expand All @@ -224,7 +236,7 @@ public SoyValue getProtoFieldOrNull(String name, boolean forceStringConversion)
if (fd.hasPresence() && !proto.hasField(fd)) {
return UndefinedData.INSTANCE;
}
return field.interpretField(proto, forceStringConversion);
return field.interpretField(proto, int64Mode);
}

public boolean hasProtoField(String name) {
Expand Down Expand Up @@ -424,8 +436,6 @@ public String toString() {
: "not-empty");
}



/**
* Provides an interface for constructing a SoyProtoValue. Used by the tofu renderer only.
*
Expand Down
4 changes: 4 additions & 0 deletions java/src/com/google/template/soy/data/SoyValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,10 @@ public SoyValue checkNullishNumber() {
throw new ClassCastException(classCastErrorMessage(this, "number"));
}

public SoyValue checkNullishGbigint() {
throw new ClassCastException(classCastErrorMessage(this, "gbigint"));
}

/** A runtime type check for this boxed Soy value. */
public SoyValue checkNullishBoolean() {
throw new ClassCastException(classCastErrorMessage(this, "bool"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.google.template.soy.data.internal.IterableImpl;
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.StringData;
Expand All @@ -46,6 +47,8 @@ private SoyValueUnconverter() {}
CONVERTERS.put(IntegerData.class, IntegerData::getValue);
CONVERTERS.put(FloatData.class, FloatData::getValue);
CONVERTERS.put(StringData.class, StringData::getValue);
CONVERTERS.put(GbigintData.class, GbigintData::getValue);
// CONVERTERS.put(BigInteger.class, GbigintData::forValue);
CONVERTERS.put(
SoyList.class,
v ->
Expand Down
Loading

0 comments on commit f03beb5

Please sign in to comment.