Mercurial > hg > graal-compiler
annotate graal/com.oracle.truffle.ruby.parser/src/com/oracle/truffle/ruby/parser/MethodTranslator.java @ 14014:ac599fff18dc
Substitution methods for injecting fake profiling data into unit tests.
author | Roland Schatz <roland.schatz@oracle.com> |
---|---|
date | Tue, 25 Feb 2014 11:24:17 +0100 |
parents | 232eb6708943 |
children |
rev | line source |
---|---|
13514 | 1 /* |
2 * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This | |
3 * code is released under a tri EPL/GPL/LGPL license. You can use it, | |
4 * redistribute it and/or modify it under the terms of the: | |
5 * | |
6 * Eclipse Public License version 1.0 | |
7 * GNU General Public License version 2 | |
8 * GNU Lesser General Public License version 2.1 | |
9 */ | |
10 package com.oracle.truffle.ruby.parser; | |
11 | |
12 import java.util.*; | |
13 | |
14 import com.oracle.truffle.api.*; | |
15 import com.oracle.truffle.api.frame.*; | |
16 import com.oracle.truffle.api.nodes.*; | |
17 import com.oracle.truffle.ruby.nodes.*; | |
18 import com.oracle.truffle.ruby.nodes.call.*; | |
19 import com.oracle.truffle.ruby.nodes.control.*; | |
20 import com.oracle.truffle.ruby.nodes.literal.*; | |
21 import com.oracle.truffle.ruby.nodes.methods.*; | |
22 import com.oracle.truffle.ruby.nodes.methods.arguments.*; | |
23 import com.oracle.truffle.ruby.nodes.methods.locals.*; | |
24 import com.oracle.truffle.ruby.runtime.*; | |
25 import com.oracle.truffle.ruby.runtime.methods.*; | |
26 | |
27 class MethodTranslator extends Translator { | |
28 | |
29 private boolean isBlock; | |
30 | |
31 public MethodTranslator(RubyContext context, Translator parent, TranslatorEnvironment environment, boolean isBlock, Source source) { | |
32 super(context, parent, environment, source); | |
33 this.isBlock = isBlock; | |
34 } | |
35 | |
36 public MethodDefinitionNode compileFunctionNode(SourceSection sourceSection, String methodName, org.jrubyparser.ast.ArgsNode argsNode, org.jrubyparser.ast.Node bodyNode) { | |
37 environment.setMethodName(methodName); | |
38 | |
39 final Arity arity = findParameters(argsNode); | |
40 | |
41 RubyNode body; | |
42 | |
43 if (bodyNode != null) { | |
44 body = (RubyNode) bodyNode.accept(this); | |
45 } else { | |
46 body = new NilNode(context, sourceSection); | |
47 } | |
48 | |
49 body = loadArgumentsIntoLocals(arity, body); | |
50 | |
51 if (environment.getFlipFlopStates().size() > 0) { | |
52 body = new SequenceNode(context, sourceSection, initFlipFlopStates(sourceSection), body); | |
53 } | |
54 | |
55 if (isBlock) { | |
56 body = new CatchNextNode(context, sourceSection, body); | |
57 } else { | |
58 body = new CatchReturnNode(context, sourceSection, body, environment.getReturnID()); | |
59 body = new CatchNextNode(context, sourceSection, body); | |
60 } | |
61 | |
13706
232eb6708943
Ruby: required fixes for moving FrameDescriptor to the RootNode.
Christian Humer <christian.humer@gmail.com>
parents:
13514
diff
changeset
|
62 final RubyRootNode pristineRootNode = new RubyRootNode(sourceSection, environment.getFrameDescriptor(), methodName, body); |
13514 | 63 |
13706
232eb6708943
Ruby: required fixes for moving FrameDescriptor to the RootNode.
Christian Humer <christian.humer@gmail.com>
parents:
13514
diff
changeset
|
64 final CallTarget callTarget = Truffle.getRuntime().createCallTarget(NodeUtil.cloneNode(pristineRootNode)); |
13514 | 65 |
66 if (isBlock) { | |
67 return new BlockDefinitionNode(context, sourceSection, methodName, environment.getUniqueMethodIdentifier(), environment.getFrameDescriptor(), environment.needsDeclarationFrame(), | |
68 pristineRootNode, callTarget); | |
69 } else { | |
70 return new MethodDefinitionNode(context, sourceSection, methodName, environment.getUniqueMethodIdentifier(), environment.getFrameDescriptor(), environment.needsDeclarationFrame(), | |
71 pristineRootNode, callTarget); | |
72 } | |
73 } | |
74 | |
75 private RubyNode loadArgumentsIntoLocals(Arity arity, RubyNode body) { | |
76 final SourceSection sourceSection = body.getEncapsulatingSourceSection(); | |
77 | |
78 final List<RubyNode> loadIndividualArgumentsNodes = new ArrayList<>(); | |
79 | |
80 if (!isBlock) { | |
81 loadIndividualArgumentsNodes.add(new CheckArityNode(context, sourceSection, arity)); | |
82 } | |
83 | |
84 final int preCount = environment.getPreParameters().size(); | |
85 final int postCount = environment.getPostParameters().size(); | |
86 | |
87 for (int n = 0; n < environment.getPreParameters().size(); n++) { | |
88 final FrameSlot param = environment.getPreParameters().get(n); | |
89 | |
90 // ReadPre reads from the start of the arguments array | |
91 | |
92 final ReadPreArgumentNode readArgumentNode = new ReadPreArgumentNode(context, sourceSection, n, false); | |
93 | |
94 final WriteLocalVariableNode writeLocal = WriteLocalVariableNodeFactory.create(context, sourceSection, param, readArgumentNode); | |
95 | |
96 loadIndividualArgumentsNodes.add(writeLocal); | |
97 } | |
98 | |
99 for (int n = 0; n < environment.getOptionalParameters().size(); n++) { | |
100 final FrameSlot param = environment.getOptionalParameters().get(n); | |
101 final RubyNode defaultValue = environment.getOptionalParametersDefaultValues().get(param); | |
102 | |
103 /* | |
104 * ReadOptional reads from the start of the arguments array, as long as it is long | |
105 * enough, else uses the default value (which may use locals with arguments just loaded, | |
106 * either from pre or preceding optionals). | |
107 */ | |
108 | |
109 final ReadOptionalArgumentNode readArgumentNode = new ReadOptionalArgumentNode(context, body.getEncapsulatingSourceSection(), preCount + n, preCount + postCount + n + 1, | |
110 (RubyNode) defaultValue.copy()); | |
111 | |
112 final WriteLocalVariableNode writeLocal = WriteLocalVariableNodeFactory.create(context, sourceSection, param, readArgumentNode); | |
113 | |
114 loadIndividualArgumentsNodes.add(writeLocal); | |
115 } | |
116 | |
117 for (int n = 0; n < environment.getPostParameters().size(); n++) { | |
118 final FrameSlot param = environment.getPostParameters().get(n); | |
119 | |
120 // ReadPost reads from the end of the arguments array | |
121 | |
122 final ReadPostArgumentNode readArgumentNode = new ReadPostArgumentNode(context, sourceSection, postCount - n - 1); | |
123 | |
124 final WriteLocalVariableNode writeLocal = WriteLocalVariableNodeFactory.create(context, sourceSection, param, readArgumentNode); | |
125 | |
126 loadIndividualArgumentsNodes.add(writeLocal); | |
127 } | |
128 | |
129 if (environment.getRestParameter() != null) { | |
130 /* | |
131 * TODO(cs): this assumes there are no optionals and therefore also no posts, which may | |
132 * not be a valid assumption. | |
133 */ | |
134 | |
135 if (postCount != 0) { | |
136 context.implementationMessage("post arguments as well as a rest argument - they will conflict"); | |
137 } | |
138 | |
139 final ReadRestArgumentNode readArgumentNode = new ReadRestArgumentNode(context, sourceSection, preCount); | |
140 | |
141 final WriteLocalVariableNode writeLocal = WriteLocalVariableNodeFactory.create(context, sourceSection, environment.getRestParameter(), readArgumentNode); | |
142 | |
143 loadIndividualArgumentsNodes.add(writeLocal); | |
144 } | |
145 | |
146 if (environment.getBlockParameter() != null) { | |
147 final FrameSlot param = environment.getBlockParameter(); | |
148 | |
149 final ReadBlockArgumentNode readArgumentNode = new ReadBlockArgumentNode(context, sourceSection, false); | |
150 | |
151 final WriteLocalVariableNode writeLocal = WriteLocalVariableNodeFactory.create(context, sourceSection, param, readArgumentNode); | |
152 | |
153 loadIndividualArgumentsNodes.add(writeLocal); | |
154 } | |
155 | |
156 final RubyNode loadIndividualArguments = new SequenceNode(context, sourceSection, loadIndividualArgumentsNodes.toArray(new RubyNode[loadIndividualArgumentsNodes.size()])); | |
157 | |
158 final RubyNode noSwitch = new SequenceNode(context, body.getSourceSection(), loadIndividualArguments, body); | |
159 | |
160 if (!isBlock) { | |
161 return noSwitch; | |
162 } | |
163 | |
164 /* | |
165 * See the test testBlockArgumentsDestructure for a motivation for this. See | |
166 * BlockDestructureSwitchNode for how it works. | |
167 */ | |
168 | |
169 if (preCount + postCount == 1 && environment.getOptionalParameters().size() == 0) { | |
170 return noSwitch; | |
171 } | |
172 | |
173 final List<RubyNode> destructureLoadArgumentsNodes = new ArrayList<>(); | |
174 | |
175 for (int n = 0; n < environment.getPreParameters().size(); n++) { | |
176 final FrameSlot param = environment.getPreParameters().get(n); | |
177 | |
178 final ReadDestructureArgumentNode readArgumentNode = new ReadDestructureArgumentNode(context, sourceSection, n); | |
179 | |
180 final WriteLocalVariableNode writeLocal = WriteLocalVariableNodeFactory.create(context, sourceSection, param, readArgumentNode); | |
181 | |
182 destructureLoadArgumentsNodes.add(writeLocal); | |
183 } | |
184 | |
185 final RubyNode destructureLoadArguments = new SequenceNode(context, body.getSourceSection(), destructureLoadArgumentsNodes.toArray(new RubyNode[destructureLoadArgumentsNodes.size()])); | |
186 | |
187 return new BlockDestructureSwitchNode(context, body.getEncapsulatingSourceSection(), loadIndividualArguments, destructureLoadArguments, body); | |
188 | |
189 } | |
190 | |
191 private Arity findParameters(org.jrubyparser.ast.ArgsNode args) { | |
192 if (args == null) { | |
193 return Arity.NO_ARGS; | |
194 } | |
195 | |
196 final SourceSection sourceSection = translate(args.getPosition()); | |
197 | |
198 if (args.getPre() != null) { | |
199 for (org.jrubyparser.ast.Node arg : args.getPre().childNodes()) { | |
200 if (arg instanceof org.jrubyparser.ast.ArgumentNode) { | |
201 final org.jrubyparser.ast.ArgumentNode argNode = (org.jrubyparser.ast.ArgumentNode) arg; | |
202 environment.getPreParameters().add(environment.declareVar(argNode.getName())); | |
203 } else if (arg instanceof org.jrubyparser.ast.MultipleAsgnNode) { | |
204 /* | |
205 * TODO(cs): I don't know how to handle this yet, so I just do my best to get | |
206 * the names out and define them so the rest of the parser succeeds. | |
207 */ | |
208 | |
209 context.implementationMessage("only extracting names from multiple assignment in arguments"); | |
210 | |
211 final org.jrubyparser.ast.MultipleAsgnNode multAsgn = (org.jrubyparser.ast.MultipleAsgnNode) arg; | |
212 | |
213 final List<String> names = new ArrayList<>(); | |
214 getNamesFromMultipleAssignment(multAsgn, names); | |
215 | |
216 for (String name : names) { | |
217 environment.getPreParameters().add(environment.declareVar(name)); | |
218 } | |
219 } | |
220 } | |
221 } | |
222 | |
223 // The JRuby parser expresses optional arguments as a block of local assignments | |
224 | |
225 /* | |
226 * Note that default values for optional params can refer to the actual value of previous | |
227 * args, so be careful with the order of args here and in loadArgumentsIntoLocals. | |
228 */ | |
229 | |
230 if (args.getOptional() != null) { | |
231 for (org.jrubyparser.ast.Node arg : args.getOptional().childNodes()) { | |
232 final org.jrubyparser.ast.OptArgNode optArgNode = (org.jrubyparser.ast.OptArgNode) arg; | |
233 | |
234 String name; | |
235 org.jrubyparser.ast.Node valueNode; | |
236 | |
237 if (optArgNode.getValue() instanceof org.jrubyparser.ast.LocalAsgnNode) { | |
238 final org.jrubyparser.ast.LocalAsgnNode optLocalAsgn = (org.jrubyparser.ast.LocalAsgnNode) optArgNode.getValue(); | |
239 name = optLocalAsgn.getName(); | |
240 valueNode = optLocalAsgn.getValue(); | |
241 } else if (optArgNode.getValue() instanceof org.jrubyparser.ast.DAsgnNode) { | |
242 final org.jrubyparser.ast.DAsgnNode optLocalAsgn = (org.jrubyparser.ast.DAsgnNode) optArgNode.getValue(); | |
243 name = optLocalAsgn.getName(); | |
244 valueNode = optLocalAsgn.getValue(); | |
245 } else { | |
246 throw new UnsupportedOperationException(optArgNode.getValue().getClass().getName()); | |
247 } | |
248 | |
249 RubyNode paramDefaultValue; | |
250 | |
251 if (valueNode == null) { | |
252 paramDefaultValue = new NilNode(context, sourceSection); | |
253 } else { | |
254 paramDefaultValue = (RubyNode) valueNode.accept(this); | |
255 } | |
256 | |
257 final FrameSlot frameSlot = environment.declareVar(name); | |
258 environment.getOptionalParameters().add(frameSlot); | |
259 environment.getOptionalParametersDefaultValues().put(frameSlot, paramDefaultValue); | |
260 } | |
261 } | |
262 | |
263 if (args.getPost() != null) { | |
264 for (org.jrubyparser.ast.Node arg : args.getPost().childNodes()) { | |
265 final org.jrubyparser.ast.ArgumentNode argNode = (org.jrubyparser.ast.ArgumentNode) arg; | |
266 environment.getPostParameters().add(environment.declareVar(argNode.getName())); | |
267 } | |
268 } | |
269 | |
270 if (args.getRest() != null) { | |
271 final org.jrubyparser.ast.RestArgNode rest = (org.jrubyparser.ast.RestArgNode) args.getRest(); | |
272 environment.setRestParameter(environment.declareVar(rest.getName())); | |
273 } | |
274 | |
275 if (args.getBlock() != null) { | |
276 final org.jrubyparser.ast.BlockArgNode blockArgNode = args.getBlock(); | |
277 final FrameSlot frameSlot = environment.declareVar(blockArgNode.getName()); | |
278 environment.setBlockParameter(frameSlot); | |
279 } | |
280 | |
281 final int minimum = environment.getPreParameters().size() + environment.getPostParameters().size(); | |
282 | |
283 int maximum = minimum + environment.getOptionalParameters().size(); | |
284 | |
285 if (args.getRest() != null) { | |
286 maximum = Arity.NO_MAXIMUM; | |
287 } | |
288 | |
289 return new Arity(minimum, maximum); | |
290 } | |
291 | |
292 private void getNamesFromMultipleAssignment(org.jrubyparser.ast.MultipleAsgnNode multAsgn, List<String> names) { | |
293 for (org.jrubyparser.ast.Node a : multAsgn.getPre().childNodes()) { | |
294 if (a instanceof org.jrubyparser.ast.DAsgnNode) { | |
295 names.add(((org.jrubyparser.ast.DAsgnNode) a).getName()); | |
296 } else if (a instanceof org.jrubyparser.ast.MultipleAsgnNode) { | |
297 getNamesFromMultipleAssignment((org.jrubyparser.ast.MultipleAsgnNode) a, names); | |
298 } else if (a instanceof org.jrubyparser.ast.LocalAsgnNode) { | |
299 names.add(((org.jrubyparser.ast.LocalAsgnNode) a).getName()); | |
300 } else { | |
301 throw new RuntimeException(a.getClass().getName()); | |
302 } | |
303 } | |
304 } | |
305 | |
306 @Override | |
307 public Object visitSuperNode(org.jrubyparser.ast.SuperNode node) { | |
308 final SourceSection sourceSection = translate(node.getPosition()); | |
309 | |
310 final ArgumentsAndBlockTranslation argumentsAndBlock = translateArgumentsAndBlock(sourceSection, node.getIter(), node.getArgs(), null); | |
311 | |
312 final String name = environment.getMethodName(); | |
313 | |
314 return new GeneralSuperCallNode(context, sourceSection, name, argumentsAndBlock.getBlock(), argumentsAndBlock.getArguments(), argumentsAndBlock.isSplatted()); | |
315 } | |
316 | |
317 @Override | |
318 public Object visitZSuperNode(org.jrubyparser.ast.ZSuperNode node) { | |
319 final SourceSection sourceSection = translate(node.getPosition()); | |
320 | |
321 final ArgumentsAndBlockTranslation argumentsAndBlock = translateArgumentsAndBlock(sourceSection, node.getIter(), null, null); | |
322 | |
323 final String name = environment.getMethodName(); | |
324 | |
325 return new GeneralSuperCallNode(context, sourceSection, name, argumentsAndBlock.getBlock(), argumentsAndBlock.getArguments(), argumentsAndBlock.isSplatted()); | |
326 } | |
327 | |
328 @Override | |
329 protected FlipFlopStateNode createFlipFlopState(SourceSection sourceSection, int depth) { | |
330 if (isBlock) { | |
331 environment.setNeedsDeclarationFrame(); | |
332 return parent.createFlipFlopState(sourceSection, depth + 1); | |
333 } else { | |
334 return super.createFlipFlopState(sourceSection, depth); | |
335 } | |
336 } | |
337 | |
338 } |