Skip to content

Commit f509762

Browse files
committed
various tweaks; simplify dom_glob; add arr
1 parent 3f1bbeb commit f509762

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+1416
-792
lines changed

.eslintrc

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
"TextDecoder": "readonly",
1818
"TextEncoder": "readonly",
1919
"ReadableStream": "readonly",
20+
"performance": "readonly",
21+
"crypto": "readonly",
2022
},
2123
"rules": {
2224
"no-restricted-globals": ["error",

arr.mjs

+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import * as l from './lang.mjs'
2+
3+
/*
4+
Subclass of `Array` with a better constructor signature, and with various method
5+
overrides that prevent surprising semantics and amortize performance penalties.
6+
7+
See implementation notes in `arr_bench.mjs`.
8+
9+
Not recommended for actual use. Subclasses of `Array` have bad performance and
10+
other issues. Prefer `Vec`.
11+
*/
12+
export class Arr extends Array {
13+
constructor(src) {
14+
if (l.isNum(src)) {
15+
super(src)
16+
return
17+
}
18+
19+
src = l.toTrueArr(src)
20+
21+
const len = src.length
22+
super(len)
23+
24+
let ind = -1
25+
while (++ind < len) this[ind] = src[ind]
26+
}
27+
28+
/* Overrides for standard behaviors. */
29+
30+
lastIndexOf(val) {
31+
if (val !== val) return -1
32+
let ind = this.length
33+
while (--ind >= 0) if (this[ind] === val) return ind
34+
return ind
35+
}
36+
37+
slice(...val) {
38+
const buf = this.toArray()
39+
return val.length ? buf.concat(...val) : buf
40+
}
41+
42+
concat(...val) {
43+
const buf = this.toArray()
44+
return val.length ? buf.concat(...val) : buf
45+
}
46+
47+
join(val) {return this.toArray().join(val)}
48+
49+
reverse() {
50+
const len = this.length
51+
const max = (len / 2) | 0
52+
let ind = -1
53+
54+
while (++ind < max) {
55+
const mir = len - 1 - ind
56+
const val = this[ind]
57+
58+
this[ind] = this[mir]
59+
this[mir] = val
60+
}
61+
return this
62+
}
63+
64+
flat(...val) {return this.toArray().flat(...val)}
65+
every(...val) {return this.toArray().every(...val)}
66+
filter(...val) {return this.toArray().filter(...val)}
67+
find(...val) {return this.toArray().find(...val)}
68+
findIndex(...val) {return this.toArray().findIndex(...val)}
69+
findLast(...val) {return this.toArray().findLast(...val)}
70+
findLastIndex(...val) {return this.toArray().findLastIndex(...val)}
71+
flatMap(...val) {return this.toArray().flatMap(...val)}
72+
forEach(...val) {return this.toArray().forEach(...val)}
73+
map(...val) {return this.toArray().map(...val)}
74+
reduce(...val) {return this.toArray().reduce(...val)}
75+
reduceRight(...val) {return this.toArray().reduceRight(...val)}
76+
some(...val) {return this.toArray().some(...val)}
77+
78+
sort(fun) {
79+
const src = this.toArray().sort(fun)
80+
const len = src.length
81+
let ind = -1
82+
while (++ind < len) this[ind] = src[ind]
83+
return this
84+
}
85+
86+
keys() {return this.toArray().keys()}
87+
values() {return this.toArray().values()}
88+
entries() {return this.toArray().entries()}
89+
[Symbol.iterator]() {return this.values()}
90+
91+
static from(src, fun) {
92+
src = l.toTrueArr(src)
93+
94+
const len = src.length
95+
const buf = this.make(len)
96+
let ind = -1
97+
98+
if (l.optFun(fun)) while (++ind < len) buf[ind] = fun(src[ind], ind)
99+
else while (++ind < len) buf[ind] = src[ind]
100+
return buf
101+
}
102+
103+
static of(...src) {return this.from(src)}
104+
105+
/* Non-standard extensions. */
106+
107+
add(val) {return this.push(val), this}
108+
109+
setLen(val) {
110+
val = l.reqNum(val)
111+
if (this.length !== val) this.length = val
112+
return this
113+
}
114+
115+
clear() {return this.setLen(0)}
116+
117+
// Our amortizer and bottleneck. Our savior and destroyer.
118+
toArray() {
119+
const len = this.length
120+
const buf = Array(len)
121+
let ind = -1
122+
while (++ind < len) buf[ind] = this[ind]
123+
return buf
124+
}
125+
126+
static make(len) {return new this(len)}
127+
}

coll.mjs

+13-26
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ export class Bset extends Set {
2828

2929
export class ClsSet extends Bset {
3030
get cls() {return Object}
31-
add(val) {return super.add(l.toInst(val, this.cls))}
31+
add(val) {return super.add(this.make(val))}
32+
make(val) {return l.toInst(val, this.cls)}
3233
}
3334

3435
export function bmap(val) {return new Bmap(val)}
@@ -79,7 +80,8 @@ export class Bmap extends Map {
7980

8081
export class ClsMap extends Bmap {
8182
get cls() {return Object}
82-
set(key, val) {super.set(key, l.toInst(val, this.cls))}
83+
set(key, val) {super.set(key, this.make(val))}
84+
make(val) {return l.toInst(val, this.cls)}
8385
}
8486

8587
// Short for "primary key optional".
@@ -119,12 +121,13 @@ export class Coll extends Bmap {
119121
export class ClsColl extends Coll {
120122
get cls() {return Object}
121123
set(key, val) {return super.set(key, l.reqInst(val, this.cls))}
122-
add(val) {return super.add(l.toInst(val, this.cls))}
123-
addOpt(val) {return super.addOpt(l.toInst(val, this.cls))}
124+
add(val) {return super.add(this.make(val))}
125+
addOpt(val) {return super.addOpt(this.make(val))}
126+
make(val) {return l.toInst(val, this.cls)}
124127
}
125128

126129
export class Vec extends l.Emp {
127-
constructor(val) {super().$ = l.laxArr(val)}
130+
constructor(val) {super().$ = l.laxTrueArr(val)}
128131

129132
add(val) {return this.$.push(val), this}
130133

@@ -142,28 +145,17 @@ export class Vec extends l.Emp {
142145

143146
[Symbol.iterator]() {return this.$.values()}
144147

145-
static of(...val) {return new this(Array.of(...val))}
146-
147-
static from(val) {
148-
if (l.isNil(val)) return new this()
149-
return new this(Array.from(l.reqIter(val)))
150-
}
151-
152148
static make(len) {return new this(Array(l.reqNat(len)))}
149+
static from(val) {return new this(l.toTrueArr(val))}
150+
static of(...val) {return new this(Array.of(...val))}
153151
}
154152

155153
// Short for "class vector".
156154
export class ClsVec extends Vec {
157-
constructor(val) {
158-
super(val)
159-
if (!this.$.every(selfIsInst, this)) {
160-
this.$ = this.$.map(selfToInst, this)
161-
}
162-
}
163-
164-
add(val) {return super.add(l.toInst(val, this.cls))}
165-
166155
get cls() {return Object}
156+
constructor(src) {super().addFrom(src)}
157+
add(val) {return super.add(this.make(val))}
158+
make(val) {return l.toInst(val, this.cls)}
167159
}
168160

169161
export class Que extends Set {
@@ -188,8 +180,3 @@ export class Que extends Set {
188180
return this
189181
}
190182
}
191-
192-
/* Internal */
193-
194-
function selfIsInst(val) {return l.isInst(val, this.cls)}
195-
function selfToInst(val) {return l.toInst(val, this.cls)}

doc/cmd_doc.mjs

+6-3
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const FEATS = [
3939
[`test`, `tools for testing and benchmarking.`],
4040
]
4141

42-
class Pkg extends o.MemGet {
42+
class Pkg extends o.Strict {
4343
constructor(feats) {
4444
super()
4545
this.feats = new co.Coll()
@@ -48,6 +48,7 @@ class Pkg extends o.MemGet {
4848
}
4949
}
5050

51+
static {o.memGet(this)}
5152
get base() {return `https://cdn.jsdelivr.net/npm/@mitranim/js`}
5253
get ver() {return VER}
5354
get url() {return s.inter(this.base, `@`, this.ver)}
@@ -73,7 +74,7 @@ class Pkg extends o.MemGet {
7374
featUrl(name) {return this.feat(name).selfUrl}
7475
}
7576

76-
class Feat extends o.MemGet {
77+
class Feat extends o.Strict {
7778
constructor(pkg, name, desc) {
7879
super()
7980
this.pkg = l.reqInst(pkg, Pkg)
@@ -83,6 +84,7 @@ class Feat extends o.MemGet {
8384

8485
pk() {return this.name}
8586

87+
static {o.memGet(this)}
8688
get codePath() {return s.str(this.name, `.mjs`)}
8789
get docCodePath() {return u.urlJoin(`..`, this.codePath)}
8890
get codeHead() {return mdLink(this.codePath, this.docCodePath)}
@@ -216,7 +218,7 @@ ${s.joinLines(i.map(idents, toUndocBullet))}
216218
featUrl(name) {return this.pkg.featUrl(name)}
217219
}
218220

219-
class Ident extends o.MemGet {
221+
class Ident extends o.Strict {
220222
constructor(feat, line, type, name) {
221223
super()
222224
this.feat = l.reqInst(feat, Feat)
@@ -227,6 +229,7 @@ class Ident extends o.MemGet {
227229

228230
pk() {return this.name}
229231

232+
static {o.memGet(this)}
230233
get row() {return this.line + 1}
231234
get testRow() {return this.$testRow()}
232235
get testLine() {return this.$testLine()}

doc/coll/_Vec.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Short for "vector". Thin wrapper around a plain array. Features:
99
Differences and advantages over `Array`:
1010

1111
* Better constructor signature.
12-
* Constructor takes exactly one argument, which is either {{link lang isNil nil}} or an {{link lang isArr array}}.
12+
* Constructor takes exactly one argument, which is either {{link lang isNil nil}} or an {{link lang isTrueArr array}}.
1313
* For comparison, the `Array` constructor has special cases that make subclassing difficult.
1414
* Can be subclassed without trashing performance.
1515
* At the time of writing, subclasses of `Array` suffer horrible deoptimization in V8.

doc/dom_glob_native_readme.md

-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
```js
1414
import * as dg from '{{featUrl dom_glob_native}}'
1515

16-
console.log(dg.document)
1716
console.log(dg.glob)
1817
```
1918

doc/dom_glob_shim_readme.md

-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
```js
1414
import * as dg from '{{featUrl dom_glob_shim}}'
1515

16-
console.log(dg.document)
1716
console.log(dg.glob)
1817
```
1918

doc/iter/arr.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
Converts an arbitrary {{link lang isSeq sequence}} to an array. Allows the following inputs:
1+
Converts an arbitrary {{link lang isSeq sequence}} to an array. Supports the following inputs:
22

33
* {{link lang isNil Nil}}: return `[]`.
4-
* {{link lang isArr Array}}: copy via `.slice()`.
4+
* {{link lang isTrueArr Array}}: return as-is.
55
* {{link lang isList List}}: convert via `Array.prototype.slice`.
66
* {{link lang isSet Set}} or arbitrary {{link lang isIterator iterator}}: convert to array by iterating.
77

8-
The output is always a shallow copy. Mutation doesn't affect the source.
8+
Unlike {{link iter values}}, this function rejects other inputs such as non-nil primitives, dicts, maps, arbitrary iterables, ensuring that the input is always a sequence.
99

10-
Unlike {{link iter values}}, `arr` rejects other inputs such as non-nil primitives, dicts, maps, arbitrary iterables, ensuring that the input is always a sequence.
10+
The input may or may not be a copy. To ensure copying, use {{link iter arrCopy}}.

doc/iter/arrCopy.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Like {{link iter arr}}, converts an arbitrary sequence to an array. Unlike `arr,` always makes a copy. Mutating the output doesn't affect the original.
1+
Similar to {{link iter arr}}, but always makes a copy, even if the input is already a true array.

doc/iter/mapCls.md

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
Signature: `(Iter<A>, {new(A): B}) => B[]`.
2+
3+
Similar to {{link iter map}}, but instead of taking an arbitrary function, takes a class and calls it with `new` for each element.
4+
5+
```js
6+
import * as i from '{{featUrl iter}}'
7+
import * as o from '{{featUrl obj}}'
8+
9+
class Model extends o.Dict {pk() {return this.id}}
10+
class Person extends Model {}
11+
12+
console.log(i.mapCls(
13+
[
14+
{id: 1, name: `Mira`},
15+
{id: 2, name: `Kara`},
16+
],
17+
Person,
18+
))
19+
20+
/*
21+
[
22+
Person { id: 1, name: "Mira" },
23+
Person { id: 2, name: "Kara" },
24+
]
25+
*/
26+
```

doc/lang/isTrueArr.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Similar to [`Array.isArray`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray) and {{link lang isArr}}, but returns true only for instances of the _exact_ `Array` class, false for instances of subclasses.
2+
3+
At the time of writing, subclasses of `Array` suffer horrible performance penalties in V8, and possibly in other engines. Using them can also cause deoptimization of code that would otherwise run much faster. We sometimes prioritize or even enforce "true" arrays for consistent performance.

doc/lang/toTrueArr.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Idempotent conversion to a {{link lang isTrueArr true array}}. Allowed inputs:
2+
3+
* {{link lang isNil Nil}} → return `[]`.
4+
* {{link lang isTrueArr True array}} → return as-is.
5+
* {{link lang isIter Iterable}} → convert to `Array`.
6+
* Otherwise → `TypeError` exception.

doc/obj/pickKeys.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
Signature: `({[Key: A]}, keys) => {[Key: A]}`.
22

3-
Returns a version of the given dict, keeping only the given properties. Keys can be either a `Set` or an arbitrary {{link iter arr sequence}}. Each key must satisfy {{link lang isKey}}. Existence is not required: missing properties are silently ignored. Returns an empty dict if the input is {{link lang isNil nil}}.
3+
Returns a version of the given dict, keeping only the given properties. Keys can be either a `Set` or an arbitrary {{link iter values sequence}}. Each key must satisfy {{link lang isKey}}. Existence is not required: missing properties are silently ignored. Returns an empty dict if the input is {{link lang isNil nil}}.

doc/prax_readme.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ import * as p from '{{featUrl prax}}'
120120
import * as dg from '{{featUrl dom_glob_shim}}'
121121
import * as dg from '{{featUrl dom_glob_native}}'
122122

123-
const ren = new p.Ren(dg.document).patchProto(dg.glob.Element)
123+
const ren = p.Ren.from(dg.glob)
124124
```
125125

126126
Rendering a complete document with doctype:
@@ -129,7 +129,7 @@ Rendering a complete document with doctype:
129129
import * as p from '{{featUrl prax}}'
130130
import * as dg from '{{featUrl dom_glob_shim}}'
131131

132-
const ren = new p.Ren(dg.document).patchProto(dg.glob.Element)
132+
const ren = p.Ren.from(dg.glob)
133133
const {E} = ren
134134
const A = p.PropBui.main
135135

doc/readme.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Alternatives that suck:
4343

4444
## Usage
4545

46-
Uses native JS modules, which can be imported by URL in browsers and Deno. The truly lazy can import all core modules at once:
46+
Uses native JS modules, which can be imported by URL in browsers and Deno. The truly lazy can import many core modules at once:
4747

4848
```js
4949
import * as a from '{{url}}/all.mjs'

0 commit comments

Comments
 (0)