Skip to content

Commit ec8186f

Browse files
committed
Added PackWithReplacements method.
1 parent 8713e01 commit ec8186f

File tree

5 files changed

+97
-15
lines changed

5 files changed

+97
-15
lines changed

lib/main/sys/Pickle.oz

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export
4444

4545
Pack
4646
PackWithCells
47+
PackWithReplacements
4748
Unpack
4849

4950
define
@@ -54,7 +55,9 @@ define
5455
%% Save and its variants
5556
%%
5657

57-
Save = BootPickle.save
58+
fun {Save URL}
59+
{BootPickle.save URL nil}
60+
end
5861

5962
proc {SaveCompressed Value FileName Level}
6063
{Save Value FileName}
@@ -89,7 +92,22 @@ define
8992
%% Pack and its variants
9093
%%
9194

92-
Pack = BootPickle.pack
95+
%%% {Pack Object} = BS
96+
%%%
97+
%%% Serialize an object into a ByteString. May throw an exception if the
98+
%%% object contains non-serializable components (e.g. cells, unbound variables)
99+
fun {Pack Value}
100+
{BootPickle.pack Value nil}
101+
end
102+
103+
%%% {PackWithReplacements Object [From1#To1 From2#To2 ...]} = BS
104+
%%%
105+
%%% Serialize an object into a ByteString with some components temporarily
106+
%%% replaced. If the object contains any of FromN, they will all be replaced
107+
%%% by ToN before the serialization starts. Deserializing the byte string
108+
%%% will only give back ToN, not FromN. The Object itself will stay the same
109+
%%% after calling PackWithReplacements.
110+
PackWithReplacements = BootPickle.pack
93111

94112
fun {PackWithCells Value}
95113
{Pack Value}

platform-test/base/pickle.oz

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@
2323

2424
functor
2525
import
26-
BootPickle(pack:Pack unpack:Unpack) at 'x-oz://boot/Pickle'
27-
%Pickle(pack:Pack unpack:Unpack)
26+
%BootPickle(pack:Pack unpack:Unpack) at 'x-oz://boot/Pickle'
27+
Pickle(pack:Pack unpack:Unpack packWithReplacements:PackWithReplacements)
2828
BootName(newUnique:NewUniqueName newNamed:NewNamedName) at 'x-oz://boot/Name'
29+
System
2930
export
3031
Return
3132
define
@@ -115,5 +116,29 @@ define
115116
{All Nogoods fun {$ X} {TryPack X}==[X] end} = true
116117
{Thread.terminate BadThread}
117118
end keys:[pickle])
119+
120+
packWithReplacements(
121+
proc {$}
122+
A = {NewCell 1}
123+
B = {NewCell 1}
124+
C = A
125+
S U
126+
Replacements = [A#a B#b]
127+
Obj = [fun {$} A#B#c end A B C d A]
128+
in
129+
S = {PackWithReplacements Obj Replacements}
130+
U = {Unpack S}
131+
[a b a d a] = U.2
132+
%a#b#c = {U.1}
133+
%% ^ TODO cannot test this on local machine, the unpickler
134+
%% will recognize the anonymous function as a global node
135+
%% and reuse the existing method in this VM.
136+
[A B C d A] = Obj.2
137+
A#B#c = {Obj.1}
138+
true = {IsCell A}
139+
true = {IsCell B}
140+
true = (A \= B)
141+
end
142+
)
118143
])
119144
end

vm/vm/main/modules/modpickle.hh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ public:
4747
public:
4848
Pack(): Builtin("pack") {}
4949

50-
static void call(VM vm, In value, Out result) {
50+
static void call(VM vm, In value, In temporaryReplacement, Out result) {
5151
std::ostringstream buf;
52-
pickle(vm, value, buf);
52+
pickle(vm, value, temporaryReplacement, buf);
5353
std::string str = buf.str();
5454
auto bytes = newLString(vm,
5555
reinterpret_cast<const unsigned char*>(str.data()), str.size());
@@ -78,13 +78,13 @@ public:
7878
public:
7979
Save(): Builtin("save") {}
8080

81-
static void call(VM vm, In value, In fileNameVS) {
81+
static void call(VM vm, In value, In temporaryReplacement, In fileNameVS) {
8282
size_t fileNameSize = ozVSLengthForBuffer(vm, fileNameVS);
8383
std::string fileName;
8484
ozVSGet(vm, fileNameVS, fileNameSize, fileName);
8585

8686
std::ofstream file(fileName, std::ios_base::binary);
87-
pickle(vm, value, file);
87+
pickle(vm, value, temporaryReplacement, file);
8888
}
8989
};
9090

vm/vm/main/pickler.cc

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,14 @@ UnstableNode Pickler::buildTypesRecord(VM vm) {
6464
4);
6565
}
6666

67-
void Pickler::pickle(RichNode value) {
67+
static void restoreNodes(VM vm, VMAllocatedList<NodeBackup>& list) {
68+
for (auto& nodeBackup : list) {
69+
nodeBackup.restore();
70+
}
71+
list.clear(vm);
72+
}
73+
74+
void Pickler::pickle(RichNode value, RichNode temporaryReplacement) {
6875
auto typesRecord = RichNode(*vm->getPickleTypesRecord()).as<Record>();
6976
auto statelessTypes = RichNode(*typesRecord.getArity()).as<Arity>();
7077

@@ -78,6 +85,29 @@ void Pickler::pickle(RichNode value) {
7885
VMAllocatedList<PickleNode> nodes;
7986
UnstableNode resources = buildNil(vm);
8087

88+
// Apply temporary replacements.
89+
VMAllocatedList<NodeBackup> nodeReplacementBackups;
90+
{
91+
VMAllocatedList<std::pair<RichNode, RichNode>> replacements;
92+
93+
ozListForEach(vm, temporaryReplacement, [&replacements, this](RichNode pair) {
94+
using namespace patternmatching;
95+
RichNode from, to;
96+
if (matchesSharp(vm, pair, capture(from), capture(to))) {
97+
replacements.push_front_new(vm, from, to);
98+
} else {
99+
raiseTypeError(vm, "Tuple of the form From#To", pair);
100+
}
101+
}, "List of pairs");
102+
103+
for (auto& replacement : replacements) {
104+
nodeReplacementBackups.push_front(vm, replacement.first.makeBackup());
105+
replacement.first.reinit(vm, replacement.second);
106+
}
107+
108+
replacements.clear(vm);
109+
}
110+
81111
// Replace serialized nodes by Serialized(index)
82112
// and add them to the nodes list
83113
while (!cb.todoFrom.empty()) {
@@ -111,11 +141,9 @@ void Pickler::pickle(RichNode value) {
111141
}
112142

113143
// Restore nodes
114-
while (!nodeBackups.empty()) {
115-
nodeBackups.front().restore();
116-
nodeBackups.remove_front(vm);
117-
}
144+
restoreNodes(vm, nodeBackups);
118145

146+
// Ensure no remaining futures or resources.
119147
if (futures) {
120148
for (auto& pickleNode : nodes) {
121149
if (isFuture(pickleNode.node)) {
@@ -127,11 +155,13 @@ void Pickler::pickle(RichNode value) {
127155
if (isFuture(pickleNode.node)) {
128156
RichNode future = pickleNode.node;
129157
nodes.clear(vm);
158+
restoreNodes(vm, nodeReplacementBackups);
130159
waitFor(vm, future);
131160
}
132161
}
133162
} else if (!RichNode(resources).is<Atom>()) {
134163
nodes.clear(vm);
164+
restoreNodes(vm, nodeReplacementBackups);
135165

136166
raiseError(vm, "dp",
137167
"generic",
@@ -161,6 +191,9 @@ void Pickler::pickle(RichNode value) {
161191

162192
nativeint eof = 0;
163193
writeSize(eof);
194+
195+
nodes.clear(vm);
196+
restoreNodes(vm, nodeReplacementBackups);
164197
}
165198

166199
void Pickler::writeValues(VMAllocatedList<PickleNode>& nodes) {

vm/vm/main/pickler.hh

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public:
5050
Pickler(VM vm, std::ostream& output):
5151
vm(vm), output(output) {}
5252

53-
void pickle(RichNode value);
53+
void pickle(RichNode value, RichNode temporaryReplacement);
5454

5555
private:
5656
void writeValues(VMAllocatedList<PickleNode>& nodes);
@@ -92,9 +92,15 @@ private:
9292
// Entry point //
9393
/////////////////
9494

95+
inline
96+
void pickle(VM vm, RichNode value, RichNode temporaryReplacement, std::ostream& output) {
97+
Pickler(vm, output).pickle(value, temporaryReplacement);
98+
}
99+
95100
inline
96101
void pickle(VM vm, RichNode value, std::ostream& output) {
97-
Pickler(vm, output).pickle(value);
102+
auto temporaryReplacement = Atom::build(vm, vm->coreatoms.nil);
103+
pickle(vm, value, temporaryReplacement, output);
98104
}
99105

100106
}

0 commit comments

Comments
 (0)