Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 23568373b59c
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 7d22b206f6d7
Choose a head ref
  • 19 commits
  • 17 files changed
  • 1 contributor

Commits on Sep 23, 2016

  1. Remove premade field-based DynamicScope and generate instead.

    Instead of having hand-written DynamicScope subtypes that use
    fields instead of an array, this logic generates those subclasses
    as needed.
    
    Currently it only overrides the base getValue and setValue methods
    and DynamicScope now implements the other previously-abstract
    methods by calling those two. This is not optimal for generic
    access via those indexed methods, but the expectation is that JIT
    can just go after the fields directly. The additional overrides
    would likely be useful for the interpreter, though.
    
    See #4167.
    headius committed Sep 23, 2016
    Copy the full SHA
    c9370c2 View commit details
  2. Copy the full SHA
    2403780 View commit details
  3. Copy the full SHA
    6049fe1 View commit details

Commits on Sep 27, 2016

  1. Copy the full SHA
    838fd07 View commit details
  2. Copy the full SHA
    eb890dc View commit details

Commits on Sep 28, 2016

  1. Expand specialization of generated DynamicScope.

    * Get and set of depth zero, offset = 0..9
    * Flip override of sets to void versions
    * Add test for the key methods
    headius committed Sep 28, 2016
    Copy the full SHA
    8a7f32a View commit details
  2. Copy the full SHA
    2dffd52 View commit details
  3. Copy the full SHA
    496c133 View commit details
  4. Tweaks to DynScope generation.

    * Limit specialized subclasses to 50 wide.
    * Move to a generator class.
    * Cache construction logic to StaticScope.
    * Cache constructor in StaticScope.
    * Reduce duplication in code gen.
    * Move offset error to a shared method in generated classes.
    * Use a single OneShotClassLoader since we never dereference.
    * Misc perf, style tweaks.
    headius committed Sep 28, 2016
    Copy the full SHA
    ccf3743 View commit details
  5. Copy the full SHA
    f261580 View commit details
  6. Copy the full SHA
    60241ad View commit details

Commits on Sep 29, 2016

  1. Copy the full SHA
    8ce1238 View commit details
  2. Clean up set code a bit.

    headius committed Sep 29, 2016
    Copy the full SHA
    380e42a View commit details
  3. Copy the full SHA
    b561339 View commit details
  4. Copy the full SHA
    1b657df View commit details
  5. Copy the full SHA
    06a0d8b View commit details
  6. Update for jitescript 4.1.

    headius committed Sep 29, 2016
    Copy the full SHA
    1a7d10a View commit details
  7. Copy the full SHA
    3156979 View commit details

Commits on Sep 30, 2016

  1. Use jitescript 0.4.1.

    headius committed Sep 30, 2016
    Copy the full SHA
    7d22b20 View commit details
5 changes: 4 additions & 1 deletion core/pom.rb
Original file line number Diff line number Diff line change
@@ -79,6 +79,8 @@
jar 'org.slf4j:slf4j-api:1.7.12', :scope => 'provided', :optional => true
jar 'org.slf4j:slf4j-simple:1.7.12', :scope => 'test'

jar 'me.qmx.jitescript:jitescript:0.4.1', :exclusions => ['org.ow2.asm:asm-all']

plugin_management do
plugin( 'org.eclipse.m2e:lifecycle-mapping:1.0.0',
'lifecycleMappingMetadata' => {
@@ -231,7 +233,8 @@
'argLine' => '-Xmx${jruby.test.memory} -XX:MaxPermSize=${jruby.test.memory.permgen} -Dfile.encoding=UTF-8 -Djava.awt.headless=true',
'includes' => [ 'org/jruby/test/MainTestSuite.java',
'org/jruby/embed/**/*Test*.java',
'org/jruby/util/**/*Test*.java' ],
'org/jruby/util/**/*Test*.java',
'org/jruby/runtime/**/*Test*.java' ],
'additionalClasspathElements' => [ '${basedir}/src/test/ruby' ] )

build do
12 changes: 12 additions & 0 deletions core/pom.xml
Original file line number Diff line number Diff line change
@@ -276,6 +276,17 @@ DO NOT MODIFIY - GENERATED CODE
<version>1.7.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>me.qmx.jitescript</groupId>
<artifactId>jitescript</artifactId>
<version>0.4.1</version>
<exclusions>
<exclusion>
<artifactId>asm-all</artifactId>
<groupId>org.ow2.asm</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<defaultGoal>package</defaultGoal>
@@ -595,6 +606,7 @@ DO NOT MODIFIY - GENERATED CODE
<include>org/jruby/test/MainTestSuite.java</include>
<include>org/jruby/embed/**/*Test*.java</include>
<include>org/jruby/util/**/*Test*.java</include>
<include>org/jruby/runtime/**/*Test*.java</include>
</includes>
<additionalClasspathElements>
<additionalClasspathElement>${basedir}/src/test/ruby</additionalClasspathElement>
Original file line number Diff line number Diff line change
@@ -567,7 +567,7 @@ protected static void setResult(Object[] temp, DynamicScope currDynScope, Variab
temp[((TemporaryLocalVariable)resultVar).offset] = result;
} else {
LocalVariable lv = (LocalVariable)resultVar;
currDynScope.setValue((IRubyObject) result, lv.getLocation(), lv.getScopeDepth());
currDynScope.setValueVoid((IRubyObject) result, lv.getLocation(), lv.getScopeDepth());
}
}

129 changes: 39 additions & 90 deletions core/src/main/java/org/jruby/ir/targets/JVMVisitor.java
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@
import org.jruby.runtime.*;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.callsite.RefinedCachingCallSite;
import org.jruby.runtime.scope.DynamicScopeGenerator;
import org.jruby.util.ByteList;
import org.jruby.util.ClassDefiningClassLoader;
import org.jruby.util.JavaNameMangler;
@@ -392,41 +393,11 @@ private org.objectweb.asm.Label getJVMLabel(Label label) {

private void jvmStoreLocal(Variable variable) {
if (variable instanceof LocalVariable) {

LocalVariable localvariable = (LocalVariable) variable;

jvmLoadLocal(DYNAMIC_SCOPE);

jvmAdapter().swap();

if (localvariable.getScopeDepth() == 0) {

switch (localvariable.getOffset()) {
case 0:
jvmAdapter().invokevirtual(p(DynamicScope.class), "setValueZeroDepthZeroVoid", sig(void.class, IRubyObject.class));
break;
case 1:
jvmAdapter().invokevirtual(p(DynamicScope.class), "setValueOneDepthZeroVoid", sig(void.class, IRubyObject.class));
break;
case 2:
jvmAdapter().invokevirtual(p(DynamicScope.class), "setValueTwoDepthZeroVoid", sig(void.class, IRubyObject.class));
break;
case 3:
jvmAdapter().invokevirtual(p(DynamicScope.class), "setValueThreeDepthZeroVoid", sig(void.class, IRubyObject.class));
break;
default:
jvmAdapter().ldc(localvariable.getOffset());
jvmAdapter().invokevirtual(p(DynamicScope.class), "setValueDepthZeroVoid", sig(void.class, IRubyObject.class, int.class));
break;
}

} else {

jvmAdapter().ldc(localvariable.getOffset());
jvmAdapter().ldc(localvariable.getScopeDepth());
jvmAdapter().invokevirtual(p(DynamicScope.class), "setValueVoid", sig(void.class, IRubyObject.class, int.class, int.class));

}
genSetValue((LocalVariable) variable);
} else if (variable instanceof TemporaryLocalVariable) {
switch (((TemporaryLocalVariable)variable).getType()) {
case FLOAT: jvmAdapter().dstore(getJVMLocalVarIndex(variable)); break;
@@ -441,44 +412,11 @@ private void jvmStoreLocal(Variable variable) {

private void jvmStoreLocal(Runnable source, Variable variable) {
if (variable instanceof LocalVariable) {

LocalVariable localvariable = (LocalVariable) variable;

jvmLoadLocal(DYNAMIC_SCOPE);

if (localvariable.getScopeDepth() == 0) {

source.run();

switch (localvariable.getOffset()) {
case 0:
jvmAdapter().invokevirtual(p(DynamicScope.class), "setValueZeroDepthZeroVoid", sig(void.class, IRubyObject.class));
break;
case 1:
jvmAdapter().invokevirtual(p(DynamicScope.class), "setValueOneDepthZeroVoid", sig(void.class, IRubyObject.class));
break;
case 2:
jvmAdapter().invokevirtual(p(DynamicScope.class), "setValueTwoDepthZeroVoid", sig(void.class, IRubyObject.class));
break;
case 3:
jvmAdapter().invokevirtual(p(DynamicScope.class), "setValueThreeDepthZeroVoid", sig(void.class, IRubyObject.class));
break;
default:
jvmAdapter().ldc(localvariable.getOffset());
jvmAdapter().invokevirtual(p(DynamicScope.class), "setValueDepthZeroVoid", sig(void.class, IRubyObject.class, int.class));
break;
}

} else {

source.run();

jvmAdapter().ldc(localvariable.getOffset());
jvmAdapter().ldc(localvariable.getScopeDepth());

jvmAdapter().invokevirtual(p(DynamicScope.class), "setValueVoid", sig(void.class, IRubyObject.class, int.class, int.class));
source.run();

}
genSetValue((LocalVariable) variable);
} else if (variable instanceof TemporaryLocalVariable) {
source.run();

@@ -495,6 +433,27 @@ private void jvmStoreLocal(Runnable source, Variable variable) {
}
}

private void genSetValue(LocalVariable localvariable) {
int depth = localvariable.getScopeDepth();
int location = localvariable.getLocation();

String baseName = p(DynamicScope.class);

if (depth == 0) {
if (location < DynamicScopeGenerator.SPECIALIZED_SETS.size()) {
jvmAdapter().invokevirtual(baseName, DynamicScopeGenerator.SPECIALIZED_SETS.get(location), sig(void.class, IRubyObject.class));
} else {
jvmAdapter().pushInt(location);
jvmAdapter().invokevirtual(baseName, "setValueDepthZeroVoid", sig(void.class, IRubyObject.class, int.class));
}
} else {
jvmAdapter().pushInt(location);
jvmAdapter().pushInt(depth);

jvmAdapter().invokevirtual(baseName, "setValueVoid", sig(void.class, IRubyObject.class, int.class, int.class));
}
}

private void jvmStoreLocal(String specialVar) {
jvmMethod().storeLocal(getJVMLocalVarIndex(specialVar));
}
@@ -2363,33 +2322,23 @@ public void Hash(Hash hash) {
@Override
public void LocalVariable(LocalVariable localvariable) {
IRBytecodeAdapter m = jvmMethod();
jvmLoadLocal(DYNAMIC_SCOPE);

int depth = localvariable.getScopeDepth();
int location = localvariable.getLocation();
OUTER: switch (depth) {
case 0:
switch (location) {
case 0:
m.adapter.invokevirtual(p(DynamicScope.class), "getValueZeroDepthZero", sig(IRubyObject.class));
break OUTER;
case 1:
m.adapter.invokevirtual(p(DynamicScope.class), "getValueOneDepthZero", sig(IRubyObject.class));
break OUTER;
case 2:
m.adapter.invokevirtual(p(DynamicScope.class), "getValueTwoDepthZero", sig(IRubyObject.class));
break OUTER;
case 3:
m.adapter.invokevirtual(p(DynamicScope.class), "getValueThreeDepthZero", sig(IRubyObject.class));
break OUTER;
default:
m.adapter.pushInt(location);
m.adapter.invokevirtual(p(DynamicScope.class), "getValueDepthZero", sig(IRubyObject.class, int.class));
break OUTER;
}
default:

jvmLoadLocal(DYNAMIC_SCOPE);

if (depth == 0) {
if (location < DynamicScopeGenerator.SPECIALIZED_GETS.size()) {
m.adapter.invokevirtual(p(DynamicScope.class), DynamicScopeGenerator.SPECIALIZED_GETS.get(location), sig(IRubyObject.class));
} else {
m.adapter.pushInt(location);
m.adapter.pushInt(depth);
m.adapter.invokevirtual(p(DynamicScope.class), "getValue", sig(IRubyObject.class, int.class, int.class));
m.adapter.invokevirtual(p(DynamicScope.class), "getValueDepthZero", sig(IRubyObject.class, int.class));
}
} else {
m.adapter.pushInt(location);
m.adapter.pushInt(depth);
m.adapter.invokevirtual(p(DynamicScope.class), "getValue", sig(IRubyObject.class, int.class, int.class));
}
}

45 changes: 42 additions & 3 deletions core/src/main/java/org/jruby/parser/StaticScope.java
Original file line number Diff line number Diff line change
@@ -29,6 +29,8 @@
package org.jruby.parser;

import java.io.Serializable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.Arrays;

import org.jruby.RubyModule;
@@ -45,10 +47,12 @@
import org.jruby.ir.IRScopeType;
import org.jruby.lexer.yacc.ISourcePosition;
import org.jruby.runtime.DynamicScope;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.Signature;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.scope.DummyDynamicScope;
import org.jruby.runtime.scope.DynamicScopeGenerator;
import org.jruby.runtime.scope.ManyVarsDynamicScope;

/**
* StaticScope represents lexical scoping of variables and module/class constants.
@@ -62,7 +66,9 @@
*
*/
public class StaticScope implements Serializable {
private static final int MAX_SPECIALIZED_SIZE = 50;
private static final long serialVersionUID = 3423852552352498148L;
private static final MethodHandles.Lookup LOOKUP = MethodHandles.publicLookup();

// Next immediate scope. Variable and constant scoping rules make use of this variable
// in different ways.
@@ -77,6 +83,8 @@ public class StaticScope implements Serializable {
// Our name holder (offsets are assigned as variables are added)
private String[] variableNames;

private int variableNamesLength;

// A list of booleans indicating which variables are named captures from regexp
private boolean[] namedCaptures;

@@ -106,6 +114,8 @@ public class StaticScope implements Serializable {

private RubyModule overlayModule;

private MethodHandle constructor;

public enum Type {
LOCAL, BLOCK, EVAL;

@@ -148,6 +158,7 @@ protected StaticScope(Type type, StaticScope enclosingScope, String[] names, int

this.enclosingScope = enclosingScope;
this.variableNames = names;
this.variableNamesLength = names.length;
this.type = type;
this.irScope = null;
this.isBlockOrEval = (type != Type.LOCAL);
@@ -164,6 +175,32 @@ public int getFirstKeywordIndex() {
return firstKeywordIndex;
}

public DynamicScope construct(DynamicScope parent) {
MethodHandle constructor = this.constructor;

if (constructor == null) constructor = acquireConstructor();

try {
return (DynamicScope) constructor.invokeExact(this, parent);
} catch (Throwable e) {
Helpers.throwException(e);
return null; // not reached
}
}

private MethodHandle acquireConstructor() {
MethodHandle constructor;
int numberOfVariables = getNumberOfVariables();

if (numberOfVariables > MAX_SPECIALIZED_SIZE) {
constructor = ManyVarsDynamicScope.CONSTRUCTOR;
} else {
constructor = DynamicScopeGenerator.generate(numberOfVariables);
}
this.constructor = constructor;
return constructor;
}

public IRScope getIRScope() {
return irScope;
}
@@ -260,14 +297,15 @@ public String[] getVariables() {
}

public int getNumberOfVariables() {
return variableNames.length;
return variableNamesLength;
}

public void setVariables(String[] names) {
assert names != null : "names is not null";
assert namesAreInterned(names);

variableNames = new String[names.length];
variableNamesLength = names.length;
System.arraycopy(names, 0, variableNames, 0, names.length);
}

@@ -563,7 +601,7 @@ public void setSignature(Signature signature) {
}

public DynamicScope getDummyScope() {
return dummyScope == null ? dummyScope = new DummyDynamicScope(this) : dummyScope;
return dummyScope == null ? dummyScope = DynamicScope.newDynamicScope(this) : dummyScope;
}

public void setCommandArgumentStack(long commandArgumentStack) {
@@ -579,6 +617,7 @@ private void growVariableNames(String name) {
String[] newVariableNames = new String[variableNames.length + 1];
System.arraycopy(variableNames, 0, newVariableNames, 0, variableNames.length);
variableNames = newVariableNames;
variableNamesLength = newVariableNames.length;
variableNames[variableNames.length - 1] = name;
}

8 changes: 1 addition & 7 deletions core/src/main/java/org/jruby/runtime/Binding.java
Original file line number Diff line number Diff line change
@@ -34,12 +34,10 @@

import org.jruby.Ruby;
import org.jruby.RubyBasicObject;
import org.jruby.parser.StaticScopeFactory;
import org.jruby.runtime.backtrace.BacktraceElement;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.scope.ManyVarsDynamicScope;
import org.jruby.runtime.scope.NoVarsDynamicScope;

/**
* Internal live representation of a block ({...} or do ... end).
@@ -51,11 +49,7 @@ public class Binding {
RubyBasicObject.NEVER,
// Can't use Frame.DUMMY because of circular static init seeing it before it's assigned
new Frame(),
Visibility.PUBLIC,
new NoVarsDynamicScope(StaticScopeFactory.newStaticScope(null, StaticScope.Type.BLOCK, null)),
"<dummy>",
"dummy",
-1);
Visibility.PUBLIC);

/**
* frame of method which defined this block
263 changes: 206 additions & 57 deletions core/src/main/java/org/jruby/runtime/DynamicScope.java

Large diffs are not rendered by default.

40 changes: 0 additions & 40 deletions core/src/main/java/org/jruby/runtime/scope/DummyDynamicScope.java

This file was deleted.

282 changes: 282 additions & 0 deletions core/src/main/java/org/jruby/runtime/scope/DynamicScopeGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
package org.jruby.runtime.scope;

import me.qmx.jitescript.CodeBlock;
import me.qmx.jitescript.JDKVersion;
import me.qmx.jitescript.JiteClass;
import org.jruby.Ruby;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.DynamicScope;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ClassDefiningClassLoader;
import org.jruby.util.OneShotClassLoader;
import org.jruby.util.collections.NonBlockingHashMapLong;
import org.objectweb.asm.Label;
import org.objectweb.asm.tree.LabelNode;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import static org.jruby.util.CodegenUtils.ci;
import static org.jruby.util.CodegenUtils.p;
import static org.jruby.util.CodegenUtils.sig;

/**
* A generator for DynamicScope subclasses, using fields for storage and specializing appropriate methods.
*/
public class DynamicScopeGenerator {
private static final NonBlockingHashMapLong<MethodHandle> specializedFactories = new NonBlockingHashMapLong<>();
private static ClassDefiningClassLoader CDCL = new OneShotClassLoader(Ruby.getClassLoader());

public static final List<String> SPECIALIZED_GETS = Collections.unmodifiableList(Arrays.asList(
"getValueZeroDepthZero",
"getValueOneDepthZero",
"getValueTwoDepthZero",
"getValueThreeDepthZero",
"getValueFourDepthZero",
"getValueFiveDepthZero",
"getValueSixDepthZero",
"getValueSevenDepthZero",
"getValueEightDepthZero",
"getValueNineDepthZero"
));

public static final List<String> SPECIALIZED_SETS = Collections.unmodifiableList(Arrays.asList(
"setValueZeroDepthZeroVoid",
"setValueOneDepthZeroVoid",
"setValueTwoDepthZeroVoid",
"setValueThreeDepthZeroVoid",
"setValueFourDepthZeroVoid",
"setValueFiveDepthZeroVoid",
"setValueSixDepthZeroVoid",
"setValueSevenDepthZeroVoid",
"setValueEightDepthZeroVoid",
"setValueNineDepthZeroVoid"
));

public static MethodHandle generate(final int size) {
MethodHandle h = getClassFromSize(size);

if (h != null) return h;

final String clsPath = "org/jruby/runtime/scopes/DynamicScope" + size;
final String clsName = clsPath.replaceAll("/", ".");

try {
// create a new one
final String[] newFields = varList(size);

final String baseName = p(DynamicScope.class);

JiteClass jiteClass = new JiteClass(clsPath, baseName, new String[0]) {{
// parent class constructor
defineMethod("<init>", ACC_PUBLIC, sig(void.class, StaticScope.class, DynamicScope.class), new CodeBlock() {{
aload(0);
aload(1);
aload(2);
invokespecial(baseName, "<init>", sig(void.class, StaticScope.class, DynamicScope.class));
voidreturn();
}});

// required overrides
defineMethod("getValue", ACC_PUBLIC, sig(IRubyObject.class, int.class, int.class), new CodeBlock() {{
LabelNode parentCall = new LabelNode(new Label());

line(0);

iload(2); // depth
ifne(parentCall);

if (size > 0) genGetSwitch(clsPath, newFields, this, 1);

line(1);

invokestatic(clsPath, "sizeError", sig(RuntimeException.class));
athrow();

label(parentCall);
line(2);

aload(0);
getfield(baseName, "parent", ci(DynamicScope.class));
iload(1);
iload(2);
pushInt(1);
isub();
invokevirtual(baseName, "getValue", sig(IRubyObject.class, int.class, int.class));
areturn();
}});

defineMethod("setValueVoid", ACC_PUBLIC, sig(void.class, IRubyObject.class, int.class, int.class), new CodeBlock() {{
LabelNode parentCall = new LabelNode(new Label());

line(3);
iload(3); // depth
ifne(parentCall);

if (size > 0) genPutSwitch(clsPath, newFields, this, 2);

line(4);

invokestatic(clsPath, "sizeError", sig(RuntimeException.class));
athrow();

label(parentCall);
line(5);

aload(0);
getfield(baseName, "parent", ci(DynamicScope.class));
aload(1);
iload(2);
iload(3);
pushInt(1);
isub();
invokevirtual(baseName, "setValueVoid", sig(void.class, IRubyObject.class, int.class, int.class));
voidreturn();
}});

// optional overrides
defineMethod("getValueDepthZero", ACC_PUBLIC, sig(IRubyObject.class, int.class), new CodeBlock() {{
line(6);

if (size > 0) genGetSwitch(clsPath, newFields, this, 1);

line(1);

invokestatic(clsPath, "sizeError", sig(RuntimeException.class));
athrow();
}});

defineMethod("setValueDepthZeroVoid", ACC_PUBLIC, sig(void.class, IRubyObject.class, int.class), new CodeBlock() {{
line(6);

if (size > 0) genPutSwitch(clsPath, newFields, this, 2);

line(1);

invokestatic(clsPath, "sizeError", sig(RuntimeException.class));
athrow();
}});

for (int i = 0; i < SPECIALIZED_GETS.size(); i++) {
final int offset = i;

defineMethod(SPECIALIZED_GETS.get(offset), ACC_PUBLIC, sig(IRubyObject.class), new CodeBlock() {{
line(6);

if (size <= offset) {
invokestatic(clsPath, "sizeError", sig(RuntimeException.class));
athrow();
} else {
aload(0);
getfield(clsPath, newFields[offset], ci(IRubyObject.class));
areturn();
}
}});
}


for (int i = 0; i < SPECIALIZED_SETS.size(); i++) {
final int offset = i;

defineMethod(SPECIALIZED_SETS.get(offset), ACC_PUBLIC, sig(void.class, IRubyObject.class), new CodeBlock() {{
line(6);

if (size <= offset) {
invokestatic(clsPath, "sizeError", sig(RuntimeException.class));
athrow();
} else {
aload(0);
aload(1);
putfield(clsPath, newFields[offset], ci(IRubyObject.class));
voidreturn();
}
}});
}

// fields
for (String prop : newFields) {
defineField(prop, ACC_PUBLIC, ci(IRubyObject.class), null);
}

// utilities
defineMethod("sizeError", ACC_PRIVATE | ACC_STATIC, sig(RuntimeException.class), new CodeBlock() {{
newobj(p(RuntimeException.class));
dup();
ldc(clsName + " only supports scopes with " + size + " variables");
invokespecial(p(RuntimeException.class), "<init>", sig(void.class, String.class));
areturn();
}});
}};

Class p = defineClass(jiteClass);

MethodHandle mh = MethodHandles.lookup().findConstructor(p, MethodType.methodType(void.class, StaticScope.class, DynamicScope.class));
mh = mh.asType(MethodType.methodType(DynamicScope.class, StaticScope.class, DynamicScope.class));
MethodHandle previousMH = specializedFactories.putIfAbsent(size, mh);
if (previousMH != null) mh = previousMH;

return mh;
} catch (Exception e) {
throw new RuntimeException(e);
}
}

private static void genGetSwitch(String clsPath, String[] newFields, CodeBlock block, int offsetVar) {
LabelNode defaultError = new LabelNode(new Label());
int size = newFields.length;
LabelNode[] cases = new LabelNode[size];
for (int i = 0; i < size; i++) {
cases[i] = new LabelNode(new Label());
}
block.iload(offsetVar);
block.tableswitch(0, size - 1, defaultError, cases);
for (int i = 0; i < size; i++) {
block.label(cases[i]);
block.aload(0);
block.getfield(clsPath, newFields[i], ci(IRubyObject.class));
block.areturn();
}
block.label(defaultError);
}

private static void genPutSwitch(String clsPath, String[] newFields, CodeBlock block, int offsetVar) {
LabelNode defaultError = new LabelNode(new Label());
int size = newFields.length;
LabelNode[] cases = new LabelNode[size];
for (int i = 0; i < size; i++) {
cases[i] = new LabelNode(new Label());
}
block.iload(offsetVar);
block.tableswitch(0, size - 1, defaultError, cases);
for (int i = 0; i < size; i++) {
block.label(cases[i]);
block.aload(0);
block.aload(1);
block.putfield(clsPath, newFields[i], ci(IRubyObject.class));
block.voidreturn();
}
block.label(defaultError);
}

private static MethodHandle getClassFromSize(int size) {
return specializedFactories.get(size);
}

private static Class defineClass(JiteClass jiteClass) {
return CDCL.defineClass(jiteClass.getClassName().replaceAll("/", "."), jiteClass.toBytes(JDKVersion.V1_7));
}

private static String[] varList(int size) {
String[] vars = new String[size];

for (int i = 0; i < size; i++) {
vars[i] = "var" + i;
}

return vars;
}
}
181 changes: 0 additions & 181 deletions core/src/main/java/org/jruby/runtime/scope/FourVarDynamicScope.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -5,6 +5,10 @@
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ArraySupport;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

/**
* Represents the the dynamic portion of scoping information. The variableValues are the
* values of assigned local or block variables. The staticScope identifies which sort of
@@ -25,13 +29,19 @@ public class ManyVarsDynamicScope extends DynamicScope {
// Our values holder (name of variables are kept in staticScope)
private IRubyObject[] variableValues;

public ManyVarsDynamicScope(StaticScope staticScope, DynamicScope parent) {
super(staticScope, parent);
allocate();
public static final MethodHandle CONSTRUCTOR;
static {
try {
CONSTRUCTOR = MethodHandles.publicLookup()
.findConstructor(ManyVarsDynamicScope.class, MethodType.methodType(void.class, StaticScope.class, DynamicScope.class))
.asType(MethodType.methodType(DynamicScope.class, StaticScope.class, DynamicScope.class));
} catch (Exception e) {
throw new RuntimeException("BUG: could not initialize constructor handle");
}
}

public ManyVarsDynamicScope(StaticScope staticScope) {
super(staticScope);
public ManyVarsDynamicScope(StaticScope staticScope, DynamicScope parent) {
super(staticScope, parent);
allocate();
}

@@ -41,10 +51,6 @@ private void allocate() {
variableValues = new IRubyObject[size];
}
}

public DynamicScope cloneScope() {
return new ManyVarsDynamicScope(staticScope, parent);
}

public IRubyObject[] getValues() {
return variableValues;
@@ -148,42 +154,72 @@ public IRubyObject getValueThreeDepthZeroOrNil(IRubyObject nil) {
* @param value to set
* @param depth how many captured scopes down this variable should be set
*/
public IRubyObject setValue(int offset, IRubyObject value, int depth) {
public void setValueVoid(IRubyObject value, int offset, int depth) {
if (depth > 0) {
assertParent();

return parent.setValue(offset, value, depth - 1);
parent.setValueVoid(value, offset, depth - 1);
} else {
assertSetValue(offset, value);

return setValueDepthZero(value, offset);
setValueDepthZeroVoid(value, offset);
}
}

public IRubyObject setValueDepthZero(IRubyObject value, int offset) {
public void setValueDepthZeroVoid(IRubyObject value, int offset) {
assertSetValueDepthZero(offset, value);

return variableValues[offset] = value;
variableValues[offset] = value;
}
public IRubyObject setValueZeroDepthZero(IRubyObject value) {
public void setValueZeroDepthZeroVoid(IRubyObject value) {
assertSetValueZeroDepthZero(value);

return variableValues[0] = value;
variableValues[0] = value;
}
public IRubyObject setValueOneDepthZero(IRubyObject value) {
public void setValueOneDepthZeroVoid(IRubyObject value) {
assertSetValueOneDepthZero(value);

return variableValues[1] = value;
variableValues[1] = value;
}
public IRubyObject setValueTwoDepthZero(IRubyObject value) {
public void setValueTwoDepthZeroVoid(IRubyObject value) {
assertSetValueTwoDepthZero(value);

return variableValues[2] = value;
variableValues[2] = value;
}
public void setValueThreeDepthZeroVoid(IRubyObject value) {
assertSetValueThreeDepthZero(value);

variableValues[3] = value;
}
public IRubyObject setValueThreeDepthZero(IRubyObject value) {
public void setValueFourDepthZeroVoid(IRubyObject value) {
assertSetValueThreeDepthZero(value);

return variableValues[3] = value;
variableValues[4] = value;
}
public void setValueFiveDepthZeroVoid(IRubyObject value) {
assertSetValueThreeDepthZero(value);

variableValues[5] = value;
}
public void setValueSixDepthZeroVoid(IRubyObject value) {
assertSetValueThreeDepthZero(value);

variableValues[6] = value;
}
public void setValueSevenDepthZeroVoid(IRubyObject value) {
assertSetValueThreeDepthZero(value);

variableValues[7] = value;
}
public void setValueEightDepthZeroVoid(IRubyObject value) {
assertSetValueThreeDepthZero(value);

variableValues[8] = value;
}
public void setValueNineDepthZeroVoid(IRubyObject value) {
assertSetValueThreeDepthZero(value);

variableValues[9] = value;
}

/**
@@ -265,4 +301,10 @@ private void assertSetValueThreeDepthZero(IRubyObject value) {
private void assertSetValueTwoDepthZero(IRubyObject value) {
assert 2 < variableValues.length : "Setting " + 2 + " to " + value + ", O: " + this;
}

@Deprecated
public DynamicScope cloneScope() {
// we construct new rather than clone to avoid sharing variableValues
return new ManyVarsDynamicScope(staticScope, parent);
}
}
121 changes: 0 additions & 121 deletions core/src/main/java/org/jruby/runtime/scope/NoVarsDynamicScope.java

This file was deleted.

119 changes: 0 additions & 119 deletions core/src/main/java/org/jruby/runtime/scope/OneVarDynamicScope.java

This file was deleted.

170 changes: 0 additions & 170 deletions core/src/main/java/org/jruby/runtime/scope/ThreeVarDynamicScope.java

This file was deleted.

147 changes: 0 additions & 147 deletions core/src/main/java/org/jruby/runtime/scope/TwoVarDynamicScope.java

This file was deleted.

4 changes: 4 additions & 0 deletions core/src/main/java/org/jruby/util/OneShotClassLoader.java
Original file line number Diff line number Diff line change
@@ -9,6 +9,10 @@ public OneShotClassLoader(JRubyClassLoader parent) {
super(parent);
}

public OneShotClassLoader(ClassLoader parent) {
super(parent);
}

public Class<?> defineClass(String name, byte[] bytes) {
return super.defineClass(name, bytes, 0, bytes.length, ClassDefiningJRubyClassLoader.DEFAULT_DOMAIN);
}
62 changes: 62 additions & 0 deletions core/src/test/java/org/jruby/runtime/TestDynamicScope.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package org.jruby.runtime;

import junit.framework.TestCase;
import org.jruby.RubyBasicObject;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.scope.DynamicScopeGenerator;
import org.junit.Assert;
import org.junit.Test;

import java.util.Arrays;

public class TestDynamicScope extends TestCase {
@Test
public void testScopeGeneration() throws Throwable {
for (int i = 0; i < 100; i++) {
DynamicScope scope = (DynamicScope) DynamicScopeGenerator.generate(i).invoke(null, null);

for (int j = 0; j < 100; j++) {
try {
scope.setValueVoid(RubyBasicObject.NEVER, j, 0);
if (j >= i) Assert.fail("expected scope of size " + i + " to raise for setValueVoid of index " + j);
} catch (Throwable t) {
if (j < i) throw t;
}
try {
Assert.assertEquals(RubyBasicObject.NEVER, scope.getValue(j, 0));
if (j >= i) Assert.fail("expected scope of size " + i + " to raise for getValue of index " + j);
} catch (Throwable t) {
if (j < i) throw t;
}
try {
scope.setValueDepthZeroVoid(RubyBasicObject.NEVER, j);
if (j >= i) Assert.fail("expected scope of size " + i + " to raise for setValueDepthZeroVoid of index " + j);
} catch (Throwable t) {
if (j < i) throw t;
}
try {
Assert.assertEquals(RubyBasicObject.NEVER, scope.getValueDepthZero(j));
if (j >= i) Assert.fail("expected scope of size " + i + " to raise for getValueDepthZero of index " + j);
} catch (Throwable t) {
if (j < i) throw t;
}
if (j < DynamicScopeGenerator.SPECIALIZED_SETS.size()) {
try {
String set = DynamicScopeGenerator.SPECIALIZED_SETS.get(j);
scope.getClass().getMethod(set, IRubyObject.class).invoke(scope, RubyBasicObject.UNDEF);
if (j >= i) Assert.fail("expected scope of size " + i + " to raise for " + set);
} catch (Throwable t) {
if (j < i) throw t;
}
try {
String get = DynamicScopeGenerator.SPECIALIZED_GETS.get(j);
Assert.assertEquals(RubyBasicObject.UNDEF, scope.getClass().getMethod(get).invoke(scope));
if (j >= i) Assert.fail("expected scope of size " + i + " to raise for " + get);
} catch (Throwable t) {
if (j < i) throw t;
}
}
}
}
}
}