diff --git a/src/org/cx4a/rsense/ruby/Context.java b/src/org/cx4a/rsense/ruby/Context.java index 842a063..45f8c19 100644 --- a/src/org/cx4a/rsense/ruby/Context.java +++ b/src/org/cx4a/rsense/ruby/Context.java @@ -49,6 +49,10 @@ public Scope getCurrentScope() { return scopes.peek(); } + public IRubyObject getConstant(String name) { + return getCurrentScope().getModule().getConstant(name); + } + public Frame getCurrentFrame() { return frame; } @@ -68,8 +72,4 @@ public Block getFrameBlock() { public Visibility getFrameVisibility() { return getCurrentFrame().getVisibility(); } - - public IRubyObject getConstant(String name) { - return getFrameModule().getConstant(name); - } } diff --git a/src/org/cx4a/rsense/typing/Graph.java b/src/org/cx4a/rsense/typing/Graph.java index 9e195a1..c20853b 100644 --- a/src/org/cx4a/rsense/typing/Graph.java +++ b/src/org/cx4a/rsense/typing/Graph.java @@ -11,6 +11,7 @@ import java.util.HashMap; import java.util.Queue; import java.util.Collections; +import java.util.Arrays; import org.jruby.ast.AliasNode; import org.jruby.ast.AndNode; @@ -571,10 +572,33 @@ public Object visitArgsNode(ArgsNode node) { } public Object visitArgsCatNode(ArgsCatNode node) { - // FIXME - Logger.fixme("argscat node is not implemented yet."); - return NULL_VERTEX; - //throw new UnsupportedOperationException(); + Vertex vertex = createEmptyVertex(node); + Vertex first = createVertex(node.getFirstNode()); + SplatVertex second = new SplatVertex(node, createVertex(node.getSecondNode())); + RuntimeHelper.splatValue(this, second); + for (IRubyObject a : first.getTypeSet()) { + List elements = new ArrayList(); + if (a instanceof Array) { + Array array = (Array) a; + if (array.getElements() != null) { + elements.addAll(Arrays.asList(array.getElements())); + } + } else { + elements.add(createFreeSingleTypeVertex(a)); + } + for (IRubyObject b : second.getTypeSet()) { + if (b instanceof Array) { + Array array = (Array) b; + if (array.getElements() != null) { + elements.addAll(Arrays.asList(array.getElements())); + } + } else { + elements.add(createFreeSingleTypeVertex(a)); + } + } + vertex.addType(RuntimeHelper.createArray(this, elements.toArray(new Vertex[0]))); + } + return vertex; } public Object visitArgsPushNode(ArgsPushNode node) { @@ -657,24 +681,8 @@ public Object visitClassVarNode(ClassVarNode node) { public Object visitCallNode(CallNode node) { Vertex receiverVertex = createVertex(node.getReceiverNode()); - Vertex[] argVertices = null; - if (node.getArgsNode() != null) { - List argNodes = node.getArgsNode().childNodes(); - argVertices = new Vertex[argNodes.size()]; - for (int i = 0; i < argVertices.length; i++) { - argVertices[i] = createVertex(argNodes.get(i)); - } - } - - Block block = null; - if (node.getIterNode() instanceof IterNode) { - IterNode iterNode = (IterNode) node.getIterNode(); - DynamicScope scope = new DynamicScope(context.getCurrentScope().getModule(), context.getCurrentScope()); - block = new Block(iterNode.getVarNode(), iterNode.getBodyNode(), context.getCurrentFrame(), scope); - } else if (node.getIterNode() != null) { - // FIXME - Logger.debug("unknnown iternode: %s", node.getIterNode()); - } + Vertex[] argVertices = RuntimeHelper.setupCallArgs(this, node.getArgsNode()); + Block block = RuntimeHelper.setupCallBlock(this, node.getIterNode()); CallVertex vertex = new CallVertex(node, receiverVertex, argVertices, block); return RuntimeHelper.call(this, vertex); } @@ -887,28 +895,8 @@ public Object visitEvStrNode(EvStrNode node) { } public Object visitFCallNode(FCallNode node) { - Vertex[] argVertices = null; - if (node.getArgsNode() != null) { - List argNodes = node.getArgsNode().childNodes(); - argVertices = new Vertex[argNodes.size()]; - for (int i = 0; i < argVertices.length; i++) { - argVertices[i] = createVertex(argNodes.get(i)); - } - } - - Block block = null; - if (node.getIterNode() != null) { - switch (node.getIterNode().getNodeType()) { - case ITERNODE: { - IterNode iterNode = (IterNode) node.getIterNode(); - block = new Block(iterNode.getVarNode(), iterNode.getBodyNode(), context.getCurrentFrame(), context.getCurrentScope()); - break; - } - case BLOCKPASSNODE: - block = context.getFrameBlock(); - break; - } - } + Vertex[] argVertices = RuntimeHelper.setupCallArgs(this, node.getArgsNode()); + Block block = RuntimeHelper.setupCallBlock(this, node.getIterNode()); CallVertex vertex = new CallVertex(node, createFreeSingleTypeVertex(context.getFrameSelf()), argVertices, block); return RuntimeHelper.call(this, vertex); } diff --git a/src/org/cx4a/rsense/typing/runtime/RuntimeHelper.java b/src/org/cx4a/rsense/typing/runtime/RuntimeHelper.java index 85a0a02..d01133a 100644 --- a/src/org/cx4a/rsense/typing/runtime/RuntimeHelper.java +++ b/src/org/cx4a/rsense/typing/runtime/RuntimeHelper.java @@ -30,6 +30,7 @@ import org.jruby.ast.Colon2ImplicitNode; import org.jruby.ast.Colon3Node; import org.jruby.ast.AssignableNode; +import org.jruby.ast.IterNode; import org.jruby.ast.YieldNode; import org.jruby.ast.ZeroArgNode; import org.jruby.ast.MethodDefNode; @@ -411,6 +412,53 @@ public static void multipleAssign(Graph graph, MultipleAsgnNode node, Array arra } } + public static Vertex[] setupCallArgs(Graph graph, Node argsNode) { + List args = new ArrayList(); + if (argsNode != null) { + switch (argsNode.getNodeType()) { + case ARGSCATNODE: + case SPLATNODE: { + Vertex arrayVertex = graph.createVertex(argsNode); + for (IRubyObject object : arrayVertex.getTypeSet()) { + if (object instanceof Array) { + Array array = (Array) object; + if (array.getElements() != null) { + for (Vertex element : array.getElements()) { + args.add(element); + } + } + } + } + break; + } + default: + for (Node arg : argsNode.childNodes()) { + args.add(graph.createVertex(arg)); + } + } + } + return args.isEmpty() ? null : args.toArray(new Vertex[0]); + } + + public static Block setupCallBlock(Graph graph, Node iterNode) { + Context context = graph.getRuntime().getContext(); + Block block = null; + if (iterNode != null) { + switch (iterNode.getNodeType()) { + case ITERNODE: { + IterNode inode = (IterNode) iterNode; + DynamicScope scope = new DynamicScope(context.getCurrentScope().getModule(), context.getCurrentScope()); + block = new Block(inode.getVarNode(), inode.getBodyNode(), context.getCurrentFrame(), scope); + break; + } + case BLOCKPASSNODE: + block = context.getFrameBlock(); + break; + } + } + return block; + } + public static Vertex call(Graph graph, CallVertex vertex) { return call(graph, vertex, false); } diff --git a/src/org/cx4a/rsense/typing/vertex/SplatVertex.java b/src/org/cx4a/rsense/typing/vertex/SplatVertex.java index 2ceabc3..b32ce2d 100644 --- a/src/org/cx4a/rsense/typing/vertex/SplatVertex.java +++ b/src/org/cx4a/rsense/typing/vertex/SplatVertex.java @@ -1,13 +1,13 @@ package org.cx4a.rsense.typing.vertex; -import org.jruby.ast.SplatNode; +import org.jruby.ast.Node; import org.cx4a.rsense.typing.Propagation; public class SplatVertex extends Vertex { private Vertex valueVertex; - public SplatVertex(SplatNode node, Vertex valueVertex) { + public SplatVertex(Node node, Vertex valueVertex) { super(node); this.valueVertex = valueVertex; valueVertex.addEdge(this); diff --git a/test/script/builtin.rsense b/test/script/builtin.rsense index 93d8660..174f9e6 100644 --- a/test/script/builtin.rsense +++ b/test/script/builtin.rsense @@ -6,6 +6,48 @@ type-inference --test=String --should-be=String ''_|_ EOF +type-inference --test=Constant1 --should-be=Fixnum +C = 1 +C_|_ +EOF + +type-inference --test=Constant2 --should-be=Fixnum +module M + C = 1 +end +M::C_|_ +EOF + +type-inference --test=Constant3 --should-be=Fixnum +C = 1 +def f + C +end +f_|_ +EOF + +type-inference --test=Constant4 --should-be=Fixnum +class C + D = 1 + def f + D + end +end +C.new.f_|_ +EOF + +type-inference --test=Constant5 --should-be=Fixnum +class C + A = 1 +end +class D < C + def f + A + end +end +D.new.f_|_ +EOF + type-inference --test=Array --should-be=Array []_|_ EOF @@ -149,6 +191,29 @@ end f(1, 2)_|_ EOF +type-inference --test=Method.Arg.Splat.Pass1 --should-be=Float +def f(x, y) + y +end +f(*[1, 2.3])_|_ +EOF + +type-inference --test=Method.Arg.Splat.Pass2 --should-be=Fixnum +def f(x, y) + x +end +f(1, *[1, 2.3])_|_ +EOF + +type-inference --test=Method.Arg.Splat.Pass3 --should-be=Float +a = [2.3, ''] +b = [1] +def f(x, y, z) + y +end +f(b, *a)_|_ +EOF + type-inference --test=Method.Arg.OptionalAndSplat1 --should-be=Fixnum def f(x, y = 1, *z) y