Mercurial > hg > graal-compiler
diff graal/com.oracle.truffle.ruby.parser/src/com/oracle/truffle/ruby/parser/MethodTranslator.java @ 13514:0fbee3eb71f0
Ruby: import project.
author | Chris Seaton <chris.seaton@oracle.com> |
---|---|
date | Mon, 06 Jan 2014 17:12:09 +0000 |
parents | |
children | 232eb6708943 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.parser/src/com/oracle/truffle/ruby/parser/MethodTranslator.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.parser; + +import java.util.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.nodes.call.*; +import com.oracle.truffle.ruby.nodes.control.*; +import com.oracle.truffle.ruby.nodes.literal.*; +import com.oracle.truffle.ruby.nodes.methods.*; +import com.oracle.truffle.ruby.nodes.methods.arguments.*; +import com.oracle.truffle.ruby.nodes.methods.locals.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.methods.*; + +class MethodTranslator extends Translator { + + private boolean isBlock; + + public MethodTranslator(RubyContext context, Translator parent, TranslatorEnvironment environment, boolean isBlock, Source source) { + super(context, parent, environment, source); + this.isBlock = isBlock; + } + + public MethodDefinitionNode compileFunctionNode(SourceSection sourceSection, String methodName, org.jrubyparser.ast.ArgsNode argsNode, org.jrubyparser.ast.Node bodyNode) { + environment.setMethodName(methodName); + + final Arity arity = findParameters(argsNode); + + RubyNode body; + + if (bodyNode != null) { + body = (RubyNode) bodyNode.accept(this); + } else { + body = new NilNode(context, sourceSection); + } + + body = loadArgumentsIntoLocals(arity, body); + + if (environment.getFlipFlopStates().size() > 0) { + body = new SequenceNode(context, sourceSection, initFlipFlopStates(sourceSection), body); + } + + if (isBlock) { + body = new CatchNextNode(context, sourceSection, body); + } else { + body = new CatchReturnNode(context, sourceSection, body, environment.getReturnID()); + body = new CatchNextNode(context, sourceSection, body); + } + + final RubyRootNode pristineRootNode = new RubyRootNode(sourceSection, methodName, body); + + final CallTarget callTarget = Truffle.getRuntime().createCallTarget(NodeUtil.cloneNode(pristineRootNode), environment.getFrameDescriptor()); + + if (isBlock) { + return new BlockDefinitionNode(context, sourceSection, methodName, environment.getUniqueMethodIdentifier(), environment.getFrameDescriptor(), environment.needsDeclarationFrame(), + pristineRootNode, callTarget); + } else { + return new MethodDefinitionNode(context, sourceSection, methodName, environment.getUniqueMethodIdentifier(), environment.getFrameDescriptor(), environment.needsDeclarationFrame(), + pristineRootNode, callTarget); + } + } + + private RubyNode loadArgumentsIntoLocals(Arity arity, RubyNode body) { + final SourceSection sourceSection = body.getEncapsulatingSourceSection(); + + final List<RubyNode> loadIndividualArgumentsNodes = new ArrayList<>(); + + if (!isBlock) { + loadIndividualArgumentsNodes.add(new CheckArityNode(context, sourceSection, arity)); + } + + final int preCount = environment.getPreParameters().size(); + final int postCount = environment.getPostParameters().size(); + + for (int n = 0; n < environment.getPreParameters().size(); n++) { + final FrameSlot param = environment.getPreParameters().get(n); + + // ReadPre reads from the start of the arguments array + + final ReadPreArgumentNode readArgumentNode = new ReadPreArgumentNode(context, sourceSection, n, false); + + final WriteLocalVariableNode writeLocal = WriteLocalVariableNodeFactory.create(context, sourceSection, param, readArgumentNode); + + loadIndividualArgumentsNodes.add(writeLocal); + } + + for (int n = 0; n < environment.getOptionalParameters().size(); n++) { + final FrameSlot param = environment.getOptionalParameters().get(n); + final RubyNode defaultValue = environment.getOptionalParametersDefaultValues().get(param); + + /* + * ReadOptional reads from the start of the arguments array, as long as it is long + * enough, else uses the default value (which may use locals with arguments just loaded, + * either from pre or preceding optionals). + */ + + final ReadOptionalArgumentNode readArgumentNode = new ReadOptionalArgumentNode(context, body.getEncapsulatingSourceSection(), preCount + n, preCount + postCount + n + 1, + (RubyNode) defaultValue.copy()); + + final WriteLocalVariableNode writeLocal = WriteLocalVariableNodeFactory.create(context, sourceSection, param, readArgumentNode); + + loadIndividualArgumentsNodes.add(writeLocal); + } + + for (int n = 0; n < environment.getPostParameters().size(); n++) { + final FrameSlot param = environment.getPostParameters().get(n); + + // ReadPost reads from the end of the arguments array + + final ReadPostArgumentNode readArgumentNode = new ReadPostArgumentNode(context, sourceSection, postCount - n - 1); + + final WriteLocalVariableNode writeLocal = WriteLocalVariableNodeFactory.create(context, sourceSection, param, readArgumentNode); + + loadIndividualArgumentsNodes.add(writeLocal); + } + + if (environment.getRestParameter() != null) { + /* + * TODO(cs): this assumes there are no optionals and therefore also no posts, which may + * not be a valid assumption. + */ + + if (postCount != 0) { + context.implementationMessage("post arguments as well as a rest argument - they will conflict"); + } + + final ReadRestArgumentNode readArgumentNode = new ReadRestArgumentNode(context, sourceSection, preCount); + + final WriteLocalVariableNode writeLocal = WriteLocalVariableNodeFactory.create(context, sourceSection, environment.getRestParameter(), readArgumentNode); + + loadIndividualArgumentsNodes.add(writeLocal); + } + + if (environment.getBlockParameter() != null) { + final FrameSlot param = environment.getBlockParameter(); + + final ReadBlockArgumentNode readArgumentNode = new ReadBlockArgumentNode(context, sourceSection, false); + + final WriteLocalVariableNode writeLocal = WriteLocalVariableNodeFactory.create(context, sourceSection, param, readArgumentNode); + + loadIndividualArgumentsNodes.add(writeLocal); + } + + final RubyNode loadIndividualArguments = new SequenceNode(context, sourceSection, loadIndividualArgumentsNodes.toArray(new RubyNode[loadIndividualArgumentsNodes.size()])); + + final RubyNode noSwitch = new SequenceNode(context, body.getSourceSection(), loadIndividualArguments, body); + + if (!isBlock) { + return noSwitch; + } + + /* + * See the test testBlockArgumentsDestructure for a motivation for this. See + * BlockDestructureSwitchNode for how it works. + */ + + if (preCount + postCount == 1 && environment.getOptionalParameters().size() == 0) { + return noSwitch; + } + + final List<RubyNode> destructureLoadArgumentsNodes = new ArrayList<>(); + + for (int n = 0; n < environment.getPreParameters().size(); n++) { + final FrameSlot param = environment.getPreParameters().get(n); + + final ReadDestructureArgumentNode readArgumentNode = new ReadDestructureArgumentNode(context, sourceSection, n); + + final WriteLocalVariableNode writeLocal = WriteLocalVariableNodeFactory.create(context, sourceSection, param, readArgumentNode); + + destructureLoadArgumentsNodes.add(writeLocal); + } + + final RubyNode destructureLoadArguments = new SequenceNode(context, body.getSourceSection(), destructureLoadArgumentsNodes.toArray(new RubyNode[destructureLoadArgumentsNodes.size()])); + + return new BlockDestructureSwitchNode(context, body.getEncapsulatingSourceSection(), loadIndividualArguments, destructureLoadArguments, body); + + } + + private Arity findParameters(org.jrubyparser.ast.ArgsNode args) { + if (args == null) { + return Arity.NO_ARGS; + } + + final SourceSection sourceSection = translate(args.getPosition()); + + if (args.getPre() != null) { + for (org.jrubyparser.ast.Node arg : args.getPre().childNodes()) { + if (arg instanceof org.jrubyparser.ast.ArgumentNode) { + final org.jrubyparser.ast.ArgumentNode argNode = (org.jrubyparser.ast.ArgumentNode) arg; + environment.getPreParameters().add(environment.declareVar(argNode.getName())); + } else if (arg instanceof org.jrubyparser.ast.MultipleAsgnNode) { + /* + * TODO(cs): I don't know how to handle this yet, so I just do my best to get + * the names out and define them so the rest of the parser succeeds. + */ + + context.implementationMessage("only extracting names from multiple assignment in arguments"); + + final org.jrubyparser.ast.MultipleAsgnNode multAsgn = (org.jrubyparser.ast.MultipleAsgnNode) arg; + + final List<String> names = new ArrayList<>(); + getNamesFromMultipleAssignment(multAsgn, names); + + for (String name : names) { + environment.getPreParameters().add(environment.declareVar(name)); + } + } + } + } + + // The JRuby parser expresses optional arguments as a block of local assignments + + /* + * Note that default values for optional params can refer to the actual value of previous + * args, so be careful with the order of args here and in loadArgumentsIntoLocals. + */ + + if (args.getOptional() != null) { + for (org.jrubyparser.ast.Node arg : args.getOptional().childNodes()) { + final org.jrubyparser.ast.OptArgNode optArgNode = (org.jrubyparser.ast.OptArgNode) arg; + + String name; + org.jrubyparser.ast.Node valueNode; + + if (optArgNode.getValue() instanceof org.jrubyparser.ast.LocalAsgnNode) { + final org.jrubyparser.ast.LocalAsgnNode optLocalAsgn = (org.jrubyparser.ast.LocalAsgnNode) optArgNode.getValue(); + name = optLocalAsgn.getName(); + valueNode = optLocalAsgn.getValue(); + } else if (optArgNode.getValue() instanceof org.jrubyparser.ast.DAsgnNode) { + final org.jrubyparser.ast.DAsgnNode optLocalAsgn = (org.jrubyparser.ast.DAsgnNode) optArgNode.getValue(); + name = optLocalAsgn.getName(); + valueNode = optLocalAsgn.getValue(); + } else { + throw new UnsupportedOperationException(optArgNode.getValue().getClass().getName()); + } + + RubyNode paramDefaultValue; + + if (valueNode == null) { + paramDefaultValue = new NilNode(context, sourceSection); + } else { + paramDefaultValue = (RubyNode) valueNode.accept(this); + } + + final FrameSlot frameSlot = environment.declareVar(name); + environment.getOptionalParameters().add(frameSlot); + environment.getOptionalParametersDefaultValues().put(frameSlot, paramDefaultValue); + } + } + + if (args.getPost() != null) { + for (org.jrubyparser.ast.Node arg : args.getPost().childNodes()) { + final org.jrubyparser.ast.ArgumentNode argNode = (org.jrubyparser.ast.ArgumentNode) arg; + environment.getPostParameters().add(environment.declareVar(argNode.getName())); + } + } + + if (args.getRest() != null) { + final org.jrubyparser.ast.RestArgNode rest = (org.jrubyparser.ast.RestArgNode) args.getRest(); + environment.setRestParameter(environment.declareVar(rest.getName())); + } + + if (args.getBlock() != null) { + final org.jrubyparser.ast.BlockArgNode blockArgNode = args.getBlock(); + final FrameSlot frameSlot = environment.declareVar(blockArgNode.getName()); + environment.setBlockParameter(frameSlot); + } + + final int minimum = environment.getPreParameters().size() + environment.getPostParameters().size(); + + int maximum = minimum + environment.getOptionalParameters().size(); + + if (args.getRest() != null) { + maximum = Arity.NO_MAXIMUM; + } + + return new Arity(minimum, maximum); + } + + private void getNamesFromMultipleAssignment(org.jrubyparser.ast.MultipleAsgnNode multAsgn, List<String> names) { + for (org.jrubyparser.ast.Node a : multAsgn.getPre().childNodes()) { + if (a instanceof org.jrubyparser.ast.DAsgnNode) { + names.add(((org.jrubyparser.ast.DAsgnNode) a).getName()); + } else if (a instanceof org.jrubyparser.ast.MultipleAsgnNode) { + getNamesFromMultipleAssignment((org.jrubyparser.ast.MultipleAsgnNode) a, names); + } else if (a instanceof org.jrubyparser.ast.LocalAsgnNode) { + names.add(((org.jrubyparser.ast.LocalAsgnNode) a).getName()); + } else { + throw new RuntimeException(a.getClass().getName()); + } + } + } + + @Override + public Object visitSuperNode(org.jrubyparser.ast.SuperNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + final ArgumentsAndBlockTranslation argumentsAndBlock = translateArgumentsAndBlock(sourceSection, node.getIter(), node.getArgs(), null); + + final String name = environment.getMethodName(); + + return new GeneralSuperCallNode(context, sourceSection, name, argumentsAndBlock.getBlock(), argumentsAndBlock.getArguments(), argumentsAndBlock.isSplatted()); + } + + @Override + public Object visitZSuperNode(org.jrubyparser.ast.ZSuperNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + final ArgumentsAndBlockTranslation argumentsAndBlock = translateArgumentsAndBlock(sourceSection, node.getIter(), null, null); + + final String name = environment.getMethodName(); + + return new GeneralSuperCallNode(context, sourceSection, name, argumentsAndBlock.getBlock(), argumentsAndBlock.getArguments(), argumentsAndBlock.isSplatted()); + } + + @Override + protected FlipFlopStateNode createFlipFlopState(SourceSection sourceSection, int depth) { + if (isBlock) { + environment.setNeedsDeclarationFrame(); + return parent.createFlipFlopState(sourceSection, depth + 1); + } else { + return super.createFlipFlopState(sourceSection, depth); + } + } + +}