001: /***** BEGIN LICENSE BLOCK *****
002: * Version: CPL 1.0/GPL 2.0/LGPL 2.1
003: *
004: * The contents of this file are subject to the Common Public
005: * License Version 1.0 (the "License"); you may not use this file
006: * except in compliance with the License. You may obtain a copy of
007: * the License at http://www.eclipse.org/legal/cpl-v10.html
008: *
009: * Software distributed under the License is distributed on an "AS
010: * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
011: * implied. See the License for the specific language governing
012: * rights and limitations under the License.
013: *
014: * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
015: * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
016: * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
017: * Copyright (C) 2004-2005 Thomas E Enebo <enebo@acm.org>
018: * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
019: * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
020: *
021: * Alternatively, the contents of this file may be used under the terms of
022: * either of the GNU General Public License Version 2 or later (the "GPL"),
023: * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
024: * in which case the provisions of the GPL or the LGPL are applicable instead
025: * of those above. If you wish to allow use of your version of this file only
026: * under the terms of either the GPL or the LGPL, and not to allow others to
027: * use your version of this file under the terms of the CPL, indicate your
028: * decision by deleting the provisions above and replace them with the notice
029: * and other provisions required by the GPL or the LGPL. If you do not delete
030: * the provisions above, a recipient may use your version of this file under
031: * the terms of any one of the CPL, the GPL or the LGPL.
032: ***** END LICENSE BLOCK *****/package org.jruby.internal.runtime.methods;
033:
034: import java.util.ArrayList;
035: import org.jruby.Ruby;
036: import org.jruby.RubyArray;
037: import org.jruby.RubyBinding;
038: import org.jruby.RubyModule;
039: import org.jruby.RubyProc;
040: import org.jruby.ast.ArgsNode;
041: import org.jruby.ast.ListNode;
042: import org.jruby.ast.Node;
043: import org.jruby.ast.executable.Script;
044: import org.jruby.compiler.ArrayCallback;
045: import org.jruby.compiler.ClosureCallback;
046: import org.jruby.compiler.Compiler;
047: import org.jruby.compiler.NodeCompilerFactory;
048: import org.jruby.compiler.impl.StandardASMCompiler;
049: import org.jruby.evaluator.AssignmentVisitor;
050: import org.jruby.evaluator.EvaluationState;
051: import org.jruby.exceptions.JumpException;
052: import org.jruby.lexer.yacc.ISourcePosition;
053: import org.jruby.parser.StaticScope;
054: import org.jruby.runtime.Arity;
055: import org.jruby.runtime.Block;
056: import org.jruby.runtime.EventHook;
057: import org.jruby.runtime.ThreadContext;
058: import org.jruby.runtime.Visibility;
059: import org.jruby.runtime.builtin.IRubyObject;
060: import org.jruby.util.CodegenUtils;
061: import org.jruby.util.JRubyClassLoader;
062: import org.jruby.util.collections.SinglyLinkedList;
063:
064: /**
065: *
066: */
067: public final class DefaultMethod extends DynamicMethod {
068:
069: private StaticScope staticScope;
070: private Node body;
071: private ArgsNode argsNode;
072: private SinglyLinkedList cref;
073: private int callCount = 0;
074: private Script jitCompiledScript;
075: private int expectedArgsCount;
076: private int restArg;
077: private boolean hasOptArgs;
078:
079: public DefaultMethod(RubyModule implementationClass,
080: StaticScope staticScope, Node body, ArgsNode argsNode,
081: Visibility visibility, SinglyLinkedList cref) {
082: super (implementationClass, visibility);
083: this .body = body;
084: this .staticScope = staticScope;
085: this .argsNode = argsNode;
086: this .cref = cref;
087: this .expectedArgsCount = argsNode.getArgsCount();
088: this .restArg = argsNode.getRestArg();
089: this .hasOptArgs = argsNode.getOptArgs() != null;
090:
091: assert argsNode != null;
092: }
093:
094: public void preMethod(ThreadContext context, RubyModule clazz,
095: IRubyObject self, String name, IRubyObject[] args,
096: boolean noSuper, Block block) {
097: context.preDefMethodInternalCall(clazz, name, self, args,
098: getArity().required(), block, noSuper, cref,
099: staticScope, this );
100: }
101:
102: public void postMethod(ThreadContext context) {
103: context.postDefMethodInternalCall();
104: }
105:
106: /**
107: * @see AbstractCallable#call(Ruby, IRubyObject, String, IRubyObject[], boolean)
108: */
109: public IRubyObject internalCall(ThreadContext context,
110: RubyModule clazz, IRubyObject self, String name,
111: IRubyObject[] args, boolean noSuper, Block block) {
112: assert args != null;
113:
114: Ruby runtime = context.getRuntime();
115:
116: if (runtime.getInstanceConfig().isJitEnabled()) {
117: runJIT(runtime, context, name);
118: }
119:
120: if (argsNode.getBlockArgNode() != null && block.isGiven()) {
121: RubyProc blockArg;
122:
123: if (block.getProcObject() != null) {
124: blockArg = (RubyProc) block.getProcObject();
125: } else {
126: blockArg = runtime.newProc(false, block);
127: blockArg.getBlock().isLambda = block.isLambda;
128: }
129: // We pass depth zero since we know this only applies to newly created local scope
130: context.getCurrentScope().setValue(
131: argsNode.getBlockArgNode().getCount(), blockArg, 0);
132: }
133:
134: try {
135: prepareArguments(context, runtime, args);
136:
137: getArity().checkArity(runtime, args);
138:
139: if (runtime.hasEventHooks()) {
140: traceCall(context, runtime, name);
141: }
142:
143: if (jitCompiledScript != null && !runtime.hasEventHooks()) {
144: return jitCompiledScript
145: .run(context, self, args, block);
146: }
147:
148: return EvaluationState.eval(runtime, context, body, self,
149: block);
150: } catch (JumpException je) {
151: if (je.getJumpType() == JumpException.JumpType.ReturnJump
152: && je.getTarget() == this ) {
153: return (IRubyObject) je.getValue();
154: }
155:
156: throw je;
157: } finally {
158: if (runtime.hasEventHooks()) {
159: traceReturn(context, runtime, name);
160: }
161: }
162: }
163:
164: private void runJIT(Ruby runtime, ThreadContext context, String name) {
165: if (callCount >= 0) {
166: String className = null;
167: if (runtime.getInstanceConfig().isJitLogging()) {
168: className = getImplementationClass().getBaseName();
169: if (className == null) {
170: className = "<anon class>";
171: }
172: }
173:
174: try {
175: callCount++;
176:
177: if (callCount >= runtime.getInstanceConfig()
178: .getJitThreshold()) {
179: NodeCompilerFactory.confirmNodeIsSafe(argsNode);
180: // FIXME: Total duplication from DefnNodeCompiler...need to refactor this
181: final ArrayCallback evalOptionalValue = new ArrayCallback() {
182: public void nextValue(Compiler context,
183: Object object, int index) {
184: ListNode optArgs = (ListNode) object;
185:
186: Node node = optArgs.get(index);
187:
188: NodeCompilerFactory.getCompiler(node)
189: .compile(node, context);
190: }
191: };
192:
193: ClosureCallback args = new ClosureCallback() {
194: public void compile(Compiler context) {
195: Arity arity = argsNode.getArity();
196:
197: if (hasOptArgs) {
198: if (restArg > -1) {
199: callCount = -1;
200: return;
201: } else {
202: int opt = expectedArgsCount
203: + argsNode.getOptArgs()
204: .size();
205: context.processRequiredArgs(arity,
206: opt);
207:
208: ListNode optArgs = argsNode
209: .getOptArgs();
210: context.assignOptionalArgs(optArgs,
211: expectedArgsCount, optArgs
212: .size(),
213: evalOptionalValue);
214: }
215: } else {
216: if (restArg > -1) {
217: callCount = -1;
218: return;
219: } else {
220: context.processRequiredArgs(arity,
221: expectedArgsCount);
222: }
223: }
224: }
225: };
226:
227: String cleanName = CodegenUtils.cg
228: .cleanJavaIdentifier(name);
229: // FIXME: not handling empty bodies correctly...
230: StandardASMCompiler compiler = new StandardASMCompiler(
231: cleanName + hashCode() + "_"
232: + context.hashCode(), body
233: .getPosition().getFile());
234: compiler.startScript();
235: Object methodToken = compiler.beginMethod(
236: "__file__", args);
237: NodeCompilerFactory.getCompiler(body).compile(body,
238: compiler);
239: compiler.endMethod(methodToken);
240: compiler.endScript();
241: Class sourceClass = compiler
242: .loadClass(new JRubyClassLoader(runtime
243: .getJRubyClassLoader()));
244: jitCompiledScript = (Script) sourceClass
245: .newInstance();
246:
247: if (runtime.getInstanceConfig().isJitLogging())
248: System.err.println("compiled: " + className
249: + "." + name);
250: callCount = -1;
251: }
252: } catch (Exception e) {
253: if (runtime.getInstanceConfig().isJitLoggingVerbose())
254: System.err.println("could not compile: "
255: + className + "." + name
256: + " because of: \"" + e.getMessage() + '"');
257: callCount = -1;
258: }
259: }
260: }
261:
262: private void prepareArguments(ThreadContext context, Ruby runtime,
263: IRubyObject[] args) {
264: if (expectedArgsCount > args.length) {
265: throw runtime.newArgumentError("Wrong # of arguments("
266: + args.length + " for " + expectedArgsCount + ")");
267: }
268:
269: // Bind 'normal' parameter values to the local scope for this method.
270: if (expectedArgsCount > 0) {
271: context.getCurrentScope().setArgValues(args,
272: expectedArgsCount);
273: }
274:
275: // optArgs and restArgs require more work, so isolate them and ArrayList creation here
276: if (hasOptArgs || restArg != -1) {
277: args = prepareOptOrRestArgs(context, runtime, args);
278: }
279:
280: context.setFrameArgs(args);
281: }
282:
283: private IRubyObject[] prepareOptOrRestArgs(ThreadContext context,
284: Ruby runtime, IRubyObject[] args) {
285: int localExpectedArgsCount = expectedArgsCount;
286: if (restArg == -1 && hasOptArgs) {
287: int opt = expectedArgsCount + argsNode.getOptArgs().size();
288:
289: if (opt < args.length) {
290: throw runtime.newArgumentError("wrong # of arguments("
291: + args.length + " for " + opt + ")");
292: }
293: }
294:
295: int count = expectedArgsCount;
296: if (argsNode.getOptArgs() != null) {
297: count += argsNode.getOptArgs().size();
298: }
299:
300: ArrayList allArgs = new ArrayList();
301:
302: // Combine static and optional args into a single list allArgs
303: for (int i = 0; i < count && i < args.length; i++) {
304: allArgs.add(args[i]);
305: }
306:
307: if (hasOptArgs) {
308: ListNode optArgs = argsNode.getOptArgs();
309:
310: int j = 0;
311: for (int i = expectedArgsCount; i < args.length
312: && j < optArgs.size(); i++, j++) {
313: // in-frame EvalState should already have receiver set as self, continue to use it
314: AssignmentVisitor.assign(runtime, context, context
315: .getFrameSelf(), optArgs.get(j), args[i],
316: Block.NULL_BLOCK, true);
317: localExpectedArgsCount++;
318: }
319:
320: // assign the default values, adding to the end of allArgs
321: while (j < optArgs.size()) {
322: // in-frame EvalState should already have receiver set as self, continue to use it
323: allArgs.add(EvaluationState.eval(runtime, context,
324: optArgs.get(j++), context.getFrameSelf(),
325: Block.NULL_BLOCK));
326: }
327: }
328:
329: // build an array from *rest type args, also adding to allArgs
330:
331: // ENEBO: Does this next comment still need to be done since I killed hasLocalVars:
332: // move this out of the scope.hasLocalVariables() condition to deal
333: // with anonymous restargs (* versus *rest)
334:
335: // none present ==> -1
336: // named restarg ==> >=0
337: // anonymous restarg ==> -2
338: if (restArg != -1) {
339: for (int i = localExpectedArgsCount; i < args.length; i++) {
340: allArgs.add(args[i]);
341: }
342:
343: // only set in scope if named
344: if (restArg >= 0) {
345: RubyArray array = runtime.newArray(args.length
346: - localExpectedArgsCount);
347: for (int i = localExpectedArgsCount; i < args.length; i++) {
348: array.append(args[i]);
349: }
350:
351: context.getCurrentScope().setValue(restArg, array, 0);
352: }
353: }
354:
355: args = (IRubyObject[]) allArgs.toArray(new IRubyObject[allArgs
356: .size()]);
357: return args;
358: }
359:
360: private void traceReturn(ThreadContext context, Ruby runtime,
361: String name) {
362: ISourcePosition position = context.getPreviousFramePosition();
363: runtime.callEventHooks(context, EventHook.RUBY_EVENT_RETURN,
364: position.getFile(), position.getStartLine(), name,
365: getImplementationClass());
366: }
367:
368: private void traceCall(ThreadContext context, Ruby runtime,
369: String name) {
370: ISourcePosition position = body != null ? body.getPosition()
371: : context.getPosition();
372:
373: runtime.callEventHooks(context, EventHook.RUBY_EVENT_CALL,
374: position.getFile(), position.getStartLine(), name,
375: getImplementationClass());
376: }
377:
378: public Arity getArity() {
379: return argsNode.getArity();
380: }
381:
382: public DynamicMethod dup() {
383: return new DefaultMethod(getImplementationClass(), staticScope,
384: body, argsNode, getVisibility(), cref);
385: }
386: }
|