@lumjs/core 1.38.8 → 1.39.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/lib/context.js CHANGED
@@ -42,12 +42,12 @@
42
42
  * @property {boolean} isBundled - Is this a bundled app?
43
43
  * @property {boolean} isMain - Is this the main renderer frame?
44
44
  * @property {?string} type - `process.type`
45
- * @property {object} root - See {@link module:@lumjs/core/types.root}
46
45
  */
47
46
 
48
- const {root,B,F,U,O,isComplex,isObj} = require('./types');
49
- const { df, dfor } = require('./obj');
47
+ const {B,F,U,O,isComplex,isObj} = require('./types/basics');
48
+ const { df, dfor } = require('./obj/df');
50
49
 
50
+ const root = globalThis;
51
51
  const fo = {enumerable: true}
52
52
  const ctx = {root};
53
53
  const rootHas = what => typeof root[what] !== U;
package/lib/enum.js CHANGED
@@ -1,3 +1,4 @@
1
+ const { deprecated } = require('./meta');
1
2
  const {S,F,def,notNil,isObj,needObj,TYPES} = require('./types');
2
3
  const {InternalObjectId} = require('./objectid');
3
4
 
@@ -79,6 +80,8 @@ const EOPT = Symbol(EID+':opts');
79
80
  * used as the `opts` for {@see module:@lumjs/core/obj.addLock addLock()},
80
81
  * which will be called on the open Enum object.
81
82
  *
83
+ * This option is **deprecated** and will be removed in v2.0.
84
+ *
82
85
  * @param {bool} [opts.configurable=false] Enum properties are configurable?
83
86
  *
84
87
  * This option is ignored if `opts.open` is `false`.
@@ -188,7 +191,9 @@ function Enum (spec, opts={})
188
191
  { // Save the options into a special Symbol property.
189
192
  def(anEnum, EOPT, {value: opts});
190
193
  if (isObj(opts.lock))
191
- { // Add lock() method.
194
+ { // Add lock() method; TODO: remove this in 2.0!
195
+ deprecated("Enum(opts.lock)");
196
+ let { addLock } = require('./obj/lock');
192
197
  addLock(anEnum, opts.lock);
193
198
  }
194
199
  }
@@ -196,7 +201,9 @@ function Enum (spec, opts={})
196
201
  else
197
202
  {
198
203
  if (notNil(opts.lock))
199
- { // Use lock() function.
204
+ { // Use lock() function; TODO: remove this in 2.0!
205
+ deprecated("Enum(opts.lock)");
206
+ let { lock } = require('./obj/lock');
200
207
  let lockOpts;
201
208
  if (Array.isArray(opts.lock))
202
209
  { // Specified the lock parameters as an array.
@@ -250,6 +257,3 @@ def(Enum, 'is', isEnum);
250
257
  TYPES.add('ENUM', 'enum', isEnum, 'isEnum');
251
258
 
252
259
  module.exports = Enum;
253
-
254
- // Loading this at the end.
255
- const {lock,addLock} = require('./obj/lock');
package/lib/env.js CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  const cp = Object.assign;
4
4
  const ctx = require('./context');
5
- const { df, lazy } = require('./obj');
6
- const { isObj } = require('./types');
5
+ const { df, lazy } = require('./obj/df');
6
+ const { isObj } = require('./types/basics');
7
7
  const NS = '@lumjs/core/env';
8
8
  const DataCache = Symbol(NS+'~DataCache');
9
9
  const Metadata = Symbol(NS+'~Metadata');
@@ -316,7 +316,8 @@ exports = module.exports =
316
316
  let info = {key, opts, env: this};
317
317
 
318
318
  let value = this.getStr(key),
319
- cache = false;
319
+ cache = false,
320
+ isdef = false;
320
321
 
321
322
  if (typeof value === 'string')
322
323
  {
@@ -326,18 +327,22 @@ exports = module.exports =
326
327
  if (value === undefined)
327
328
  {
328
329
  value = (typeof opts.default === 'function')
329
- ? opts.default.call(this, key, opts)
330
+ ? opts.default.call(this, key, opts, this)
330
331
  : opts.default;
332
+ isdef = true;
331
333
  }
332
334
 
333
- if (isObj(value))
334
- {
335
- cache = (opts.cache > 0);
336
- this.extendObject(value, info);
337
- }
338
- else if (value !== undefined)
335
+ if (!isdef || opts.cache > 2)
339
336
  {
340
- cache = (opts.cache > 1);
337
+ if (isObj(value))
338
+ {
339
+ cache = (opts.cache > 0);
340
+ this.extendObject(value, info);
341
+ }
342
+ else if (value !== undefined)
343
+ {
344
+ cache = (opts.cache > 1);
345
+ }
341
346
  }
342
347
 
343
348
  if (cache)
@@ -453,7 +458,7 @@ exports = module.exports =
453
458
  let meth = opts.dataSave;
454
459
  if (value[meth] !== undefined)
455
460
  {
456
- console.error({key, opts, data: value});
461
+ console.error({meth, value, opts, info});
457
462
  throw new RangeError(meth+' property already exists');
458
463
  }
459
464
 
@@ -874,10 +879,14 @@ exports = module.exports =
874
879
  */
875
880
  exports.delete = exports.remove;
876
881
 
882
+ //console.debug('Main env exports setup', exports);
883
+
877
884
  // Now the platform-specific methods are added!
878
885
 
879
886
  if (ctx.isNode)
880
887
  {
888
+ //console.debug('Adding node env helpers');
889
+
881
890
  exports.getStr = function(key)
882
891
  {
883
892
  return process.env[key];
@@ -893,6 +902,7 @@ if (ctx.isNode)
893
902
  exports.unset = function(key)
894
903
  {
895
904
  delete process.env[key];
905
+ return this;
896
906
  }
897
907
 
898
908
  exports.forEach = function(cb, opts)
@@ -907,6 +917,8 @@ if (ctx.isNode)
907
917
  }
908
918
  else if (ctx.isBrowser)
909
919
  {
920
+ console.debug('Adding browser env helpers');
921
+
910
922
  exports.getStr = function(key)
911
923
  { // getItem returns null for no value, but we want undefined.
912
924
  return localStorage.getItem(key) ?? undefined;
@@ -935,3 +947,7 @@ else if (ctx.isBrowser)
935
947
  return cb.done();
936
948
  }
937
949
  }
950
+ else
951
+ {
952
+ throw new RangeError('Invalid environment');
953
+ }
package/lib/index.js CHANGED
@@ -24,18 +24,23 @@ exports = module.exports =
24
24
  Enum: require('./enum'),
25
25
  env: require('./env'),
26
26
  flags: require('./flags'),
27
- lazy,
27
+ lazy, // ← TODO: nix in 2.x
28
28
  maps: require('./maps'),
29
29
  meta,
30
- ...(meta), // ← TODO: nix in 2.x
31
30
  ...(require('./objectid')),
32
31
  // obj: require('./obj'), // ← TODO: uncomment in 2.x
33
32
  opt: require('./opt'),
33
+ proxy: require('./proxy'),
34
34
  state: require('./state'),
35
35
  strings: require('./strings'),
36
36
  types,
37
37
  }
38
38
 
39
+ meta.deprecateAll(meta, {
40
+ dep: '@lumjs/core',
41
+ rep: '@lumjs/core.meta.{DEP}',
42
+ }, exports);
43
+
39
44
  // ↓ TODO: nix everything below here in 2.x
40
45
 
41
46
  df(exports, 'AbstractClass',
package/lib/meta.js CHANGED
@@ -4,6 +4,12 @@
4
4
  */
5
5
  'use strict';
6
6
 
7
+ const { getOwnKeys, keyName } = require('./obj/keys');
8
+ const { isIterable, isObj } = require('./types/basics');
9
+
10
+ const cp = Object.assign;
11
+ const dp = Object.defineProperty;
12
+
7
13
  /**
8
14
  * Get a stacktrace.
9
15
  *
@@ -17,13 +23,10 @@
17
23
  * @returns {string[]} An array of stack strings.
18
24
  * @alias module:@lumjs/core/meta.stacktrace
19
25
  */
20
- function stacktrace(msg)
21
- {
26
+ function stacktrace(msg) {
22
27
  return (new Error(msg)).stack.split("\n");
23
28
  }
24
29
 
25
- exports.stacktrace = stacktrace;
26
-
27
30
  /**
28
31
  * An Error that can be thrown from abstract methods.
29
32
  *
@@ -54,8 +57,7 @@ exports.stacktrace = stacktrace;
54
57
  *
55
58
  * @alias module:@lumjs/core/meta.AbstractError
56
59
  */
57
- class AbstractError extends Error
58
- {
60
+ class AbstractError extends Error {
59
61
  /**
60
62
  * Construct an AbstractError
61
63
  *
@@ -69,8 +71,7 @@ class AbstractError extends Error
69
71
  * error message to use 'getter' instead of 'method'.
70
72
  *
71
73
  */
72
- constructor(name, getter=false)
73
- {
74
+ constructor(name, getter = false) {
74
75
  let msg = "Abstract ";
75
76
  msg += (getter ? 'getter ' : 'method ');
76
77
  if (name) msg += `'${name}' `;
@@ -80,8 +81,6 @@ class AbstractError extends Error
80
81
  }
81
82
  }
82
83
 
83
- exports.AbstractError = AbstractError;
84
-
85
84
  /**
86
85
  * Function prototypes for async, generator, and async generator functions.
87
86
  * @alias module:@lumjs/core/meta.Functions
@@ -91,22 +90,20 @@ const Functions =
91
90
  /**
92
91
  * Constructor for dynamic generator functions.
93
92
  */
94
- Generator: Object.getPrototypeOf(function*(){}).constructor,
93
+ Generator: Object.getPrototypeOf(function* () { }).constructor,
95
94
 
96
95
  /**
97
96
  * Constructor for dynamic async functions.
98
97
  */
99
- Async: Object.getPrototypeOf(async function(){}).constructor,
98
+ Async: Object.getPrototypeOf(async function () { }).constructor,
100
99
 
101
100
  /**
102
101
  * Constructor for dynamic async generator functions.
103
102
  */
104
- AsyncGenerator: Object.getPrototypeOf(async function*(){}).constructor,
103
+ AsyncGenerator: Object.getPrototypeOf(async function* () { }).constructor,
105
104
 
106
105
  }
107
106
 
108
- exports.Functions = Functions;
109
-
110
107
  /**
111
108
  * A placeholder function for when something is not implemented.
112
109
  *
@@ -117,54 +114,134 @@ exports.Functions = Functions;
117
114
  * @returns {void}
118
115
  * @alias module:@lumjs/core/meta.NYI
119
116
  */
120
- function NYI(fatal=true, prefix='')
121
- {
122
- const msg = prefix+"« NOT YET IMPLEMENTED »";
117
+ function NYI(fatal = true, prefix = '') {
118
+ const msg = prefix + "« NOT YET IMPLEMENTED »";
123
119
  if (fatal)
124
120
  throw new Error(msg);
125
121
  else
126
122
  console.error(msg);
127
123
  }
128
124
 
129
- exports.NYI = NYI;
125
+ function defaultDeprecateItemHandler(item) {
126
+ let { depVar } = this.opts;
127
+ if (depVar && typeof item === 'string') {
128
+ return item.trim().replaceAll(depVar, this.dep);
129
+ }
130
+ else if (this.setOpts && isObj(item)) {
131
+ cp(this.opts, item);
132
+ return this.next = true;
133
+ }
134
+ return item;
135
+ }
136
+
137
+ const DEP_OPTS =
138
+ {
139
+ depVar: '{DEP}',
140
+ handle: defaultDeprecateItemHandler,
141
+ preDep: '[DEPRECATED]:',
142
+ }
130
143
 
131
144
  /**
132
- * Send a deprecation message to the console.
145
+ * Send a deprecation message to the console (low-level).
133
146
  *
134
- * Will default to using `console.warn()` to send the message,
135
- * unless `core.state.get('core.traceDeprecated')` evaluates to true,
136
- * in which case it will use `console.trace()` instead.
147
+ * Will default to using `console.warn()` to send the message, however
148
+ * it can be made to use `console.trace()` instead if one of the following
149
+ * evaluates to a true value:
150
+ * - `core.env.get('LUM_TRACE_DEPRECATED')`
151
+ * - `core.state.get('core.traceDeprecated')`
152
+ * As the state module is deprecated, as of 2.x only the env.get() test
153
+ * will continue to work.
137
154
  *
138
- * @param {string} dep - Name of what is deprecated
139
- * @param {(string|string[])} [rep] Replacement suggestion(s).
140
- * @param {*} [ret] Value to return
141
- * @returns {mixed} `ret`
142
- * @alias module:@lumjs/core/meta.deprecated
155
+ * @param {string} dep - Name of what is deprecated.
156
+ * @param {...mixed} [info] Additional information.
157
+ *
158
+ * This may be used to suggest replacements, or whatever other
159
+ * information you want to include in the logs.
160
+ *
161
+ * TODO: document how functions are handled.
143
162
  */
144
- function deprecated(dep, rep, ret)
145
- {
146
- const fn = require('./state').get('core.traceDeprecated')
147
- ? 'trace'
163
+ function deprecate(dep, ...info) {
164
+
165
+ let stateLib = require('./state');
166
+ let fn = stateLib.eors('LUM_TRACE_DEPRECATED', 'core.traceDeprecated')
167
+ ? 'trace'
148
168
  : 'warn';
149
- const msgs = [dep,'is deprecated'];
150
- if (rep)
151
- {
152
- msgs.push('replace with', rep);
169
+
170
+ let ctx = {
171
+ add: true,
172
+ dep,
173
+ done: false,
174
+ info,
175
+ logs: [],
176
+ next: false,
177
+ opts: cp({showDep: dep}, DEP_OPTS),
178
+ setOpts: true,
179
+ }
180
+
181
+ for (let item of info) {
182
+ if (typeof item === 'function') {
183
+ item.call(ctx, ctx);
184
+ }
185
+ else {
186
+ if (typeof ctx.opts.handle === 'function') {
187
+ ctx.next = false;
188
+ item = ctx.opts.handle.call(ctx, item, ctx);
189
+ if (ctx.next) continue; // Move on.
190
+ }
191
+
192
+ if (ctx.add) {
193
+ if (ctx.logs.length === 0 && ctx.opts.preDep) {
194
+ ctx.logs.push(ctx.opts.preDep);
195
+ }
196
+ ctx.logs.push(ctx.opts.showDep || ctx.dep || dep);
197
+ ctx.add = false;
198
+ }
199
+
200
+ if (item) {
201
+ ctx.logs.push(item);
202
+ }
203
+ }
204
+ if (ctx.done) break; // No more items, we're done.
153
205
  }
154
- console[fn](...msgs);
155
- return ret;
206
+
207
+ console[fn](...ctx.logs);
208
+ return ctx.return ?? ctx.opts.return;
156
209
  }
157
210
 
158
- exports.deprecated = deprecated;
211
+ const DED_OPTS =
212
+ {
213
+ preRep: 'replace with',
214
+ }
215
+
216
+ /**
217
+ * Send a deprecation message to the console (simple).
218
+ *
219
+ * This is now a frontend to the newer `deprecate()` function,
220
+ * which may replace it entirely one day.
221
+ *
222
+ * @param {string} dep - Name of what is deprecated.
223
+ * @param {?(string|string[])} [rep] Replacement suggestion(s).
224
+ * @param {mixed} [ret] Value to return
225
+ * @param {object} [opts] Formatting options.
226
+ *
227
+ * @returns {mixed} `ret`
228
+ * @alias module:@lumjs/core/meta.deprecated
229
+ */
230
+ function deprecated(dep, rep, ret, opts) {
231
+ opts = cp({}, DED_OPTS, opts);
232
+ rep = Array.isArray(rep) ? rep.slice(0) : [rep];
233
+ if (opts.preRep) rep.unshift(opts.preRep);
234
+ return deprecate(dep, opts, ...rep);
235
+ }
159
236
 
160
237
  /**
161
238
  * Assign a getter property that when accessed will
162
239
  * show a deprecation message via `deprecated()` function
163
240
  * before returning the deprecated property value.
164
241
  *
165
- * @param {object} obj - Target to assign property on
166
- * @param {string} prop - Property to assign
167
- * @param {(object|function)} spec - Specification
242
+ * @param {(object|function)} obj - Target to assign property on.
243
+ * @param {(string|symbol)} prop - Property to assign.
244
+ * @param {(object|function)} spec - Specification.
168
245
  *
169
246
  * If this is a `function` it will be used as the `spec.get` value.
170
247
  *
@@ -172,29 +249,154 @@ exports.deprecated = deprecated;
172
249
  * @param {string} [spec.dep=prop] Name of what is deprecated;
173
250
  * defaults to `prop` if omitted.
174
251
  * @param {(string|string[])} [spec.rep] Replacement suggestion(s).
175
- * @param {object} [spec.opts] Options for `def()` to add getter with.
176
- *
177
252
  * @returns {object} `obj`
178
253
  *
179
254
  * @alias module:@lumjs/core/meta.wrapDepr
180
255
  */
181
- function wrapDepr(obj,prop,spec)
182
- {
256
+ function wrapDepr(obj, prop, spec) {
183
257
  if (typeof spec === 'function')
184
- spec = {get: spec};
185
- if (typeof spec.get !== 'function')
258
+ spec = { get: spec };
259
+ if (typeof spec.get !== 'function')
186
260
  throw new TypeError("invalid init");
187
261
 
188
- return df(obj, prop,
189
- {
190
- get: () =>
191
- deprecated(spec.dep??prop, spec.rep, spec.get())
192
- }, spec.opts);
262
+ let getter;
263
+ if (spec.rep) { // Original style using deprecated() function.
264
+ getter = () =>
265
+ deprecated(spec.dep ?? prop, spec.rep, spec.get(), spec)
266
+ }
267
+ else {
268
+ getter = () => {
269
+ let info = Array.isArray(spec.info) ? spec.info.slice(0) : [];
270
+ return deprecate(spec.dep ?? prop, {return: spec.get()}, spec, ...info);
271
+ }
272
+ }
273
+
274
+ return dp(obj, prop, {
275
+ configurable: true,
276
+ get: getter,
277
+ });
193
278
  }
194
279
 
195
- exports.wrapDepr = wrapDepr;
280
+ wrapDepr(exports, 'AbstractClass', () => require('./old/abstractclass'));
196
281
 
197
- // This is near the bottom, but before any calls to wrapDepr.
198
- const {df} = require('./obj/df');
282
+ const FUN_RESERVED = new Set(['prototype', 'length', 'name']);
283
+ const FUN_FILTER = (key) => !FUN_RESERVED.has(key);
199
284
 
200
- wrapDepr(exports, 'AbstractClass', () => require('./old/abstractclass'));
285
+ /**
286
+ * Deprecate an entire module.
287
+ *
288
+ * This will use `getOwnKeys()` to get a list of properties in a module,
289
+ * and will create a wrapped version that will use `wrapDepr()` for each
290
+ * of the properties.
291
+ *
292
+ * @param {(object|function)} src - The exported module.
293
+ *
294
+ * If this is a function, a wrapped version that calls deprecated()
295
+ * before passing all arguments will be created. It will also use
296
+ * the name of the function as a default `basespec.dep` value.
297
+ *
298
+ * @param {(object|string)} [basespec] Base specification.
299
+ *
300
+ * If this is a string it will be used as the `basespec.dep` option.
301
+ *
302
+ * Properties in this will be used as the defaults for the `spec` for each
303
+ * property in the `src` when calling the `wrapDepr()` function, with the
304
+ * exception of `dep` (see below for how it is handled), and `get` which
305
+ * is generated automatically for each property.
306
+ *
307
+ * @param {string} [basespec.dep] The name of the module being deprecated.
308
+ *
309
+ * If this is specified as a non-empty string then the spec for each property
310
+ * will use `{dep: basespec.dep+sep+key}`; otherwise it will use `key` alone.
311
+ *
312
+ * @param {function} [basespec.filter] Filter the properties.
313
+ *
314
+ * If this is specified it will be passed to `keys.filter()` to filter
315
+ * the keys that will actually be created in the destination object.
316
+ *
317
+ * If `src` is a *function*, and this is NOT specified, then a *default*
318
+ * will be used that excludes `[prototype, length, name]` from the keys.
319
+ *
320
+ * There is NO default when `src` is an *object*.
321
+ *
322
+ * @param {string} [basespec.sep] Value to use as `sep` (see `basespec.dep`).
323
+ *
324
+ * The default is a single dot (`.`) character.
325
+ *
326
+ * @param {object} [dest] Object to add wrapped properties to.
327
+ *
328
+ * This is only applicable if `src` is an *object*.
329
+ * It will NOT be used when `src` is a *function*.
330
+ * If not specified, an empty object will be created.
331
+ *
332
+ * @returns {(object|function)} Return value depends on the type of `src`.
333
+ */
334
+ function deprecateAll(src, basespec, dest = {}) {
335
+ if (typeof basespec === 'string') {
336
+ basespec = cp({ dep: basespec }, DED_OPTS);
337
+ }
338
+ else {
339
+ basespec = cp({}, DED_OPTS, basespec);
340
+ }
341
+
342
+ let hasName = () =>
343
+ (typeof basespec.dep === 'string' && basespec.dep.trim() !== '');
344
+
345
+ let getInfo = (spec) => {
346
+ let info = Array.isArray(spec.info)
347
+ ? spec.info.slice(0)
348
+ : [];
349
+ if (spec.rep) {
350
+ info.push(spec.rep);
351
+ delete spec.rep;
352
+ }
353
+ return info;
354
+ }
355
+
356
+ let keys = getOwnKeys(src);
357
+ let sep = basespec.sep ?? '.';
358
+
359
+ if (typeof src === 'function') {
360
+ if (!hasName()) basespec.dep = src.name;
361
+ if (!basespec.filter) basespec.filter = FUN_FILTER;
362
+
363
+ dest = function () {
364
+ let fun = {src, arguments};
365
+ let spec = cp({}, basespec, {fun});
366
+ let info = getInfo(spec);
367
+ deprecate(spec.dep, spec, ...info);
368
+ return src.apply(this, arguments);
369
+ }
370
+ }
371
+
372
+ if (typeof basespec.filter === 'function') {
373
+ keys = keys.filter(basespec.filter);
374
+ }
375
+
376
+ hasName = hasName(); // finalise
377
+
378
+ for (let key of keys) {
379
+ //let dep = hasName ? basespec.dep + sep + keyName(key) : key;
380
+ let spec = cp({}, basespec, {
381
+ dep: key,
382
+ get: () => src[key],
383
+ });
384
+
385
+ if (hasName) {
386
+ spec.info = getInfo(spec);
387
+ if (basespec.preRep) spec.info.unshift(basespec.preRep);
388
+ spec.showDep = basespec.dep + sep + keyName(key);
389
+ }
390
+
391
+ wrapDepr(dest, key, spec);
392
+ }
393
+
394
+ return dest;
395
+ }
396
+
397
+ cp(deprecateAll, {FUN_FILTER, FUN_RESERVED});
398
+
399
+ exports = module.exports = {
400
+ AbstractError, deprecate, deprecated, deprecateAll, Functions,
401
+ NYI, stacktrace, wrapDepr,
402
+ }
package/lib/obj/apply.js CHANGED
@@ -1,5 +1,3 @@
1
- const {F} = require('../types');
2
-
3
1
  /**
4
2
  * ApplyInfo objects will have a property with this Symbol
5
3
  * as its key and boolean `true` as its value.
@@ -208,7 +206,7 @@ function apply(target, ...values)
208
206
 
209
207
  for (let value of values)
210
208
  {
211
- if (typeof value === F)
209
+ if (typeof value === 'function')
212
210
  {
213
211
  value.apply(opts.ctx, opts.args);
214
212
  }
@@ -0,0 +1,12 @@
1
+ 'use strict';
2
+
3
+ const getProperty = require('./getproperty.js');
4
+ const getPrototypeOf = require('./getprotos.js');
5
+ const { getOwnKeys, isEmptyObject, keyName } = require('./keys.js');
6
+ const ownCount = require('./owncount.js');
7
+ const unlocked = require('./unlocked.js');
8
+
9
+ module.exports = {
10
+ getOwnKeys, getProperty, getPrototypeOf, keyName, isEmptyObject,
11
+ ownCount, unlocked,
12
+ }
package/lib/obj/cp.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Object property copying utilities.
3
3
  *
4
- * NOTE: This now just re-exports the `@lumjs/cp` package.
4
+ * NOTE: This now just re-exports the `@lumjs/cp/obj` package.
5
5
  *
6
6
  * @module @lumjs/core/obj/cp
7
7
  */