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) 2007 Ola Bini <ola@ologix.com>
015: *
016: * Alternatively, the contents of this file may be used under the terms of
017: * either of the GNU General Public License Version 2 or later (the "GPL"),
018: * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
019: * in which case the provisions of the GPL or the LGPL are applicable instead
020: * of those above. If you wish to allow use of your version of this file only
021: * under the terms of either the GPL or the LGPL, and not to allow others to
022: * use your version of this file under the terms of the CPL, indicate your
023: * decision by deleting the provisions above and replace them with the notice
024: * and other provisions required by the GPL or the LGPL. If you do not delete
025: * the provisions above, a recipient may use your version of this file under
026: * the terms of any one of the CPL, the GPL or the LGPL.
027: ***** END LICENSE BLOCK *****/package org.jruby.ast.executable;
028:
029: import java.io.Reader;
030: import java.io.IOException;
031:
032: import java.util.Iterator;
033: import java.util.List;
034: import java.util.Map;
035: import java.util.HashMap;
036: import java.util.IdentityHashMap;
037:
038: import org.jruby.Ruby;
039: import org.jruby.RubyFile;
040: import org.jruby.RubyArray;
041: import org.jruby.RubyNumeric;
042: import org.jruby.RubyString;
043: import org.jruby.RubySymbol;
044: import org.jruby.parser.LocalStaticScope;
045: import org.jruby.parser.StaticScope;
046: import org.jruby.runtime.DynamicScope;
047: import org.jruby.runtime.ThreadContext;
048: import org.jruby.runtime.builtin.IRubyObject;
049:
050: /**
051: * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
052: */
053: public class YARVCompiledRunner {
054: private Ruby runtime;
055: private YARVMachine ym = YARVMachine.INSTANCE;
056:
057: private YARVMachine.InstructionSequence iseq;
058:
059: private Map jumps = new IdentityHashMap();
060: private Map labels = new HashMap();
061:
062: public YARVCompiledRunner(Ruby runtime, Reader reader,
063: String filename) {
064: this .runtime = runtime;
065: char[] first = new char[4];
066: try {
067: reader.read(first);
068: if (first[0] != 'R' || first[1] != 'B' || first[2] != 'C'
069: || first[3] != 'M') {
070: throw new RuntimeException(
071: "File is not a compiled YARV file");
072: }
073: RubyFile f = new RubyFile(runtime, filename, reader);
074: IRubyObject arr = runtime.getModule("Marshal").callMethod(
075: runtime.getCurrentContext(), "load", f);
076: iseq = transformIntoSequence(arr);
077: } catch (IOException e) {
078: throw new RuntimeException("Couldn't read from source", e);
079: }
080: }
081:
082: public YARVCompiledRunner(Ruby runtime,
083: YARVMachine.InstructionSequence iseq) {
084: this .runtime = runtime;
085: this .iseq = iseq;
086: }
087:
088: public IRubyObject run() {
089: ThreadContext context = runtime.getCurrentContext();
090: StaticScope scope = new LocalStaticScope(null);
091: scope.setVariables(iseq.locals);
092: context.setPosition(new ISeqPosition(iseq));
093: return ym.exec(context, runtime.getObject(), new DynamicScope(
094: scope, null), iseq.body);
095: }
096:
097: private YARVMachine.InstructionSequence transformIntoSequence(
098: IRubyObject arr) {
099: if (!(arr instanceof RubyArray)) {
100: throw new RuntimeException(
101: "Error when reading compiled YARV file");
102: }
103: labels.clear();
104: jumps.clear();
105:
106: YARVMachine.InstructionSequence seq = new YARVMachine.InstructionSequence(
107: runtime, null, null, null);
108: Iterator internal = (((RubyArray) arr).getList()).iterator();
109: seq.magic = internal.next().toString();
110: seq.major = RubyNumeric.fix2int((IRubyObject) internal.next());
111: seq.minor = RubyNumeric.fix2int((IRubyObject) internal.next());
112: seq.format_type = RubyNumeric.fix2int((IRubyObject) internal
113: .next());
114: IRubyObject misc = (IRubyObject) internal.next();
115: if (misc.isNil()) {
116: seq.misc = null;
117: } else {
118: seq.misc = misc;
119: }
120: seq.name = internal.next().toString();
121: seq.filename = internal.next().toString();
122: seq.line = new Object[0];
123: internal.next();
124: seq.type = internal.next().toString();
125: seq.locals = toStringArray((IRubyObject) internal.next());
126: IRubyObject argo = (IRubyObject) internal.next();
127: if (argo instanceof RubyArray) {
128: List arglist = ((RubyArray) argo).getList();
129: seq.args_argc = RubyNumeric.fix2int((IRubyObject) arglist
130: .get(0));
131: seq.args_arg_opts = RubyNumeric
132: .fix2int((IRubyObject) arglist.get(1));
133: seq.args_opt_labels = toStringArray((IRubyObject) arglist
134: .get(2));
135: seq.args_rest = RubyNumeric.fix2int((IRubyObject) arglist
136: .get(3));
137: seq.args_block = RubyNumeric.fix2int((IRubyObject) arglist
138: .get(4));
139: } else {
140: seq.args_argc = RubyNumeric.fix2int(argo);
141: }
142:
143: seq.exception = getExceptionInformation((IRubyObject) internal
144: .next());
145:
146: List bodyl = ((RubyArray) internal.next()).getList();
147: YARVMachine.Instruction[] body = new YARVMachine.Instruction[bodyl
148: .size()];
149: int real = 0;
150: int i = 0;
151: for (Iterator iter = bodyl.iterator(); iter.hasNext(); i++) {
152: IRubyObject is = (IRubyObject) iter.next();
153: if (is instanceof RubyArray) {
154: body[real] = intoInstruction((RubyArray) is, real, seq);
155: real++;
156: } else if (is instanceof RubySymbol) {
157: labels.put(is.toString(), new Integer(real + 1));
158: }
159: }
160: YARVMachine.Instruction[] nbody = new YARVMachine.Instruction[real];
161: System.arraycopy(body, 0, nbody, 0, real);
162: seq.body = nbody;
163:
164: for (Iterator iter = jumps.keySet().iterator(); iter.hasNext();) {
165: YARVMachine.Instruction k = (YARVMachine.Instruction) iter
166: .next();
167: k.l_op0 = ((Integer) labels.get(jumps.get(k))).intValue() - 1;
168: }
169:
170: return seq;
171: }
172:
173: private String[] toStringArray(IRubyObject obj) {
174: if (obj.isNil()) {
175: return new String[0];
176: } else {
177: List l = ((RubyArray) obj).getList();
178: String[] s = new String[l.size()];
179: int i = 0;
180: for (Iterator iter = l.iterator(); iter.hasNext(); i++) {
181: s[i] = iter.next().toString();
182: }
183: return s;
184: }
185: }
186:
187: private YARVMachine.Instruction intoInstruction(RubyArray obj,
188: int n, YARVMachine.InstructionSequence iseq) {
189: List internal = obj.getList();
190: String name = internal.get(0).toString();
191: int instruction = YARVMachine.instruction(name);
192: YARVMachine.Instruction i = new YARVMachine.Instruction(
193: instruction);
194: if (internal.size() > 1) {
195: IRubyObject first = (IRubyObject) internal.get(1);
196: if (instruction == YARVInstructions.GETLOCAL
197: || instruction == YARVInstructions.SETLOCAL) {
198: i.l_op0 = (iseq.locals.length + 1)
199: - RubyNumeric.fix2long(first);
200: } else if (instruction == YARVInstructions.PUTOBJECT
201: || instruction == YARVInstructions.OPT_REGEXPMATCH1
202: || instruction == YARVInstructions.GETINLINECACHE) {
203: i.o_op0 = first;
204: } else if (first instanceof RubyString
205: || first instanceof RubySymbol) {
206: i.s_op0 = first.toString();
207: } else if (first instanceof RubyNumeric) {
208: i.l_op0 = RubyNumeric.fix2long(first);
209: }
210: if (instruction == YARVInstructions.SEND) {
211: i.i_op1 = RubyNumeric.fix2int((IRubyObject) internal
212: .get(2));
213: i.i_op3 = RubyNumeric.fix2int((IRubyObject) internal
214: .get(4));
215: }
216: if (instruction == YARVInstructions.DEFINEMETHOD) {
217: i.iseq_op = transformIntoSequence((IRubyObject) internal
218: .get(2));
219: }
220: if (isJump(instruction)) {
221: i.index = n;
222: jumps.put(i, internal.get(jumpIndex(instruction))
223: .toString());
224: }
225: }
226: return i;
227: }
228:
229: private boolean isJump(int i) {
230: return i == YARVInstructions.JUMP
231: || i == YARVInstructions.BRANCHIF
232: || i == YARVInstructions.BRANCHUNLESS
233: || i == YARVInstructions.GETINLINECACHE
234: || i == YARVInstructions.SETINLINECACHE;
235: }
236:
237: private int jumpIndex(int i) {
238: if (i == YARVInstructions.GETINLINECACHE) {
239: return 2;
240: } else {
241: return 1;
242: }
243: }
244:
245: private Object[] getExceptionInformation(IRubyObject obj) {
246: // System.err.println(obj.callMethod(runtime.getCurrentContext(),"inspect"));
247: return new Object[0];
248: }
249: }// YARVCompiledRunner
|