1 33 package org.jruby.internal.runtime.methods; 34 35 import java.util.ArrayList ; 36 import java.util.Iterator ; 37 38 import org.jruby.Ruby; 39 import org.jruby.RubyArray; 40 import org.jruby.RubyModule; 41 import org.jruby.RubyProc; 42 import org.jruby.ast.ArgsNode; 43 import org.jruby.ast.ListNode; 44 import org.jruby.ast.Node; 45 import org.jruby.ast.executable.Script; 46 import org.jruby.compiler.NodeCompilerFactory; 47 import org.jruby.compiler.impl.StandardASMCompiler; 48 import org.jruby.evaluator.AssignmentVisitor; 49 import org.jruby.evaluator.CreateJumpTargetVisitor; 50 import org.jruby.evaluator.EvaluationState; 51 import org.jruby.exceptions.JumpException; 52 import org.jruby.lexer.yacc.ISourcePosition; 53 import org.jruby.parser.StaticScope; 54 import org.jruby.runtime.Arity; 55 import org.jruby.runtime.Block; 56 import org.jruby.runtime.ThreadContext; 57 import org.jruby.runtime.Visibility; 58 import org.jruby.runtime.builtin.IRubyObject; 59 import org.jruby.util.collections.SinglyLinkedList; 60 61 64 public final class DefaultMethod extends DynamicMethod { 65 private StaticScope staticScope; 66 private Node body; 67 private ArgsNode argsNode; 68 private SinglyLinkedList cref; 69 private boolean hasBeenTargeted = false; 70 private int callCount = 0; 71 private static final int COMPILE_COUNT = 50; 72 private Script jitCompiledScript; 73 74 private static final boolean JIT_ENABLED = Boolean.getBoolean("jruby.jit.enabled"); 76 77 public DefaultMethod(RubyModule implementationClass, StaticScope staticScope, Node body, 78 ArgsNode argsNode, Visibility visibility, SinglyLinkedList cref) { 79 super(implementationClass, visibility); 80 this.body = body; 81 this.staticScope = staticScope; 82 this.argsNode = argsNode; 83 this.cref = cref; 84 85 assert argsNode != null; 86 } 87 88 public void preMethod(ThreadContext context, RubyModule clazz, IRubyObject self, String name, 89 IRubyObject[] args, boolean noSuper, Block block) { 90 context.preDefMethodInternalCall(clazz, name, self, args, block, noSuper, cref, staticScope); 91 } 92 93 public void postMethod(ThreadContext context) { 94 context.postDefMethodInternalCall(); 95 } 96 97 100 public IRubyObject call(ThreadContext context, IRubyObject self, 104 RubyModule clazz, String name, IRubyObject[] args, boolean noSuper, Block block) { 105 if (jitCompiledScript != null) { 106 try { 107 context.preCompiledMethod(implementationClass, cref); 108 return jitCompiledScript.run(context, self, args, Block.NULL_BLOCK); 110 } finally { 111 context.postCompiledMethod(); 112 } 113 } 114 115 return super.call(context, self, clazz, name, args, noSuper, block); 116 } 117 118 121 public IRubyObject internalCall(ThreadContext context, RubyModule clazz, 122 IRubyObject self, String name, IRubyObject[] args, boolean noSuper, Block block) { 123 assert args != null; 124 if (JIT_ENABLED && jitCompiledScript != null) { 125 return jitCompiledScript.run(context, self, args, Block.NULL_BLOCK); 126 } 127 128 Ruby runtime = context.getRuntime(); 129 130 if (!hasBeenTargeted) { 131 CreateJumpTargetVisitor.setJumpTarget(this, body); 132 hasBeenTargeted = true; 133 } 134 135 if (argsNode.getBlockArgNode() != null && block.isGiven()) { 136 RubyProc blockArg; 137 138 if (block.getProcObject() != null) { 139 blockArg = (RubyProc) block.getProcObject(); 140 } else { 141 blockArg = runtime.newProc(false, block); 142 blockArg.getBlock().isLambda = block.isLambda; 143 } 144 context.getCurrentScope().setValue(argsNode.getBlockArgNode().getCount(), blockArg, 0); 146 } 147 148 try { 149 prepareArguments(context, runtime, args); 150 151 getArity().checkArity(runtime, args); 152 153 traceCall(context, runtime, self, name); 154 155 if (JIT_ENABLED) runJIT(runtime, name); 156 157 return EvaluationState.eval(runtime, context, body, self, block); 158 } catch (JumpException je) { 159 if (je.getJumpType() == JumpException.JumpType.ReturnJump && je.getTarget() == this) { 160 return (IRubyObject) je.getValue(); 161 } 162 163 throw je; 164 } finally { 165 traceReturn(context, runtime, self, name); 166 } 167 } 168 169 private void runJIT(Ruby runtime, String name) { 170 if (callCount >= 0 && getArity().isFixed()) { 171 callCount++; 172 if (callCount >= COMPILE_COUNT) { 173 try { 175 String cleanName = cleanJavaIdentifier(name); 176 StandardASMCompiler compiler = new StandardASMCompiler(cleanName + hashCode(), body.getPosition().getFile()); 177 compiler.startScript(); 178 Object methodToken = compiler.beginMethod("__file__", getArity().getValue(), staticScope.getNumberOfVariables()); 179 NodeCompilerFactory.getCompiler(body).compile(body, compiler); 180 compiler.endMethod(methodToken); 181 compiler.endScript(); 182 Class sourceClass = compiler.loadClass(runtime); 183 jitCompiledScript = (Script)sourceClass.newInstance(); 184 185 String className = getImplementationClass().getBaseName(); 186 if (className == null) { 187 className = "<anon class>"; 188 } 189 System.out.println("compiled: " + className + "." + name); 190 } catch (Exception e) { 191 } finally { 193 callCount = -1; 194 } 195 } 196 } 197 } 198 199 private void prepareArguments(ThreadContext context, Ruby runtime, IRubyObject[] args) { 200 int expectedArgsCount = argsNode.getArgsCount(); 201 202 int restArg = argsNode.getRestArg(); 203 boolean hasOptArgs = argsNode.getOptArgs() != null; 204 205 if (expectedArgsCount > args.length) { 207 throw runtime.newArgumentError("Wrong # of arguments(" + args.length + " for " + expectedArgsCount + ")"); 208 } 209 210 if (expectedArgsCount > 0) { 212 context.getCurrentScope().setArgValues(args, expectedArgsCount); 213 } 214 215 if (hasOptArgs || restArg != -1) { 217 args = prepareOptOrRestArgs(context, runtime, args, expectedArgsCount, restArg, hasOptArgs); 218 } 219 220 context.setFrameArgs(args); 221 } 222 223 private IRubyObject[] prepareOptOrRestArgs(ThreadContext context, Ruby runtime, IRubyObject[] args, int expectedArgsCount, int restArg, boolean hasOptArgs) { 224 if (restArg == -1 && hasOptArgs) { 225 int opt = expectedArgsCount + argsNode.getOptArgs().size(); 226 227 if (opt < args.length) { 228 throw runtime.newArgumentError("wrong # of arguments(" + args.length + " for " + opt + ")"); 229 } 230 } 231 232 int count = expectedArgsCount; 233 if (argsNode.getOptArgs() != null) { 234 count += argsNode.getOptArgs().size(); 235 } 236 237 ArrayList allArgs = new ArrayList (); 238 239 for (int i = 0; i < count && i < args.length; i++) { 241 allArgs.add(args[i]); 242 } 243 244 if (hasOptArgs) { 245 ListNode optArgs = argsNode.getOptArgs(); 246 247 Iterator iter = optArgs.iterator(); 248 for (int i = expectedArgsCount; i < args.length && iter.hasNext(); i++) { 249 AssignmentVisitor.assign(runtime, context, context.getFrameSelf(), (Node)iter.next(), args[i], Block.NULL_BLOCK, true); 251 expectedArgsCount++; 252 } 253 254 while (iter.hasNext()) { 256 allArgs.add(EvaluationState.eval(runtime, context, (Node) iter.next(), context.getFrameSelf(), Block.NULL_BLOCK)); 258 } 259 } 260 261 263 267 268 if (restArg != -1) { 272 for (int i = expectedArgsCount; i < args.length; i++) { 273 allArgs.add(args[i]); 274 } 275 276 if (restArg >= 0) { 278 RubyArray array = runtime.newArray(args.length - expectedArgsCount); 279 for (int i = expectedArgsCount; i < args.length; i++) { 280 array.append(args[i]); 281 } 282 283 context.getCurrentScope().setValue(restArg, array, 0); 284 } 285 } 286 287 args = (IRubyObject[])allArgs.toArray(new IRubyObject[allArgs.size()]); 288 return args; 289 } 290 291 private void traceReturn(ThreadContext context, Ruby runtime, IRubyObject self, String name) { 292 if (runtime.getTraceFunction() == null) { 293 return; 294 } 295 296 ISourcePosition position = context.getPreviousFramePosition(); 297 runtime.callTraceFunction(context, "return", position, self, name, getImplementationClass()); 298 } 299 300 private void traceCall(ThreadContext context, Ruby runtime, IRubyObject self, String name) { 301 if (runtime.getTraceFunction() == null) return; 302 303 ISourcePosition position = body != null ? body.getPosition() : context.getPosition(); 304 305 runtime.callTraceFunction(context, "call", position, self, name, getImplementationClass()); 306 } 307 308 public Arity getArity() { 309 return argsNode.getArity(); 310 } 311 312 public DynamicMethod dup() { 313 return new DefaultMethod(getImplementationClass(), staticScope, body, argsNode, getVisibility(), cref); 314 } 315 316 private String cleanJavaIdentifier(String name) { 317 char[] characters = name.toCharArray(); 318 StringBuffer cleanBuffer = new StringBuffer (); 319 boolean prevWasReplaced = false; 320 for (int i = 0; i < characters.length; i++) { 321 if (Character.isJavaIdentifierStart(characters[i])) { 322 cleanBuffer.append(characters[i]); 323 prevWasReplaced = false; 324 } else { 325 if (!prevWasReplaced) { 326 cleanBuffer.append("_"); 327 } 328 prevWasReplaced = true; 329 switch (characters[i]) { 330 case '?': 331 cleanBuffer.append("p_"); 332 continue; 333 case '!': 334 cleanBuffer.append("b_"); 335 continue; 336 case '<': 337 cleanBuffer.append("lt_"); 338 continue; 339 case '>': 340 cleanBuffer.append("gt_"); 341 continue; 342 case '=': 343 cleanBuffer.append("equal_"); 344 continue; 345 case '[': 346 if ((i + 1) < characters.length && characters[i + 1] == ']') { 347 cleanBuffer.append("aref_"); 348 i++; 349 } else { 350 cleanBuffer.append("lbracket_"); 352 } 353 continue; 354 case ']': 355 cleanBuffer.append("rbracket_"); 357 continue; 358 case '+': 359 cleanBuffer.append("plus_"); 360 continue; 361 case '-': 362 cleanBuffer.append("minus_"); 363 continue; 364 case '*': 365 cleanBuffer.append("times_"); 366 continue; 367 case '/': 368 cleanBuffer.append("div_"); 369 continue; 370 case '&': 371 cleanBuffer.append("and_"); 372 continue; 373 default: 374 cleanBuffer.append(Integer.toHexString(characters[i])).append("_"); 375 } 376 } 377 } 378 return cleanBuffer.toString(); 379 } 380 } 381 | Popular Tags |