@@ -113,12 +113,14 @@ Bytecode compile() {
113113
114114 private void compileCondition (Condition condition ) {
115115 compileExpression (condition .getFunction ());
116- condition .getResult ().ifPresent (result -> {
117- byte register = registerAllocator .getOrAllocateRegister (result .toString ());
118- writer .writeByte (Opcodes .SET_REGISTER );
116+ var result = condition .getResult ();
117+ if (result .isPresent ()) {
118+ byte register = registerAllocator .getOrAllocateRegister (result .get ().toString ());
119+ writer .writeByte (Opcodes .SET_REG_RETURN );
119120 writer .writeByte (register );
120- });
121- writer .writeByte (Opcodes .RETURN_VALUE );
121+ } else {
122+ writer .writeByte (Opcodes .RETURN_VALUE );
123+ }
122124 }
123125
124126 private void compileEndpointRule (EndpointRule rule ) {
@@ -150,7 +152,7 @@ private void compileEndpointRule(EndpointRule rule) {
150152 compileMapCreation (e .getProperties ().size ());
151153 }
152154
153- compileExpression (e .getUrl ());
155+ compileEndpointUrl (e .getUrl ());
154156
155157 // Add the return endpoint instruction
156158 writer .writeByte (Opcodes .RETURN_ENDPOINT );
@@ -164,6 +166,95 @@ private void compileEndpointRule(EndpointRule rule) {
164166 writer .writeByte (packed );
165167 }
166168
169+ /**
170+ * Compile the URL expression for an endpoint, attempting to decompose it into scheme/host/path
171+ * so that BUILD_URI can construct a SmithyUri directly without string-to-URI parsing.
172+ *
173+ * <p>Falls back to compileExpression (producing a String) if the URL can't be decomposed.
174+ */
175+ private void compileEndpointUrl (Expression urlExpression ) {
176+ // Only optimize StringLiteral templates with multiple parts where the first part
177+ // is a literal starting with a scheme (e.g., "https://...")
178+ if (urlExpression instanceof StringLiteral sl ) {
179+ var template = sl .value ();
180+ var parts = template .getParts ();
181+ if (parts .size () > 1 && parts .getFirst () instanceof Template .Literal firstLit ) {
182+ String firstStr = firstLit .toString ();
183+ int schemeEnd = firstStr .indexOf ("://" );
184+ if (schemeEnd > 0 ) {
185+ String scheme = firstStr .substring (0 , schemeEnd );
186+ String afterScheme = firstStr .substring (schemeEnd + 3 );
187+
188+ // Find where path starts: look for a part that is "/" or starts with "/"
189+ // The host is everything between "://" and the first "/" separator
190+ // In most S3 templates, there's no explicit "/" — the path is empty
191+ // In some, there's a "/" part followed by bucket + url.path
192+ int pathPartIndex = -1 ;
193+ for (int i = 0 ; i < parts .size (); i ++) {
194+ if (parts .get (i ) instanceof Template .Literal lit && lit .toString ().startsWith ("/" )
195+ && i > 0 ) {
196+ pathPartIndex = i ;
197+ break ;
198+ }
199+ }
200+
201+ // Compile host parts: afterScheme + parts[1..pathPartIndex)
202+ int hostPartCount = 0 ;
203+ if (!afterScheme .isEmpty ()) {
204+ addLoadConst (afterScheme );
205+ hostPartCount ++;
206+ }
207+ int hostEnd = pathPartIndex > 0 ? pathPartIndex : parts .size ();
208+ for (int i = 1 ; i < hostEnd ; i ++) {
209+ var part = parts .get (i );
210+ if (part instanceof Template .Dynamic d ) {
211+ compileExpression (d .toExpression ());
212+ } else {
213+ addLoadConst (part .toString ());
214+ }
215+ hostPartCount ++;
216+ }
217+ // Resolve host template to a single string
218+ if (hostPartCount == 1 ) {
219+ // Already a single value on stack
220+ } else if (hostPartCount > 1 ) {
221+ writer .writeByte (Opcodes .RESOLVE_TEMPLATE );
222+ writer .writeByte (hostPartCount );
223+ } else {
224+ addLoadConst ("" );
225+ }
226+
227+ // Compile path parts
228+ if (pathPartIndex > 0 ) {
229+ int pathPartCount = 0 ;
230+ for (int i = pathPartIndex ; i < parts .size (); i ++) {
231+ var part = parts .get (i );
232+ if (part instanceof Template .Dynamic d ) {
233+ compileExpression (d .toExpression ());
234+ } else {
235+ addLoadConst (part .toString ());
236+ }
237+ pathPartCount ++;
238+ }
239+ if (pathPartCount > 1 ) {
240+ writer .writeByte (Opcodes .RESOLVE_TEMPLATE );
241+ writer .writeByte (pathPartCount );
242+ }
243+ } else {
244+ addLoadConst ("" );
245+ }
246+
247+ // BUILD_URI pops host and path, pushes SmithyUri
248+ writer .writeByte (Opcodes .BUILD_URI );
249+ writer .writeShort (writer .getConstantIndex (scheme ));
250+ return ;
251+ }
252+ }
253+ }
254+ // Fallback: compile as regular string expression
255+ compileExpression (urlExpression );
256+ }
257+
167258 private void compileErrorRule (ErrorRule rule ) {
168259 compileExpression (rule .getError ());
169260 writer .writeByte (Opcodes .RETURN_ERROR );
@@ -288,6 +379,20 @@ public Void visitStringEquals(Expression left, Expression right) {
288379 }
289380 }
290381 }
382+ if (left instanceof Reference ref && right instanceof StringLiteral sl
383+ && sl .value ().getParts ().size () == 1 ) {
384+ writer .writeByte (Opcodes .STRING_EQUALS_REG_CONST );
385+ writer .writeByte (registerAllocator .getRegister (ref .getName ().toString ()));
386+ writer .writeShort (writer .getConstantIndex (sl .value ().getParts ().get (0 ).toString ()));
387+ return null ;
388+ }
389+ if (right instanceof Reference ref && left instanceof StringLiteral sl
390+ && sl .value ().getParts ().size () == 1 ) {
391+ writer .writeByte (Opcodes .STRING_EQUALS_REG_CONST );
392+ writer .writeByte (registerAllocator .getRegister (ref .getName ().toString ()));
393+ writer .writeShort (writer .getConstantIndex (sl .value ().getParts ().get (0 ).toString ()));
394+ return null ;
395+ }
291396 compileExpression (left );
292397 compileExpression (right );
293398 writer .writeByte (Opcodes .STRING_EQUALS );
@@ -532,7 +637,14 @@ private void compileLiteral(Literal literal) {
532637 compileLiteral (e .getValue ()); // value then key to make popping ordered
533638 addLoadConst (e .getKey ().toString ());
534639 }
535- compileMapCreation (r .members ().size ());
640+ int size = r .members ().size ();
641+ if (size <= 8 ) {
642+ // Small records: PropertyGetter with linear scan beats Map hashing
643+ writer .writeByte (Opcodes .STRUCTN );
644+ writer .writeByte (size );
645+ } else {
646+ compileMapCreation (size );
647+ }
536648 }
537649 case BooleanLiteral b -> addLoadConst (b .value ().getValue ());
538650 case IntegerLiteral i -> addLoadConst (i .toNode ().expectNumberNode ().getValue ());
0 commit comments