Skip to content
Snippets Groups Projects
Commit c326636f authored by Lukas Stadler's avatar Lukas Stadler
Browse files

remove unnecessary NodeField annotations in write variable hierarchy

parent 2cf9e49f
No related branches found
No related tags found
No related merge requests found
Showing
with 149 additions and 213 deletions
...@@ -88,7 +88,7 @@ public abstract class Mapply extends RBuiltinNode { ...@@ -88,7 +88,7 @@ public abstract class Mapply extends RBuiltinNode {
this.vectorElementName = "*" + AnonymousFrameVariable.create(vectorElementName); this.vectorElementName = "*" + AnonymousFrameVariable.create(vectorElementName);
this.lengthNode = insert(LengthNodeGen.create()); this.lengthNode = insert(LengthNodeGen.create());
this.indexedLoadNode = insert(SubscriptNodeGen.create()); this.indexedLoadNode = insert(SubscriptNodeGen.create());
this.writeVectorElementNode = insert(WriteVariableNode.createAnonymous(this.vectorElementName, null, Mode.REGULAR)); this.writeVectorElementNode = insert(WriteVariableNode.createAnonymous(this.vectorElementName, Mode.REGULAR, null));
this.argName = argName; this.argName = argName;
} }
} }
......
...@@ -24,8 +24,6 @@ package com.oracle.truffle.r.nodes.access; ...@@ -24,8 +24,6 @@ package com.oracle.truffle.r.nodes.access;
import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeField;
import com.oracle.truffle.api.dsl.NodeFields;
import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.Frame;
import com.oracle.truffle.api.frame.FrameSlot; import com.oracle.truffle.api.frame.FrameSlot;
import com.oracle.truffle.api.frame.FrameSlotKind; import com.oracle.truffle.api.frame.FrameSlotKind;
...@@ -38,7 +36,6 @@ import com.oracle.truffle.r.runtime.data.RShareable; ...@@ -38,7 +36,6 @@ import com.oracle.truffle.r.runtime.data.RShareable;
import com.oracle.truffle.r.runtime.nodes.RNode; import com.oracle.truffle.r.runtime.nodes.RNode;
@NodeChild(value = "rhs", type = RNode.class) @NodeChild(value = "rhs", type = RNode.class)
@NodeFields({@NodeField(name = "name", type = Object.class)})
/** /**
* Common code/state for all the variants of {@code WriteVariableNode}. At this level, we just have * Common code/state for all the variants of {@code WriteVariableNode}. At this level, we just have
* a {@code name} for the variable and expression {@code rhs} to be assigned to. * a {@code name} for the variable and expression {@code rhs} to be assigned to.
...@@ -47,6 +44,10 @@ import com.oracle.truffle.r.runtime.nodes.RNode; ...@@ -47,6 +44,10 @@ import com.oracle.truffle.r.runtime.nodes.RNode;
*/ */
abstract class BaseWriteVariableNode extends WriteVariableNode { abstract class BaseWriteVariableNode extends WriteVariableNode {
protected BaseWriteVariableNode(Object name) {
super(name);
}
private final ConditionProfile isObjectProfile = ConditionProfile.createBinaryProfile(); private final ConditionProfile isObjectProfile = ConditionProfile.createBinaryProfile();
private final ConditionProfile isCurrentProfile = ConditionProfile.createBinaryProfile(); private final ConditionProfile isCurrentProfile = ConditionProfile.createBinaryProfile();
private final ConditionProfile isShareableProfile = ConditionProfile.createBinaryProfile(); private final ConditionProfile isShareableProfile = ConditionProfile.createBinaryProfile();
...@@ -136,7 +137,7 @@ abstract class BaseWriteVariableNode extends WriteVariableNode { ...@@ -136,7 +137,7 @@ abstract class BaseWriteVariableNode extends WriteVariableNode {
return isKind(frameSlot, FrameSlotKind.Double); return isKind(frameSlot, FrameSlotKind.Double);
} }
private boolean isKind(FrameSlot frameSlot, FrameSlotKind kind) { protected boolean isKind(FrameSlot frameSlot, FrameSlotKind kind) {
if (frameSlot.getKind() == kind) { if (frameSlot.getKind() == kind) {
return true; return true;
} else { } else {
......
/* /*
* Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -22,118 +22,73 @@ ...@@ -22,118 +22,73 @@
*/ */
package com.oracle.truffle.r.nodes.access; package com.oracle.truffle.r.nodes.access;
import static com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor.findOrAddFrameSlot; import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.NodeField;
import com.oracle.truffle.api.dsl.NodeFields;
import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.FrameSlot; import com.oracle.truffle.api.frame.FrameSlot;
import com.oracle.truffle.api.frame.FrameSlotKind; import com.oracle.truffle.api.frame.FrameSlotKind;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.NodeCost;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.api.profiles.ValueProfile; import com.oracle.truffle.api.profiles.ValueProfile;
import com.oracle.truffle.r.nodes.access.WriteLocalFrameVariableNodeFactory.ResolvedWriteLocalFrameVariableNodeGen;
import com.oracle.truffle.r.nodes.access.WriteLocalFrameVariableNodeFactory.UnresolvedWriteLocalFrameVariableNodeGen;
import com.oracle.truffle.r.runtime.RError;
import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor; import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
import com.oracle.truffle.r.runtime.nodes.RNode; import com.oracle.truffle.r.runtime.nodes.RNode;
/** /**
* {@link WriteLocalFrameVariableNode} captures a write to the "local", i.e., current, frame. There * {@link WriteLocalFrameVariableNode} captures a write to the "local", i.e., current, frame. There
* are several clients capturing different "kinds" of writes to the frame, e.g. saving an argument, * are several clients capturing different "kinds" of writes to the frame, e.g. saving an argument,
* a source-level write, a a write helper for another source node (e.g. {@code ForNode}. * a source-level write, a a write helper for another source node (e.g. {@code ForNode}).
*
* The state starts out a "unresolved" and transforms to "resolved".
*/ */
@ImportStatic(FrameSlotKind.class)
public abstract class WriteLocalFrameVariableNode extends BaseWriteVariableNode { public abstract class WriteLocalFrameVariableNode extends BaseWriteVariableNode {
public static WriteLocalFrameVariableNode create(String name, RNode rhs, Mode mode) { public static WriteLocalFrameVariableNode create(Object name, Mode mode, RNode rhs) {
return UnresolvedWriteLocalFrameVariableNodeGen.create(rhs, name, mode); return WriteLocalFrameVariableNodeGen.create(name, mode, rhs);
} }
public static WriteLocalFrameVariableNode createForRefCount(Object name) { public static WriteLocalFrameVariableNode createForRefCount(Object name) {
return UnresolvedWriteLocalFrameVariableNodeGen.create(null, name, Mode.INVISIBLE); return WriteLocalFrameVariableNodeGen.create(name, Mode.INVISIBLE, null);
} }
@NodeField(name = "mode", type = Mode.class) private final Mode mode;
@NodeInfo(cost = NodeCost.UNINITIALIZED)
abstract static class UnresolvedWriteLocalFrameVariableNode extends WriteLocalFrameVariableNode {
public abstract Mode getMode();
@Specialization
protected byte doLogical(VirtualFrame frame, byte value) {
resolveAndSet(frame, value, FrameSlotKind.Byte);
return value;
}
@Specialization private final ValueProfile storedObjectProfile = ValueProfile.createClassProfile();
protected int doInteger(VirtualFrame frame, int value) { private final BranchProfile invalidateProfile = BranchProfile.create();
resolveAndSet(frame, value, FrameSlotKind.Int);
return value;
}
@Specialization protected WriteLocalFrameVariableNode(Object name, Mode mode) {
protected double doDouble(VirtualFrame frame, double value) { super(name);
resolveAndSet(frame, value, FrameSlotKind.Double); this.mode = mode;
return value;
}
@Specialization
protected Object doObject(VirtualFrame frame, Object value) {
resolveAndSet(frame, value, FrameSlotKind.Object);
return value;
}
private void resolveAndSet(VirtualFrame frame, Object value, FrameSlotKind initialKind) {
CompilerDirectives.transferToInterpreterAndInvalidate();
// it's slow path (unconditional replace) so toString() is fine as well
if (getName().toString().isEmpty()) {
throw RError.error(RError.NO_CALLER, RError.Message.ZERO_LENGTH_VARIABLE);
}
FrameSlot frameSlot = findOrAddFrameSlot(frame.getFrameDescriptor(), getName(), initialKind);
replace(ResolvedWriteLocalFrameVariableNode.create(getRhs(), getName(), frameSlot, getMode())).execute(frame, value);
}
} }
@NodeFields({@NodeField(name = "frameSlot", type = FrameSlot.class), @NodeField(name = "mode", type = Mode.class)}) protected FrameSlot findOrAddFrameSlot(VirtualFrame frame, FrameSlotKind initialKind) {
abstract static class ResolvedWriteLocalFrameVariableNode extends WriteLocalFrameVariableNode { return FrameSlotChangeMonitor.findOrAddFrameSlot(frame.getFrameDescriptor(), getName(), initialKind);
}
private final ValueProfile storedObjectProfile = ValueProfile.createClassProfile();
private final BranchProfile invalidateProfile = BranchProfile.create();
public abstract Mode getMode();
private static ResolvedWriteLocalFrameVariableNode create(RNode rhs, Object name, FrameSlot frameSlot, Mode mode) {
return ResolvedWriteLocalFrameVariableNodeGen.create(rhs, name, frameSlot, mode);
}
@Specialization(guards = "isLogicalKind(frame, frameSlot)") @Specialization(guards = "isLogicalKind(frame, frameSlot)")
protected byte doLogical(VirtualFrame frame, FrameSlot frameSlot, byte value) { protected byte doLogical(VirtualFrame frame, byte value,
FrameSlotChangeMonitor.setByteAndInvalidate(frame, frameSlot, value, false, invalidateProfile); @Cached("findOrAddFrameSlot(frame, Byte)") FrameSlot frameSlot) {
return value; FrameSlotChangeMonitor.setByteAndInvalidate(frame, frameSlot, value, false, invalidateProfile);
} return value;
}
@Specialization(guards = "isIntegerKind(frame, frameSlot)") @Specialization(guards = "isIntegerKind(frame, frameSlot)")
protected int doInteger(VirtualFrame frame, FrameSlot frameSlot, int value) { protected int doInteger(VirtualFrame frame, int value,
FrameSlotChangeMonitor.setIntAndInvalidate(frame, frameSlot, value, false, invalidateProfile); @Cached("findOrAddFrameSlot(frame, Int)") FrameSlot frameSlot) {
return value; FrameSlotChangeMonitor.setIntAndInvalidate(frame, frameSlot, value, false, invalidateProfile);
} return value;
}
@Specialization(guards = "isDoubleKind(frame, frameSlot)") @Specialization(guards = "isDoubleKind(frame, frameSlot)")
protected double doDouble(VirtualFrame frame, FrameSlot frameSlot, double value) { protected double doDouble(VirtualFrame frame, double value,
FrameSlotChangeMonitor.setDoubleAndInvalidate(frame, frameSlot, value, false, invalidateProfile); @Cached("findOrAddFrameSlot(frame, Double)") FrameSlot frameSlot) {
return value; FrameSlotChangeMonitor.setDoubleAndInvalidate(frame, frameSlot, value, false, invalidateProfile);
} return value;
}
@Specialization @Specialization
protected Object doObject(VirtualFrame frame, FrameSlot frameSlot, Object value) { protected Object doObject(VirtualFrame frame, Object value,
Object newValue = shareObjectValue(frame, frameSlot, storedObjectProfile.profile(value), getMode(), false); @Cached("findOrAddFrameSlot(frame, Object)") FrameSlot frameSlot) {
FrameSlotChangeMonitor.setObjectAndInvalidate(frame, frameSlot, newValue, false, invalidateProfile); Object newValue = shareObjectValue(frame, frameSlot, storedObjectProfile.profile(value), mode, false);
return value; FrameSlotChangeMonitor.setObjectAndInvalidate(frame, frameSlot, newValue, false, invalidateProfile);
} return value;
} }
} }
/* /*
* Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -26,7 +26,6 @@ import static com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor.find ...@@ -26,7 +26,6 @@ import static com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor.find
import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.FrameSlot; import com.oracle.truffle.api.frame.FrameSlot;
import com.oracle.truffle.api.frame.FrameSlotKind; import com.oracle.truffle.api.frame.FrameSlotKind;
...@@ -35,10 +34,8 @@ import com.oracle.truffle.api.frame.VirtualFrame; ...@@ -35,10 +34,8 @@ import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.api.profiles.ConditionProfile; import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.api.profiles.ValueProfile; import com.oracle.truffle.api.profiles.ValueProfile;
import com.oracle.truffle.r.nodes.access.WriteLocalFrameVariableNodeFactory.UnresolvedWriteLocalFrameVariableNodeGen;
import com.oracle.truffle.r.nodes.access.WriteSuperFrameVariableNodeFactory.ResolvedWriteSuperFrameVariableNodeGen; import com.oracle.truffle.r.nodes.access.WriteSuperFrameVariableNodeFactory.ResolvedWriteSuperFrameVariableNodeGen;
import com.oracle.truffle.r.runtime.RArguments; import com.oracle.truffle.r.runtime.RArguments;
import com.oracle.truffle.r.runtime.RError;
import com.oracle.truffle.r.runtime.env.REnvironment; import com.oracle.truffle.r.runtime.env.REnvironment;
import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor; import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
import com.oracle.truffle.r.runtime.nodes.RNode; import com.oracle.truffle.r.runtime.nodes.RNode;
...@@ -50,8 +47,12 @@ import com.oracle.truffle.r.runtime.nodes.RNode; ...@@ -50,8 +47,12 @@ import com.oracle.truffle.r.runtime.nodes.RNode;
*/ */
abstract class WriteSuperFrameVariableNode extends BaseWriteVariableNode { abstract class WriteSuperFrameVariableNode extends BaseWriteVariableNode {
static WriteVariableNode create(String name, RNode rhs, Mode mode) { protected WriteSuperFrameVariableNode(Object name) {
return new UnresolvedWriteSuperFrameVariableNode(name, rhs, mode); super(name);
}
static WriteVariableNode create(String name, Mode mode, RNode rhs) {
return new UnresolvedWriteSuperFrameVariableNode(name, mode, rhs);
} }
protected abstract void execute(VirtualFrame frame, Object value, MaterializedFrame enclosingFrame); protected abstract void execute(VirtualFrame frame, Object value, MaterializedFrame enclosingFrame);
...@@ -63,7 +64,8 @@ abstract class WriteSuperFrameVariableNode extends BaseWriteVariableNode { ...@@ -63,7 +64,8 @@ abstract class WriteSuperFrameVariableNode extends BaseWriteVariableNode {
return value; return value;
} }
@NodeChildren({@NodeChild(value = "enclosingFrame", type = AccessEnclosingFrameNode.class), @NodeChild(value = "frameSlotNode", type = FrameSlotNode.class)}) @NodeChild(value = "enclosingFrame", type = AccessEnclosingFrameNode.class)
@NodeChild(value = "frameSlotNode", type = FrameSlotNode.class)
protected abstract static class ResolvedWriteSuperFrameVariableNode extends WriteSuperFrameVariableNode { protected abstract static class ResolvedWriteSuperFrameVariableNode extends WriteSuperFrameVariableNode {
private final ValueProfile storedObjectProfile = ValueProfile.createClassProfile(); private final ValueProfile storedObjectProfile = ValueProfile.createClassProfile();
...@@ -72,7 +74,8 @@ abstract class WriteSuperFrameVariableNode extends BaseWriteVariableNode { ...@@ -72,7 +74,8 @@ abstract class WriteSuperFrameVariableNode extends BaseWriteVariableNode {
private final Mode mode; private final Mode mode;
public ResolvedWriteSuperFrameVariableNode(Mode mode) { public ResolvedWriteSuperFrameVariableNode(Object name, Mode mode) {
super(name);
this.mode = mode; this.mode = mode;
} }
...@@ -105,18 +108,12 @@ abstract class WriteSuperFrameVariableNode extends BaseWriteVariableNode { ...@@ -105,18 +108,12 @@ abstract class WriteSuperFrameVariableNode extends BaseWriteVariableNode {
@Child private RNode rhs; @Child private RNode rhs;
private final String name;
private final Mode mode; private final Mode mode;
UnresolvedWriteSuperFrameVariableNode(String name, RNode rhs, Mode mode) { UnresolvedWriteSuperFrameVariableNode(Object name, Mode mode, RNode rhs) {
this.rhs = rhs; super(name);
this.name = name;
this.mode = mode; this.mode = mode;
} this.rhs = rhs;
@Override
public String getName() {
return name;
} }
@Override @Override
...@@ -127,9 +124,6 @@ abstract class WriteSuperFrameVariableNode extends BaseWriteVariableNode { ...@@ -127,9 +124,6 @@ abstract class WriteSuperFrameVariableNode extends BaseWriteVariableNode {
@Override @Override
public void execute(VirtualFrame frame, Object value, MaterializedFrame enclosingFrame) { public void execute(VirtualFrame frame, Object value, MaterializedFrame enclosingFrame) {
CompilerDirectives.transferToInterpreterAndInvalidate(); CompilerDirectives.transferToInterpreterAndInvalidate();
if (name.isEmpty()) {
throw RError.error(RError.NO_CALLER, RError.Message.ZERO_LENGTH_VARIABLE);
}
final WriteSuperFrameVariableNode writeNode; final WriteSuperFrameVariableNode writeNode;
if (REnvironment.isGlobalEnvFrame(enclosingFrame)) { if (REnvironment.isGlobalEnvFrame(enclosingFrame)) {
/* /*
...@@ -137,11 +131,11 @@ abstract class WriteSuperFrameVariableNode extends BaseWriteVariableNode { ...@@ -137,11 +131,11 @@ abstract class WriteSuperFrameVariableNode extends BaseWriteVariableNode {
* in the chain, needs the rhs and enclosingFrame nodes * in the chain, needs the rhs and enclosingFrame nodes
*/ */
AccessEnclosingFrameNode enclosingFrameNode = RArguments.getEnclosingFrame(frame) == enclosingFrame ? new AccessEnclosingFrameNode() : null; AccessEnclosingFrameNode enclosingFrameNode = RArguments.getEnclosingFrame(frame) == enclosingFrame ? new AccessEnclosingFrameNode() : null;
writeNode = ResolvedWriteSuperFrameVariableNodeGen.create(mode, rhs, enclosingFrameNode, writeNode = ResolvedWriteSuperFrameVariableNodeGen.create(getName(), mode, rhs, enclosingFrameNode,
FrameSlotNode.create(findOrAddFrameSlot(enclosingFrame.getFrameDescriptor(), name, FrameSlotKind.Illegal)), name); FrameSlotNode.create(findOrAddFrameSlot(enclosingFrame.getFrameDescriptor(), getName(), FrameSlotKind.Illegal)));
} else { } else {
ResolvedWriteSuperFrameVariableNode actualWriteNode = ResolvedWriteSuperFrameVariableNodeGen.create(mode, null, null, FrameSlotNode.create(name), name); ResolvedWriteSuperFrameVariableNode actualWriteNode = ResolvedWriteSuperFrameVariableNodeGen.create(getName(), mode, null, null, FrameSlotNode.createTemp(getName(), false));
writeNode = new WriteSuperFrameVariableConditionalNode(actualWriteNode, new UnresolvedWriteSuperFrameVariableNode(name, null, mode), rhs); writeNode = new WriteSuperFrameVariableConditionalNode(getName(), actualWriteNode, new UnresolvedWriteSuperFrameVariableNode(getName(), mode, null), rhs);
} }
replace(writeNode).execute(frame, value, enclosingFrame); replace(writeNode).execute(frame, value, enclosingFrame);
} }
...@@ -154,7 +148,7 @@ abstract class WriteSuperFrameVariableNode extends BaseWriteVariableNode { ...@@ -154,7 +148,7 @@ abstract class WriteSuperFrameVariableNode extends BaseWriteVariableNode {
execute(frame, value, enclosingFrame); execute(frame, value, enclosingFrame);
} else { } else {
// we're in global scope, do a local write instead // we're in global scope, do a local write instead
replace(UnresolvedWriteLocalFrameVariableNodeGen.create(rhs, name, mode)).execute(frame, value); replace(WriteLocalFrameVariableNode.create(getName(), mode, rhs)).execute(frame, value);
} }
} }
} }
...@@ -169,17 +163,13 @@ abstract class WriteSuperFrameVariableNode extends BaseWriteVariableNode { ...@@ -169,17 +163,13 @@ abstract class WriteSuperFrameVariableNode extends BaseWriteVariableNode {
private final ConditionProfile hasValueProfile = ConditionProfile.createBinaryProfile(); private final ConditionProfile hasValueProfile = ConditionProfile.createBinaryProfile();
private final ConditionProfile nullSuperFrameProfile = ConditionProfile.createBinaryProfile(); private final ConditionProfile nullSuperFrameProfile = ConditionProfile.createBinaryProfile();
WriteSuperFrameVariableConditionalNode(ResolvedWriteSuperFrameVariableNode writeNode, WriteSuperFrameVariableNode nextNode, RNode rhs) { WriteSuperFrameVariableConditionalNode(Object name, ResolvedWriteSuperFrameVariableNode writeNode, WriteSuperFrameVariableNode nextNode, RNode rhs) {
super(name);
this.writeNode = writeNode; this.writeNode = writeNode;
this.nextNode = nextNode; this.nextNode = nextNode;
this.rhs = rhs; this.rhs = rhs;
} }
@Override
public Object getName() {
return writeNode.getName();
}
@Override @Override
public RNode getRhs() { public RNode getRhs() {
return rhs; return rhs;
......
/*
* Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.truffle.r.nodes.access;
import com.oracle.truffle.api.frame.MaterializedFrame;
import com.oracle.truffle.api.frame.VirtualFrame;
/**
* Helper class for the WriteSuperFrame variants. This ought to be a static class in
* {@link WriteSuperFrameVariableNode} but that causes compilation problems.
*/
abstract class WriteSuperFrameVariableNodeHelper extends BaseWriteVariableNode {
public abstract void execute(VirtualFrame frame, Object value, MaterializedFrame enclosingFrame);
@Override
public final Object execute(VirtualFrame frame) {
Object value = getRhs().execute(frame);
execute(frame, value);
return value;
}
}
/* /*
* Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -43,7 +43,15 @@ public abstract class WriteVariableNode extends RNode { ...@@ -43,7 +43,15 @@ public abstract class WriteVariableNode extends RNode {
INVISIBLE INVISIBLE
} }
public abstract Object getName(); private final Object name;
protected WriteVariableNode(Object name) {
this.name = name;
}
public final Object getName() {
return name;
}
public abstract RNode getRhs(); public abstract RNode getRhs();
...@@ -54,27 +62,27 @@ public abstract class WriteVariableNode extends RNode { ...@@ -54,27 +62,27 @@ public abstract class WriteVariableNode extends RNode {
*/ */
public static WriteVariableNode createArgSave(String name, RNode rhs) { public static WriteVariableNode createArgSave(String name, RNode rhs) {
if (FastROptions.InvisibleArgs.getBooleanValue()) { if (FastROptions.InvisibleArgs.getBooleanValue()) {
return WriteLocalFrameVariableNode.create(name, rhs, Mode.INVISIBLE); return WriteLocalFrameVariableNode.create(name, Mode.INVISIBLE, rhs);
} else { } else {
return WriteLocalFrameVariableNode.create(name, rhs, Mode.REGULAR); return WriteLocalFrameVariableNode.create(name, Mode.REGULAR, rhs);
} }
} }
/** /**
* Variant for anonymous variables in the current frame. * Variant for anonymous variables in the current frame.
*/ */
public static WriteVariableNode createAnonymous(String name, RNode rhs, Mode mode) { public static WriteVariableNode createAnonymous(String name, Mode mode, RNode rhs) {
return WriteLocalFrameVariableNode.create(name, rhs, mode); return WriteLocalFrameVariableNode.create(name, mode, rhs);
} }
/** /**
* Variant for anonymous variables in either the current or a super frame.. * Variant for anonymous variables in either the current or a super frame..
*/ */
public static WriteVariableNode createAnonymous(String name, RNode rhs, Mode mode, boolean isSuper) { public static WriteVariableNode createAnonymous(String name, Mode mode, RNode rhs, boolean isSuper) {
if (isSuper) { if (isSuper) {
return WriteSuperFrameVariableNode.create(name, rhs, mode); return WriteSuperFrameVariableNode.create(name, mode, rhs);
} else { } else {
return WriteLocalFrameVariableNode.create(name, rhs, mode); return WriteLocalFrameVariableNode.create(name, mode, rhs);
} }
} }
} }
...@@ -27,18 +27,19 @@ import static com.oracle.truffle.api.nodes.NodeCost.NONE; ...@@ -27,18 +27,19 @@ import static com.oracle.truffle.api.nodes.NodeCost.NONE;
import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.r.nodes.access.WriteVariableNode.Mode; import com.oracle.truffle.r.nodes.access.WriteVariableNode.Mode;
import com.oracle.truffle.r.nodes.control.OperatorNode; import com.oracle.truffle.r.nodes.control.OperatorNode;
import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode; import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
import com.oracle.truffle.r.runtime.ArgumentsSignature; import com.oracle.truffle.r.runtime.ArgumentsSignature;
import com.oracle.truffle.r.runtime.RError;
import com.oracle.truffle.r.runtime.RInternalError;
import com.oracle.truffle.r.runtime.nodes.RNode; import com.oracle.truffle.r.runtime.nodes.RNode;
import com.oracle.truffle.r.runtime.nodes.RSyntaxConstant;
import com.oracle.truffle.r.runtime.nodes.RSyntaxElement; import com.oracle.truffle.r.runtime.nodes.RSyntaxElement;
import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup; import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup;
/**
* This node represents a write to a variable on the syntax level, i.e., in the R source code.
*/
@NodeInfo(cost = NONE) @NodeInfo(cost = NONE)
public final class WriteVariableSyntaxNode extends OperatorNode { public final class WriteVariableSyntaxNode extends OperatorNode {
...@@ -47,25 +48,10 @@ public final class WriteVariableSyntaxNode extends OperatorNode { ...@@ -47,25 +48,10 @@ public final class WriteVariableSyntaxNode extends OperatorNode {
private final RSyntaxElement lhs; private final RSyntaxElement lhs;
public WriteVariableSyntaxNode(SourceSection source, RSyntaxLookup operator, RSyntaxElement lhs, RNode rhs, boolean isSuper) { public WriteVariableSyntaxNode(SourceSection source, RSyntaxLookup operator, RSyntaxElement lhs, String name, RNode rhs, boolean isSuper) {
super(source, operator); super(source, operator);
this.lhs = lhs; this.lhs = lhs;
String name; this.write = WriteVariableNode.createAnonymous(name, Mode.REGULAR, rhs, isSuper);
if (lhs instanceof RSyntaxLookup) {
name = ((RSyntaxLookup) lhs).getIdentifier();
} else if (lhs instanceof RSyntaxConstant) {
RSyntaxConstant c = (RSyntaxConstant) lhs;
if (c.getValue() instanceof String) {
name = (String) c.getValue();
} else {
// "this" needs to be initialized for error reporting to work
this.write = WriteVariableNode.createAnonymous("dummy", rhs, Mode.REGULAR, isSuper);
throw RError.error(this, RError.Message.INVALID_LHS, "do_set");
}
} else {
throw RInternalError.unimplemented("unexpected lhs type in replacement: " + lhs.getClass());
}
this.write = WriteVariableNode.createAnonymous(name, rhs, Mode.REGULAR, isSuper);
assert write != null; assert write != null;
} }
...@@ -79,6 +65,21 @@ public final class WriteVariableSyntaxNode extends OperatorNode { ...@@ -79,6 +65,21 @@ public final class WriteVariableSyntaxNode extends OperatorNode {
return write.execute(frame); return write.execute(frame);
} }
@Override
public int executeInteger(VirtualFrame frame) throws UnexpectedResultException {
return write.executeInteger(frame);
}
@Override
public double executeDouble(VirtualFrame frame) throws UnexpectedResultException {
return write.executeDouble(frame);
}
@Override
public byte executeByte(VirtualFrame frame) throws UnexpectedResultException {
return write.executeByte(frame);
}
@Override @Override
public Object visibleExecute(VirtualFrame frame) { public Object visibleExecute(VirtualFrame frame) {
Object result = write.execute(frame); Object result = write.execute(frame);
......
/* /*
* Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
......
...@@ -62,9 +62,9 @@ public final class ForNode extends AbstractLoopNode implements RSyntaxNode, RSyn ...@@ -62,9 +62,9 @@ public final class ForNode extends AbstractLoopNode implements RSyntaxNode, RSyn
String rangeName = AnonymousFrameVariable.create("FOR_RANGE"); String rangeName = AnonymousFrameVariable.create("FOR_RANGE");
String lengthName = AnonymousFrameVariable.create("FOR_LENGTH"); String lengthName = AnonymousFrameVariable.create("FOR_LENGTH");
this.writeIndexNode = WriteVariableNode.createAnonymous(indexName, null, Mode.REGULAR); this.writeIndexNode = WriteVariableNode.createAnonymous(indexName, Mode.REGULAR, null);
this.writeRangeNode = WriteVariableNode.createAnonymous(rangeName, range, Mode.REGULAR); this.writeRangeNode = WriteVariableNode.createAnonymous(rangeName, Mode.REGULAR, range);
this.writeLengthNode = WriteVariableNode.createAnonymous(lengthName, RLengthNodeGen.create(ReadVariableNode.create(rangeName)), Mode.REGULAR); this.writeLengthNode = WriteVariableNode.createAnonymous(lengthName, Mode.REGULAR, RLengthNodeGen.create(ReadVariableNode.create(rangeName)));
this.loopNode = Truffle.getRuntime().createLoopNode(new ForRepeatingNode(this, var.getIdentifier(), body, indexName, lengthName, rangeName)); this.loopNode = Truffle.getRuntime().createLoopNode(new ForRepeatingNode(this, var.getIdentifier(), body, indexName, lengthName, rangeName));
} }
...@@ -96,12 +96,12 @@ public final class ForNode extends AbstractLoopNode implements RSyntaxNode, RSyn ...@@ -96,12 +96,12 @@ public final class ForNode extends AbstractLoopNode implements RSyntaxNode, RSyn
ForRepeatingNode(ForNode forNode, String var, RNode body, String indexName, String lengthName, String rangeName) { ForRepeatingNode(ForNode forNode, String var, RNode body, String indexName, String lengthName, String rangeName) {
this.forNode = forNode; this.forNode = forNode;
this.writeElementNode = WriteVariableNode.createAnonymous(var, createIndexedLoad(indexName, rangeName), Mode.REGULAR, false); this.writeElementNode = WriteVariableNode.createAnonymous(var, Mode.REGULAR, createIndexedLoad(indexName, rangeName), false);
this.body = body; this.body = body;
this.readIndexNode = ReadVariableNode.create(indexName); this.readIndexNode = ReadVariableNode.create(indexName);
this.readLengthNode = ReadVariableNode.create(lengthName); this.readLengthNode = ReadVariableNode.create(lengthName);
this.writeIndexNode = WriteVariableNode.createAnonymous(indexName, null, Mode.REGULAR); this.writeIndexNode = WriteVariableNode.createAnonymous(indexName, Mode.REGULAR, null);
// pre-initialize the profile so that loop exits to not deoptimize // pre-initialize the profile so that loop exits to not deoptimize
conditionProfile.profile(false); conditionProfile.profile(false);
} }
......
...@@ -37,6 +37,7 @@ import com.oracle.truffle.r.nodes.access.WriteVariableSyntaxNode; ...@@ -37,6 +37,7 @@ import com.oracle.truffle.r.nodes.access.WriteVariableSyntaxNode;
import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode; import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
import com.oracle.truffle.r.runtime.ArgumentsSignature; import com.oracle.truffle.r.runtime.ArgumentsSignature;
import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RError;
import com.oracle.truffle.r.runtime.RInternalError;
import com.oracle.truffle.r.runtime.data.RLanguage; import com.oracle.truffle.r.runtime.data.RLanguage;
import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.data.RNull;
import com.oracle.truffle.r.runtime.nodes.RNode; import com.oracle.truffle.r.runtime.nodes.RNode;
...@@ -91,14 +92,36 @@ public final class ReplacementDispatchNode extends OperatorNode { ...@@ -91,14 +92,36 @@ public final class ReplacementDispatchNode extends OperatorNode {
public RNode create(boolean isVoid) { public RNode create(boolean isVoid) {
RNode replacement; RNode replacement;
if (lhs.asRSyntaxNode() instanceof RSyntaxCall) { RSyntaxNode lhsSyntax = lhs.asRSyntaxNode();
if (lhsSyntax instanceof RSyntaxCall) {
replacement = createReplacementNode(isVoid); replacement = createReplacementNode(isVoid);
} else { } else {
replacement = new WriteVariableSyntaxNode(getLazySourceSection(), operator, lhs.asRSyntaxNode(), rhs, isSuper); replacement = createWriteVariableNode(lhsSyntax);
} }
return replace(replacement); return replace(replacement);
} }
private RNode createWriteVariableNode(RSyntaxNode lhsSyntax) {
String name;
if (lhsSyntax instanceof RSyntaxLookup) {
name = ((RSyntaxLookup) lhsSyntax).getIdentifier();
} else if (lhsSyntax instanceof RSyntaxConstant) {
RSyntaxConstant c = (RSyntaxConstant) lhsSyntax;
if (c.getValue() instanceof String) {
name = (String) c.getValue();
} else {
// "this" needs to be initialized for error reporting to work
throw RError.error(this, RError.Message.INVALID_LHS, "do_set");
}
} else {
throw RInternalError.unimplemented("unexpected lhs type in replacement: " + lhsSyntax.getClass());
}
if (name.isEmpty()) {
throw RError.error(RError.NO_CALLER, RError.Message.ZERO_LENGTH_VARIABLE);
}
return new WriteVariableSyntaxNode(getLazySourceSection(), operator, lhsSyntax, name, rhs, isSuper);
}
@Override @Override
public ArgumentsSignature getSyntaxSignature() { public ArgumentsSignature getSyntaxSignature() {
return ArgumentsSignature.empty(2); return ArgumentsSignature.empty(2);
......
/* /*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -174,7 +174,7 @@ abstract class ReplacementNode extends OperatorNode { ...@@ -174,7 +174,7 @@ abstract class ReplacementNode extends OperatorNode {
super(source, operator, lhs); super(source, operator, lhs);
this.rhs = rhs; this.rhs = rhs;
this.storeRhs = WriteVariableNode.createAnonymous("*rhs*" + tempNamesStartIndex, rhs, WriteVariableNode.Mode.INVISIBLE); this.storeRhs = WriteVariableNode.createAnonymous("*rhs*" + tempNamesStartIndex, WriteVariableNode.Mode.INVISIBLE, rhs);
this.removeRhs = RemoveAndAnswerNode.create("*rhs*" + tempNamesStartIndex); this.removeRhs = RemoveAndAnswerNode.create("*rhs*" + tempNamesStartIndex);
} }
...@@ -368,7 +368,7 @@ abstract class ReplacementNode extends OperatorNode { ...@@ -368,7 +368,7 @@ abstract class ReplacementNode extends OperatorNode {
for (int i = calls.size() - 1, tmpIndex = 0; i >= 1; i--, tmpIndex++) { for (int i = calls.size() - 1, tmpIndex = 0; i >= 1; i--, tmpIndex++) {
ReadVariableNode newFirstArg = ReadVariableNode.create("*tmp*" + (tempNamesStartIndex + tmpIndex)); ReadVariableNode newFirstArg = ReadVariableNode.create("*tmp*" + (tempNamesStartIndex + tmpIndex));
RNode update = createSpecialFunctionQuery(calls.get(i), newFirstArg, codeBuilderContext); RNode update = createSpecialFunctionQuery(calls.get(i), newFirstArg, codeBuilderContext);
instructions.add(WriteVariableNode.createAnonymous("*tmp*" + (tempNamesStartIndex + tmpIndex + 1), update, WriteVariableNode.Mode.INVISIBLE)); instructions.add(WriteVariableNode.createAnonymous("*tmp*" + (tempNamesStartIndex + tmpIndex + 1), WriteVariableNode.Mode.INVISIBLE, update));
} }
/* /*
* Create the update calls, for "a(b(x)) <- z", this would be `a<-` and `b<-`, the * Create the update calls, for "a(b(x)) <- z", this would be `a<-` and `b<-`, the
...@@ -379,14 +379,14 @@ abstract class ReplacementNode extends OperatorNode { ...@@ -379,14 +379,14 @@ abstract class ReplacementNode extends OperatorNode {
String tmprName = i == 0 ? ("*rhs*" + tempNamesStartIndex) : ("*tmpr*" + (tempNamesStartIndex + i - 1)); String tmprName = i == 0 ? ("*rhs*" + tempNamesStartIndex) : ("*tmpr*" + (tempNamesStartIndex + i - 1));
RNode update = createFunctionUpdate(source, ReadVariableNode.create("*tmp*" + tmpIndex), ReadVariableNode.create(tmprName), calls.get(i), codeBuilderContext); RNode update = createFunctionUpdate(source, ReadVariableNode.create("*tmp*" + tmpIndex), ReadVariableNode.create(tmprName), calls.get(i), codeBuilderContext);
if (i < calls.size() - 1) { if (i < calls.size() - 1) {
instructions.add(WriteVariableNode.createAnonymous("*tmpr*" + (tempNamesStartIndex + i), update, WriteVariableNode.Mode.INVISIBLE)); instructions.add(WriteVariableNode.createAnonymous("*tmpr*" + (tempNamesStartIndex + i), WriteVariableNode.Mode.INVISIBLE, update));
} else { } else {
instructions.add(WriteVariableNode.createAnonymous(targetVarName, update, WriteVariableNode.Mode.REGULAR, isSuper)); instructions.add(WriteVariableNode.createAnonymous(targetVarName, WriteVariableNode.Mode.REGULAR, update, isSuper));
} }
} }
this.updates = instructions.toArray(new RNode[instructions.size()]); this.updates = instructions.toArray(new RNode[instructions.size()]);
this.targetTmpWrite = WriteVariableNode.createAnonymous(getTargetTmpName(tempNamesStartIndex), target, WriteVariableNode.Mode.INVISIBLE); this.targetTmpWrite = WriteVariableNode.createAnonymous(getTargetTmpName(tempNamesStartIndex), WriteVariableNode.Mode.INVISIBLE, target);
this.targetTmpRemove = RemoveAndAnswerNode.create(getTargetTmpName(tempNamesStartIndex)); this.targetTmpRemove = RemoveAndAnswerNode.create(getTargetTmpName(tempNamesStartIndex));
} }
......
...@@ -51,10 +51,10 @@ abstract class LoadMethod extends RBaseNode { ...@@ -51,10 +51,10 @@ abstract class LoadMethod extends RBaseNode {
@Child private GetFixedAttributeNode definedAttrAccess = GetFixedAttributeNode.create(RRuntime.R_DEFINED); @Child private GetFixedAttributeNode definedAttrAccess = GetFixedAttributeNode.create(RRuntime.R_DEFINED);
@Child private GetFixedAttributeNode nextMethodAttrAccess = GetFixedAttributeNode.create(RRuntime.R_NEXT_METHOD); @Child private GetFixedAttributeNode nextMethodAttrAccess = GetFixedAttributeNode.create(RRuntime.R_NEXT_METHOD);
@Child private GetFixedAttributeNode sourceAttrAccess = GetFixedAttributeNode.create(RRuntime.R_SOURCE); @Child private GetFixedAttributeNode sourceAttrAccess = GetFixedAttributeNode.create(RRuntime.R_SOURCE);
@Child private WriteLocalFrameVariableNode writeRTarget = WriteLocalFrameVariableNode.create(RRuntime.R_DOT_TARGET, null, WriteVariableNode.Mode.REGULAR); @Child private WriteLocalFrameVariableNode writeRTarget = WriteLocalFrameVariableNode.create(RRuntime.R_DOT_TARGET, WriteVariableNode.Mode.REGULAR, null);
@Child private WriteLocalFrameVariableNode writeRDefined = WriteLocalFrameVariableNode.create(RRuntime.R_DOT_DEFINED, null, WriteVariableNode.Mode.REGULAR); @Child private WriteLocalFrameVariableNode writeRDefined = WriteLocalFrameVariableNode.create(RRuntime.R_DOT_DEFINED, WriteVariableNode.Mode.REGULAR, null);
@Child private WriteLocalFrameVariableNode writeRNextMethod = WriteLocalFrameVariableNode.create(RRuntime.R_DOT_NEXT_METHOD, null, WriteVariableNode.Mode.REGULAR); @Child private WriteLocalFrameVariableNode writeRNextMethod = WriteLocalFrameVariableNode.create(RRuntime.R_DOT_NEXT_METHOD, WriteVariableNode.Mode.REGULAR, null);
@Child private WriteLocalFrameVariableNode writeRMethod = WriteLocalFrameVariableNode.create(RRuntime.R_DOT_METHOD, null, WriteVariableNode.Mode.REGULAR); @Child private WriteLocalFrameVariableNode writeRMethod = WriteLocalFrameVariableNode.create(RRuntime.R_DOT_METHOD, WriteVariableNode.Mode.REGULAR, null);
@Child private LocalReadVariableNode methodsEnvRead = LocalReadVariableNode.create("methods", true); @Child private LocalReadVariableNode methodsEnvRead = LocalReadVariableNode.create("methods", true);
@Child private ReadVariableNode loadMethodFind; @Child private ReadVariableNode loadMethodFind;
@Child private CallRFunctionNode loadMethodCall; @Child private CallRFunctionNode loadMethodCall;
......
/* /*
* Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
......
/* /*
* Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment