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
|
|
62 final RubyRootNode pristineRootNode = new RubyRootNode(sourceSection, methodName, body);
|
|
63
|
|
64 final CallTarget callTarget = Truffle.getRuntime().createCallTarget(NodeUtil.cloneNode(pristineRootNode), environment.getFrameDescriptor());
|
|
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 }
|