zuzu-js 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +5 -0
- package/README.md +113 -0
- package/bin/zuzu +17 -0
- package/bin/zuzu-build-browser-bundle +57 -0
- package/bin/zuzu-generate-browser-stdlib +584 -0
- package/bin/zuzu-js +23 -0
- package/bin/zuzu-js-compile +152 -0
- package/bin/zuzu-js-electron +19 -0
- package/dist/zuzu-browser-worker.js +45574 -0
- package/dist/zuzu-browser.js +45362 -0
- package/lib/browser-bundle-entry.js +160 -0
- package/lib/browser-gui-renderer.js +387 -0
- package/lib/browser-runtime.js +167 -0
- package/lib/browser-worker-entry.js +413 -0
- package/lib/browser-ztests/runner.html +103 -0
- package/lib/browser-ztests/runner.js +369 -0
- package/lib/cli.js +350 -0
- package/lib/collections.js +367 -0
- package/lib/compiler.js +303 -0
- package/lib/electron/launcher.js +70 -0
- package/lib/electron/main.js +956 -0
- package/lib/electron/preload.js +80 -0
- package/lib/electron/renderer.html +122 -0
- package/lib/electron/renderer.js +24 -0
- package/lib/execution-metadata.js +18 -0
- package/lib/gui/dom-renderer.js +778 -0
- package/lib/host/browser-host.js +278 -0
- package/lib/host/capabilities.js +47 -0
- package/lib/host/electron-host.js +15 -0
- package/lib/host/node-host.js +74 -0
- package/lib/paths.js +150 -0
- package/lib/runtime-entrypoints.js +60 -0
- package/lib/runtime-helpers.js +886 -0
- package/lib/runtime.js +3529 -0
- package/lib/tap.js +37 -0
- package/lib/transpiler-new/ast.js +23 -0
- package/lib/transpiler-new/codegen.js +2455 -0
- package/lib/transpiler-new/errors.js +28 -0
- package/lib/transpiler-new/index.js +26 -0
- package/lib/transpiler-new/lexer.js +834 -0
- package/lib/transpiler-new/parser.js +2332 -0
- package/lib/transpiler-new/validate-bindings.js +326 -0
- package/lib/transpiler-utils.js +95 -0
- package/lib/transpiler.js +33 -0
- package/lib/zuzu.js +53 -0
- package/modules/javascript.js +193 -0
- package/modules/std/archive.js +603 -0
- package/modules/std/clib.js +338 -0
- package/modules/std/data/csv.js +1331 -0
- package/modules/std/data/json.js +531 -0
- package/modules/std/data/xml.js +441 -0
- package/modules/std/data/yaml.js +256 -0
- package/modules/std/db-worker.js +250 -0
- package/modules/std/db.js +664 -0
- package/modules/std/digest/_hash.js +443 -0
- package/modules/std/digest/md5.js +26 -0
- package/modules/std/digest/sha.js +72 -0
- package/modules/std/eval.js +10 -0
- package/modules/std/gui/objects.js +1519 -0
- package/modules/std/internals.js +571 -0
- package/modules/std/io/socks-worker.js +318 -0
- package/modules/std/io/socks.js +186 -0
- package/modules/std/io.js +475 -0
- package/modules/std/marshal/cbor.js +463 -0
- package/modules/std/marshal/graph.js +1624 -0
- package/modules/std/marshal.js +87 -0
- package/modules/std/math/bignum.js +91 -0
- package/modules/std/math.js +79 -0
- package/modules/std/net/dns.js +306 -0
- package/modules/std/net/http.js +820 -0
- package/modules/std/net/smtp.js +943 -0
- package/modules/std/net/url.js +109 -0
- package/modules/std/proc.js +602 -0
- package/modules/std/secure.js +3724 -0
- package/modules/std/string/base64.js +138 -0
- package/modules/std/string.js +299 -0
- package/modules/std/task.js +914 -0
- package/modules/std/time.js +579 -0
- package/modules/std/tui.js +188 -0
- package/modules/std/worker-thread.js +246 -0
- package/modules/std/worker.js +790 -0
- package/package.json +67 -0
- package/stdlib/modules/javascript.zzm +99 -0
- package/stdlib/modules/perl.zzm +105 -0
- package/stdlib/modules/std/archive.zzm +132 -0
- package/stdlib/modules/std/cache/lru.zzm +174 -0
- package/stdlib/modules/std/clib.zzm +112 -0
- package/stdlib/modules/std/colour.zzm +220 -0
- package/stdlib/modules/std/config.zzm +818 -0
- package/stdlib/modules/std/data/cbor.zzm +497 -0
- package/stdlib/modules/std/data/csv.zzm +285 -0
- package/stdlib/modules/std/data/ini.zzm +472 -0
- package/stdlib/modules/std/data/json/schema/core.zzm +573 -0
- package/stdlib/modules/std/data/json/schema/format.zzm +581 -0
- package/stdlib/modules/std/data/json/schema/model.zzm +255 -0
- package/stdlib/modules/std/data/json/schema/output.zzm +272 -0
- package/stdlib/modules/std/data/json/schema/relative_pointer.zzm +299 -0
- package/stdlib/modules/std/data/json/schema/validation.zzm +1503 -0
- package/stdlib/modules/std/data/json/schema.zzm +306 -0
- package/stdlib/modules/std/data/json.zzm +102 -0
- package/stdlib/modules/std/data/kdl/json.zzm +460 -0
- package/stdlib/modules/std/data/kdl/xml.zzm +387 -0
- package/stdlib/modules/std/data/kdl.zzm +1631 -0
- package/stdlib/modules/std/data/toml.zzm +756 -0
- package/stdlib/modules/std/data/toon.zzm +1017 -0
- package/stdlib/modules/std/data/xml/escape.zzm +156 -0
- package/stdlib/modules/std/data/xml.zzm +276 -0
- package/stdlib/modules/std/data/yaml.zzm +94 -0
- package/stdlib/modules/std/db.zzm +173 -0
- package/stdlib/modules/std/defer.zzm +75 -0
- package/stdlib/modules/std/digest/crc32.zzm +196 -0
- package/stdlib/modules/std/digest/md5.zzm +54 -0
- package/stdlib/modules/std/digest/sha.zzm +83 -0
- package/stdlib/modules/std/dump.zzm +317 -0
- package/stdlib/modules/std/eval.zzm +63 -0
- package/stdlib/modules/std/getopt.zzm +432 -0
- package/stdlib/modules/std/gui/dialogue.zzm +592 -0
- package/stdlib/modules/std/gui/objects.zzm +123 -0
- package/stdlib/modules/std/gui.zzm +1914 -0
- package/stdlib/modules/std/internals.zzm +139 -0
- package/stdlib/modules/std/io/socks.zzm +139 -0
- package/stdlib/modules/std/io.zzm +157 -0
- package/stdlib/modules/std/lingua/en.zzm +347 -0
- package/stdlib/modules/std/log.zzm +169 -0
- package/stdlib/modules/std/mail.zzm +2726 -0
- package/stdlib/modules/std/marshal.zzm +138 -0
- package/stdlib/modules/std/math/bignum.zzm +98 -0
- package/stdlib/modules/std/math/range.zzm +116 -0
- package/stdlib/modules/std/math/roman.zzm +156 -0
- package/stdlib/modules/std/math.zzm +141 -0
- package/stdlib/modules/std/net/dns.zzm +93 -0
- package/stdlib/modules/std/net/http.zzm +278 -0
- package/stdlib/modules/std/net/smtp.zzm +257 -0
- package/stdlib/modules/std/net/url.zzm +69 -0
- package/stdlib/modules/std/path/jsonpointer.zzm +526 -0
- package/stdlib/modules/std/path/kdl.zzm +1003 -0
- package/stdlib/modules/std/path/simple.zzm +520 -0
- package/stdlib/modules/std/path/z/context.zzm +147 -0
- package/stdlib/modules/std/path/z/evaluate.zzm +549 -0
- package/stdlib/modules/std/path/z/functions.zzm +874 -0
- package/stdlib/modules/std/path/z/lexer.zzm +490 -0
- package/stdlib/modules/std/path/z/node.zzm +1455 -0
- package/stdlib/modules/std/path/z/operators.zzm +445 -0
- package/stdlib/modules/std/path/z/parser.zzm +359 -0
- package/stdlib/modules/std/path/z.zzm +403 -0
- package/stdlib/modules/std/path/zz/functions.zzm +828 -0
- package/stdlib/modules/std/path/zz/operators.zzm +1036 -0
- package/stdlib/modules/std/path/zz.zzm +100 -0
- package/stdlib/modules/std/proc.zzm +155 -0
- package/stdlib/modules/std/result.zzm +149 -0
- package/stdlib/modules/std/secure.zzm +606 -0
- package/stdlib/modules/std/string/base64.zzm +66 -0
- package/stdlib/modules/std/string/quoted_printable.zzm +485 -0
- package/stdlib/modules/std/string.zzm +179 -0
- package/stdlib/modules/std/task.zzm +221 -0
- package/stdlib/modules/std/template/z.zzm +531 -0
- package/stdlib/modules/std/template/zz.zzm +62 -0
- package/stdlib/modules/std/time.zzm +188 -0
- package/stdlib/modules/std/tui.zzm +89 -0
- package/stdlib/modules/std/uuid.zzm +223 -0
- package/stdlib/modules/std/web/session.zzm +388 -0
- package/stdlib/modules/std/web/static.zzm +329 -0
- package/stdlib/modules/std/web.zzm +1942 -0
- package/stdlib/modules/std/worker.zzm +202 -0
- package/stdlib/modules/std/zuzuzoo.zzm +3960 -0
- package/stdlib/modules/test/more.zzm +528 -0
- package/stdlib/modules/test/parser.zzm +209 -0
|
@@ -0,0 +1,1624 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
BinaryString,
|
|
5
|
+
ZuzuBinary,
|
|
6
|
+
ZuzuBag,
|
|
7
|
+
Pair,
|
|
8
|
+
PairList,
|
|
9
|
+
isPairListLike,
|
|
10
|
+
isWeakCell,
|
|
11
|
+
makeWeakValue,
|
|
12
|
+
retainCollectionValue,
|
|
13
|
+
retainValue,
|
|
14
|
+
resolveWeakValue,
|
|
15
|
+
} = require( '../../../lib/runtime-helpers' );
|
|
16
|
+
const cbor = require( './cbor' );
|
|
17
|
+
|
|
18
|
+
const KIND_PAIR = 1;
|
|
19
|
+
const KIND_ARRAY = 2;
|
|
20
|
+
const KIND_DICT = 3;
|
|
21
|
+
const KIND_PAIRLIST = 4;
|
|
22
|
+
const KIND_SET = 5;
|
|
23
|
+
const KIND_BAG = 6;
|
|
24
|
+
const KIND_OBJECT = 7;
|
|
25
|
+
const KIND_FUNCTION = 8;
|
|
26
|
+
const KIND_CLASS = 9;
|
|
27
|
+
const KIND_TRAIT = 10;
|
|
28
|
+
const KIND_BOUND_METHOD = 11;
|
|
29
|
+
const KIND_TIME = 12;
|
|
30
|
+
const KIND_PATH = 13;
|
|
31
|
+
|
|
32
|
+
const CODE_FUNCTION = 1;
|
|
33
|
+
const CODE_CLASS = 2;
|
|
34
|
+
const CODE_TRAIT = 3;
|
|
35
|
+
const MAX_SAFE_INTEGER = 9_007_199_254_740_991;
|
|
36
|
+
const classRegistry = new Map();
|
|
37
|
+
const codeRegistry = new Map();
|
|
38
|
+
let runtimePolicy = {
|
|
39
|
+
host_name: 'node',
|
|
40
|
+
repo_root: null,
|
|
41
|
+
include_paths: [],
|
|
42
|
+
deny_modules: [],
|
|
43
|
+
debug_level: 0,
|
|
44
|
+
transpiler: null,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
function setRuntimePolicy( policy = {} ) {
|
|
48
|
+
runtimePolicy = {
|
|
49
|
+
...runtimePolicy,
|
|
50
|
+
...policy,
|
|
51
|
+
include_paths: Array.isArray( policy.include_paths )
|
|
52
|
+
? policy.include_paths.slice()
|
|
53
|
+
: runtimePolicy.include_paths,
|
|
54
|
+
deny_modules: Array.isArray( policy.deny_modules )
|
|
55
|
+
? policy.deny_modules.slice()
|
|
56
|
+
: runtimePolicy.deny_modules,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function dumpGraph( value ) {
|
|
61
|
+
const state = {
|
|
62
|
+
objects: [],
|
|
63
|
+
ids: new WeakMap(),
|
|
64
|
+
code: [],
|
|
65
|
+
codeIds: new WeakMap(),
|
|
66
|
+
onDump: new WeakSet(),
|
|
67
|
+
};
|
|
68
|
+
const root = encodeValue( value, state );
|
|
69
|
+
return cbor.encodeOne(
|
|
70
|
+
cbor.tag( cbor.SELF_DESCRIBED_TAG, [
|
|
71
|
+
cbor.textString( 'ZUZU-MARSHAL' ),
|
|
72
|
+
1,
|
|
73
|
+
new Map(),
|
|
74
|
+
root,
|
|
75
|
+
state.objects,
|
|
76
|
+
state.code,
|
|
77
|
+
] )
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function loadGraph( blob ) {
|
|
82
|
+
if ( !( blob instanceof BinaryString ) ) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
`TypeException: std/marshal.load expects BinaryString, got ${typeName( blob )}`
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
const decoded = cbor.decodeOne( blob );
|
|
88
|
+
const envelope = decodeEnvelope( decoded );
|
|
89
|
+
const codeValues = loadCodeTable( envelope.code );
|
|
90
|
+
const placeholders = allocatePlaceholders( envelope.objects, codeValues );
|
|
91
|
+
fillPlaceholders( envelope.objects, placeholders );
|
|
92
|
+
const value = decodeValue( envelope.root, placeholders, {
|
|
93
|
+
allowWeak: false,
|
|
94
|
+
context: 'Envelope root',
|
|
95
|
+
} );
|
|
96
|
+
runOnLoadHooks( envelope.objects, placeholders );
|
|
97
|
+
return value;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function safeToDumpGraph( value ) {
|
|
101
|
+
try {
|
|
102
|
+
dumpGraph( value );
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
catch ( _err ) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function encodeValue( value, state ) {
|
|
111
|
+
if ( isWeakCell( value ) ) {
|
|
112
|
+
return [ 1, encodeValue( resolveWeakValue( value ), state ) ];
|
|
113
|
+
}
|
|
114
|
+
if ( value == null ) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
if ( typeof value === 'boolean' ) {
|
|
118
|
+
return value;
|
|
119
|
+
}
|
|
120
|
+
if ( typeof value === 'number' ) {
|
|
121
|
+
return encodeNumber( value );
|
|
122
|
+
}
|
|
123
|
+
if ( typeof value === 'string' ) {
|
|
124
|
+
return cbor.textString( value );
|
|
125
|
+
}
|
|
126
|
+
if ( value instanceof ZuzuBinary ) {
|
|
127
|
+
return cbor.byteString( value.bytes );
|
|
128
|
+
}
|
|
129
|
+
if ( isBoundMethodValue( value ) ) {
|
|
130
|
+
return encodeBoundMethod( value, state );
|
|
131
|
+
}
|
|
132
|
+
if ( isUserClassValue( value ) ) {
|
|
133
|
+
return encodeClassReference( value, state );
|
|
134
|
+
}
|
|
135
|
+
if ( isTraitValue( value ) ) {
|
|
136
|
+
return encodeTraitReference( value, state );
|
|
137
|
+
}
|
|
138
|
+
if ( isFunctionValue( value ) ) {
|
|
139
|
+
return encodeFunctionReference( value, state );
|
|
140
|
+
}
|
|
141
|
+
if ( value instanceof Pair ) {
|
|
142
|
+
return encodeObjectTableValue( value, KIND_PAIR, state, encodePairPayload );
|
|
143
|
+
}
|
|
144
|
+
if ( Array.isArray( value ) ) {
|
|
145
|
+
return encodeObjectTableValue( value, KIND_ARRAY, state, encodeArrayPayload );
|
|
146
|
+
}
|
|
147
|
+
if ( isPairListLike( value ) ) {
|
|
148
|
+
return encodeObjectTableValue(
|
|
149
|
+
value,
|
|
150
|
+
KIND_PAIRLIST,
|
|
151
|
+
state,
|
|
152
|
+
encodePairListPayload
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
if ( value instanceof Set ) {
|
|
156
|
+
return encodeObjectTableValue( value, KIND_SET, state, encodeSetPayload );
|
|
157
|
+
}
|
|
158
|
+
if ( value instanceof ZuzuBag ) {
|
|
159
|
+
return encodeObjectTableValue( value, KIND_BAG, state, encodeBagPayload );
|
|
160
|
+
}
|
|
161
|
+
if ( isTimeValue( value ) ) {
|
|
162
|
+
return encodeObjectTableValue( value, KIND_TIME, state, encodeTimePayload );
|
|
163
|
+
}
|
|
164
|
+
if ( isPathValue( value ) ) {
|
|
165
|
+
return encodeObjectTableValue( value, KIND_PATH, state, encodePathPayload );
|
|
166
|
+
}
|
|
167
|
+
if ( isPlainDict( value ) ) {
|
|
168
|
+
return encodeObjectTableValue( value, KIND_DICT, state, encodeDictPayload );
|
|
169
|
+
}
|
|
170
|
+
if ( isUserObjectValue( value ) ) {
|
|
171
|
+
return encodeUserObject( value, state );
|
|
172
|
+
}
|
|
173
|
+
throw new Error( `Value of type ${typeName( value )} is not marshalable in this phase` );
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function encodeNumber( value ) {
|
|
177
|
+
if ( !Number.isFinite( value ) ) {
|
|
178
|
+
throw new Error( 'Number values must be finite' );
|
|
179
|
+
}
|
|
180
|
+
if (
|
|
181
|
+
!Object.is( value, -0 )
|
|
182
|
+
&& Number.isInteger( value )
|
|
183
|
+
&& Math.abs( value ) <= MAX_SAFE_INTEGER
|
|
184
|
+
) {
|
|
185
|
+
return value;
|
|
186
|
+
}
|
|
187
|
+
return Number( value );
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function encodeObjectTableValue( value, kind, state, payloadEncoder ) {
|
|
191
|
+
if ( state.ids.has( value ) ) {
|
|
192
|
+
return [ 0, state.ids.get( value ) ];
|
|
193
|
+
}
|
|
194
|
+
const id = state.objects.length;
|
|
195
|
+
state.ids.set( value, id );
|
|
196
|
+
state.objects.push( null );
|
|
197
|
+
state.objects[id] = [ kind, payloadEncoder( value, state ) ];
|
|
198
|
+
return [ 0, id ];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function encodeArrayPayload( value, state ) {
|
|
202
|
+
return value.map( (item) => encodeValue( item, state ) );
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function encodeDictPayload( value, state ) {
|
|
206
|
+
return Object.keys( value )
|
|
207
|
+
.sort()
|
|
208
|
+
.map( (key) => [
|
|
209
|
+
cbor.textString( key ),
|
|
210
|
+
encodeValue( value[key], state ),
|
|
211
|
+
] );
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function encodePairListPayload( value, state ) {
|
|
215
|
+
return value.list.map( (pair) => [
|
|
216
|
+
cbor.textString( pair[0] ),
|
|
217
|
+
encodeValue( pair[1], state ),
|
|
218
|
+
] );
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function encodeSetPayload( value, state ) {
|
|
222
|
+
return [ ...value ].map( (item) => encodeValue( item, state ) );
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function encodeBagPayload( value, state ) {
|
|
226
|
+
return value.items.map( (item) => encodeValue( item, state ) );
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function encodePairPayload( value, state ) {
|
|
230
|
+
return [
|
|
231
|
+
cbor.textString( value.key() ),
|
|
232
|
+
encodeValue( value.value(), state ),
|
|
233
|
+
];
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function encodeTimePayload( value, _state ) {
|
|
237
|
+
const epoch = typeof value.epoch === 'function' ? value.epoch() : value._epoch;
|
|
238
|
+
if ( typeof epoch !== 'number' || !Number.isFinite( epoch ) ) {
|
|
239
|
+
throw new Error( 'Time value has invalid internal epoch' );
|
|
240
|
+
}
|
|
241
|
+
const zone = typeof value.timezone === 'function'
|
|
242
|
+
? value.timezone().to_String()
|
|
243
|
+
: value._timezone;
|
|
244
|
+
if ( zone == null ) {
|
|
245
|
+
return [ encodeNumber( epoch ) ];
|
|
246
|
+
}
|
|
247
|
+
return [ encodeNumber( epoch ), cbor.textString( String( zone ) ) ];
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function encodePathPayload( value, _state ) {
|
|
251
|
+
const pathText = typeof value.to_String === 'function'
|
|
252
|
+
? value.to_String()
|
|
253
|
+
: value.value;
|
|
254
|
+
if ( pathText == null ) {
|
|
255
|
+
throw new Error( 'Path value has invalid internal path' );
|
|
256
|
+
}
|
|
257
|
+
return [ cbor.textString( pathText ) ];
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function encodeUserObject( value, state ) {
|
|
261
|
+
if ( state.ids.has( value ) ) {
|
|
262
|
+
return [ 0, state.ids.get( value ) ];
|
|
263
|
+
}
|
|
264
|
+
if ( !state.onDump.has( value ) ) {
|
|
265
|
+
state.onDump.add( value );
|
|
266
|
+
if ( typeof value.__on_dump__ === 'function' ) {
|
|
267
|
+
try {
|
|
268
|
+
value.__on_dump__();
|
|
269
|
+
}
|
|
270
|
+
catch ( err ) {
|
|
271
|
+
throw new Error(
|
|
272
|
+
`__on_dump__ for ${typeName( value )} failed: ${err.message || err}`
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
const id = state.objects.length;
|
|
278
|
+
state.ids.set( value, id );
|
|
279
|
+
state.objects.push( null );
|
|
280
|
+
const slots = Object.keys( value )
|
|
281
|
+
.filter( (key) => isMarshalableObjectSlot( value[key] ) )
|
|
282
|
+
.sort()
|
|
283
|
+
.map( (key) => [
|
|
284
|
+
cbor.textString( key ),
|
|
285
|
+
encodeValue( value[key], state ),
|
|
286
|
+
] );
|
|
287
|
+
state.objects[id] = [
|
|
288
|
+
KIND_OBJECT,
|
|
289
|
+
[
|
|
290
|
+
encodeClassReference( value.constructor, state ),
|
|
291
|
+
slots,
|
|
292
|
+
],
|
|
293
|
+
];
|
|
294
|
+
return [ 0, id ];
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function encodeBoundMethod( value, state ) {
|
|
298
|
+
if ( state.ids.has( value ) ) {
|
|
299
|
+
return [ 0, state.ids.get( value ) ];
|
|
300
|
+
}
|
|
301
|
+
const receiver = value.__zuzu_bound_receiver || value.receiver || null;
|
|
302
|
+
const methodName = value.__zuzu_bound_method_name || value.name;
|
|
303
|
+
if ( !receiver || !methodName ) {
|
|
304
|
+
throw new Error( 'Bound method values are not marshalable in this phase' );
|
|
305
|
+
}
|
|
306
|
+
const id = state.objects.length;
|
|
307
|
+
state.ids.set( value, id );
|
|
308
|
+
state.objects.push( null );
|
|
309
|
+
state.objects[id] = [
|
|
310
|
+
KIND_BOUND_METHOD,
|
|
311
|
+
[
|
|
312
|
+
encodeValue( receiver, state ),
|
|
313
|
+
cbor.textString( methodName ),
|
|
314
|
+
],
|
|
315
|
+
];
|
|
316
|
+
return [ 0, id ];
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function encodeFunctionReference( fn, state ) {
|
|
320
|
+
return encodeCodeBackedObject( fn, KIND_FUNCTION, state, encodeFunctionCode );
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function encodeTraitReference( trait, state ) {
|
|
324
|
+
return encodeCodeBackedObject( trait, KIND_TRAIT, state, encodeTraitCode );
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function encodeCodeBackedObject( value, kind, state, codeEncoder ) {
|
|
328
|
+
if ( state.ids.has( value ) ) {
|
|
329
|
+
return [ 0, state.ids.get( value ) ];
|
|
330
|
+
}
|
|
331
|
+
const id = state.objects.length;
|
|
332
|
+
state.ids.set( value, id );
|
|
333
|
+
state.objects.push( null );
|
|
334
|
+
const codeId = codeEncoder( value, state );
|
|
335
|
+
state.objects[id] = [ kind, [ codeId ] ];
|
|
336
|
+
return [ 0, id ];
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function encodeClassReference( klass, state ) {
|
|
340
|
+
if ( !isUserClassValue( klass ) ) {
|
|
341
|
+
throw new Error( 'Object class is not marshalable in this phase' );
|
|
342
|
+
}
|
|
343
|
+
return encodeCodeBackedObject( klass, KIND_CLASS, state, encodeClassCode );
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function encodeClassCode( klass, state ) {
|
|
347
|
+
if ( !klass.__zuzu_marshal_meta ) {
|
|
348
|
+
Object.defineProperty( klass, '__zuzu_marshal_meta', {
|
|
349
|
+
value: {
|
|
350
|
+
kind: 'class',
|
|
351
|
+
name: klass.__zuzu_class_name || klass.name,
|
|
352
|
+
source: marshalClassSource( klass, klass.__zuzu_class_name || klass.name ),
|
|
353
|
+
captures: {},
|
|
354
|
+
},
|
|
355
|
+
enumerable: false,
|
|
356
|
+
configurable: true,
|
|
357
|
+
writable: true,
|
|
358
|
+
} );
|
|
359
|
+
}
|
|
360
|
+
return encodeCodeRecord( klass, state, CODE_CLASS );
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function encodeFunctionCode( fn, state, preferredName = null ) {
|
|
364
|
+
return encodeCodeRecord( fn, state, CODE_FUNCTION, preferredName );
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function encodeTraitCode( trait, state, preferredName = null ) {
|
|
368
|
+
return encodeCodeRecord( trait, state, CODE_TRAIT, preferredName );
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function encodeCodeRecord( value, state, kind, preferredName = null ) {
|
|
372
|
+
if ( state.codeIds.has( value ) ) {
|
|
373
|
+
return state.codeIds.get( value );
|
|
374
|
+
}
|
|
375
|
+
const meta = value.__zuzu_marshal_meta || {};
|
|
376
|
+
if ( !meta.source ) {
|
|
377
|
+
throw new Error( `Value of type ${typeName( value )} is not marshalable in this phase` );
|
|
378
|
+
}
|
|
379
|
+
const id = state.code.length;
|
|
380
|
+
state.codeIds.set( value, id );
|
|
381
|
+
state.code.push( null );
|
|
382
|
+
const bindingName = marshalBindingName( preferredName || meta.name, id );
|
|
383
|
+
const captures = [];
|
|
384
|
+
const dependencies = [];
|
|
385
|
+
for ( const [ name, captured ] of Object.entries( meta.captures || {} ).sort() ) {
|
|
386
|
+
if ( isFunctionValue( captured ) ) {
|
|
387
|
+
dependencies.push( [ 0, encodeFunctionCode( captured, state, name ) ] );
|
|
388
|
+
}
|
|
389
|
+
else if ( isUserClassValue( captured ) ) {
|
|
390
|
+
dependencies.push( [ 0, encodeClassCode( captured, state, name ) ] );
|
|
391
|
+
}
|
|
392
|
+
else if ( isTraitValue( captured ) ) {
|
|
393
|
+
dependencies.push( [ 0, encodeTraitCode( captured, state, name ) ] );
|
|
394
|
+
}
|
|
395
|
+
else if ( isScalarForCapture( captured ) ) {
|
|
396
|
+
captures.push( [ cbor.textString( name ), encodeValue( captured, state ) ] );
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
registerCodeValue( kind, bindingName, meta.source, value );
|
|
400
|
+
state.code[id] = [
|
|
401
|
+
kind,
|
|
402
|
+
cbor.textString( bindingName ),
|
|
403
|
+
cbor.textString( meta.source ),
|
|
404
|
+
captures,
|
|
405
|
+
dependencies,
|
|
406
|
+
];
|
|
407
|
+
return id;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function decodeEnvelope( decoded ) {
|
|
411
|
+
if ( !cbor.isTagged( decoded ) || cbor.tagNumber( decoded ) !== cbor.SELF_DESCRIBED_TAG ) {
|
|
412
|
+
throw new Error( 'Top-level item is not tag 55799' );
|
|
413
|
+
}
|
|
414
|
+
const envelope = cbor.tagValue( decoded );
|
|
415
|
+
if ( !Array.isArray( envelope ) ) {
|
|
416
|
+
throw new Error( 'Envelope must be an array' );
|
|
417
|
+
}
|
|
418
|
+
if ( envelope.length !== 6 ) {
|
|
419
|
+
throw new Error( 'Envelope must contain exactly 6 fields' );
|
|
420
|
+
}
|
|
421
|
+
const [ magic, version, options, root, objects, code ] = envelope;
|
|
422
|
+
if ( !cbor.isTextString( magic ) || cbor.textValue( magic ) !== 'ZUZU-MARSHAL' ) {
|
|
423
|
+
throw new Error( 'Envelope magic is invalid' );
|
|
424
|
+
}
|
|
425
|
+
if ( typeof version !== 'number' || version !== 1 ) {
|
|
426
|
+
throw new Error( 'Unsupported Zuzu Marshal version' );
|
|
427
|
+
}
|
|
428
|
+
if ( !( options instanceof Map ) ) {
|
|
429
|
+
throw new Error( 'Envelope options must be a map' );
|
|
430
|
+
}
|
|
431
|
+
if ( !Array.isArray( objects ) ) {
|
|
432
|
+
throw new Error( 'Envelope object table must be an array' );
|
|
433
|
+
}
|
|
434
|
+
if ( !Array.isArray( code ) ) {
|
|
435
|
+
throw new Error( 'Envelope code table must be an array' );
|
|
436
|
+
}
|
|
437
|
+
return { root, objects, code };
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function loadCodeTable( code ) {
|
|
441
|
+
const records = code.map( validateCodeRecord );
|
|
442
|
+
const values = [];
|
|
443
|
+
const loading = new Set();
|
|
444
|
+
function loadById( id ) {
|
|
445
|
+
if ( values[id] ) {
|
|
446
|
+
return values[id];
|
|
447
|
+
}
|
|
448
|
+
if ( loading.has( id ) ) {
|
|
449
|
+
throw new Error( `Cyclic code dependency involving record ${id}` );
|
|
450
|
+
}
|
|
451
|
+
loading.add( id );
|
|
452
|
+
const value = loadCodeRecord( records[id], id, loadById );
|
|
453
|
+
values[id] = value;
|
|
454
|
+
loading.delete( id );
|
|
455
|
+
return value;
|
|
456
|
+
}
|
|
457
|
+
for ( let id = 0; id < records.length; id++ ) {
|
|
458
|
+
loadById( id );
|
|
459
|
+
}
|
|
460
|
+
return values;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function validateCodeRecord( record, id ) {
|
|
464
|
+
assertRecordArray( record, `Code table entry ${id}`, 5 );
|
|
465
|
+
const [ kind, bindingNameValue, sourceValue, captures, dependencies ] = record;
|
|
466
|
+
assertInteger( kind, `Code table entry ${id} kind` );
|
|
467
|
+
if ( ![ CODE_FUNCTION, CODE_CLASS, CODE_TRAIT ].includes( kind ) ) {
|
|
468
|
+
throw new Error( `Unsupported code kind ${kind} in current loader` );
|
|
469
|
+
}
|
|
470
|
+
if ( !cbor.isTextString( bindingNameValue ) ) {
|
|
471
|
+
throw new Error( `Code table entry ${id} binding name must be a text string` );
|
|
472
|
+
}
|
|
473
|
+
if ( !cbor.isTextString( sourceValue ) ) {
|
|
474
|
+
throw new Error( `Code table entry ${id} source must be a text string` );
|
|
475
|
+
}
|
|
476
|
+
if ( !Array.isArray( captures ) ) {
|
|
477
|
+
throw new Error( `Code table entry ${id} captures must be an array` );
|
|
478
|
+
}
|
|
479
|
+
if ( !Array.isArray( dependencies ) ) {
|
|
480
|
+
throw new Error( `Code table entry ${id} dependencies must be an array` );
|
|
481
|
+
}
|
|
482
|
+
return {
|
|
483
|
+
kind,
|
|
484
|
+
bindingName: cbor.textValue( bindingNameValue ),
|
|
485
|
+
source: cbor.textValue( sourceValue ),
|
|
486
|
+
captures,
|
|
487
|
+
dependencies,
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function loadCodeRecord( record, id, loadById ) {
|
|
492
|
+
const registered = codeRegistry.get(
|
|
493
|
+
codeRegistryKey( record.kind, record.bindingName, record.source )
|
|
494
|
+
);
|
|
495
|
+
if ( registered ) {
|
|
496
|
+
return registered;
|
|
497
|
+
}
|
|
498
|
+
const env = Object.create( null );
|
|
499
|
+
for ( const capture of record.captures ) {
|
|
500
|
+
assertRecordArray( capture, `Capture in code record ${id}`, 2 );
|
|
501
|
+
const [ nameValue, encodedValue ] = capture;
|
|
502
|
+
if ( !cbor.isTextString( nameValue ) ) {
|
|
503
|
+
throw new Error( `Capture name in code record ${id} must be a text string` );
|
|
504
|
+
}
|
|
505
|
+
const name = cbor.textValue( nameValue );
|
|
506
|
+
if ( isWeakStorageRecord( encodedValue ) ) {
|
|
507
|
+
throw new Error(
|
|
508
|
+
`Capture '${name}' in code record ${id} weak storage record is not allowed here`
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
env[name] = decodeScalar( encodedValue );
|
|
512
|
+
}
|
|
513
|
+
for ( const dependency of record.dependencies ) {
|
|
514
|
+
assertRecordArray( dependency, `Code dependency in record ${id}`, 2 );
|
|
515
|
+
const [ depKind, depId ] = dependency;
|
|
516
|
+
assertInteger( depKind, `Code dependency in record ${id} kind` );
|
|
517
|
+
if ( depKind !== 0 ) {
|
|
518
|
+
throw new Error( `Unsupported code dependency kind ${depKind} in record ${id}` );
|
|
519
|
+
}
|
|
520
|
+
assertInteger( depId, `Internal dependency in record ${id}` );
|
|
521
|
+
const value = loadById( depId );
|
|
522
|
+
const name = value.__zuzu_class_name
|
|
523
|
+
|| value.__zuzu_trait_name
|
|
524
|
+
|| value.__zuzu_marshal_meta && value.__zuzu_marshal_meta.name;
|
|
525
|
+
if ( name ) {
|
|
526
|
+
env[name] = value;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
if ( record.kind === CODE_FUNCTION ) {
|
|
530
|
+
return buildFunctionFromSource( record.bindingName, record.source, env );
|
|
531
|
+
}
|
|
532
|
+
if ( record.kind === CODE_CLASS ) {
|
|
533
|
+
return buildClassFromSource( record.bindingName, record.source, env );
|
|
534
|
+
}
|
|
535
|
+
return buildTraitFromSource( record.bindingName, record.source, env );
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
function allocatePlaceholders( objects, codeValues ) {
|
|
539
|
+
const placeholders = [];
|
|
540
|
+
for ( let id = 0; id < objects.length; id++ ) {
|
|
541
|
+
const entry = objects[id];
|
|
542
|
+
assertRecordArray( entry, `Object table entry ${id}`, 2 );
|
|
543
|
+
const kind = entry[0];
|
|
544
|
+
assertInteger( kind, `Object table entry ${id} kind` );
|
|
545
|
+
switch ( kind ) {
|
|
546
|
+
case KIND_PAIR:
|
|
547
|
+
placeholders[id] = new Pair( { pair: [] } );
|
|
548
|
+
break;
|
|
549
|
+
case KIND_ARRAY:
|
|
550
|
+
placeholders[id] = [];
|
|
551
|
+
break;
|
|
552
|
+
case KIND_DICT:
|
|
553
|
+
placeholders[id] = {};
|
|
554
|
+
break;
|
|
555
|
+
case KIND_PAIRLIST:
|
|
556
|
+
placeholders[id] = new PairList( [] );
|
|
557
|
+
break;
|
|
558
|
+
case KIND_SET:
|
|
559
|
+
placeholders[id] = new Set();
|
|
560
|
+
break;
|
|
561
|
+
case KIND_BAG:
|
|
562
|
+
placeholders[id] = new ZuzuBag( [] );
|
|
563
|
+
break;
|
|
564
|
+
case KIND_OBJECT:
|
|
565
|
+
placeholders[id] = {};
|
|
566
|
+
break;
|
|
567
|
+
case KIND_FUNCTION:
|
|
568
|
+
placeholders[id] = decodeFunctionPayload( id, entry[1], codeValues );
|
|
569
|
+
break;
|
|
570
|
+
case KIND_CLASS:
|
|
571
|
+
placeholders[id] = decodeClassPayload( id, entry[1], codeValues );
|
|
572
|
+
break;
|
|
573
|
+
case KIND_TRAIT:
|
|
574
|
+
placeholders[id] = decodeTraitPayload( id, entry[1], codeValues );
|
|
575
|
+
break;
|
|
576
|
+
case KIND_BOUND_METHOD:
|
|
577
|
+
placeholders[id] = createBoundMethodPlaceholder( id );
|
|
578
|
+
break;
|
|
579
|
+
case KIND_TIME:
|
|
580
|
+
placeholders[id] = new ( timeClass() )( 0 );
|
|
581
|
+
break;
|
|
582
|
+
case KIND_PATH:
|
|
583
|
+
placeholders[id] = new ( pathClass() )( '.' );
|
|
584
|
+
break;
|
|
585
|
+
default:
|
|
586
|
+
throw new Error( `Unsupported object kind ${kind} in current loader` );
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
return placeholders;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
function fillPlaceholders( objects, placeholders ) {
|
|
593
|
+
for ( let id = 0; id < objects.length; id++ ) {
|
|
594
|
+
const [ kind, payload ] = objects[id];
|
|
595
|
+
switch ( kind ) {
|
|
596
|
+
case KIND_PAIR:
|
|
597
|
+
fillPair( id, payload, placeholders );
|
|
598
|
+
break;
|
|
599
|
+
case KIND_ARRAY:
|
|
600
|
+
fillArray( id, payload, placeholders );
|
|
601
|
+
break;
|
|
602
|
+
case KIND_DICT:
|
|
603
|
+
fillDict( id, payload, placeholders );
|
|
604
|
+
break;
|
|
605
|
+
case KIND_PAIRLIST:
|
|
606
|
+
fillPairList( id, payload, placeholders );
|
|
607
|
+
break;
|
|
608
|
+
case KIND_SET:
|
|
609
|
+
fillSet( id, payload, placeholders );
|
|
610
|
+
break;
|
|
611
|
+
case KIND_BAG:
|
|
612
|
+
fillBag( id, payload, placeholders );
|
|
613
|
+
break;
|
|
614
|
+
case KIND_OBJECT:
|
|
615
|
+
fillObject( id, payload, placeholders );
|
|
616
|
+
break;
|
|
617
|
+
case KIND_FUNCTION:
|
|
618
|
+
break;
|
|
619
|
+
case KIND_CLASS:
|
|
620
|
+
break;
|
|
621
|
+
case KIND_TRAIT:
|
|
622
|
+
break;
|
|
623
|
+
case KIND_BOUND_METHOD:
|
|
624
|
+
break;
|
|
625
|
+
case KIND_TIME:
|
|
626
|
+
fillTime( id, payload, placeholders );
|
|
627
|
+
break;
|
|
628
|
+
case KIND_PATH:
|
|
629
|
+
fillPath( id, payload, placeholders );
|
|
630
|
+
break;
|
|
631
|
+
default:
|
|
632
|
+
throw new Error( `Unsupported object kind ${kind} in current loader` );
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
for ( let id = 0; id < objects.length; id++ ) {
|
|
636
|
+
const [ kind, payload ] = objects[id];
|
|
637
|
+
if ( kind === KIND_BOUND_METHOD ) {
|
|
638
|
+
fillBoundMethodPayload( id, payload, placeholders );
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
function fillPair( id, payload, placeholders ) {
|
|
644
|
+
assertRecordArray( payload, `Pair object payload ${id}`, 2 );
|
|
645
|
+
const [ key, value ] = payload;
|
|
646
|
+
if ( !cbor.isTextString( key ) ) {
|
|
647
|
+
throw new Error( `Pair object payload ${id} key must be a text string` );
|
|
648
|
+
}
|
|
649
|
+
placeholders[id].pair = [
|
|
650
|
+
cbor.textValue( key ),
|
|
651
|
+
storeStrongLoadedValue( decodeValue( value, placeholders, {
|
|
652
|
+
context: `Pair object payload ${id} value`,
|
|
653
|
+
} ) ),
|
|
654
|
+
];
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
function fillArray( id, payload, placeholders ) {
|
|
658
|
+
if ( !Array.isArray( payload ) ) {
|
|
659
|
+
throw new Error( `Array object payload ${id} must be an array` );
|
|
660
|
+
}
|
|
661
|
+
placeholders[id].splice(
|
|
662
|
+
0,
|
|
663
|
+
placeholders[id].length,
|
|
664
|
+
...payload.map( (item) => storeCollectionLoadedValue(
|
|
665
|
+
placeholders[id],
|
|
666
|
+
decodeValue(
|
|
667
|
+
item,
|
|
668
|
+
placeholders,
|
|
669
|
+
{ context: `Array object payload ${id} item` }
|
|
670
|
+
)
|
|
671
|
+
) )
|
|
672
|
+
);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
function fillDict( id, payload, placeholders ) {
|
|
676
|
+
if ( !Array.isArray( payload ) ) {
|
|
677
|
+
throw new Error( `Dict object payload ${id} must be an array` );
|
|
678
|
+
}
|
|
679
|
+
const target = placeholders[id];
|
|
680
|
+
for ( const pair of payload ) {
|
|
681
|
+
const [ key, value ] = decodeKeyValueRecord(
|
|
682
|
+
`Dict object payload ${id}`,
|
|
683
|
+
pair,
|
|
684
|
+
placeholders
|
|
685
|
+
);
|
|
686
|
+
if ( Object.prototype.hasOwnProperty.call( target, key ) ) {
|
|
687
|
+
throw new Error( `Dict object payload ${id} contains duplicate key '${key}'` );
|
|
688
|
+
}
|
|
689
|
+
target[key] = storeCollectionLoadedValue( target, value );
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
function fillPairList( id, payload, placeholders ) {
|
|
694
|
+
if ( !Array.isArray( payload ) ) {
|
|
695
|
+
throw new Error( `PairList object payload ${id} must be an array` );
|
|
696
|
+
}
|
|
697
|
+
placeholders[id].list = payload.map( (pair) => {
|
|
698
|
+
const [ key, value ] = decodeKeyValueRecord(
|
|
699
|
+
`PairList object payload ${id}`,
|
|
700
|
+
pair,
|
|
701
|
+
placeholders
|
|
702
|
+
);
|
|
703
|
+
return [ key, storeCollectionLoadedValue( placeholders[id], value ) ];
|
|
704
|
+
} );
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
function fillSet( id, payload, placeholders ) {
|
|
708
|
+
for ( const item of decodeItemPayload(
|
|
709
|
+
`Set object payload ${id}`,
|
|
710
|
+
payload,
|
|
711
|
+
placeholders
|
|
712
|
+
) ) {
|
|
713
|
+
placeholders[id].add( storeCollectionLoadedValue( placeholders[id], item ) );
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
function fillBag( id, payload, placeholders ) {
|
|
718
|
+
placeholders[id].items = decodeItemPayload(
|
|
719
|
+
`Bag object payload ${id}`,
|
|
720
|
+
payload,
|
|
721
|
+
placeholders
|
|
722
|
+
).map( (item) => storeCollectionLoadedValue( placeholders[id], item ) );
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
function fillTime( id, payload, placeholders ) {
|
|
726
|
+
if ( !Array.isArray( payload ) || ( payload.length !== 1 && payload.length !== 2 ) ) {
|
|
727
|
+
throw new Error( `Time object payload ${id} must contain epoch and optional timezone` );
|
|
728
|
+
}
|
|
729
|
+
const epoch = payload[0];
|
|
730
|
+
if ( typeof epoch !== 'number' ) {
|
|
731
|
+
throw new Error( `Time object payload ${id} epoch must be a number` );
|
|
732
|
+
}
|
|
733
|
+
placeholders[id]._epoch = epoch;
|
|
734
|
+
placeholders[id]._timezone = payload.length > 1 && cbor.isTextString( payload[1] )
|
|
735
|
+
? cbor.textValue( payload[1] )
|
|
736
|
+
: 'UTC';
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
function fillPath( id, payload, placeholders ) {
|
|
740
|
+
assertRecordArray( payload, `Path object payload ${id}`, 1 );
|
|
741
|
+
const pathValue = payload[0];
|
|
742
|
+
if ( !cbor.isTextString( pathValue ) ) {
|
|
743
|
+
throw new Error( `Path object payload ${id} path must be a text string` );
|
|
744
|
+
}
|
|
745
|
+
placeholders[id].value = cbor.textValue( pathValue );
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
function decodeFunctionPayload( id, payload, codeValues ) {
|
|
749
|
+
return decodeCodeBackedPayload( 'Function', id, payload, codeValues );
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
function decodeClassPayload( id, payload, codeValues ) {
|
|
753
|
+
return decodeCodeBackedPayload( 'Class', id, payload, codeValues );
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
function decodeTraitPayload( id, payload, codeValues ) {
|
|
757
|
+
return decodeCodeBackedPayload( 'Trait', id, payload, codeValues );
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
function decodeCodeBackedPayload( label, id, payload, codeValues ) {
|
|
761
|
+
assertRecordArray( payload, `${label} object payload ${id}`, 1 );
|
|
762
|
+
const codeId = payload[0];
|
|
763
|
+
if ( isWeakStorageRecord( codeId ) ) {
|
|
764
|
+
throw new Error( `${label} object payload ${id} code id weak storage record is not allowed here` );
|
|
765
|
+
}
|
|
766
|
+
assertInteger( codeId, `${label} object payload ${id} code id` );
|
|
767
|
+
if ( codeId < 0 || codeId >= codeValues.length ) {
|
|
768
|
+
throw new Error( `${label} object payload ${id} code id is outside the code table` );
|
|
769
|
+
}
|
|
770
|
+
return codeValues[codeId];
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
function createBoundMethodPlaceholder( id ) {
|
|
774
|
+
const fn = function __zuzu_loaded_bound_method( ...args ) {
|
|
775
|
+
const receiver = fn.__zuzu_bound_receiver;
|
|
776
|
+
const methodName = fn.__zuzu_bound_method_name;
|
|
777
|
+
if ( receiver == null || typeof receiver[methodName] !== 'function' ) {
|
|
778
|
+
throw new Error( `Bound method object payload ${id} method is not loaded` );
|
|
779
|
+
}
|
|
780
|
+
return receiver[methodName]( ...args );
|
|
781
|
+
};
|
|
782
|
+
Object.defineProperty( fn, '__zuzu_method', {
|
|
783
|
+
value: true,
|
|
784
|
+
enumerable: false,
|
|
785
|
+
configurable: true,
|
|
786
|
+
} );
|
|
787
|
+
fn.invoke = function invoke( _self, args = [] ) {
|
|
788
|
+
return fn( ...args );
|
|
789
|
+
};
|
|
790
|
+
return fn;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
function fillBoundMethodPayload( id, payload, placeholders ) {
|
|
794
|
+
assertRecordArray( payload, `Bound method object payload ${id}`, 2 );
|
|
795
|
+
const receiver = decodeValue( payload[0], placeholders, {
|
|
796
|
+
allowWeak: false,
|
|
797
|
+
context: `Bound method object payload ${id} receiver`,
|
|
798
|
+
} );
|
|
799
|
+
if ( receiver == null || typeof receiver !== 'object' ) {
|
|
800
|
+
throw new Error( `Bound method object payload ${id} receiver must resolve to an Object` );
|
|
801
|
+
}
|
|
802
|
+
if ( !cbor.isTextString( payload[1] ) ) {
|
|
803
|
+
throw new Error( `Bound method object payload ${id} method name must be a text string` );
|
|
804
|
+
}
|
|
805
|
+
const methodName = cbor.textValue( payload[1] );
|
|
806
|
+
if ( typeof receiver[methodName] !== 'function' ) {
|
|
807
|
+
throw new Error(
|
|
808
|
+
`Bound method object payload ${id} method '${methodName}' was not found`
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
const fn = placeholders[id];
|
|
812
|
+
Object.defineProperty( fn, '__zuzu_bound_receiver', {
|
|
813
|
+
value: receiver,
|
|
814
|
+
enumerable: false,
|
|
815
|
+
configurable: true,
|
|
816
|
+
writable: true,
|
|
817
|
+
} );
|
|
818
|
+
Object.defineProperty( fn, '__zuzu_bound_method_name', {
|
|
819
|
+
value: methodName,
|
|
820
|
+
enumerable: false,
|
|
821
|
+
configurable: true,
|
|
822
|
+
writable: true,
|
|
823
|
+
} );
|
|
824
|
+
fn.invoke = function invoke( _self, args = [] ) {
|
|
825
|
+
return receiver[methodName]( ...args );
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
function fillObject( id, payload, placeholders ) {
|
|
830
|
+
assertRecordArray( payload, `Object payload ${id}`, 2 );
|
|
831
|
+
const [ classRef, slotPayload ] = payload;
|
|
832
|
+
const klass = decodeValue( classRef, placeholders, {
|
|
833
|
+
allowWeak: false,
|
|
834
|
+
context: `Object payload ${id} class`,
|
|
835
|
+
} );
|
|
836
|
+
if ( typeof klass !== 'function' ) {
|
|
837
|
+
throw new Error( `Object payload ${id} class must resolve to a Class` );
|
|
838
|
+
}
|
|
839
|
+
if ( !Array.isArray( slotPayload ) ) {
|
|
840
|
+
throw new Error( `Object payload ${id} slots must be an array` );
|
|
841
|
+
}
|
|
842
|
+
const object = placeholders[id];
|
|
843
|
+
Object.setPrototypeOf( object, klass.prototype );
|
|
844
|
+
const seen = new Set();
|
|
845
|
+
for ( const record of slotPayload ) {
|
|
846
|
+
assertRecordArray( record, `Object payload ${id} slot records`, 2 );
|
|
847
|
+
const [ nameValue, encodedValue ] = record;
|
|
848
|
+
if ( !cbor.isTextString( nameValue ) ) {
|
|
849
|
+
throw new Error( `Object payload ${id} slot names must be text strings` );
|
|
850
|
+
}
|
|
851
|
+
const name = cbor.textValue( nameValue );
|
|
852
|
+
if ( seen.has( name ) ) {
|
|
853
|
+
throw new Error( `Object payload ${id} contains duplicate slot '${name}'` );
|
|
854
|
+
}
|
|
855
|
+
seen.add( name );
|
|
856
|
+
object[name] = storeStrongLoadedValue( decodeValue( encodedValue, placeholders, {
|
|
857
|
+
context: `Object payload ${id} slot '${name}'`,
|
|
858
|
+
} ) );
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
function runOnLoadHooks( objects, placeholders ) {
|
|
863
|
+
for ( let id = 0; id < objects.length; id++ ) {
|
|
864
|
+
const [ kind ] = objects[id];
|
|
865
|
+
if ( kind !== KIND_OBJECT ) {
|
|
866
|
+
continue;
|
|
867
|
+
}
|
|
868
|
+
const object = placeholders[id];
|
|
869
|
+
if ( typeof object.__on_load__ === 'function' ) {
|
|
870
|
+
try {
|
|
871
|
+
object.__on_load__();
|
|
872
|
+
}
|
|
873
|
+
catch ( err ) {
|
|
874
|
+
throw new Error(
|
|
875
|
+
`__on_load__ for ${typeName( object )} failed: ${err.message || err}`
|
|
876
|
+
);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
function decodeItemPayload( context, payload, placeholders ) {
|
|
883
|
+
if ( !Array.isArray( payload ) ) {
|
|
884
|
+
throw new Error( `${context} must be an array` );
|
|
885
|
+
}
|
|
886
|
+
return payload.map( (item) => decodeValue( item, placeholders, {
|
|
887
|
+
context: `${context} item`,
|
|
888
|
+
} ) );
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
function decodeKeyValueRecord( context, pair, placeholders ) {
|
|
892
|
+
assertRecordArray( pair, `${context} entries`, 2 );
|
|
893
|
+
const [ key, value ] = pair;
|
|
894
|
+
if ( !cbor.isTextString( key ) ) {
|
|
895
|
+
throw new Error( `${context} keys must be text strings` );
|
|
896
|
+
}
|
|
897
|
+
return [
|
|
898
|
+
cbor.textValue( key ),
|
|
899
|
+
decodeValue( value, placeholders, {
|
|
900
|
+
context: `${context} value`,
|
|
901
|
+
} ),
|
|
902
|
+
];
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
function decodeValue( value, placeholders, opts = {} ) {
|
|
906
|
+
const allowWeak = Object.prototype.hasOwnProperty.call( opts, 'allowWeak' )
|
|
907
|
+
? opts.allowWeak
|
|
908
|
+
: true;
|
|
909
|
+
const context = opts.context || 'Encoded value';
|
|
910
|
+
|
|
911
|
+
if ( isEncodedScalar( value ) ) {
|
|
912
|
+
return decodeScalar( value );
|
|
913
|
+
}
|
|
914
|
+
if ( Array.isArray( value ) ) {
|
|
915
|
+
if ( value.length !== 2 ) {
|
|
916
|
+
throw new Error( `${context} array must be [0, id] or [1, value]` );
|
|
917
|
+
}
|
|
918
|
+
const [ marker, id ] = value;
|
|
919
|
+
if ( !isInteger( marker ) || ( marker !== 0 && marker !== 1 ) ) {
|
|
920
|
+
throw new Error( `${context} marker must be 0 or 1` );
|
|
921
|
+
}
|
|
922
|
+
if ( marker === 1 ) {
|
|
923
|
+
validateWeakStorageRecord( value, placeholders, context );
|
|
924
|
+
if ( !allowWeak ) {
|
|
925
|
+
throw new Error( `${context} weak storage record is not allowed here` );
|
|
926
|
+
}
|
|
927
|
+
return makeWeakValue( decodeValue( id, placeholders, {
|
|
928
|
+
allowWeak: false,
|
|
929
|
+
context: `${context} weak storage value`,
|
|
930
|
+
} ) );
|
|
931
|
+
}
|
|
932
|
+
if ( !isInteger( id ) ) {
|
|
933
|
+
throw new Error( 'Encoded reference id must be an integer' );
|
|
934
|
+
}
|
|
935
|
+
if ( id < 0 || id >= placeholders.length ) {
|
|
936
|
+
throw new Error( `Reference id ${id} is outside the object table` );
|
|
937
|
+
}
|
|
938
|
+
return placeholders[id];
|
|
939
|
+
}
|
|
940
|
+
throw new Error( 'Envelope root is not a scalar or supported reference' );
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
function storeStrongLoadedValue( value ) {
|
|
944
|
+
return isWeakCell( value ) ? value : retainValue( value );
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
function storeCollectionLoadedValue( owner, value ) {
|
|
948
|
+
return isWeakCell( value ) ? value : retainCollectionValue( owner, value );
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
function validateWeakStorageRecord( record, placeholders, context ) {
|
|
952
|
+
assertRecordArray( record, `${context} weak storage record`, 2 );
|
|
953
|
+
const inner = record[1];
|
|
954
|
+
if ( isWeakStorageRecord( inner ) ) {
|
|
955
|
+
throw new Error( `${context} nested weak storage records are invalid` );
|
|
956
|
+
}
|
|
957
|
+
decodeValue( inner, placeholders, {
|
|
958
|
+
allowWeak: false,
|
|
959
|
+
context: `${context} weak storage value`,
|
|
960
|
+
} );
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
function isWeakStorageRecord( value ) {
|
|
964
|
+
return Array.isArray( value ) && value.length === 2 && value[0] === 1;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
function isEncodedScalar( value ) {
|
|
968
|
+
return value == null
|
|
969
|
+
|| typeof value === 'boolean'
|
|
970
|
+
|| typeof value === 'number'
|
|
971
|
+
|| cbor.isTextString( value )
|
|
972
|
+
|| cbor.isByteString( value );
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
function decodeScalar( value ) {
|
|
976
|
+
if ( value == null ) {
|
|
977
|
+
return null;
|
|
978
|
+
}
|
|
979
|
+
if ( typeof value === 'boolean' || typeof value === 'number' ) {
|
|
980
|
+
return value;
|
|
981
|
+
}
|
|
982
|
+
if ( cbor.isTextString( value ) ) {
|
|
983
|
+
return cbor.textValue( value );
|
|
984
|
+
}
|
|
985
|
+
if ( cbor.isByteString( value ) ) {
|
|
986
|
+
return new BinaryString( cbor.bytesValue( value ) );
|
|
987
|
+
}
|
|
988
|
+
throw new Error( 'Envelope root is not a scalar value' );
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
function assertRecordArray( value, context, length ) {
|
|
992
|
+
if ( !Array.isArray( value ) || value.length !== length ) {
|
|
993
|
+
const item = length === 1 ? 'one-item' : `${length}-item`;
|
|
994
|
+
throw new Error( `${context} must be a ${item} array` );
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
function assertInteger( value, context ) {
|
|
999
|
+
if ( !isInteger( value ) ) {
|
|
1000
|
+
throw new Error( `${context} must be an integer` );
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
function isInteger( value ) {
|
|
1005
|
+
return typeof value === 'number' && Number.isInteger( value );
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
function isPlainDict( value ) {
|
|
1009
|
+
if ( value == null || typeof value !== 'object' ) {
|
|
1010
|
+
return false;
|
|
1011
|
+
}
|
|
1012
|
+
if ( value instanceof ZuzuBinary || value instanceof Pair || isPairListLike( value ) ) {
|
|
1013
|
+
return false;
|
|
1014
|
+
}
|
|
1015
|
+
if ( value instanceof Set || value instanceof ZuzuBag || Array.isArray( value ) ) {
|
|
1016
|
+
return false;
|
|
1017
|
+
}
|
|
1018
|
+
if ( isTimeValue( value ) || isPathValue( value ) ) {
|
|
1019
|
+
return false;
|
|
1020
|
+
}
|
|
1021
|
+
const proto = Object.getPrototypeOf( value );
|
|
1022
|
+
if ( proto === null || proto === Object.prototype ) {
|
|
1023
|
+
return true;
|
|
1024
|
+
}
|
|
1025
|
+
return Object.prototype.toString.call( value ) === '[object Object]'
|
|
1026
|
+
&& value.constructor
|
|
1027
|
+
&& value.constructor.name === 'Object';
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
function isUserObjectValue( value ) {
|
|
1031
|
+
return value
|
|
1032
|
+
&& typeof value === 'object'
|
|
1033
|
+
&& !Array.isArray( value )
|
|
1034
|
+
&& isUserClassValue( value.constructor )
|
|
1035
|
+
&& !isTimeValue( value )
|
|
1036
|
+
&& !isPathValue( value );
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
function isFunctionValue( value ) {
|
|
1040
|
+
return typeof value === 'function'
|
|
1041
|
+
&& !isUserClassValue( value )
|
|
1042
|
+
&& value.__zuzu_marshal_meta
|
|
1043
|
+
&& value.__zuzu_marshal_meta.kind === 'function';
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
function isUserClassValue( value ) {
|
|
1047
|
+
return typeof value === 'function'
|
|
1048
|
+
&& typeof value.__zuzu_class_name === 'string'
|
|
1049
|
+
&& value.__zuzu_class_spec
|
|
1050
|
+
&& typeof value.__zuzu_class_spec === 'object';
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
function isTraitValue( value ) {
|
|
1054
|
+
return value
|
|
1055
|
+
&& typeof value === 'object'
|
|
1056
|
+
&& value.__zuzu_trait_methods
|
|
1057
|
+
&& typeof value.__zuzu_trait_name === 'string';
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
function isBoundMethodValue( value ) {
|
|
1061
|
+
return value
|
|
1062
|
+
&& (
|
|
1063
|
+
value.__zuzu_method
|
|
1064
|
+
|| value.constructor && value.constructor.name === 'ZuzuMethod'
|
|
1065
|
+
);
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
function isScalarForCapture( value ) {
|
|
1069
|
+
return value == null
|
|
1070
|
+
|| typeof value === 'boolean'
|
|
1071
|
+
|| typeof value === 'number'
|
|
1072
|
+
|| typeof value === 'string'
|
|
1073
|
+
|| value instanceof ZuzuBinary;
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
function isMarshalableObjectSlot( value ) {
|
|
1077
|
+
return typeof value !== 'function';
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
function isTimeValue( value ) {
|
|
1081
|
+
return value
|
|
1082
|
+
&& typeof value === 'object'
|
|
1083
|
+
&& (
|
|
1084
|
+
value.__zuzu_type_name === 'Time'
|
|
1085
|
+
|| ( value.constructor && value.constructor.name === 'Time' )
|
|
1086
|
+
)
|
|
1087
|
+
&& ( typeof value.epoch === 'function' || typeof value._epoch === 'number' );
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
function isPathValue( value ) {
|
|
1091
|
+
return value
|
|
1092
|
+
&& typeof value === 'object'
|
|
1093
|
+
&& value.constructor
|
|
1094
|
+
&& value.constructor.name === 'Path'
|
|
1095
|
+
&& ( typeof value.to_String === 'function' || typeof value.value === 'string' );
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
function timeClass() {
|
|
1099
|
+
return require( '../time' ).Time;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
function pathClass() {
|
|
1103
|
+
if ( runtimePolicy.host_name === 'browser' ) {
|
|
1104
|
+
throw new Error( 'UnmarshallingException: Path requires std/io' );
|
|
1105
|
+
}
|
|
1106
|
+
const dynamicRequire = require;
|
|
1107
|
+
return dynamicRequire( '../io' ).Path;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
function marshalBindingName( rawName, id ) {
|
|
1111
|
+
const name = String( rawName || '' );
|
|
1112
|
+
if ( /^[A-Za-z_][A-Za-z0-9_]*$/u.test( name ) ) {
|
|
1113
|
+
return name;
|
|
1114
|
+
}
|
|
1115
|
+
return `__zuzu_marshal_class_${id}`;
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
function marshalClassSource( klass, bindingName ) {
|
|
1119
|
+
const fields = classFields( klass ).map( sourceField ).join( '\n' );
|
|
1120
|
+
return fields
|
|
1121
|
+
? `class ${bindingName} {\n${fields}\n}`
|
|
1122
|
+
: `class ${bindingName} {}`;
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
function classFields( klass ) {
|
|
1126
|
+
const spec = klass.__zuzu_class_spec || {};
|
|
1127
|
+
return Array.isArray( spec.fields ) ? spec.fields : [];
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
function sourceField( field ) {
|
|
1131
|
+
const kind = field.kind === 'const' ? 'const' : 'let';
|
|
1132
|
+
const typeName = field.typeName ? `${field.typeName} ` : '';
|
|
1133
|
+
const accessors = Array.isArray( field.accessors ) && field.accessors.length > 0
|
|
1134
|
+
? ` with ${field.accessors.join( ', ' )}`
|
|
1135
|
+
: '';
|
|
1136
|
+
return `\t${kind} ${typeName}${field.name}${accessors};`;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
function registerCodeValue( kind, bindingName, source, value ) {
|
|
1140
|
+
codeRegistry.set( codeRegistryKey( kind, bindingName, source ), value );
|
|
1141
|
+
if ( kind === CODE_CLASS ) {
|
|
1142
|
+
classRegistry.set( classRegistryKey( bindingName, source ), value );
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
function classRegistryKey( bindingName, source ) {
|
|
1147
|
+
return `${bindingName}\0${source}`;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
function codeRegistryKey( kind, bindingName, source ) {
|
|
1151
|
+
return `${kind}\0${bindingName}\0${source}`;
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
function buildFunctionFromSource( bindingName, source, env = {} ) {
|
|
1155
|
+
const names = Object.keys( env );
|
|
1156
|
+
const values = names.map( (name) => env[name] );
|
|
1157
|
+
let fn;
|
|
1158
|
+
if ( [ 'node', 'browser', 'electron' ].includes( runtimePolicy.host_name ) ) {
|
|
1159
|
+
try {
|
|
1160
|
+
fn = buildFunctionWithRuntime( bindingName, source, env );
|
|
1161
|
+
}
|
|
1162
|
+
catch ( runtimeErr ) {
|
|
1163
|
+
try {
|
|
1164
|
+
fn = Function( ...names, `return ( ${source} );` )( ...values );
|
|
1165
|
+
}
|
|
1166
|
+
catch ( rawErr ) {
|
|
1167
|
+
fn = unloadedFunction( bindingName, runtimeErr, rawErr );
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
else {
|
|
1172
|
+
try {
|
|
1173
|
+
fn = Function( ...names, `return ( ${source} );` )( ...values );
|
|
1174
|
+
}
|
|
1175
|
+
catch ( rawErr ) {
|
|
1176
|
+
fn = unloadedFunction( bindingName, rawErr );
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
Object.defineProperty( fn, '__zuzu_marshal_meta', {
|
|
1180
|
+
value: {
|
|
1181
|
+
kind: 'function',
|
|
1182
|
+
name: bindingName,
|
|
1183
|
+
source,
|
|
1184
|
+
captures: env,
|
|
1185
|
+
},
|
|
1186
|
+
enumerable: false,
|
|
1187
|
+
configurable: true,
|
|
1188
|
+
writable: true,
|
|
1189
|
+
} );
|
|
1190
|
+
return fn;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
function unloadedFunction( bindingName, ...errors ) {
|
|
1194
|
+
return function __zuzu_loaded_function() {
|
|
1195
|
+
const err = errors.find( (item) => item && item.message );
|
|
1196
|
+
const message = err
|
|
1197
|
+
? err.message
|
|
1198
|
+
: `Loaded function '${bindingName}' is not executable in JS`;
|
|
1199
|
+
throw new Error( message );
|
|
1200
|
+
};
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
function buildFunctionWithRuntime( bindingName, source, env = {} ) {
|
|
1204
|
+
if (
|
|
1205
|
+
runtimePolicy.host_name
|
|
1206
|
+
&& ![ 'node', 'browser', 'electron' ].includes( runtimePolicy.host_name )
|
|
1207
|
+
) {
|
|
1208
|
+
throw new Error( `Loaded function '${bindingName}' is not executable in JS` );
|
|
1209
|
+
}
|
|
1210
|
+
const denyCapabilities = [];
|
|
1211
|
+
for ( const capability of [
|
|
1212
|
+
'fs',
|
|
1213
|
+
'net',
|
|
1214
|
+
'perl',
|
|
1215
|
+
'js',
|
|
1216
|
+
'proc',
|
|
1217
|
+
'db',
|
|
1218
|
+
'clib',
|
|
1219
|
+
'gui',
|
|
1220
|
+
'worker',
|
|
1221
|
+
] ) {
|
|
1222
|
+
if ( runtimePolicy[`deny_${capability}`] ) {
|
|
1223
|
+
denyCapabilities.push( capability );
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
const runtimeOptions = {
|
|
1227
|
+
repoRoot: runtimePolicy.repo_root || undefined,
|
|
1228
|
+
includePaths: runtimePolicy.include_paths || [],
|
|
1229
|
+
denyCapabilities,
|
|
1230
|
+
denyModules: runtimePolicy.deny_modules || [],
|
|
1231
|
+
debugLevel: runtimePolicy.debug_level || 0,
|
|
1232
|
+
transpiler: runtimePolicy.transpiler || undefined,
|
|
1233
|
+
};
|
|
1234
|
+
let runtime;
|
|
1235
|
+
if ( runtimePolicy.host_name === 'browser' ) {
|
|
1236
|
+
const { createBrowserRuntime } = require( '../../../lib/browser-runtime' );
|
|
1237
|
+
const defaults = typeof globalThis !== 'undefined'
|
|
1238
|
+
&& globalThis.__ZUZU_BROWSER_DEFAULT_RUNTIME_OPTIONS__
|
|
1239
|
+
&& typeof globalThis.__ZUZU_BROWSER_DEFAULT_RUNTIME_OPTIONS__ === 'object'
|
|
1240
|
+
? globalThis.__ZUZU_BROWSER_DEFAULT_RUNTIME_OPTIONS__
|
|
1241
|
+
: {};
|
|
1242
|
+
runtime = createBrowserRuntime( {
|
|
1243
|
+
...defaults,
|
|
1244
|
+
...runtimeOptions,
|
|
1245
|
+
} ).runtime;
|
|
1246
|
+
}
|
|
1247
|
+
else {
|
|
1248
|
+
const entrypointsModule = '../../../lib/' + 'runtime-entrypoints';
|
|
1249
|
+
const { createNodeRuntime } = require( entrypointsModule );
|
|
1250
|
+
runtime = createNodeRuntime( runtimeOptions );
|
|
1251
|
+
}
|
|
1252
|
+
const js = runtime.transpile(
|
|
1253
|
+
`let __zuzu_marshal_value := ${source};\n`
|
|
1254
|
+
+ '__global__{__zuzu_marshal_result} := __zuzu_marshal_value;'
|
|
1255
|
+
);
|
|
1256
|
+
const context = runtime.buildContext( {
|
|
1257
|
+
filename: '<std/marshal-code>',
|
|
1258
|
+
globals: env,
|
|
1259
|
+
} );
|
|
1260
|
+
context.__global__ = Object.create( null );
|
|
1261
|
+
runtime.installCollectionMethods( context );
|
|
1262
|
+
runtime.host.runInContext(
|
|
1263
|
+
js,
|
|
1264
|
+
context,
|
|
1265
|
+
{ filename: '<std/marshal-code>' },
|
|
1266
|
+
);
|
|
1267
|
+
const fn = context.__global__.__zuzu_marshal_result;
|
|
1268
|
+
if ( typeof fn !== 'function' ) {
|
|
1269
|
+
throw new Error( `Loaded function '${bindingName}' is not executable in JS` );
|
|
1270
|
+
}
|
|
1271
|
+
return fn;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
function buildClassFromSource( bindingName, source, env = {} ) {
|
|
1275
|
+
const spec = parseClassSource( source, env );
|
|
1276
|
+
return buildMarshalClass(
|
|
1277
|
+
bindingName,
|
|
1278
|
+
spec.baseName ? env[spec.baseName] : null,
|
|
1279
|
+
spec.fields,
|
|
1280
|
+
spec.methods,
|
|
1281
|
+
spec.statics,
|
|
1282
|
+
spec.traitNames.map( (name) => env[name] ).filter( isTraitValue ),
|
|
1283
|
+
env,
|
|
1284
|
+
source
|
|
1285
|
+
);
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
function buildTraitFromSource( bindingName, source, env = {} ) {
|
|
1289
|
+
const trait = {
|
|
1290
|
+
__zuzu_trait_name: bindingName,
|
|
1291
|
+
__zuzu_trait_methods: parseMethods( source, env ).methods,
|
|
1292
|
+
};
|
|
1293
|
+
Object.defineProperty( trait, '__zuzu_marshal_meta', {
|
|
1294
|
+
value: {
|
|
1295
|
+
kind: 'trait',
|
|
1296
|
+
name: bindingName,
|
|
1297
|
+
source,
|
|
1298
|
+
captures: env,
|
|
1299
|
+
},
|
|
1300
|
+
enumerable: false,
|
|
1301
|
+
configurable: true,
|
|
1302
|
+
writable: true,
|
|
1303
|
+
} );
|
|
1304
|
+
return trait;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
function parseClassSource( source, env = {} ) {
|
|
1308
|
+
return {
|
|
1309
|
+
baseName: parseClassBaseName( source ),
|
|
1310
|
+
fields: parseClassFields( source ),
|
|
1311
|
+
traitNames: parseClassTraitNames( source ),
|
|
1312
|
+
...parseMethods( source, env ),
|
|
1313
|
+
};
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
function parseClassBaseName( source ) {
|
|
1317
|
+
const match = String( source || '' ).match(
|
|
1318
|
+
/\bclass\s+[A-Za-z_][A-Za-z0-9_]*\s+extends\s+([A-Za-z_][A-Za-z0-9_]*)/u
|
|
1319
|
+
);
|
|
1320
|
+
return match ? match[1] : null;
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
function parseClassFields( source ) {
|
|
1324
|
+
const body = String( source || '' ).replace( /\/\*[\s\S]*?\*\//gu, '' );
|
|
1325
|
+
const fields = [];
|
|
1326
|
+
const byName = new Map();
|
|
1327
|
+
const rx = /\b(let|const)\s+(?:(Null|Any|Boolean|Number|String|BinaryString|Array|Dict|PairList|Set|Bag|Pair|Time|Path|Object)\s+)?([A-Za-z_][A-Za-z0-9_]*)(?:\s+with\s+([^;:=]+?))?\s*(?::=\s*([^;]*))?;/gu;
|
|
1328
|
+
let match = rx.exec( body );
|
|
1329
|
+
while ( match ) {
|
|
1330
|
+
const field = {
|
|
1331
|
+
kind: match[1],
|
|
1332
|
+
typeName: match[2] || null,
|
|
1333
|
+
name: match[3],
|
|
1334
|
+
accessors: match[4]
|
|
1335
|
+
? match[4].split( ',' ).map( (item) => item.trim() ).filter( Boolean )
|
|
1336
|
+
: [],
|
|
1337
|
+
defaultSource: match[5] ? match[5].trim() : null,
|
|
1338
|
+
};
|
|
1339
|
+
fields.push( field );
|
|
1340
|
+
byName.set( field.name, field );
|
|
1341
|
+
match = rx.exec( body );
|
|
1342
|
+
}
|
|
1343
|
+
const methodRx = /\bmethod\s+(get|set|clear|has)_([A-Za-z_][A-Za-z0-9_]*)\s*\(([^)]*)\)/gu;
|
|
1344
|
+
let methodMatch = methodRx.exec( body );
|
|
1345
|
+
while ( methodMatch ) {
|
|
1346
|
+
const field = byName.get( methodMatch[2] );
|
|
1347
|
+
if ( field && !field.accessors.includes( methodMatch[1] ) ) {
|
|
1348
|
+
field.accessors.push( methodMatch[1] );
|
|
1349
|
+
}
|
|
1350
|
+
if ( field && methodMatch[1] === 'set' && !field.typeName ) {
|
|
1351
|
+
const typeMatch = methodMatch[3].match( /\b(Null|Any|Boolean|Number|String|BinaryString|Array|Dict|PairList|Set|Bag|Pair|Time|Path|Object)\s+[A-Za-z_][A-Za-z0-9_]*\b/u );
|
|
1352
|
+
if ( typeMatch ) {
|
|
1353
|
+
field.typeName = typeMatch[1];
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
methodMatch = methodRx.exec( body );
|
|
1357
|
+
}
|
|
1358
|
+
return fields;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
function parseClassTraitNames( source ) {
|
|
1362
|
+
const match = String( source || '' ).match(
|
|
1363
|
+
/\bclass\s+[A-Za-z_][A-Za-z0-9_]*(?:\s+extends\s+[A-Za-z_][A-Za-z0-9_]*)?\s+(?:with|but)\s+([^{;]+)/u
|
|
1364
|
+
);
|
|
1365
|
+
if ( !match ) {
|
|
1366
|
+
return [];
|
|
1367
|
+
}
|
|
1368
|
+
return match[1]
|
|
1369
|
+
.split( ',' )
|
|
1370
|
+
.map( (item) => item.trim() )
|
|
1371
|
+
.filter( (item) => /^[A-Za-z_][A-Za-z0-9_]*$/u.test( item ) );
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
function parseMethods( source, env = {} ) {
|
|
1375
|
+
const text = String( source || '' ).replace( /\/\*[\s\S]*?\*\//gu, '' );
|
|
1376
|
+
const methods = {};
|
|
1377
|
+
const statics = {};
|
|
1378
|
+
const rx = /\b(static\s+)?method\s+([A-Za-z_][A-Za-z0-9_]*)\s*\(([^)]*)\)\s*(?:->\s*[A-Za-z_][A-Za-z0-9_]*)?\s*\{([\s\S]*?)\}/gu;
|
|
1379
|
+
let match = rx.exec( text );
|
|
1380
|
+
while ( match ) {
|
|
1381
|
+
const target = match[1] ? statics : methods;
|
|
1382
|
+
target[match[2]] = compileSimpleMethod(
|
|
1383
|
+
match[2],
|
|
1384
|
+
parseParamNames( match[3] ),
|
|
1385
|
+
match[4],
|
|
1386
|
+
env
|
|
1387
|
+
);
|
|
1388
|
+
match = rx.exec( text );
|
|
1389
|
+
}
|
|
1390
|
+
return { methods, statics };
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
function parseParamNames( source ) {
|
|
1394
|
+
return String( source || '' )
|
|
1395
|
+
.split( ',' )
|
|
1396
|
+
.map( (item) => item.trim() )
|
|
1397
|
+
.filter( Boolean )
|
|
1398
|
+
.map( (item) => {
|
|
1399
|
+
const match = item.match( /([A-Za-z_][A-Za-z0-9_]*)$/u );
|
|
1400
|
+
return match ? match[1] : null;
|
|
1401
|
+
} )
|
|
1402
|
+
.filter( Boolean );
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
function compileSimpleMethod( name, paramNames, body, env ) {
|
|
1406
|
+
const match = String( body || '' ).match( /\breturn\s+([\s\S]*?)\s*;/u );
|
|
1407
|
+
if ( !match ) {
|
|
1408
|
+
return function _loaded_method() {
|
|
1409
|
+
return null;
|
|
1410
|
+
};
|
|
1411
|
+
}
|
|
1412
|
+
const expression = match[1];
|
|
1413
|
+
return {
|
|
1414
|
+
[name]: function _loaded_method( ...args ) {
|
|
1415
|
+
const params = Object.create( null );
|
|
1416
|
+
for ( let i = 0; i < paramNames.length; i++ ) {
|
|
1417
|
+
params[paramNames[i]] = args[i];
|
|
1418
|
+
}
|
|
1419
|
+
return evalSimpleExpression( expression, this, env, params );
|
|
1420
|
+
},
|
|
1421
|
+
}[name];
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
function evalSimpleExpression( source, self, env = {}, params = {} ) {
|
|
1425
|
+
const expression = normalizeSimpleExpression( source );
|
|
1426
|
+
const envNames = Object.keys( env );
|
|
1427
|
+
const paramNames = Object.keys( params );
|
|
1428
|
+
const fn = Function(
|
|
1429
|
+
'self',
|
|
1430
|
+
...envNames,
|
|
1431
|
+
...paramNames,
|
|
1432
|
+
`with ( self ) { return ( ${expression} ); }`
|
|
1433
|
+
);
|
|
1434
|
+
return fn(
|
|
1435
|
+
self || {},
|
|
1436
|
+
...envNames.map( (name) => env[name] ),
|
|
1437
|
+
...paramNames.map( (name) => params[name] )
|
|
1438
|
+
);
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
function normalizeSimpleExpression( source ) {
|
|
1442
|
+
return String( source || 'null' ).replace( /\s+_\s+/gu, ' + ' );
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
function buildMarshalClass(
|
|
1446
|
+
name,
|
|
1447
|
+
base = null,
|
|
1448
|
+
fields,
|
|
1449
|
+
methods = {},
|
|
1450
|
+
statics = {},
|
|
1451
|
+
traits = [],
|
|
1452
|
+
env = {},
|
|
1453
|
+
source = ''
|
|
1454
|
+
) {
|
|
1455
|
+
const parent = typeof base === 'function' ? base : Object;
|
|
1456
|
+
const ctor = {
|
|
1457
|
+
[name]: class extends parent {
|
|
1458
|
+
constructor( initial = {} ) {
|
|
1459
|
+
super( initial );
|
|
1460
|
+
for ( const field of fields ) {
|
|
1461
|
+
let value = field.defaultSource
|
|
1462
|
+
? evalSimpleExpression( field.defaultSource, this, env )
|
|
1463
|
+
: null;
|
|
1464
|
+
if ( isPairListLike( initial ) ) {
|
|
1465
|
+
value = initial.get( field.name, value );
|
|
1466
|
+
}
|
|
1467
|
+
else if (
|
|
1468
|
+
initial
|
|
1469
|
+
&& typeof initial === 'object'
|
|
1470
|
+
&& Object.prototype.hasOwnProperty.call( initial, field.name )
|
|
1471
|
+
) {
|
|
1472
|
+
value = initial[field.name];
|
|
1473
|
+
}
|
|
1474
|
+
this[field.name] = value;
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
},
|
|
1478
|
+
}[name];
|
|
1479
|
+
for ( const field of fields ) {
|
|
1480
|
+
if ( !Array.isArray( field.accessors ) ) {
|
|
1481
|
+
continue;
|
|
1482
|
+
}
|
|
1483
|
+
if ( field.accessors.includes( 'get' ) ) {
|
|
1484
|
+
ctor.prototype[`get_${field.name}`] = function _get_field() {
|
|
1485
|
+
return this[field.name];
|
|
1486
|
+
};
|
|
1487
|
+
}
|
|
1488
|
+
if ( field.accessors.includes( 'set' ) ) {
|
|
1489
|
+
ctor.prototype[`set_${field.name}`] = function _set_field( value ) {
|
|
1490
|
+
if ( field.typeName && value != null && !typeMatches( value, field.typeName ) ) {
|
|
1491
|
+
throw new Error(
|
|
1492
|
+
`TypeException: field '${field.name}' must be ${field.typeName}, got ${typeName( value )}`
|
|
1493
|
+
);
|
|
1494
|
+
}
|
|
1495
|
+
this[field.name] = value;
|
|
1496
|
+
return this;
|
|
1497
|
+
};
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
for ( const trait of traits ) {
|
|
1501
|
+
for ( const [ methodName, fn ] of Object.entries( trait.__zuzu_trait_methods ) ) {
|
|
1502
|
+
if ( typeof fn === 'function' && typeof ctor.prototype[methodName] !== 'function' ) {
|
|
1503
|
+
ctor.prototype[methodName] = fn;
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
const generatedAccessors = new Set();
|
|
1508
|
+
for ( const field of fields ) {
|
|
1509
|
+
for ( const accessor of field.accessors || [] ) {
|
|
1510
|
+
generatedAccessors.add( `${accessor}_${field.name}` );
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
for ( const [ methodName, fn ] of Object.entries( methods ) ) {
|
|
1514
|
+
if ( generatedAccessors.has( methodName ) ) {
|
|
1515
|
+
continue;
|
|
1516
|
+
}
|
|
1517
|
+
ctor.prototype[methodName] = fn;
|
|
1518
|
+
}
|
|
1519
|
+
for ( const [ methodName, fn ] of Object.entries( statics ) ) {
|
|
1520
|
+
ctor[methodName] = fn;
|
|
1521
|
+
}
|
|
1522
|
+
Object.defineProperty( ctor, '__zuzu_class_name', {
|
|
1523
|
+
value: name,
|
|
1524
|
+
enumerable: false,
|
|
1525
|
+
configurable: true,
|
|
1526
|
+
writable: true,
|
|
1527
|
+
} );
|
|
1528
|
+
Object.defineProperty( ctor, '__zuzu_class_spec', {
|
|
1529
|
+
value: { fields, traits, methods, statics, nested: {} },
|
|
1530
|
+
enumerable: false,
|
|
1531
|
+
configurable: true,
|
|
1532
|
+
writable: true,
|
|
1533
|
+
} );
|
|
1534
|
+
Object.defineProperty( ctor, '__zuzu_marshal_meta', {
|
|
1535
|
+
value: {
|
|
1536
|
+
kind: 'class',
|
|
1537
|
+
name,
|
|
1538
|
+
source,
|
|
1539
|
+
captures: env,
|
|
1540
|
+
},
|
|
1541
|
+
enumerable: false,
|
|
1542
|
+
configurable: true,
|
|
1543
|
+
writable: true,
|
|
1544
|
+
} );
|
|
1545
|
+
return ctor;
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
function typeMatches( value, expected ) {
|
|
1549
|
+
if ( expected === 'Any' || expected == null ) {
|
|
1550
|
+
return true;
|
|
1551
|
+
}
|
|
1552
|
+
if ( expected === 'Null' ) {
|
|
1553
|
+
return value == null;
|
|
1554
|
+
}
|
|
1555
|
+
if ( expected === 'Boolean' ) {
|
|
1556
|
+
return typeof value === 'boolean';
|
|
1557
|
+
}
|
|
1558
|
+
if ( expected === 'Number' ) {
|
|
1559
|
+
return typeof value === 'number';
|
|
1560
|
+
}
|
|
1561
|
+
if ( expected === 'String' ) {
|
|
1562
|
+
return typeof value === 'string';
|
|
1563
|
+
}
|
|
1564
|
+
if ( expected === 'BinaryString' ) {
|
|
1565
|
+
return value instanceof BinaryString;
|
|
1566
|
+
}
|
|
1567
|
+
if ( expected === 'Array' ) {
|
|
1568
|
+
return Array.isArray( value );
|
|
1569
|
+
}
|
|
1570
|
+
if ( expected === 'Dict' ) {
|
|
1571
|
+
return isPlainDict( value );
|
|
1572
|
+
}
|
|
1573
|
+
if ( expected === 'PairList' ) {
|
|
1574
|
+
return isPairListLike( value );
|
|
1575
|
+
}
|
|
1576
|
+
if ( expected === 'Set' ) {
|
|
1577
|
+
return value instanceof Set;
|
|
1578
|
+
}
|
|
1579
|
+
if ( expected === 'Bag' ) {
|
|
1580
|
+
return value instanceof ZuzuBag;
|
|
1581
|
+
}
|
|
1582
|
+
if ( expected === 'Pair' ) {
|
|
1583
|
+
return value instanceof Pair;
|
|
1584
|
+
}
|
|
1585
|
+
if ( expected === 'Time' ) {
|
|
1586
|
+
return isTimeValue( value );
|
|
1587
|
+
}
|
|
1588
|
+
if ( expected === 'Path' ) {
|
|
1589
|
+
return isPathValue( value );
|
|
1590
|
+
}
|
|
1591
|
+
return true;
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
function typeName( value ) {
|
|
1595
|
+
if ( value == null ) {
|
|
1596
|
+
return 'Null';
|
|
1597
|
+
}
|
|
1598
|
+
if ( value instanceof BinaryString ) {
|
|
1599
|
+
return 'BinaryString';
|
|
1600
|
+
}
|
|
1601
|
+
if ( typeof value === 'boolean' ) {
|
|
1602
|
+
return 'Boolean';
|
|
1603
|
+
}
|
|
1604
|
+
if ( typeof value === 'number' ) {
|
|
1605
|
+
return 'Number';
|
|
1606
|
+
}
|
|
1607
|
+
if ( typeof value === 'string' ) {
|
|
1608
|
+
return 'String';
|
|
1609
|
+
}
|
|
1610
|
+
if ( Array.isArray( value ) ) {
|
|
1611
|
+
return 'Array';
|
|
1612
|
+
}
|
|
1613
|
+
if ( value && value.constructor && value.constructor.name ) {
|
|
1614
|
+
return value.constructor.name;
|
|
1615
|
+
}
|
|
1616
|
+
return typeof value;
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
module.exports = {
|
|
1620
|
+
dumpGraph,
|
|
1621
|
+
loadGraph,
|
|
1622
|
+
safeToDumpGraph,
|
|
1623
|
+
setRuntimePolicy,
|
|
1624
|
+
};
|