* Eliminate visibility checking for almost all fcall and vcall paths.
[jruby.git] / src / org / jruby / ast / executable / RubiniusMachine.java
blob8084628c2931f77cfdbca02a4e754a47d26f72c1
1 /***** BEGIN LICENSE BLOCK *****
2 * Version: CPL 1.0/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Common Public
5 * License Version 1.0 (the "License"); you may not use this file
6 * except in compliance with the License. You may obtain a copy of
7 * the License at http://www.eclipse.org/legal/cpl-v10.html
9 * Software distributed under the License is distributed on an "AS
10 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
11 * implied. See the License for the specific language governing
12 * rights and limitations under the License.
14 * Copyright (C) 2007 Ola Bini <ola.bini@gmail.com>
16 * Alternatively, the contents of this file may be used under the terms of
17 * either of the GNU General Public License Version 2 or later (the "GPL"),
18 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
19 * in which case the provisions of the GPL or the LGPL are applicable instead
20 * of those above. If you wish to allow use of your version of this file only
21 * under the terms of either the GPL or the LGPL, and not to allow others to
22 * use your version of this file under the terms of the CPL, indicate your
23 * decision by deleting the provisions above and replace them with the notice
24 * and other provisions required by the GPL or the LGPL. If you do not delete
25 * the provisions above, a recipient may use your version of this file under
26 * the terms of any one of the CPL, the GPL or the LGPL.
27 ***** END LICENSE BLOCK *****/
28 package org.jruby.ast.executable;
30 import org.jruby.MetaClass;
31 import org.jruby.Ruby;
32 import org.jruby.RubyArray;
33 import org.jruby.RubyFixnum;
34 import org.jruby.RubyModule;
35 import org.jruby.RubyString;
36 import org.jruby.parser.StaticScope;
37 import org.jruby.parser.LocalStaticScope;
38 import org.jruby.runtime.Block;
39 import org.jruby.runtime.CallType;
40 import org.jruby.runtime.MethodIndex;
41 import org.jruby.runtime.ThreadContext;
42 import org.jruby.runtime.Visibility;
43 import org.jruby.runtime.builtin.IRubyObject;
44 import org.jruby.internal.runtime.methods.WrapperMethod;
45 import org.jruby.internal.runtime.methods.RubiniusMethod;
46 import org.jruby.javasupport.util.RuntimeHelpers;
48 /**
49 * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
51 public class RubiniusMachine {
52 public final static RubiniusMachine INSTANCE = new RubiniusMachine();
54 public final static int getInt(char[] bytecodes, int ix) {
55 int val = 0;
56 val += (bytecodes[ix+0]<<24);
57 val += (bytecodes[ix+1]<<16);
58 val += (bytecodes[ix+2]<<8);
59 val += (bytecodes[ix+3]);
60 return val;
63 public IRubyObject exec(ThreadContext context, IRubyObject self, char[] bytecodes, IRubyObject[] literals, IRubyObject[] args) {
64 IRubyObject[] stack = new IRubyObject[20];
65 int stackTop = 0;
66 stack[stackTop] = context.getRuntime().getNil();
67 for(int i=0;i<args.length;i++) {
68 stack[++stackTop] = args[i];
70 int ip = 0;
71 int call_flags = -1;
72 int cache_index = -1;
73 Ruby runtime = context.getRuntime();
74 IRubyObject recv;
75 IRubyObject other;
77 loop: while (ip < bytecodes.length) {
78 int ix = ip;
79 int code = bytecodes[ip++];
81 System.err.print(RubiniusInstructions.NAMES[code] + " (" + code + ") ");
82 if(RubiniusInstructions.ONE_INT[code]) {
83 System.err.print("[" + getInt(bytecodes, ip) + "] ");
84 } else if(RubiniusInstructions.TWO_INT[code]) {
85 System.err.print("[" + getInt(bytecodes, ip) + ", " + getInt(bytecodes, ip+4) + "] ");
87 System.err.println("{" + ix + "}");
89 for(int i=stackTop; i>=0; i--) {
90 System.err.println(" [" + i + "]=" + stack[i].callMethod(context, "inspect"));
91 }*/
92 switch(code) {
93 case RubiniusInstructions.NOOP: {
94 break;
96 case RubiniusInstructions.ADD_METHOD: {
97 int val = getInt(bytecodes, ip);
98 ip += 4;
99 String name = literals[val].toString();
100 RubyModule clzz = (RubyModule)stack[stackTop--];
101 RubyArray method = (RubyArray)stack[stackTop--];
103 Visibility visibility = context.getCurrentVisibility();
104 if (name == "initialize" || visibility == Visibility.MODULE_FUNCTION) {
105 visibility = Visibility.PRIVATE;
108 RubiniusCMethod cmethod = new RubiniusCMethod(method);
110 StaticScope staticScope = new LocalStaticScope(context.getCurrentScope().getStaticScope());
111 staticScope.setVariables(new String[cmethod.locals]);
112 staticScope.determineModule();
114 RubiniusMethod newMethod = new RubiniusMethod(clzz, cmethod, staticScope, visibility);
116 clzz.addMethod(name, newMethod);
118 if (context.getCurrentVisibility() == Visibility.MODULE_FUNCTION) {
119 clzz.getSingletonClass().addMethod(
120 name,
121 new WrapperMethod(clzz.getSingletonClass(), newMethod,
122 Visibility.PUBLIC));
123 clzz.callMethod(context, "singleton_method_added", literals[val]);
126 if (clzz.isSingleton()) {
127 ((MetaClass) clzz).getAttached().callMethod(
128 context, "singleton_method_added", literals[val]);
129 } else {
130 clzz.callMethod(context, "method_added", literals[val]);
132 stack[++stackTop] = method;
133 break;
135 case RubiniusInstructions.META_PUSH_NEG_1: {
136 stack[++stackTop] = RubyFixnum.minus_one(runtime);
137 break;
139 case RubiniusInstructions.CHECK_ARGCOUNT: {
140 int min = getInt(bytecodes, ip);
141 ip += 4;
142 int max = getInt(bytecodes, ip);
143 ip += 4;
145 if(args.length < min) {
146 throw runtime.newArgumentError("wrong # of arguments(" + args.length + " for " + min + ")");
147 } else if(max>0 && args.length>max) {
148 throw runtime.newArgumentError("wrong # of arguments(" + args.length + " for " + max + ")");
150 break;
152 case RubiniusInstructions.META_PUSH_0: {
153 stack[++stackTop] = RubyFixnum.zero(runtime);
154 break;
156 case RubiniusInstructions.META_PUSH_1: {
157 stack[++stackTop] = RubyFixnum.one(runtime);
158 break;
160 case RubiniusInstructions.META_PUSH_2: {
161 stack[++stackTop] = runtime.newFixnum(2);
162 break;
164 case RubiniusInstructions.SET_LOCAL: {
165 int local = getInt(bytecodes, ip);
166 ip += 4;
167 context.getCurrentScope().setValue(local,stack[stackTop],0);
168 break;
170 case RubiniusInstructions.PUSH_LOCAL: {
171 int local = getInt(bytecodes, ip);
172 ip += 4;
173 stack[++stackTop] = context.getCurrentScope().getValue(local,0);
174 break;
176 case RubiniusInstructions.PUSH_NIL: {
177 stack[++stackTop] = runtime.getNil();
178 break;
180 case RubiniusInstructions.PUSH_TRUE: {
181 stack[++stackTop] = runtime.getTrue();
182 break;
184 case RubiniusInstructions.PUSH_FALSE: {
185 stack[++stackTop] = runtime.getFalse();
186 break;
188 case RubiniusInstructions.PUSH_SELF: {
189 stack[++stackTop] = self;
190 break;
192 case RubiniusInstructions.STRING_DUP: {
193 stack[stackTop] = ((RubyString)stack[stackTop]).strDup(context.getRuntime());
194 break;
196 case RubiniusInstructions.PUSH_LITERAL: {
197 int val = getInt(bytecodes, ip);
198 ip += 4;
199 stack[++stackTop] = literals[val];
200 break;
202 case RubiniusInstructions.META_SEND_OP_LT: {
203 IRubyObject t1 = stack[stackTop--];
204 IRubyObject t2 = stack[stackTop--];
205 if((t1 instanceof RubyFixnum) && (t1 instanceof RubyFixnum)) {
206 stack[++stackTop] = (((RubyFixnum)t1).getLongValue() < ((RubyFixnum)t2).getLongValue()) ? runtime.getTrue() : runtime.getFalse();
207 } else {
208 stack[++stackTop] = t1.callMethod(context, MethodIndex.OP_LT, "<", t2);
210 break;
213 case RubiniusInstructions.META_SEND_OP_GT: {
214 IRubyObject t1 = stack[stackTop--];
215 IRubyObject t2 = stack[stackTop--];
216 if((t1 instanceof RubyFixnum) && (t1 instanceof RubyFixnum)) {
217 stack[++stackTop] = (((RubyFixnum)t1).getLongValue() > ((RubyFixnum)t1).getLongValue()) ? runtime.getTrue() : runtime.getFalse();
218 } else {
219 stack[++stackTop] = t1.callMethod(context, MethodIndex.OP_GT, ">", t2);
221 break;
224 case RubiniusInstructions.META_SEND_OP_PLUS: {
225 IRubyObject t1 = stack[stackTop--];
226 IRubyObject t2 = stack[stackTop--];
227 if((t1 instanceof RubyFixnum) && (t2 instanceof RubyFixnum)) {
228 stack[++stackTop] = ((RubyFixnum)t1).op_plus(context, t2);
229 } else {
230 stack[++stackTop] = t1.callMethod(context, MethodIndex.OP_PLUS, "+", t2);
232 break;
234 case RubiniusInstructions.META_SEND_OP_MINUS: {
236 IRubyObject t1 = stack[stackTop--];
237 IRubyObject t2 = stack[stackTop--];
238 if((t1 instanceof RubyFixnum) && (t2 instanceof RubyFixnum)) {
239 stack[++stackTop] = ((RubyFixnum)t1).op_minus(context, t2);
240 } else {
241 stack[++stackTop] = t1.callMethod(context, MethodIndex.OP_MINUS, "-", t2);
243 break;
245 case RubiniusInstructions.POP: {
246 stackTop--;
247 break;
249 case RubiniusInstructions.SET_CALL_FLAGS: {
250 int val = getInt(bytecodes, ip);
251 ip += 4;
252 call_flags = val;
253 break;
255 case RubiniusInstructions.SET_CACHE_INDEX: {
256 int val = getInt(bytecodes, ip);
257 ip += 4;
258 cache_index = val;
259 break;
261 case RubiniusInstructions.SEND_STACK: {
262 int index = getInt(bytecodes, ip);
263 ip += 4;
264 int num_args = getInt(bytecodes, ip);
265 ip += 4;
267 String name = literals[index].toString();
268 int ixi = MethodIndex.getIndex(name);
269 recv = stack[stackTop--];
270 IRubyObject[] argu = new IRubyObject[num_args];
271 for(int i=0;i<num_args;i++) {
272 argu[i] = stack[stackTop--];
274 if((call_flags & 0x01) == 0x01) { //Functional
275 stack[++stackTop] = RuntimeHelpers.invoke(context, recv, name, argu, Block.NULL_BLOCK);
276 } else {
277 stack[++stackTop] = RuntimeHelpers.invoke(context, recv, name, argu, CallType.NORMAL, Block.NULL_BLOCK);
279 break;
281 case RubiniusInstructions.GOTO_IF_FALSE: {
282 int val = getInt(bytecodes, ip);
283 ip += 4;
284 if(!stack[stackTop--].isTrue()) {
285 ip = val;
287 break;
289 case RubiniusInstructions.GOTO_IF_TRUE: {
290 int val = getInt(bytecodes, ip);
291 ip += 4;
292 if(stack[stackTop--].isTrue()) {
293 ip = val;
295 break;
297 case RubiniusInstructions.SWAP_STACK: {
298 IRubyObject swap = stack[stackTop];
299 stack[stackTop] = stack[stackTop-1];
300 stack[stackTop-1] = swap;
301 break;
303 case RubiniusInstructions.DUP_TOP: {
304 stack[stackTop+1] = stack[stackTop];
305 stackTop++;
306 break;
308 case RubiniusInstructions.GOTO: {
309 int val = getInt(bytecodes, ip);
310 ip += 4;
311 ip = val;
312 break;
314 case RubiniusInstructions.RET: {
315 return stack[stackTop];
317 case RubiniusInstructions.PUSH_INT: {
318 int val = getInt(bytecodes, ip);
319 ip += 4;
320 stack[++stackTop] = runtime.newFixnum(val);
321 break;
323 case RubiniusInstructions.PUSH_CONST: {
324 int index = getInt(bytecodes, ip);
325 ip += 4;
327 String name = literals[index].toString();
328 stack[++stackTop] = context.getConstant(name);
329 break;
331 default:
332 System.err.println("--COULDN'T");
333 if(RubiniusInstructions.ONE_INT[code]) {
334 ip+=4;
335 } else if(RubiniusInstructions.TWO_INT[code]) {
336 ip+=8;
338 break;
341 return null;
343 }// RubiniusMachine