2 ***** BEGIN LICENSE BLOCK *****
3 * Version: CPL 1.0/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Common Public
6 * License Version 1.0 (the "License"); you may not use this file
7 * except in compliance with the License. You may obtain a copy of
8 * the License at http://www.eclipse.org/legal/cpl-v10.html
10 * Software distributed under the License is distributed on an "AS
11 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
12 * implied. See the License for the specific language governing
13 * rights and limitations under the License.
15 * Copyright (C) 2006-2007 Thomas E Enebo <enebo@acm.org>
17 * Alternatively, the contents of this file may be used under the terms of
18 * either of the GNU General Public License Version 2 or later (the "GPL"),
19 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
20 * in which case the provisions of the GPL or the LGPL are applicable instead
21 * of those above. If you wish to allow use of your version of this file only
22 * under the terms of either the GPL or the LGPL, and not to allow others to
23 * use your version of this file under the terms of the CPL, indicate your
24 * decision by deleting the provisions above and replace them with the notice
25 * and other provisions required by the GPL or the LGPL. If you do not delete
26 * the provisions above, a recipient may use your version of this file under
27 * the terms of any one of the CPL, the GPL or the LGPL.
28 ***** END LICENSE BLOCK *****/
29 package org
.jruby
.ast
;
31 import java
.util
.List
;
33 import org
.jruby
.Ruby
;
34 import org
.jruby
.RubyArray
;
35 import org
.jruby
.RubyClass
;
36 import org
.jruby
.ast
.types
.INameNode
;
37 import org
.jruby
.ast
.visitor
.NodeVisitor
;
38 import org
.jruby
.evaluator
.ASTInterpreter
;
39 import org
.jruby
.evaluator
.Instruction
;
40 import org
.jruby
.exceptions
.JumpException
;
41 import org
.jruby
.internal
.runtime
.methods
.DynamicMethod
;
42 import org
.jruby
.javasupport
.util
.RuntimeHelpers
;
43 import org
.jruby
.lexer
.yacc
.ISourcePosition
;
44 import org
.jruby
.runtime
.Block
;
45 import org
.jruby
.runtime
.CallSite
;
46 import org
.jruby
.runtime
.CallType
;
47 import org
.jruby
.runtime
.MethodIndex
;
48 import org
.jruby
.runtime
.ThreadContext
;
49 import org
.jruby
.runtime
.Visibility
;
50 import org
.jruby
.runtime
.builtin
.IRubyObject
;
53 * Node that represents an assignment of either an array element or attribute.
55 public class AttrAssignNode
extends Node
implements INameNode
, IArgumentNode
{
56 protected final Node receiverNode
;
58 private Node argsNode
;
59 public CallSite variableCallAdapter
;
60 public CallSite normalCallAdapter
;
62 public AttrAssignNode(ISourcePosition position
, Node receiverNode
, String name
, Node argsNode
) {
63 super(position
, NodeType
.ATTRASSIGNNODE
);
65 assert receiverNode
!= null : "receiverNode is not null";
66 // TODO: At least ParserSupport.attrset passes argsNode as null. ImplicitNil is wrong magic for
67 // setupArgs since it will IRubyObject[] { nil }. So we need to figure out a nice fast
68 // null pattern for setupArgs.
69 // assert argsNode != null : "receiverNode is not null";
71 this.receiverNode
= receiverNode
;
73 setArgsInternal(argsNode
);
74 this.normalCallAdapter
= MethodIndex
.getCallSite(name
);
75 this.variableCallAdapter
= MethodIndex
.getVariableCallSite(name
);
79 * Accept for the visitor pattern.
80 * @param visitor the visitor
82 public Instruction
accept(NodeVisitor visitor
) {
83 return visitor
.visitAttrAssignNode(this);
88 * name is the name of the method called
91 public String
getName() {
96 * Gets the receiverNode.
97 * receiverNode is the object on which the method is being called
98 * @return receiverNode
100 public Node
getReceiverNode() {
106 * argsNode representing the method's arguments' value for this call.
109 public Node
getArgsNode() {
114 protected Node
newAttrAssignNode(ArrayNode argsNode
) {
115 int size
= argsNode
.size();
119 return new AttrAssignOneArgNode(getPosition(), receiverNode
, name
, argsNode
);
121 return new AttrAssignTwoArgNode(getPosition(), receiverNode
, name
, argsNode
);
123 return new AttrAssignThreeArgNode(getPosition(), receiverNode
, name
, argsNode
);
125 return new AttrAssignNode(getPosition(), receiverNode
, name
, argsNode
);
129 protected Node
newMutatedAttrAssignNode(ArrayNode argsNode
) {
130 int size
= argsNode
.size();
134 if (!(this instanceof AttrAssignOneArgNode
)) {
135 return new AttrAssignOneArgNode(getPosition(), receiverNode
, name
, argsNode
);
140 if (!(this instanceof AttrAssignTwoArgNode
)) {
141 return new AttrAssignTwoArgNode(getPosition(), receiverNode
, name
, argsNode
);
146 if (!(this instanceof AttrAssignThreeArgNode
)) {
147 return new AttrAssignThreeArgNode(getPosition(), receiverNode
, name
, argsNode
);
152 return new AttrAssignNode(getPosition(), receiverNode
, name
, argsNode
);
159 * @param argsNode set the arguments for this node.
161 public Node
setArgsNode(Node argsNode
) {
162 // Empirical Observations:
163 // null -> Some arity
164 // argsNode == this.argsNode then check for arity changes
165 // newline(splatnode) -> argspushnode
166 if (this.argsNode
== null && argsNode
instanceof ArrayNode
) {
167 return newAttrAssignNode((ArrayNode
) argsNode
);
168 } else if (this.argsNode
== argsNode
) {
169 return newMutatedAttrAssignNode((ArrayNode
)argsNode
);
172 setArgsInternal(argsNode
);
177 private void setArgsInternal(Node argsNode
) {
178 this.argsNode
= argsNode
;
180 if (argsNode
instanceof ArrayNode
) ((ArrayNode
)argsNode
).setLightweight(true);
183 public List
<Node
> childNodes() {
184 return Node
.createList(receiverNode
, argsNode
);
188 public IRubyObject
interpret(Ruby runtime
, ThreadContext context
, IRubyObject self
, Block aBlock
) {
189 IRubyObject receiver
= receiverNode
.interpret(runtime
, context
, self
, aBlock
);
190 IRubyObject
[] args
= ASTInterpreter
.setupArgs(runtime
, context
, argsNode
, self
, aBlock
);
192 assert receiver
.getMetaClass() != null : receiver
.getClass().getName();
194 // If reciever is self then we do the call the same way as vcall
195 if (receiver
== self
) {
196 variableCallAdapter
.call(context
, receiver
, args
);
198 normalCallAdapter
.call(context
, receiver
, args
);
201 return args
[args
.length
- 1];
205 public IRubyObject
assign(Ruby runtime
, ThreadContext context
, IRubyObject self
, IRubyObject value
, Block block
, boolean checkArity
) {
206 IRubyObject receiver
= receiverNode
.interpret(runtime
, context
, self
, block
);
208 // If reciever is self then we do the call the same way as vcall
209 if (receiver
== self
) {
210 return selfAssign(runtime
, context
, self
, value
, block
, checkArity
);
212 return otherAssign(runtime
, context
, self
, value
, block
, checkArity
);
216 private IRubyObject
selfAssign(Ruby runtime
, ThreadContext context
, IRubyObject self
, IRubyObject value
, Block block
, boolean checkArity
) {
217 IRubyObject receiver
= receiverNode
.interpret(runtime
, context
, self
, block
);
219 if (argsNode
== null) { // attribute set.
220 RuntimeHelpers
.invoke(context
, receiver
, name
, value
, Block
.NULL_BLOCK
);
221 } else { // element set
222 RubyArray args
= (RubyArray
) argsNode
.interpret(runtime
, context
, self
, block
);
224 RuntimeHelpers
.invoke(context
, receiver
, name
, args
.toJavaArray(), Block
.NULL_BLOCK
);
227 return runtime
.getNil();
230 private IRubyObject
otherAssign(Ruby runtime
, ThreadContext context
, IRubyObject self
, IRubyObject value
, Block block
, boolean checkArity
) {
231 IRubyObject receiver
= receiverNode
.interpret(runtime
, context
, self
, block
);
233 if (argsNode
== null) { // attribute set.
234 RuntimeHelpers
.invoke(context
, receiver
, name
, value
, CallType
.NORMAL
, Block
.NULL_BLOCK
);
235 } else { // element set
236 RubyArray args
= (RubyArray
) argsNode
.interpret(runtime
, context
, self
, block
);
238 RuntimeHelpers
.invoke(context
, receiver
, name
, args
.toJavaArray(), CallType
.NORMAL
, Block
.NULL_BLOCK
);
241 return runtime
.getNil();
245 public String
definition(Ruby runtime
, ThreadContext context
, IRubyObject self
, Block aBlock
) {
246 if (receiverNode
.definition(runtime
, context
, self
, aBlock
) != null) {
248 IRubyObject receiver
= receiverNode
.interpret(runtime
, context
, self
, aBlock
);
249 RubyClass metaClass
= receiver
.getMetaClass();
250 DynamicMethod method
= metaClass
.searchMethod(name
);
251 Visibility visibility
= method
.getVisibility();
253 if (visibility
!= Visibility
.PRIVATE
&&
254 (visibility
!= Visibility
.PROTECTED
|| metaClass
.getRealClass().isInstance(self
))) {
255 if (metaClass
.isMethodBound(name
, false)) {
256 return ASTInterpreter
.getArgumentDefinition(runtime
, context
, argsNode
, "assignment", self
, aBlock
);
259 } catch (JumpException e
) {