Skip to content
Permalink
Browse files
[Truffle] Make $_ thread local and use it in implicit matches.
  • Loading branch information
chrisseaton committed Dec 5, 2014
1 parent d7aac8c commit 8e54c7d5c9e0ba7b9daea741c19b86579aebc047
@@ -0,0 +1,32 @@
/*
* Copyright (c) 2014 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 org.jruby.truffle.nodes;

import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.RubyBasicObject;

public class ThreadLocalObjectNode extends RubyNode {

public ThreadLocalObjectNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@Override
public RubyBasicObject executeRubyBasicObject(VirtualFrame frame) {
return getContext().getThreadManager().getCurrentThread().getThreadLocals();
}

@Override
public Object execute(VirtualFrame frame) {
return executeRubyBasicObject(frame);
}
}
@@ -9,8 +9,11 @@
*/
package org.jruby.truffle.runtime.core;

import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.*;

import org.jcodings.util.Hash;
import org.jruby.RubyThread.Status;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.runtime.RubyContext;
@@ -64,9 +67,12 @@ public RubyBasicObject newInstance(RubyNode currentNode) {
private RubyException exception;
private Object value;

private RubyBasicObject threadLocals;

public RubyThread(RubyClass rubyClass, ThreadManager manager) {
super(rubyClass);
this.manager = manager;
threadLocals = new RubyBasicObject(rubyClass.getContext().getCoreLibrary().getObjectClass());
}

public void initialize(RubyContext context, RubyNode currentNode, RubyProc block) {
@@ -139,4 +145,8 @@ public Status getStatus() {
return status;
}

public RubyBasicObject getThreadLocals() {
return threadLocals;
}

}
@@ -78,10 +78,8 @@ public class BodyTranslator extends Translator {
debugIgnoredCalls.add("upto");
}

/**
* Global variables which in common usage have frame local semantics.
*/
public static final Set<String> FRAME_LOCAL_GLOBAL_VARIABLES = new HashSet<>(Arrays.asList("$_", "$~", "$+", "$&", "$`", "$'", "$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9"));
public static final Set<String> FRAME_LOCAL_GLOBAL_VARIABLES = new HashSet<>(Arrays.asList("$~", "$+", "$&", "$`", "$'", "$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9"));
public static final Set<String> THREAD_LOCAL_GLOBAL_VARIABLES = new HashSet<>(Arrays.asList("$_"));

public BodyTranslator(RubyNode currentNode, RubyContext context, BodyTranslator parent, TranslatorEnvironment environment, Source source) {
super(currentNode, context, source);
@@ -1031,6 +1029,36 @@ public RubyNode visitGlobalAsgnNode(org.jruby.ast.GlobalAsgnNode node) {

if (readOnlyGlobalVariables.contains(name)) {
return new WriteReadOnlyGlobalNode(context, sourceSection, name, rhs);
} else if (THREAD_LOCAL_GLOBAL_VARIABLES.contains(name)) {
final ThreadLocalObjectNode threadLocalVariablesObjectNode = new ThreadLocalObjectNode(context, sourceSection);
return new WriteInstanceVariableNode(context, sourceSection, name, threadLocalVariablesObjectNode, rhs, true);
} else if (FRAME_LOCAL_GLOBAL_VARIABLES.contains(name)) {
if (environment.getNeverAssignInParentScope()) {
environment.declareVar(node.getName());
}

RubyNode localVarNode = environment.findLocalVarNode(node.getName(), sourceSection);

if (localVarNode == null) {
if (environment.hasOwnScopeForAssignments()) {
environment.declareVar(node.getName());
}

TranslatorEnvironment environmentToDeclareIn = environment;

while (!environmentToDeclareIn.hasOwnScopeForAssignments()) {
environmentToDeclareIn = environmentToDeclareIn.getParent();
}

environmentToDeclareIn.declareVar(node.getName());
localVarNode = environment.findLocalVarNode(node.getName(), sourceSection);

if (localVarNode == null) {
throw new RuntimeException("shoudln't be here");
}
}

return ((ReadNode) localVarNode).makeWriteNode(rhs);
} else {
if (name.equals("$~")) {
rhs = new CheckMatchVariableTypeNode(context, sourceSection, rhs);
@@ -1054,9 +1082,11 @@ public RubyNode visitGlobalVarNode(org.jruby.ast.GlobalVarNode node) {
final RubyNode readNode = environment.findLocalVarNode(name, sourceSection);

return readNode;
} else if (THREAD_LOCAL_GLOBAL_VARIABLES.contains(name)) {
final ThreadLocalObjectNode threadLocalVariablesObjectNode = new ThreadLocalObjectNode(context, sourceSection);
return new ReadInstanceVariableNode(context, sourceSection, name, threadLocalVariablesObjectNode, true);
} else {
final ObjectLiteralNode globalVariablesObjectNode = new ObjectLiteralNode(context, sourceSection, context.getCoreLibrary().getGlobalVariablesObject());

return new ReadInstanceVariableNode(context, sourceSection, name, globalVariablesObjectNode, true);
}
}
@@ -60,3 +60,4 @@ fails:Predefined global $-0 does not call #to_str to convert the object to a Str
fails:Predefined global $-0 raises a TypeError if assigned a Fixnum
fails:Predefined global $-0 raises a TypeError if assigned a boolean
fails:Global variable $0 raises a TypeError when not given an object that can be coerced to a String
fails:Predefined global $~ raises an error if assigned an object not nil or instanceof MatchData
@@ -1,2 +1 @@
fails:Literal Regexps matches against $_ (last input) in a conditional if no explicit matchee provided
fails:Literal Regexps allows unescaped / to be used with %r

0 comments on commit 8e54c7d

Please sign in to comment.