|
102 | 102 | import org.truffleruby.Layouts;
|
103 | 103 | import org.truffleruby.annotations.CoreMethod;
|
104 | 104 | import org.truffleruby.builtins.CoreMethodArrayArgumentsNode;
|
105 |
| -import org.truffleruby.builtins.CoreMethodNode; |
106 | 105 | import org.truffleruby.annotations.CoreModule;
|
107 | 106 | import org.truffleruby.annotations.Primitive;
|
108 | 107 | import org.truffleruby.builtins.PrimitiveArrayArgumentsNode;
|
|
127 | 126 | import org.truffleruby.core.format.unpack.UnpackCompiler;
|
128 | 127 | import org.truffleruby.core.kernel.KernelNodes;
|
129 | 128 | import org.truffleruby.core.klass.RubyClass;
|
| 129 | +import org.truffleruby.core.numeric.FixnumLowerNode; |
130 | 130 | import org.truffleruby.core.numeric.FixnumOrBignumNode;
|
131 | 131 | import org.truffleruby.core.proc.RubyProc;
|
132 | 132 | import org.truffleruby.core.range.RangeNodes;
|
|
143 | 143 | import org.truffleruby.language.Nil;
|
144 | 144 | import org.truffleruby.language.NotProvided;
|
145 | 145 | import org.truffleruby.language.RubyBaseNode;
|
146 |
| -import org.truffleruby.language.RubyBaseNodeWithExecute; |
147 | 146 | import org.truffleruby.language.RubyGuards;
|
148 |
| -import org.truffleruby.language.RubyNode; |
149 | 147 | import org.truffleruby.annotations.Visibility;
|
150 | 148 | import org.truffleruby.language.arguments.ReadCallerVariablesNode;
|
151 | 149 | import org.truffleruby.language.control.DeferredRaiseException;
|
|
164 | 162 | import com.oracle.truffle.api.dsl.Cached;
|
165 | 163 | import com.oracle.truffle.api.dsl.Fallback;
|
166 | 164 | import com.oracle.truffle.api.dsl.ImportStatic;
|
167 |
| -import com.oracle.truffle.api.dsl.NodeChild; |
168 | 165 | import com.oracle.truffle.api.dsl.ReportPolymorphism;
|
169 | 166 | import com.oracle.truffle.api.dsl.Specialization;
|
170 | 167 | import com.oracle.truffle.api.frame.VirtualFrame;
|
@@ -230,65 +227,74 @@ protected RubyString add(Object string, Object other,
|
230 | 227 | }
|
231 | 228 |
|
232 | 229 | @CoreMethod(names = "*", required = 1)
|
233 |
| - @NodeChild(value = "string", type = RubyNode.class) |
234 |
| - @NodeChild(value = "times", type = RubyBaseNodeWithExecute.class) |
235 | 230 | @ImportStatic(StringGuards.class)
|
236 |
| - public abstract static class MulNode extends CoreMethodNode { |
| 231 | + public abstract static class StringMulNode extends CoreMethodArrayArgumentsNode { |
237 | 232 |
|
238 |
| - // @CreateCast("times") |
239 |
| - // protected RubyBaseNodeWithExecute coerceToInteger(RubyBaseNodeWithExecute times) { |
240 |
| - // // Not ToIntNode, because this works with empty strings, and must throw a different error |
241 |
| - // // for long values that don't fit in an int. |
242 |
| - // return FixnumLowerNode.create(ToLongNode.create(times)); |
243 |
| - // } |
| 233 | + // Not ToIntNode, because this works with empty strings, and must throw a different error |
| 234 | + // for long values that don't fit in an int. |
| 235 | + @Specialization |
| 236 | + protected RubyString doMul(Object string, Object timesObject, |
| 237 | + @Cached FixnumLowerNode fixnumLowerNode, |
| 238 | + @Cached ToLongNode toLongNode, |
| 239 | + @Cached MulNode mulNode) { |
| 240 | + var times = fixnumLowerNode.execute(this, toLongNode.execute(this, timesObject)); |
| 241 | + return mulNode.execute(this, string, times); |
| 242 | + } |
| 243 | + } |
| 244 | + |
| 245 | + @GenerateCached(false) |
| 246 | + @GenerateInline |
| 247 | + public abstract static class MulNode extends RubyBaseNode { |
| 248 | + |
| 249 | + public abstract RubyString execute(Node node, Object String, Object times); |
244 | 250 |
|
245 | 251 | @Specialization(guards = "times == 0")
|
246 |
| - protected RubyString multiplyZero(Object string, int times, |
| 252 | + protected static RubyString multiplyZero(Node node, Object string, int times, |
247 | 253 | @Cached @Shared RubyStringLibrary libString) {
|
248 | 254 | final RubyEncoding encoding = libString.getEncoding(string);
|
249 |
| - return createString(encoding.tencoding.getEmpty(), encoding); |
| 255 | + return createString(node, encoding.tencoding.getEmpty(), encoding); |
250 | 256 | }
|
251 | 257 |
|
252 | 258 | @Specialization(guards = "times < 0")
|
253 |
| - protected RubyString multiplyTimesNegative(Object string, long times) { |
254 |
| - throw new RaiseException(getContext(), coreExceptions().argumentError("negative argument", this)); |
| 259 | + protected static RubyString multiplyTimesNegative(Node node, Object string, long times) { |
| 260 | + throw new RaiseException(getContext(node), coreExceptions(node).argumentError("negative argument", node)); |
255 | 261 | }
|
256 | 262 |
|
257 | 263 | @Specialization(guards = { "times > 0", "!libString.getTString(string).isEmpty()" })
|
258 |
| - protected RubyString multiply(Object string, int times, |
| 264 | + protected static RubyString multiply(Node node, Object string, int times, |
259 | 265 | @Cached InlinedBranchProfile tooBigProfile,
|
260 | 266 | @Cached @Shared RubyStringLibrary libString,
|
261 |
| - @Cached TruffleString.RepeatNode repeatNode) { |
| 267 | + @Cached(inline = false) TruffleString.RepeatNode repeatNode) { |
262 | 268 | var tstring = libString.getTString(string);
|
263 | 269 | var encoding = libString.getEncoding(string);
|
264 | 270 |
|
265 | 271 | long longLength = (long) times * tstring.byteLength(encoding.tencoding);
|
266 | 272 | if (longLength > Integer.MAX_VALUE) {
|
267 |
| - tooBigProfile.enter(this); |
268 |
| - throw tooBig(); |
| 273 | + tooBigProfile.enter(node); |
| 274 | + throw tooBig(node); |
269 | 275 | }
|
270 | 276 |
|
271 |
| - return createString(repeatNode.execute(tstring, times, encoding.tencoding), encoding); |
| 277 | + return createString(node, repeatNode.execute(tstring, times, encoding.tencoding), encoding); |
272 | 278 | }
|
273 | 279 |
|
274 | 280 | @Specialization(guards = { "times > 0", "libString.getTString(string).isEmpty()" })
|
275 |
| - protected RubyString multiplyEmpty(Object string, long times, |
| 281 | + protected static RubyString multiplyEmpty(Node node, Object string, long times, |
276 | 282 | @Cached @Shared RubyStringLibrary libString) {
|
277 | 283 | var encoding = libString.getEncoding(string);
|
278 |
| - return createString(encoding.tencoding.getEmpty(), encoding); |
| 284 | + return createString(node, encoding.tencoding.getEmpty(), encoding); |
279 | 285 | }
|
280 | 286 |
|
281 | 287 | @Specialization(guards = { "times > 0", "!libString.getTString(string).isEmpty()" })
|
282 |
| - protected RubyString multiplyNonEmpty(Object string, long times, |
| 288 | + protected static RubyString multiplyNonEmpty(Node node, Object string, long times, |
283 | 289 | @Cached @Shared RubyStringLibrary libString) {
|
284 | 290 | assert !CoreLibrary.fitsIntoInteger(times);
|
285 |
| - throw tooBig(); |
| 291 | + throw tooBig(node); |
286 | 292 | }
|
287 | 293 |
|
288 |
| - private RaiseException tooBig() { |
| 294 | + private static RaiseException tooBig(Node node) { |
289 | 295 | // MRI throws this error whenever the total size of the resulting string would exceed LONG_MAX.
|
290 | 296 | // In TruffleRuby, strings have max length Integer.MAX_VALUE.
|
291 |
| - return new RaiseException(getContext(), coreExceptions().argumentError("argument too big", this)); |
| 297 | + return new RaiseException(getContext(node), coreExceptions(node).argumentError("argument too big", node)); |
292 | 298 | }
|
293 | 299 | }
|
294 | 300 |
|
|
0 commit comments