Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
036a5a3
Smithy: per-service model transforms and awsQueryCompatible ResponseM…
sbaluja Aug 19, 2026
e61fa21
Smithy: recursively include nested container element types in CppType…
sbaluja Aug 19, 2026
f0c62c1
Smithy: guard Windows-macro-colliding enum constants in EnumRenderer
sbaluja Aug 19, 2026
7a48648
Smithy: honor per-service result-class suffix in ResultRenderer
sbaluja Aug 19, 2026
75e457a
Smithy: blob-payload event rendering and dual-role sub-object request…
sbaluja Aug 19, 2026
34f7dce
Smithy: EC2 transforms fast-fail on Result/Response and SecureBlob co…
sbaluja Aug 24, 2026
4b8e193
Smithy: wire reserved request-member rename with raw-smithy-name skip…
sbaluja Aug 24, 2026
1e0e6da
Smithy: AccessAnalyzerTransforms renames GeneratedPolicyResult(s) wit…
sbaluja Aug 24, 2026
2df9ef7
Carry over serde traits when we rename members
sbaluja Aug 26, 2026
e1a2763
Smithy: add static DynamoDB AttributeValue resource bodies
sbaluja Aug 27, 2026
6222513
Smithy: DynamoDbRenderer emits bespoke AttributeValue; suppress defau…
sbaluja Aug 27, 2026
52d3bff
Smithy: test ModelGenerator suppresses default AttributeValue render …
sbaluja Aug 27, 2026
2c2660d
Smithy: move DynamoDB AttributeValue suppression into a service-level…
sbaluja Aug 27, 2026
911da25
OperationContextParamsTrait handling and JmesPath parsing
sbaluja Aug 27, 2026
665c5b6
Smithy: scaffold S3Transforms and register in ModelCodegenPlugin
sbaluja Aug 28, 2026
54352dc
Smithy: S3Transforms renames CopyObjectResult to CopyObjectResultDetails
sbaluja Aug 28, 2026
46e87f0
Smithy: remove dead ShapeUtil collision-resolution helpers (folded in…
sbaluja Aug 28, 2026
fe84821
Smithy: S3Transforms adds Expires/ExpiresString backward-compat member
sbaluja Aug 28, 2026
56a733b
Smithy: scope S3 ExpiresString to outputs and retype Expires to times…
sbaluja Aug 28, 2026
00b805e
Smithy: S3Transforms injects GetObject Id2/RequestId header members
sbaluja Aug 28, 2026
a835e8e
Smithy: S3Transforms appends missing BucketLocationConstraint regions
sbaluja Aug 28, 2026
d78656f
Smithy: S3Transforms normalizes ReplicationStatus COMPLETE to COMPLETED
sbaluja Aug 28, 2026
6bb76ee
Smithy: record S3 serde-phased customizations as deferred parity deltas
sbaluja Aug 28, 2026
a28c2f3
Smithy: S3Transforms injects customizedAccessLogTag request member
sbaluja Aug 28, 2026
e198be9
Smithy: S3ControlTransforms adds top-level HostId to result classes
sbaluja Aug 28, 2026
b3ddae7
Smithy: fix S3 byte-parity — CopyObjectResult member rename, GetObjec…
sbaluja Aug 28, 2026
673e571
Smithy: S3Transforms invert ExpiresString guard and add Unit-input guard
sbaluja Aug 28, 2026
e8ba45f
Fix event stream dead code generation (unions, empty events)
sbaluja Aug 31, 2026
c4991fa
Smithy: restore S3 request AddQueryStringParameters and HasEmbeddedEr…
sbaluja Aug 31, 2026
1ddfb2d
Smithy: emit real (constant) HasEmbeddedError body instead of stub
sbaluja Aug 31, 2026
d1b2f2e
Smithy: S3Transforms retypes PartNumberMarker/NextPartNumberMarker ba…
sbaluja Aug 31, 2026
47eeef6
Smithy: S3Transforms ports IsStreaming override and checksum-member s…
sbaluja Aug 31, 2026
1affeec
Query string parameters and Request specific headers parity
sbaluja Sep 1, 2026
40ad6dc
glacier customizations and code cleanup
sbaluja Sep 1, 2026
e534839
Smithy: emit request DumpBodyToUrl override protocol-agnostically
sbaluja Sep 1, 2026
7e148a5
Smithy: ignore @deprecated operations so orphaned request/result stru…
sbaluja Sep 1, 2026
5bdb74f
Smithy: injectResponseMetadata skips @deprecated operation outputs
sbaluja Sep 2, 2026
29aa6bf
Smithy: RequestRenderer emits SignBody override for @unsignedPayload …
sbaluja Sep 2, 2026
0710a06
Smithy: emit request IsChunked() override for chunked-encoding operat…
sbaluja Sep 2, 2026
939dcd9
Smithy: emit request IsLongPollingOperation() override for long-polli…
sbaluja Sep 2, 2026
043b7d5
Smithy: stamp SupportsPresigning on operation to keep DumpBodyToUrl d…
sbaluja Sep 2, 2026
b18ffe4
Comment cleanup
sbaluja Sep 2, 2026
d7522b5
Refactor Model transform interface. use streams over loops
sbaluja Sep 2, 2026
379184a
slash some comments out
sbaluja Sep 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -64,54 +64,6 @@ public class ShapeUtil {
"cloudfront", "2020_05_31"
);

/**
* Hardcoded shape rename collision resolutions from C2J.
* These shapes had name collisions with operation result wrappers in C2J
* and were given specific alternative names.
* Map: service-name -> Map of original-shape-name -> resolved-name
*/
private static final Map<String, Map<String, String>> HARDCODED_COLLISION_RESOLUTIONS = Map.of(
"s3", Map.of("CopyObjectResult", "CopyObjectResultDetails"),
"accessanalyzer", Map.of("GeneratedPolicyResult", "GeneratedPolicyResults"),
"cloudsearchdomain", Map.of("SearchResult", "SearchResultDetails")
);

/**
* S3 shapes that exist in C2J but not in Smithy.
* These must be synthetically injected into the model before generation.
*/
public static final Map<String, Set<String>> C2J_ONLY_SHAPES = Map.of(
"s3", Set.of(
"CopyObjectResultDetails", "SelectObjectContentEventStreamUnmarshallerError",
"CloudFunctionConfiguration", "QueueConfigurationDeprecated",
"TopicConfigurationDeprecated", "NotificationConfigurationDeprecated",
"RequestPaymentConfiguration", "PutObjectLockConfigurationRequestAlias",
"GetObjectLockConfigurationResultAlias", "ObjectLockConfigurationAlias",
"ObjectLockRuleAlias", "DefaultRetentionAlias", "ObjectLockRetentionAlias"
)
);

/**
* Returns the hardcoded collision resolution for a shape, if one exists.
*/
public static Optional<String> getHardcodedResolution(String smithyServiceName, String shapeName) {
Map<String, String> serviceResolutions = HARDCODED_COLLISION_RESOLUTIONS.get(smithyServiceName);
if (serviceResolutions == null) return Optional.empty();
return Optional.ofNullable(serviceResolutions.get(shapeName));
}

/**
* Returns the C++ class name for a shape, applying collision renames and numeric prefix rules.
*/
public static String getShapeCppName(String shapeName, String smithyServiceName) {
Optional<String> resolved = getHardcodedResolution(smithyServiceName, shapeName);
if (resolved.isPresent()) return resolved.get();
if (!shapeName.isEmpty() && Character.isDigit(shapeName.charAt(0))) {
return "The" + shapeName;
}
return shapeName;
}

/**
* C2J/Smithy model mismatches: tokens that are integers in C2J but strings in Smithy.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,9 @@ private CppTypeMapper() {
}

/**
* Returns the C++ type/file name for a shape, capitalizing the first character so that
* lowerCamel Smithy shape names (e.g. IAM's {@code statusType}) become UpperCamel C++
* identifiers ({@code StatusType}). This matches the legacy C2J normalization, which
* upper-camel-cases every shape name at model load
* ({@code C2jModelToGeneratorModelTransformer}).
* Returns the C++ type/file name for a shape, upper-casing the first character so lowerCamel
* Smithy names (e.g. {@code statusType}) become UpperCamel C++ identifiers ({@code StatusType}).
* Matches C2J's model-load normalization.
*
* @param shape the shape whose C++ type/file name is needed
* @return the shape's name with its first character upper-cased
Expand Down Expand Up @@ -68,12 +66,8 @@ public static String getCppType(Shape shape, Model model) {
* @param shape the shape to map
* @param model the model (needed to resolve list/map member targets)
* @param wideIntegers when {@code true}, {@code integer} maps to {@code int64_t} instead of
* {@code int}. C2J applies this only under the CBOR protocol
* ({@code CORAL_TYPE_TO_CBOR_CPP_TYPE_MAPPING}: {@code integer -> int64_t}), and only
* in the file kinds whose templates set {@code $protocol == "smithy-rpc-v2-cbor"} —
* the CBOR sub-object and result headers. Request headers use the shared
* {@code RequestHeader.vm}, which does not, so they keep {@code int}. {@code long} is
* {@code long long} in every mapping and is unaffected.
* {@code int}. C2J applies this only for CBOR sub-object and result headers; request
* headers keep {@code int}. {@code long} is {@code long long} everywhere, unaffected.
*/
public static String getCppType(Shape shape, Model model, boolean wideIntegers) {
// Check enum BEFORE string — a Smithy 2.0 EnumShape extends StringShape, and a Smithy 1.0
Expand Down Expand Up @@ -266,37 +260,20 @@ public static List<String> getIncludesForShape(Shape structureShape, Model model
Shape target = model.expectShape(member.getTarget());
if (isRecursiveStructMember(structureShape, target, model)) {
// A recursive member is stored as std::shared_ptr<T>. A mutually-referenced T is
// forward-declared (see getForwardDeclarations), so the header needs the allocator
// header for the inline MakeShared setter rather than T's own header. A directly
// self-referential member (T == enclosing) needs neither: the class declares itself
// and MakeShared resolves transitively. Both match C2J.
// forward-declared, so the header needs the allocator header (for the inline
// MakeShared setter) not T's own. A directly self-referential member needs neither.
// Both match C2J.
if (!target.getId().equals(selfId)) {
includes.add("<aws/core/utils/memory/stl/AWSAllocator.h>");
}
} else {
addMemberInclude(includes, target, selfId, model, projectName);
// For list/map, also include the element/key/value types
if (target.isListShape()) {
ListShape list = target.asListShape().get();
addMemberInclude(includes, model.expectShape(list.getMember().getTarget()),
selfId, model, projectName);
}
if (target.isMapShape()) {
MapShape map = target.asMapShape().get();
addMemberInclude(includes, model.expectShape(map.getKey().getTarget()),
selfId, model, projectName);
addMemberInclude(includes, model.expectShape(map.getValue().getTarget()),
selfId, model, projectName);
}
// A @sparse list/map wraps its element/value in Aws::Crt::Optional, declared in
// <aws/crt/Optional.h>. Matches C2J's generated SparseNullsOperationRequest.h.
if ((target.isListShape() || target.isMapShape()) && target.hasTrait(SparseTrait.class)) {
includes.add("<aws/crt/Optional.h>");
}
// For list/map, recursively include every nested element/key/value type so leaf
// struct/enum headers surface even through nested containers.
addContainerIncludes(includes, target, selfId, model, projectName);
}
// @idempotencyToken members are brace-initialized with
// Aws::Utils::UUID::PseudoRandomUUID(), which requires UUID.h. Matches C2J
// (CppViewHelper.computeMemberIncludeName).
// @idempotencyToken members are brace-initialized with PseudoRandomUUID(), needing
// UUID.h. Matches C2J.
if (member.hasTrait(IdempotencyTokenTrait.class)) {
includes.add("<aws/core/utils/UUID.h>");
}
Expand All @@ -316,11 +293,39 @@ private static void addMemberInclude(Set<String> includes, Shape shape, ShapeId
}
}

/**
* Recursively adds member-type includes for every nested element/key/value of a list or map,
* descending only through further list/map shapes (bounded by nesting depth). Lets a member
* typed {@code Map<String, Map<String, Leaf>>} reach {@code Leaf}'s header. C2J parity.
*
* <p>{@code @sparse} adds {@code <aws/crt/Optional.h>} at each sparse nesting level.
*/
private static void addContainerIncludes(Set<String> includes, Shape target, ShapeId selfId,
Model model, String projectName) {
if (target.isListShape()) {
Shape elem = model.expectShape(target.asListShape().get().getMember().getTarget());
addMemberInclude(includes, elem, selfId, model, projectName);
addContainerIncludes(includes, elem, selfId, model, projectName);
} else if (target.isMapShape()) {
MapShape map = target.asMapShape().get();
Shape key = model.expectShape(map.getKey().getTarget());
Shape value = model.expectShape(map.getValue().getTarget());
addMemberInclude(includes, key, selfId, model, projectName);
addMemberInclude(includes, value, selfId, model, projectName);
addContainerIncludes(includes, key, selfId, model, projectName);
addContainerIncludes(includes, value, selfId, model, projectName);
}
// A @sparse list/map wraps its element/value in Aws::Crt::Optional (<aws/crt/Optional.h>).
// C2J parity.
if ((target.isListShape() || target.isMapShape()) && target.hasTrait(SparseTrait.class)) {
includes.add("<aws/crt/Optional.h>");
}
}

/**
* Returns the sorted C++ class names of every direct member whose target forms a reference
* cycle with {@code structureShape} (see {@link #isRecursiveStructMember}). These are stored
* as {@code std::shared_ptr<T>} and must be forward-declared (not included) in the header to
* break the otherwise-infinite by-value member. Matches C2J's {@code computeForwardDeclarations}.
* cycle with {@code structureShape}. Stored as {@code std::shared_ptr<T>}, they are
* forward-declared (not included) to break the otherwise-infinite by-value member. C2J parity.
*
* @param structureShape the enclosing structure/union
* @param model the model
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -50,11 +51,24 @@ public static void renderHeader(CppWriter writer, Shape enumShape, String servic
writer.write("#include <aws/$1L/$2L_EXPORTS.h>",
projectName, serviceName);
writer.write("");

// Windows defines some enum values as preprocessor macros (e.g. EC2's `interface` via
// <combaseapi.h>). Undefine them so the generated enum constant compiles, matching C2J's
// ModelEnumHeader.vm predefined-symbol guard.
List<String> windowsMacros = predefinedWindowsSymbols(serviceName, values);
if (!windowsMacros.isEmpty()) {
for (String macro : windowsMacros) {
writer.write("#if defined(_WIN32) && defined($L)", macro);
writer.write("#undef $L", macro);
writer.write("#endif");
}
writer.write("");
}

writer.write("namespace Aws {");
writer.write("namespace $L {", serviceName);
writer.write("namespace Model {");

// Enum class declaration
// Use single-line format if it fits within ~140 chars, multi-line otherwise
String singleLine = "enum class " + enumName + " { NOT_SET, " +
String.join(", ", values) + " };";
Expand All @@ -74,7 +88,6 @@ public static void renderHeader(CppWriter writer, Shape enumShape, String servic
}
writer.write("");

// Mapper namespace
writer.write("namespace $LMapper {", enumName);
writer.write("$1L $2L Get$2LForName(const Aws::String& name);", exportMacro, enumName);
writer.write("");
Expand Down Expand Up @@ -115,14 +128,12 @@ public static void renderSource(CppWriter writer, Shape enumShape, String servic
writer.write("namespace $LMapper {", enumName);
writer.write("");

// Hash constants
for (int i = 0; i < values.size(); i++) {
writer.write(" static const int $1L_HASH = HashingUtils::HashString(\"$2L\");",
values.get(i), wireValues.get(i));
}
writer.write("");

// GetForName
writer.write(" $1L Get$1LForName(const Aws::String& name) {", enumName);
writer.write(" int hashCode = HashingUtils::HashString(name.c_str());");
for (int i = 0; i < values.size(); i++) {
Expand All @@ -140,7 +151,6 @@ public static void renderSource(CppWriter writer, Shape enumShape, String servic
writer.write(" }");
writer.write("");

// GetNameFor
writer.write(" Aws::String GetNameFor$1L($1L enumValue) {", enumName);
writer.write(" switch (enumValue) {");
writer.write(" case $1L::NOT_SET:", enumName);
Expand Down Expand Up @@ -225,6 +235,34 @@ private static List<String> getEnumWireValues(Shape enumShape) {
"STATIC", "T_CHAR", "DOMAIN", "OVERFLOW", "WINDOWS"
);

/**
* Per-service enum constant names that collide with a Windows preprocessor macro and must be
* {@code #undef}'d in the enum header. Keyed by C++ service namespace, mirroring C2J
* PlatformAndKeywordSanitizer.PREDEFINED_SYMBOLS_MAPPING.
*/
private static final Map<String, Set<String>> PREDEFINED_WINDOWS_SYMBOLS = Map.of(
"DynamoDB", Set.of("IN"),
"EC2", Set.of("interface"),
"S3Crt", Set.of("IGNORE")
);

/**
* Returns, in enum-declaration order, the sanitized enum constant names of {@code values} that
* collide with a Windows macro for {@code serviceNamespace} (see
* {@link #PREDEFINED_WINDOWS_SYMBOLS}). Empty when the service has no such symbols.
*
* @param serviceNamespace the C++ service namespace (e.g., "EC2")
* @param values the sanitized enum constant names in declaration order
* @return the subset needing a {@code #undef} guard, preserving declaration order
*/
static List<String> predefinedWindowsSymbols(String serviceNamespace, List<String> values) {
Set<String> symbols = PREDEFINED_WINDOWS_SYMBOLS.get(serviceNamespace);
if (symbols == null) {
return List.of();
}
return values.stream().filter(symbols::contains).collect(Collectors.toList());
}

/**
* Sanitizes an enum wire value into a valid C++ identifier, matching C2J
* PlatformAndKeywordSanitizer.fixEnumValue() behavior.
Expand Down
Loading
Loading