1 28 29 package org.jruby.compiler.impl; 30 31 import java.io.File ; 32 import java.io.FileOutputStream ; 33 import java.io.IOException ; 34 import java.io.PrintStream ; 35 import java.util.Stack ; 36 import org.jruby.Ruby; 37 import org.jruby.MetaClass; 38 import org.jruby.RubyArray; 39 import org.jruby.RubyBoolean; 40 import org.jruby.RubyClass; 41 import org.jruby.RubyFixnum; 42 import org.jruby.RubyModule; 43 import org.jruby.RubyString; 44 import org.jruby.RubySymbol; 45 import org.jruby.ast.Node; 46 import org.jruby.ast.executable.Script; 47 import org.jruby.compiler.*; 48 import org.jruby.compiler.Compiler; 49 import org.jruby.evaluator.EvaluationState; 50 import org.jruby.exceptions.RaiseException; 51 import org.jruby.internal.runtime.GlobalVariables; 52 import org.jruby.internal.runtime.methods.DynamicMethod; 53 import org.jruby.internal.runtime.methods.WrapperMethod; 54 import org.jruby.lexer.yacc.ISourcePosition; 55 import org.jruby.parser.StaticScope; 56 import org.jruby.runtime.Arity; 57 import org.jruby.runtime.Block; 58 import org.jruby.runtime.CallType; 59 import org.jruby.runtime.CallbackFactory; 60 import org.jruby.runtime.CompiledBlock; 61 import org.jruby.runtime.CompiledBlockCallback; 62 import org.jruby.runtime.MethodFactory; 63 import org.jruby.runtime.MethodIndex; 64 import org.jruby.runtime.ThreadContext; 65 import org.jruby.runtime.Visibility; 66 import org.jruby.runtime.builtin.IRubyObject; 67 import org.jruby.util.ByteList; 68 import org.jruby.util.CodegenUtils; 69 import org.jruby.util.JRubyClassLoader; 70 import org.jruby.util.collections.SinglyLinkedList; 71 import org.objectweb.asm.ClassVisitor; 72 import org.objectweb.asm.ClassWriter; 73 import org.objectweb.asm.Label; 74 import org.objectweb.asm.MethodVisitor; 75 import org.objectweb.asm.Opcodes; 76 77 81 public class StandardASMCompiler implements Compiler { 82 private static final CodegenUtils cg = CodegenUtils.instance; 83 private static final String THREADCONTEXT = cg.p(ThreadContext.class); 84 private static final String RUBY = cg.p(Ruby.class); 85 private static final String IRUBYOBJECT = cg.p(IRubyObject.class); 86 87 private static final String METHOD_SIGNATURE = 88 cg.sig(IRubyObject.class, new Class [] { ThreadContext.class, IRubyObject.class, IRubyObject[].class, Block.class }); 89 private static final String CLOSURE_SIGNATURE = 90 cg.sig(IRubyObject.class, new Class [] { ThreadContext.class, IRubyObject.class, IRubyObject[].class, Block.class, IRubyObject[][].class }); 91 92 private static final int THREADCONTEXT_INDEX = 0; 93 private static final int SELF_INDEX = 1; 94 private static final int ARGS_INDEX = 2; 95 private static final int CLOSURE_INDEX = 3; 96 private static final int SCOPE_INDEX = 4; 97 private static final int RUNTIME_INDEX = 5; 98 private static final int LOCAL_VARS_INDEX = 6; 99 100 private Stack methodVisitors = new Stack (); 101 private Stack arities = new Stack (); 102 private Stack scopeStarts = new Stack (); 103 104 private String classname; 105 private String sourcename; 106 107 private ClassWriter classWriter; 109 ClassWriter currentMultiStub = null; 110 int methodIndex = -1; 111 int multiStubCount = -1; 112 int innerIndex = -1; 113 114 int lastLine = -1; 115 116 117 public StandardASMCompiler(String classname, String sourcename) { 118 this.classname = classname; 119 this.sourcename = sourcename; 120 } 121 122 public StandardASMCompiler(Node node) { 123 classname = "EVAL" + hashCode(); 126 sourcename = "EVAL" + hashCode(); 127 } 128 129 public Class loadClass(Ruby runtime) throws ClassNotFoundException { 130 JRubyClassLoader jcl = runtime.getJRubyClassLoader(); 131 132 jcl.defineClass(cg.c(classname), classWriter.toByteArray()); 133 134 return jcl.loadClass(cg.c(classname)); 135 } 136 137 public void writeClass(File destination) throws IOException { 138 writeClass(classname, destination, classWriter); 139 } 140 141 private void writeClass(String classname, File destination, ClassWriter writer) throws IOException { 142 String fullname = classname + ".class"; 143 String filename = null; 144 String path = null; 145 if (fullname.lastIndexOf("/") == -1) { 146 filename = fullname; 147 path = ""; 148 } else { 149 filename = fullname.substring(fullname.lastIndexOf("/") + 1); 150 path = fullname.substring(0, fullname.lastIndexOf("/")); 151 } 152 File pathfile = new File (destination, path); 154 pathfile.mkdirs(); 155 156 FileOutputStream out = new FileOutputStream (new File (pathfile, filename)); 157 158 out.write(writer.toByteArray()); 159 } 160 161 public String getClassname() { 162 return classname; 163 } 164 165 public String getSourcename() { 166 return sourcename; 167 } 168 169 public ClassVisitor getClassVisitor() { 170 return classWriter; 171 } 172 173 public MethodVisitor getMethodVisitor() { 174 return (MethodVisitor)methodVisitors.peek(); 175 } 176 177 public MethodVisitor popMethodVisitor() { 178 return (MethodVisitor)methodVisitors.pop(); 179 } 180 181 public void pushMethodVisitor(MethodVisitor mv) { 182 methodVisitors.push(mv); 183 } 184 185 public int getArity() { 186 return ((Integer )arities.peek()).intValue(); 187 } 188 189 public void pushArity(int arity) { 190 arities.push(new Integer (arity)); 191 } 192 193 public int popArity() { 194 return ((Integer )arities.pop()).intValue(); 195 } 196 197 public void pushScopeStart(Label start) { 198 scopeStarts.push(start); 199 } 200 201 public Label popScopeStart() { 202 return (Label)scopeStarts.pop(); 203 } 204 205 public void startScript() { 206 classWriter = new ClassWriter(true); 207 208 classWriter.visit(Opcodes.V1_2, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, classname, null, cg.p(Object .class), new String [] {cg.p(Script.class)}); 210 classWriter.visitSource(sourcename, null); 211 212 createConstructor(); 213 } 214 215 public void endScript() { 216 String methodName = "__file__"; 219 MethodVisitor mv = getClassVisitor().visitMethod(Opcodes.ACC_PUBLIC, "run", METHOD_SIGNATURE, null, null); 220 mv.visitCode(); 221 222 mv.visitVarInsn(Opcodes.ALOAD, THREADCONTEXT_INDEX + 1); 225 mv.visitVarInsn(Opcodes.ALOAD, SELF_INDEX + 1); 226 mv.visitVarInsn(Opcodes.ALOAD, ARGS_INDEX + 1); 227 mv.visitVarInsn(Opcodes.ALOAD, CLOSURE_INDEX + 1); 228 229 mv.visitMethodInsn(Opcodes.INVOKESTATIC, classname, methodName, METHOD_SIGNATURE); 230 mv.visitInsn(Opcodes.ARETURN); 231 mv.visitMaxs(1, 1); 232 mv.visitEnd(); 233 234 mv = getClassVisitor().visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "main", cg.sig(Void.TYPE, cg.params(String [].class)), null, null); 237 mv.visitCode(); 238 239 mv.visitTypeInsn(Opcodes.NEW, classname); 241 mv.visitInsn(Opcodes.DUP); 242 mv.visitMethodInsn(Opcodes.INVOKESPECIAL, classname, "<init>", cg.sig(Void.TYPE)); 243 244 mv.visitMethodInsn(Opcodes.INVOKESTATIC, cg.p(Ruby.class), "getDefaultInstance", cg.sig(Ruby.class)); 246 mv.visitInsn(Opcodes.DUP); 247 248 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, RUBY, "getCurrentContext", cg.sig(ThreadContext.class)); 249 mv.visitInsn(Opcodes.SWAP); 250 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, RUBY, "getTopSelf", cg.sig(IRubyObject.class)); 251 mv.visitInsn(Opcodes.ACONST_NULL); 252 mv.visitInsn(Opcodes.ACONST_NULL); 253 254 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, classname, "run", METHOD_SIGNATURE); 255 mv.visitInsn(Opcodes.RETURN); 256 mv.visitMaxs(1, 1); 257 mv.visitEnd(); 258 } 259 260 private void createConstructor() { 261 ClassVisitor cv = getClassVisitor(); 262 263 MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC, "<init>", cg.sig(Void.TYPE), null, null); 264 mv.visitCode(); 265 mv.visitVarInsn(Opcodes.ALOAD, 0); 266 mv.visitMethodInsn(Opcodes.INVOKESPECIAL, cg.p(Object .class), "<init>", 267 cg.sig(Void.TYPE)); 268 mv.visitInsn(Opcodes.RETURN); 269 mv.visitMaxs(1, 1); 270 mv.visitEnd(); 271 } 272 273 public Object beginMethod(String friendlyName, int arity, int localVarCount) { 274 MethodVisitor newMethod = getClassVisitor().visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, friendlyName, METHOD_SIGNATURE, null, null); 275 pushMethodVisitor(newMethod); 276 277 newMethod.visitCode(); 278 279 newMethod.visitLdcInsn(new Integer (localVarCount)); 281 newMethod.visitTypeInsn(Opcodes.ANEWARRAY, cg.p(IRubyObject.class)); 282 283 newMethod.visitVarInsn(Opcodes.ASTORE, LOCAL_VARS_INDEX); 285 286 newMethod.visitInsn(Opcodes.ACONST_NULL); 288 newMethod.visitVarInsn(Opcodes.ASTORE, SCOPE_INDEX); 289 290 newMethod.visitVarInsn(Opcodes.ALOAD, THREADCONTEXT_INDEX); 292 invokeThreadContext("getRuntime", cg.sig(Ruby.class)); 293 newMethod.visitVarInsn(Opcodes.ASTORE, RUNTIME_INDEX); 294 295 Label start = new Label(); 297 newMethod.visitLabel(start); 298 pushScopeStart(start); 299 300 pushArity(arity); 302 303 return newMethod; 304 } 305 306 public void endMethod(Object token) { 307 assert token instanceof MethodVisitor; 308 309 MethodVisitor mv = (MethodVisitor)token; 310 mv.visitInsn(Opcodes.ARETURN); 312 313 Label end = new Label(); 315 mv.visitLabel(end); 316 317 mv.visitLocalVariable("lvars", cg.ci(IRubyObject[].class), null, popScopeStart(), end, LOCAL_VARS_INDEX); 319 320 mv.visitMaxs(1, 1); mv.visitEnd(); 322 323 popMethodVisitor(); 324 popArity(); 325 } 326 327 public void lineNumber(ISourcePosition position) { 328 if (lastLine == (lastLine = position.getEndLine())) return; 330 Label l = new Label(); 331 MethodVisitor mv = getMethodVisitor(); 332 mv.visitLabel(l); 333 mv.visitLineNumber(position.getEndLine(), l); 334 } 335 336 public void invokeDynamic(String name, boolean hasReceiver, boolean hasArgs, ClosureCallback closureArg) { 337 MethodVisitor mv = getMethodVisitor(); 338 String callType = null; 339 String callSig = cg.sig(IRubyObject.class, cg.params(ThreadContext.class, String .class, IRubyObject[].class, CallType.class, Block.class)); 340 String callSigIndexed = cg.sig(IRubyObject.class, cg.params(ThreadContext.class, Byte.TYPE, String .class, IRubyObject[].class, CallType.class, Block.class)); 341 342 byte index = MethodIndex.getIndex(name); 343 344 if (hasArgs) { 345 if (hasReceiver) { 346 349 loadThreadContext(); 350 mv.visitInsn(Opcodes.SWAP); 352 353 callType = "NORMAL"; 354 } else { 355 loadSelf(); 358 mv.visitInsn(Opcodes.SWAP); 360 361 loadThreadContext(); 362 mv.visitInsn(Opcodes.SWAP); 364 365 callType = "FUNCTIONAL"; 366 } 367 368 if (index != 0) { 369 mv.visitLdcInsn(new Byte (index)); 371 mv.visitInsn(Opcodes.SWAP); 373 } 374 375 mv.visitLdcInsn(name); 376 mv.visitInsn(Opcodes.SWAP); 378 } else { 379 if (hasReceiver) { 380 383 loadThreadContext(); 384 385 callType = "FUNCTIONAL"; 386 } else { 387 loadSelf(); 390 391 loadThreadContext(); 392 393 callType = "VARIABLE"; 394 } 395 396 397 if (index != 0) { 398 mv.visitLdcInsn(new Byte (index)); 400 } 401 402 mv.visitLdcInsn(name); 403 404 mv.visitFieldInsn(Opcodes.GETSTATIC, cg.p(IRubyObject.class), "NULL_ARRAY", cg.ci(IRubyObject[].class)); 406 } 407 408 mv.visitFieldInsn(Opcodes.GETSTATIC, cg.p(CallType.class), callType, cg.ci(CallType.class)); 409 410 if (closureArg == null) { 411 mv.visitInsn(Opcodes.ACONST_NULL); 412 } else { 413 closureArg.compile(this); 414 } 415 416 if (index != 0) { 417 invokeIRubyObject("callMethod", callSigIndexed); 418 } else { 419 invokeIRubyObject("callMethod", callSig); 420 } 421 } 422 423 public void yield(boolean hasArgs) { 424 loadClosure(); 425 426 MethodVisitor method = getMethodVisitor(); 427 428 method.visitInsn(Opcodes.DUP); 429 loadThreadContext(); 430 method.visitInsn(Opcodes.SWAP); 431 432 invokeThreadContext("raiseErrorIfNoBlock", cg.sig(Void.TYPE, cg.params(Block.class))); 433 434 if (hasArgs) { 435 method.visitInsn(Opcodes.SWAP); 436 437 loadThreadContext(); 438 method.visitInsn(Opcodes.SWAP); 439 440 } else { 442 loadThreadContext(); 443 444 method.visitInsn(Opcodes.ACONST_NULL); 446 } 447 448 loadSelf(); 449 getRubyClass(); 450 method.visitLdcInsn(Boolean.FALSE); 451 452 method.visitMethodInsn(Opcodes.INVOKEVIRTUAL, cg.p(Block.class), "yield", cg.sig(IRubyObject.class, cg.params(ThreadContext.class, IRubyObject.class, IRubyObject.class, RubyModule.class, Boolean.TYPE))); 453 } 454 455 private void invokeIRubyObject(String methodName, String signature) { 456 getMethodVisitor().visitMethodInsn(Opcodes.INVOKEINTERFACE, IRUBYOBJECT, methodName, signature); 457 } 458 459 public void loadThreadContext() { 460 getMethodVisitor().visitVarInsn(Opcodes.ALOAD, THREADCONTEXT_INDEX); 461 } 462 463 public void loadClosure() { 464 getMethodVisitor().visitVarInsn(Opcodes.ALOAD, CLOSURE_INDEX); 465 } 466 467 public void loadSelf() { 468 getMethodVisitor().visitVarInsn(Opcodes.ALOAD, SELF_INDEX); 469 } 470 471 public void loadRuntime() { 472 getMethodVisitor().visitVarInsn(Opcodes.ALOAD, RUNTIME_INDEX); 473 } 474 475 public void loadNil() { 476 loadRuntime(); 477 invokeIRuby("getNil", cg.sig(IRubyObject.class)); 478 } 479 480 public void consumeCurrentValue() { 481 getMethodVisitor().visitInsn(Opcodes.POP); 482 } 483 484 public void retrieveSelf() { 485 loadSelf(); 486 } 487 488 public void assignLocalVariable(int index) { 489 MethodVisitor mv = getMethodVisitor(); 490 mv.visitInsn(Opcodes.DUP); 491 495 mv.visitVarInsn(Opcodes.ALOAD, LOCAL_VARS_INDEX); 500 502 mv.visitInsn(Opcodes.SWAP); 503 mv.visitLdcInsn(new Integer (index)); 504 mv.visitInsn(Opcodes.SWAP); 505 mv.visitInsn(Opcodes.AASTORE); 506 } 507 508 public void retrieveLocalVariable(int index) { 509 MethodVisitor mv = getMethodVisitor(); 510 511 512 517 mv.visitVarInsn(Opcodes.ALOAD, LOCAL_VARS_INDEX); 523 mv.visitLdcInsn(new Integer (index)); 524 mv.visitInsn(Opcodes.AALOAD); 525 } 527 528 public void assignLocalVariable(int index, int depth) { 529 if (depth == 0) { 530 assignLocalVariable(index); 531 return; 532 } 533 534 MethodVisitor mv = getMethodVisitor(); 535 mv.visitInsn(Opcodes.DUP); 536 537 mv.visitVarInsn(Opcodes.ALOAD, SCOPE_INDEX); 539 mv.visitLdcInsn(new Integer (depth - 1)); 540 mv.visitInsn(Opcodes.AALOAD); 541 542 mv.visitInsn(Opcodes.SWAP); 544 mv.visitLdcInsn(new Integer (index)); 545 mv.visitInsn(Opcodes.SWAP); 546 mv.visitInsn(Opcodes.AASTORE); 547 } 548 549 public void retrieveLocalVariable(int index, int depth) { 550 if (depth == 0) { 551 retrieveLocalVariable(index); 552 return; 553 } 554 555 MethodVisitor mv = getMethodVisitor(); 556 557 mv.visitVarInsn(Opcodes.ALOAD, SCOPE_INDEX); 559 mv.visitLdcInsn(new Integer (depth - 1)); 560 mv.visitInsn(Opcodes.AALOAD); 561 562 mv.visitLdcInsn(new Integer (index)); 564 mv.visitInsn(Opcodes.AALOAD); 565 } 566 567 public void retrieveConstant(String name) { 568 MethodVisitor mv = getMethodVisitor(); 569 570 loadThreadContext(); 572 mv.visitLdcInsn(name); 573 invokeThreadContext("getConstant", cg.sig(IRubyObject.class, cg.params(String .class))); 574 } 575 576 public void createNewFixnum(long value) { 577 MethodVisitor mv = getMethodVisitor(); 578 579 loadRuntime(); 580 mv.visitLdcInsn(new Long (value)); 581 582 invokeIRuby("newFixnum", cg.sig(RubyFixnum.class, cg.params(Long.TYPE))); 583 } 584 585 public void createNewBignum(java.math.BigInteger value) { 586 MethodVisitor mv = getMethodVisitor(); 587 588 loadRuntime(); 589 mv.visitLdcInsn(value.toString()); 590 591 mv.visitMethodInsn(Opcodes.INVOKESTATIC,cg.p(org.jruby.RubyBignum.class) , "newBignum", cg.sig(org.jruby.RubyBignum.class,cg.params(Ruby.class,String .class))); 592 } 593 594 public void createNewString(ByteList value) { 595 MethodVisitor mv = getMethodVisitor(); 596 597 loadRuntime(); 598 mv.visitLdcInsn(value.toString()); 599 600 invokeIRuby("newString", cg.sig(RubyString.class, cg.params(String .class))); 601 } 602 603 public void createNewSymbol(String name) { 604 loadRuntime(); 605 getMethodVisitor().visitLdcInsn(name); 606 invokeIRuby("newSymbol", cg.sig(RubySymbol.class, cg.params(String .class))); 607 } 608 609 public void createNewArray() { 610 MethodVisitor mv = getMethodVisitor(); 611 612 loadRuntime(); 613 mv.visitInsn(Opcodes.SWAP); 615 616 invokeIRuby("newArray", cg.sig(RubyArray.class, cg.params(IRubyObject[].class))); 617 } 618 619 public void createEmptyArray() { 620 MethodVisitor mv = getMethodVisitor(); 621 622 loadRuntime(); 623 624 invokeIRuby("newArray", cg.sig(RubyArray.class, cg.params())); 625 } 626 627 public void createObjectArray(Object [] sourceArray, ArrayCallback callback) { 628 buildObjectArray(IRUBYOBJECT, sourceArray, callback); 629 } 630 631 private void buildObjectArray(String type, Object [] sourceArray, ArrayCallback callback) { 632 MethodVisitor mv = getMethodVisitor(); 633 634 mv.visitLdcInsn(new Integer (sourceArray.length)); 635 mv.visitTypeInsn(Opcodes.ANEWARRAY, type); 636 637 for (int i = 0; i < sourceArray.length; i++) { 638 mv.visitInsn(Opcodes.DUP); 639 mv.visitLdcInsn(new Integer (i)); 640 641 callback.nextValue(this, sourceArray, i); 642 643 mv.visitInsn(Opcodes.AASTORE); 644 } 645 } 646 649 private void isTrue() { 650 invokeIRubyObject("isTrue", cg.sig(Boolean.TYPE)); 651 } 652 653 public void performBooleanBranch(BranchCallback trueBranch, BranchCallback falseBranch) { 654 Label afterJmp = new Label(); 655 Label falseJmp = new Label(); 656 657 MethodVisitor mv = getMethodVisitor(); 658 659 isTrue(); 661 662 mv.visitJumpInsn(Opcodes.IFEQ, falseJmp); trueBranch.branch(this); 664 mv.visitJumpInsn(Opcodes.GOTO, afterJmp); 665 666 mv.visitLabel(falseJmp); 668 falseBranch.branch(this); 669 670 mv.visitLabel(afterJmp); 671 } 672 673 public void performLogicalAnd(BranchCallback longBranch) { 674 Label afterJmp = new Label(); 675 Label falseJmp = new Label(); 676 677 MethodVisitor mv = getMethodVisitor(); 678 679 mv.visitInsn(Opcodes.DUP); 681 682 isTrue(); 684 685 mv.visitJumpInsn(Opcodes.IFEQ, falseJmp); mv.visitInsn(Opcodes.POP); 688 longBranch.branch(this); 689 mv.visitLabel(falseJmp); 690 } 691 692 public void performLogicalOr(BranchCallback longBranch) { 693 Label afterJmp = new Label(); 694 Label falseJmp = new Label(); 695 696 MethodVisitor mv = getMethodVisitor(); 697 698 mv.visitInsn(Opcodes.DUP); 700 701 isTrue(); 703 704 mv.visitJumpInsn(Opcodes.IFNE, falseJmp); mv.visitInsn(Opcodes.POP); 707 longBranch.branch(this); 708 mv.visitLabel(falseJmp); 709 } 710 711 public void performBooleanLoop(BranchCallback condition, BranchCallback body, boolean checkFirst) { 712 MethodVisitor mv = getMethodVisitor(); 714 715 Label endJmp = new Label(); 716 if (checkFirst) { 717 condition.branch(this); 719 isTrue(); 721 722 mv.visitJumpInsn(Opcodes.IFEQ, endJmp); } 724 725 Label topJmp = new Label(); 726 727 mv.visitLabel(topJmp); 728 729 body.branch(this); 730 731 mv.visitInsn(Opcodes.POP); 733 734 condition.branch(this); 736 isTrue(); 738 739 mv.visitJumpInsn(Opcodes.IFNE, topJmp); 741 if (checkFirst) { 742 mv.visitLabel(endJmp); 743 } 744 745 loadNil(); 746 } 747 748 public static CompiledBlock createBlock(ThreadContext context, IRubyObject self, int arity, IRubyObject[][] scopes, CompiledBlockCallback callback) { 749 return new CompiledBlock(context, self, Arity.createArity(arity), scopes, callback); 750 } 751 752 public void createNewClosure(StaticScope scope, int arity, ClosureCallback body) { 753 ClassVisitor cv = getClassVisitor(); 755 MethodVisitor method; 756 757 String closureMethodName = "closure" + ++innerIndex; 758 String closureFieldName = "_" + closureMethodName; 759 760 cv.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, closureFieldName, cg.ci(CompiledBlockCallback.class), null, null); 762 763 method = cv.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, closureMethodName, CLOSURE_SIGNATURE, null, null); 766 pushMethodVisitor(method); 767 768 method.visitCode(); 769 770 method.visitLdcInsn(new Integer (scope.getNumberOfVariables())); 772 method.visitTypeInsn(Opcodes.ANEWARRAY, cg.p(IRubyObject.class)); 773 774 method.visitVarInsn(Opcodes.ASTORE, LOCAL_VARS_INDEX); 776 777 if (arity != 0) { 779 786 } 787 788 790 method.visitVarInsn(Opcodes.ALOAD, THREADCONTEXT_INDEX); 792 invokeThreadContext("getRuntime", cg.sig(Ruby.class)); 793 method.visitVarInsn(Opcodes.ASTORE, RUNTIME_INDEX); 794 795 Label start = new Label(); 797 method.visitLabel(start); 798 799 body.compile(this); 801 802 method.visitInsn(Opcodes.ARETURN); 803 804 Label end = new Label(); 806 method.visitLabel(end); 807 method.visitMaxs(1, 1); 808 method.visitEnd(); 809 810 popMethodVisitor(); 811 812 method = getMethodVisitor(); 813 814 816 method.visitFieldInsn(Opcodes.GETSTATIC, classname, closureFieldName, cg.ci(CompiledBlockCallback.class)); 818 Label alreadyCreated = new Label(); 819 method.visitJumpInsn(Opcodes.IFNONNULL, alreadyCreated); 820 821 getCallbackFactory(); 823 824 method.visitLdcInsn(closureMethodName); 825 method.visitMethodInsn(Opcodes.INVOKEVIRTUAL, cg.p(CallbackFactory.class), "getBlockCallback", cg.sig(CompiledBlockCallback.class, cg.params(String .class))); 826 method.visitFieldInsn(Opcodes.PUTSTATIC, classname, closureFieldName, cg.ci(CompiledBlockCallback.class)); 827 828 method.visitLabel(alreadyCreated); 829 830 loadThreadContext(); 832 loadSelf(); 833 method.visitLdcInsn(new Integer (arity)); 834 835 837 method.visitVarInsn(Opcodes.ALOAD, SCOPE_INDEX); 839 Label noScopes = new Label(); 840 Label copyLocals = new Label(); 841 method.visitJumpInsn(Opcodes.IFNULL, noScopes); 842 843 845 method.visitVarInsn(Opcodes.ALOAD, SCOPE_INDEX); 847 method.visitInsn(Opcodes.ARRAYLENGTH); 848 method.visitInsn(Opcodes.ICONST_1); 849 method.visitInsn(Opcodes.IADD); 850 851 method.visitTypeInsn(Opcodes.ANEWARRAY, cg.p(IRubyObject[].class)); 853 854 method.visitInsn(Opcodes.DUP); 856 method.visitVarInsn(Opcodes.ALOAD, SCOPE_INDEX); 857 method.visitInsn(Opcodes.SWAP); 858 method.visitInsn(Opcodes.ICONST_0); 859 method.visitInsn(Opcodes.SWAP); 860 method.visitInsn(Opcodes.ICONST_1); 862 method.visitVarInsn(Opcodes.ALOAD, SCOPE_INDEX); 863 method.visitInsn(Opcodes.ARRAYLENGTH); 864 method.visitMethodInsn(Opcodes.INVOKESTATIC, cg.p(System .class), "arraycopy", cg.sig(Void.TYPE, cg.params(Object .class, Integer.TYPE, Object .class, Integer.TYPE, Integer.TYPE))); 865 866 method.visitJumpInsn(Opcodes.GOTO, copyLocals); 867 868 method.visitLabel(noScopes); 869 870 method.visitInsn(Opcodes.ICONST_1); 872 method.visitTypeInsn(Opcodes.ANEWARRAY, cg.p(IRubyObject[].class)); 873 874 method.visitLabel(copyLocals); 875 876 method.visitInsn(Opcodes.DUP); 878 method.visitInsn(Opcodes.ICONST_0); 879 method.visitVarInsn(Opcodes.ALOAD, LOCAL_VARS_INDEX); 880 method.visitInsn(Opcodes.AASTORE); 881 882 method.visitFieldInsn(Opcodes.GETSTATIC, classname, closureFieldName, cg.ci(CompiledBlockCallback.class)); 883 884 method.visitMethodInsn(Opcodes.INVOKESTATIC, cg.p(StandardASMCompiler.class), "createBlock", 885 cg.sig(CompiledBlock.class, cg.params(ThreadContext.class, IRubyObject.class, Integer.TYPE, IRubyObject[][].class, CompiledBlockCallback.class))); 886 } 887 888 private void invokeThreadContext(String methodName, String signature) { 889 MethodVisitor mv = getMethodVisitor(); 890 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, THREADCONTEXT, methodName, signature); 891 } 892 893 private void invokeIRuby(String methodName, String signature) { 894 MethodVisitor mv = getMethodVisitor(); 895 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, RUBY, methodName, signature); 896 } 897 898 private void getCallbackFactory() { 899 loadRuntime(); 900 MethodVisitor mv = getMethodVisitor(); 901 mv.visitLdcInsn(classname); 902 mv.visitMethodInsn(Opcodes.INVOKESTATIC, cg.p(Class .class), "forName", cg.sig(Class .class, cg.params(String .class))); 903 invokeIRuby("callbackFactory", cg.sig(CallbackFactory.class, cg.params(Class .class))); 904 } 905 906 private void getRubyClass() { 907 loadSelf(); 908 invokeIRubyObject("getMetaClass", cg.sig(RubyClass.class)); 910 } 911 912 private void getCRef() { 913 loadThreadContext(); 914 invokeThreadContext("peekCRef", cg.sig(SinglyLinkedList.class)); 916 } 917 918 private void newTypeError(String error) { 919 loadRuntime(); 920 getMethodVisitor().visitLdcInsn(error); 921 invokeIRuby("newTypeError", cg.sig(RaiseException.class, cg.params(String .class))); 922 } 923 924 private void getCurrentVisibility() { 925 loadThreadContext(); 926 invokeThreadContext("getCurrentVisibility", cg.sig(Visibility.class)); 927 } 928 929 private void println() { 930 MethodVisitor mv = getMethodVisitor(); 931 932 mv.visitInsn(Opcodes.DUP); 933 mv.visitFieldInsn(Opcodes.GETSTATIC, cg.p(System .class), "out", cg.ci(PrintStream .class)); 934 mv.visitInsn(Opcodes.SWAP); 935 936 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, cg.p(PrintStream .class), "println", cg.sig(Void.TYPE, cg.params(Object .class))); 937 } 938 939 public void defineAlias(String newName, String oldName) { 940 getRubyClass(); 941 getMethodVisitor().visitLdcInsn(newName); 942 getMethodVisitor().visitLdcInsn(oldName); 943 getMethodVisitor().visitMethodInsn(Opcodes.INVOKEVIRTUAL, cg.p(RubyModule.class), "defineAlias", cg.sig(Void.TYPE,cg.params(String .class,String .class))); 944 loadNil(); 945 } 947 948 public static IRubyObject def(ThreadContext context, IRubyObject self, Class compiledClass, String name, String javaName, int arity) { 949 Ruby runtime = context.getRuntime(); 950 951 RubyModule containingClass = self.getMetaClass(); 954 955 if (containingClass == null) { 956 throw runtime.newTypeError("No class to add method."); 957 } 958 959 if (containingClass == runtime.getObject() && name == "initialize") { 960 runtime.getWarnings().warn("redefining Object#initialize may cause infinite loop"); 961 } 962 963 Visibility visibility = context.getCurrentVisibility(); 964 if (name == "initialize" || visibility.isModuleFunction() || context.isTopLevel()) { 965 visibility = Visibility.PRIVATE; 966 } 967 968 SinglyLinkedList cref = context.peekCRef(); 969 970 MethodFactory factory = MethodFactory.createFactory(); 971 DynamicMethod method = factory.getCompiledMethod(containingClass, compiledClass, javaName, Arity.createArity(arity), visibility, cref); 972 973 containingClass.addMethod(name, method); 974 975 if (context.getCurrentVisibility().isModuleFunction()) { 976 containingClass.getSingletonClass().addMethod( 977 name, 978 new WrapperMethod(containingClass.getSingletonClass(), method, 979 Visibility.PUBLIC)); 980 containingClass.callMethod(context, "singleton_method_added", runtime.newSymbol(name)); 981 } 982 983 if (containingClass.isSingleton()) { 985 ((MetaClass) containingClass).getAttachedObject().callMethod( 986 context, "singleton_method_added", runtime.newSymbol(name)); 987 } else { 988 containingClass.callMethod(context, "method_added", runtime.newSymbol(name)); 989 } 990 991 return runtime.getNil(); 992 } 993 994 public void defineNewMethod(String name, int arity, int localVarCount, ClosureCallback body) { 995 ++methodIndex; 997 String methodName = name + "__" + methodIndex; 998 beginMethod(methodName, arity, localVarCount); 999 1000 MethodVisitor mv = getMethodVisitor(); 1001 1002 mv.visitCode(); 1003 1004 mv.visitVarInsn(Opcodes.ALOAD, ARGS_INDEX); 1006 mv.visitInsn(Opcodes.ICONST_0); 1007 mv.visitVarInsn(Opcodes.ALOAD, LOCAL_VARS_INDEX); 1008 mv.visitInsn(Opcodes.ICONST_2); 1009 mv.visitLdcInsn(new Integer (arity)); 1010 mv.visitMethodInsn(Opcodes.INVOKESTATIC, cg.p(System .class), "arraycopy", cg.sig(Void.TYPE, cg.params(Object .class, Integer.TYPE, Object .class, Integer.TYPE, Integer.TYPE))); 1011 1012 mv.visitInsn(Opcodes.ACONST_NULL); 1014 mv.visitVarInsn(Opcodes.ASTORE, SCOPE_INDEX); 1015 1016 body.compile(this); 1018 1019 endMethod(mv); 1020 1021 mv = getMethodVisitor(); 1023 1024 loadThreadContext(); 1026 1027 loadSelf(); 1028 1029 mv.visitLdcInsn(classname.replace('/', '.')); 1030 mv.visitMethodInsn(Opcodes.INVOKESTATIC, cg.p(Class .class), "forName", cg.sig(Class .class, cg.params(String .class))); 1031 1032 mv.visitLdcInsn(name); 1033 1034 mv.visitLdcInsn(methodName); 1035 1036 mv.visitLdcInsn(new Integer (arity)); 1037 1038 mv.visitMethodInsn(Opcodes.INVOKESTATIC, 1039 cg.p(StandardASMCompiler.class), 1040 "def", 1041 cg.sig(IRubyObject.class, cg.params(ThreadContext.class, IRubyObject.class, Class .class, String .class, String .class, Integer.TYPE))); 1042 } 1043 1044 public void loadFalse() { 1045 loadRuntime(); 1046 invokeIRuby("getFalse", cg.sig(RubyBoolean.class)); 1047 } 1048 1049 public void loadTrue() { 1050 loadRuntime(); 1051 invokeIRuby("getTrue", cg.sig(RubyBoolean.class)); 1052 } 1053 1054 public void retrieveInstanceVariable(String name) { 1055 loadSelf(); 1056 1057 MethodVisitor mv = getMethodVisitor(); 1058 1059 mv.visitLdcInsn(name); 1060 invokeIRubyObject("getInstanceVariable", cg.sig(IRubyObject.class, cg.params(String .class))); 1061 1062 mv.visitInsn(Opcodes.DUP); 1064 Label notNull = new Label(); 1065 mv.visitJumpInsn(Opcodes.IFNONNULL, notNull); 1066 1067 mv.visitInsn(Opcodes.POP); 1069 loadNil(); 1071 1072 mv.visitLabel(notNull); 1073 } 1074 1075 public void assignInstanceVariable(String name) { 1076 MethodVisitor mv = getMethodVisitor(); 1077 1078 loadSelf(); 1079 mv.visitInsn(Opcodes.SWAP); 1080 1081 mv.visitLdcInsn(name); 1082 mv.visitInsn(Opcodes.SWAP); 1083 1084 invokeIRubyObject("setInstanceVariable", cg.sig(IRubyObject.class, cg.params(String .class, IRubyObject.class))); 1085 } 1086 1087 public void retrieveGlobalVariable(String name) { 1088 loadRuntime(); 1089 1090 MethodVisitor mv = getMethodVisitor(); 1091 1092 invokeIRuby("getGlobalVariables", cg.sig(GlobalVariables.class)); 1093 mv.visitLdcInsn(name); 1094 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, cg.p(GlobalVariables.class), "get", cg.sig(IRubyObject.class, cg.params(String .class))); 1095 } 1096 1097 public void assignGlobalVariable(String name) { 1098 loadRuntime(); 1099 1100 MethodVisitor mv = getMethodVisitor(); 1101 1102 invokeIRuby("getGlobalVariables", cg.sig(GlobalVariables.class)); 1103 mv.visitInsn(Opcodes.SWAP); 1104 mv.visitLdcInsn(name); 1105 mv.visitInsn(Opcodes.SWAP); 1106 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, cg.p(GlobalVariables.class), "set", cg.sig(IRubyObject.class, cg.params(String .class, IRubyObject.class))); 1107 } 1108 1109 public void negateCurrentValue() { 1110 MethodVisitor mv = getMethodVisitor(); 1111 1112 isTrue(); 1113 Label isTrue = new Label(); 1114 Label end = new Label(); 1115 mv.visitJumpInsn(Opcodes.IFNE, isTrue); 1116 loadTrue(); 1117 mv.visitJumpInsn(Opcodes.GOTO, end); 1118 mv.visitLabel(isTrue); 1119 loadFalse(); 1120 mv.visitLabel(end); 1121 } 1122 1123 public void splatCurrentValue() { 1124 MethodVisitor method = getMethodVisitor(); 1125 1126 method.visitMethodInsn(Opcodes.INVOKESTATIC, cg.p(EvaluationState.class), "splatValue", cg.sig(IRubyObject.class, cg.params(IRubyObject.class))); 1127 } 1128 1129 public void singlifySplattedValue() { 1130 MethodVisitor method = getMethodVisitor(); 1131 method.visitMethodInsn(Opcodes.INVOKESTATIC, cg.p(EvaluationState.class), "aValueSplat", cg.sig(IRubyObject.class, cg.params(IRubyObject.class))); 1132 } 1133 1134 public void ensureRubyArray() { 1135 MethodVisitor method = getMethodVisitor(); 1136 1137 method.visitMethodInsn(Opcodes.INVOKESTATIC, cg.p(StandardASMCompiler.class), "ensureRubyArray", cg.sig(RubyArray.class, cg.params(IRubyObject.class))); 1138 } 1139 1140 public static RubyArray ensureRubyArray(IRubyObject value) { 1141 if (!(value instanceof RubyArray)) { 1142 value = RubyArray.newArray(value.getRuntime(), value); 1143 } 1144 return (RubyArray)value; 1145 } 1146 1147 public void forEachInValueArray(int start, int count, Object source, ArrayCallback callback) { 1148 MethodVisitor method = getMethodVisitor(); 1149 1150 Label noMoreArrayElements = new Label(); 1151 for (; start < count; start++) { 1152 method.visitInsn(Opcodes.DUP); method.visitMethodInsn(Opcodes.INVOKEVIRTUAL, cg.p(RubyArray.class), "getLength", cg.sig(Integer.TYPE, cg.params())); 1155 method.visitLdcInsn(new Integer (start)); 1156 method.visitJumpInsn(Opcodes.IFLE, noMoreArrayElements); 1158 method.visitInsn(Opcodes.DUP); method.visitLdcInsn(new Integer (start)); method.visitMethodInsn(Opcodes.INVOKEVIRTUAL, cg.p(RubyArray.class), "entry", 1162 cg.sig(IRubyObject.class, cg.params(Long.TYPE))); callback.nextValue(this, source, start); 1164 } 1165 method.visitLabel(noMoreArrayElements); 1166 } 1167 1168 public void loadInteger(int value) { 1169 throw new UnsupportedOperationException ("Not supported yet."); 1170 } 1171 1172 public void performGEBranch(BranchCallback trueBranch, 1173 BranchCallback falseBranch) { 1174 throw new UnsupportedOperationException ("Not supported yet."); 1175 } 1176 1177 public void performGTBranch(BranchCallback trueBranch, 1178 BranchCallback falseBranch) { 1179 throw new UnsupportedOperationException ("Not supported yet."); 1180 } 1181 1182 public void performLEBranch(BranchCallback trueBranch, 1183 BranchCallback falseBranch) { 1184 throw new UnsupportedOperationException ("Not supported yet."); 1185 } 1186 1187 public void performLTBranch(BranchCallback trueBranch, 1188 BranchCallback falseBranch) { 1189 throw new UnsupportedOperationException ("Not supported yet."); 1190 } 1191 1192 public void loadRubyArraySize() { 1193 throw new UnsupportedOperationException ("Not supported yet."); 1194 } 1195} 1196 | Popular Tags |