Skip to content

Commit 95431ea

Browse files
bmecktargos
authored andcommitted
policy: minor perf opts and cleanup
PR-URL: #29322 Reviewed-By: James M Snell <[email protected]>
1 parent 75c559d commit 95431ea

File tree

7 files changed

+131
-82
lines changed

7 files changed

+131
-82
lines changed

lib/internal/errors.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1124,7 +1124,7 @@ E('ERR_SOCKET_DGRAM_IS_CONNECTED', 'Already connected', Error);
11241124
E('ERR_SOCKET_DGRAM_NOT_CONNECTED', 'Not connected', Error);
11251125
E('ERR_SOCKET_DGRAM_NOT_RUNNING', 'Not running', Error);
11261126
E('ERR_SRI_PARSE',
1127-
'Subresource Integrity string %s had an unexpected at %d',
1127+
'Subresource Integrity string %j had an unexpected %j at position %d',
11281128
SyntaxError);
11291129
E('ERR_STREAM_CANNOT_PIPE', 'Cannot pipe, not readable', Error);
11301130
E('ERR_STREAM_DESTROYED', 'Cannot call %s after a stream was destroyed', Error);

lib/internal/modules/cjs/helpers.js

+25-19
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
const { Object } = primordials;
3+
const { Object, SafeMap } = primordials;
44
const {
55
ERR_MANIFEST_DEPENDENCY_MISSING,
66
ERR_UNKNOWN_BUILTIN_MODULE
@@ -28,34 +28,40 @@ function loadNativeModule(filename, request, experimentalModules) {
2828
// Invoke with makeRequireFunction(module) where |module| is the Module object
2929
// to use as the context for the require() function.
3030
// Use redirects to set up a mapping from a policy and restrict dependencies
31+
const urlToFileCache = new SafeMap();
3132
function makeRequireFunction(mod, redirects) {
3233
const Module = mod.constructor;
3334

3435
let require;
3536
if (redirects) {
36-
const { map, reaction } = redirects;
37+
const { resolve, reaction } = redirects;
3738
const id = mod.filename || mod.id;
3839
require = function require(path) {
3940
let missing = true;
40-
if (map === true) {
41+
const destination = resolve(path);
42+
if (destination === true) {
4143
missing = false;
42-
} else if (map.has(path)) {
43-
const redirect = map.get(path);
44-
if (redirect === true) {
45-
missing = false;
46-
} else if (typeof redirect === 'string') {
47-
const parsed = new URL(redirect);
48-
if (parsed.protocol === 'node:') {
49-
const specifier = parsed.pathname;
50-
const mod = loadNativeModule(
51-
specifier,
52-
redirect,
53-
experimentalModules);
54-
if (mod && mod.canBeRequiredByUsers) return mod.exports;
55-
throw new ERR_UNKNOWN_BUILTIN_MODULE(specifier);
56-
} else if (parsed.protocol === 'file:') {
57-
return mod.require(fileURLToPath(parsed));
44+
} else if (destination) {
45+
const href = destination.href;
46+
if (destination.protocol === 'node:') {
47+
const specifier = destination.pathname;
48+
const mod = loadNativeModule(
49+
specifier,
50+
href,
51+
experimentalModules);
52+
if (mod && mod.canBeRequiredByUsers) {
53+
return mod.exports;
5854
}
55+
throw new ERR_UNKNOWN_BUILTIN_MODULE(specifier);
56+
} else if (destination.protocol === 'file:') {
57+
let filepath;
58+
if (urlToFileCache.has(href)) {
59+
filepath = urlToFileCache.get(href);
60+
} else {
61+
filepath = fileURLToPath(destination);
62+
urlToFileCache.set(href, filepath);
63+
}
64+
return mod.require(filepath);
5965
}
6066
}
6167
if (missing) {

lib/internal/modules/cjs/loader.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -848,7 +848,7 @@ Module.prototype._compile = function(content, filename) {
848848
let redirects;
849849
if (manifest) {
850850
moduleURL = pathToFileURL(filename);
851-
redirects = manifest.getRedirects(moduleURL);
851+
redirects = manifest.getRedirector(moduleURL);
852852
manifest.assertIntegrity(moduleURL, content);
853853
}
854854

lib/internal/per_context/primordials.js

+11-3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// by the native module compiler.
1414

1515
const ReflectApply = Reflect.apply;
16+
const ReflectConstruct = Reflect.construct;
1617

1718
// This function is borrowed from the function with the same name on V8 Extras'
1819
// `utils` object. V8 implements Reflect.apply very efficiently in conjunction
@@ -102,10 +103,17 @@ primordials.SafePromise = makeSafe(
102103
'String',
103104
'Symbol',
104105
].forEach((name) => {
105-
const target = primordials[name] = Object.create(null);
106-
copyProps(global[name], target);
106+
const original = global[name];
107+
const target = primordials[name] = Object.setPrototypeOf({
108+
[name]: function(...args) {
109+
return new.target ?
110+
ReflectConstruct(original, args, new.target) :
111+
ReflectApply(original, this, args);
112+
}
113+
}[name], null);
114+
copyProps(original, target);
107115
const proto = primordials[name + 'Prototype'] = Object.create(null);
108-
copyPrototype(global[name].prototype, proto);
116+
copyPrototype(original.prototype, proto);
109117
});
110118

111119
Object.setPrototypeOf(primordials, null);

lib/internal/policy/manifest.js

+86-51
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
'use strict';
22

33
const {
4-
SafeMap,
5-
SafeWeakMap,
4+
Map,
5+
MapPrototype,
66
Object,
77
RegExpPrototype,
8+
SafeMap,
89
uncurryThis
910
} = primordials;
1011
const {
@@ -21,16 +22,13 @@ const debug = require('internal/util/debuglog').debuglog('policy');
2122
const SRI = require('internal/policy/sri');
2223
const crypto = require('crypto');
2324
const { Buffer } = require('buffer');
24-
const { URL } = require('url');
25+
const { URL } = require('internal/url');
2526
const { createHash, timingSafeEqual } = crypto;
2627
const HashUpdate = uncurryThis(crypto.Hash.prototype.update);
2728
const HashDigest = uncurryThis(crypto.Hash.prototype.digest);
2829
const BufferEquals = uncurryThis(Buffer.prototype.equals);
2930
const BufferToString = uncurryThis(Buffer.prototype.toString);
3031
const { entries } = Object;
31-
const kIntegrities = new SafeWeakMap();
32-
const kDependencies = new SafeWeakMap();
33-
const kReactions = new SafeWeakMap();
3432
const kRelativeURLStringPattern = /^\.{0,2}\//;
3533
const { getOptionValue } = require('internal/options');
3634
const shouldAbortOnUncaughtException =
@@ -54,13 +52,12 @@ function REACTION_LOG(error) {
5452
}
5553

5654
class Manifest {
55+
#integrities = new SafeMap();
56+
#dependencies = new SafeMap();
57+
#reaction = null;
5758
constructor(obj, manifestURL) {
58-
const integrities = {
59-
__proto__: null,
60-
};
61-
const dependencies = {
62-
__proto__: null,
63-
};
59+
const integrities = this.#integrities;
60+
const dependencies = this.#dependencies;
6461
let reaction = REACTION_THROW;
6562

6663
if (obj.onerror) {
@@ -75,23 +72,33 @@ class Manifest {
7572
}
7673
}
7774

78-
kReactions.set(this, reaction);
75+
this.#reaction = reaction;
7976
const manifestEntries = entries(obj.resources);
8077

78+
const parsedURLs = new SafeMap();
8179
for (var i = 0; i < manifestEntries.length; i++) {
82-
let url = manifestEntries[i][0];
83-
const originalURL = url;
84-
if (RegExpPrototype.test(kRelativeURLStringPattern, url)) {
85-
url = new URL(url, manifestURL).href;
80+
let resourceHREF = manifestEntries[i][0];
81+
const originalHREF = resourceHREF;
82+
let resourceURL;
83+
if (parsedURLs.has(resourceHREF)) {
84+
resourceURL = parsedURLs.get(resourceHREF);
85+
resourceHREF = resourceURL.href;
86+
} else if (
87+
RegExpPrototype.test(kRelativeURLStringPattern, resourceHREF)
88+
) {
89+
resourceURL = new URL(resourceHREF, manifestURL);
90+
resourceHREF = resourceURL.href;
91+
parsedURLs.set(originalHREF, resourceURL);
92+
parsedURLs.set(resourceHREF, resourceURL);
8693
}
8794
let integrity = manifestEntries[i][1].integrity;
8895
if (!integrity) integrity = null;
8996
if (integrity != null) {
90-
debug(`Manifest contains integrity for url ${originalURL}`);
97+
debug(`Manifest contains integrity for url ${originalHREF}`);
9198
if (typeof integrity === 'string') {
9299
const sri = Object.freeze(SRI.parse(integrity));
93-
if (url in integrities) {
94-
const old = integrities[url];
100+
if (integrities.has(resourceHREF)) {
101+
const old = integrities.get(resourceHREF);
95102
let mismatch = false;
96103

97104
if (old.length !== sri.length) {
@@ -112,14 +119,16 @@ class Manifest {
112119
}
113120

114121
if (mismatch) {
115-
throw new ERR_MANIFEST_INTEGRITY_MISMATCH(url);
122+
throw new ERR_MANIFEST_INTEGRITY_MISMATCH(resourceURL);
116123
}
117124
}
118-
integrities[url] = sri;
125+
integrities.set(resourceHREF, sri);
119126
} else if (integrity === true) {
120-
integrities[url] = true;
127+
integrities.set(resourceHREF, true);
121128
} else {
122-
throw new ERR_MANIFEST_INVALID_RESOURCE_FIELD(url, 'integrity');
129+
throw new ERR_MANIFEST_INVALID_RESOURCE_FIELD(
130+
resourceHREF,
131+
'integrity');
123132
}
124133
}
125134

@@ -128,50 +137,72 @@ class Manifest {
128137
dependencyMap = {};
129138
}
130139
if (typeof dependencyMap === 'object' && !Array.isArray(dependencyMap)) {
131-
dependencies[url] = new SafeMap(Object.entries(dependencyMap).map(
132-
([ from, to ]) => {
140+
/**
141+
* @returns {true | URL}
142+
*/
143+
const dependencyRedirectList = (toSpecifier) => {
144+
if (toSpecifier in dependencyMap !== true) {
145+
return null;
146+
} else {
147+
const to = dependencyMap[toSpecifier];
133148
if (to === true) {
134-
return [from, to];
149+
return true;
135150
}
136-
if (canBeRequiredByUsers(to)) {
137-
return [from, `node:${to}`];
151+
if (parsedURLs.has(to)) {
152+
return parsedURLs.get(to);
153+
} else if (canBeRequiredByUsers(to)) {
154+
const href = `node:${to}`;
155+
const resolvedURL = new URL(href);
156+
parsedURLs.set(to, resolvedURL);
157+
parsedURLs.set(href, resolvedURL);
158+
return resolvedURL;
138159
} else if (RegExpPrototype.test(kRelativeURLStringPattern, to)) {
139-
return [from, new URL(to, manifestURL).href];
160+
const resolvedURL = new URL(to, manifestURL);
161+
const href = resourceURL.href;
162+
parsedURLs.set(to, resolvedURL);
163+
parsedURLs.set(href, resolvedURL);
164+
return resolvedURL;
140165
}
141-
return [from, new URL(to).href];
142-
})
143-
);
166+
const resolvedURL = new URL(to);
167+
const href = resourceURL.href;
168+
parsedURLs.set(to, resolvedURL);
169+
parsedURLs.set(href, resolvedURL);
170+
return resolvedURL;
171+
}
172+
};
173+
dependencies.set(resourceHREF, dependencyRedirectList);
144174
} else if (dependencyMap === true) {
145-
dependencies[url] = true;
175+
const arbitraryDependencies = () => true;
176+
dependencies.set(resourceHREF, arbitraryDependencies);
146177
} else {
147-
throw new ERR_MANIFEST_INVALID_RESOURCE_FIELD(url, 'dependencies');
178+
throw new ERR_MANIFEST_INVALID_RESOURCE_FIELD(
179+
resourceHREF,
180+
'dependencies');
148181
}
149182
}
150-
Object.freeze(integrities);
151-
kIntegrities.set(this, integrities);
152-
Object.freeze(dependencies);
153-
kDependencies.set(this, dependencies);
154183
Object.freeze(this);
155184
}
156185

157-
getRedirects(requester) {
158-
const dependencies = kDependencies.get(this);
159-
if (dependencies && requester in dependencies) {
186+
getRedirector(requester) {
187+
requester = `${requester}`;
188+
const dependencies = this.#dependencies;
189+
if (dependencies.has(requester)) {
160190
return {
161-
map: dependencies[requester],
162-
reaction: kReactions.get(this)
191+
resolve: (to) => dependencies.get(requester)(`${to}`),
192+
reaction: this.#reaction
163193
};
164194
}
165195
return null;
166196
}
167197

168198
assertIntegrity(url, content) {
169-
debug(`Checking integrity of ${url}`);
170-
const integrities = kIntegrities.get(this);
171-
const realIntegrities = new SafeMap();
199+
const href = `${url}`;
200+
debug(`Checking integrity of ${href}`);
201+
const integrities = this.#integrities;
202+
const realIntegrities = new Map();
172203

173-
if (integrities && url in integrities) {
174-
const integrityEntries = integrities[url];
204+
if (integrities.has(href)) {
205+
const integrityEntries = integrities.get(href);
175206
if (integrityEntries === true) return true;
176207
// Avoid clobbered Symbol.iterator
177208
for (var i = 0; i < integrityEntries.length; i++) {
@@ -186,11 +217,15 @@ class Manifest {
186217
timingSafeEqual(digest, expected)) {
187218
return true;
188219
}
189-
realIntegrities.set(algorithm, BufferToString(digest, 'base64'));
220+
MapPrototype.set(
221+
realIntegrities,
222+
algorithm,
223+
BufferToString(digest, 'base64')
224+
);
190225
}
191226
}
192227
const error = new ERR_MANIFEST_ASSERT_INTEGRITY(url, realIntegrities);
193-
kReactions.get(this)(error);
228+
this.#reaction(error);
194229
}
195230
}
196231

lib/internal/policy/sri.js

+6-6
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,16 @@ const {
1313
} = require('internal/errors').codes;
1414
const kWSP = '[\\x20\\x09]';
1515
const kVCHAR = '[\\x21-\\x7E]';
16-
const kHASH_ALGO = 'sha256|sha384|sha512';
16+
const kHASH_ALGO = 'sha(?:256|384|512)';
1717
// Base64
1818
const kHASH_VALUE = '[A-Za-z0-9+/]+[=]{0,2}';
1919
const kHASH_EXPRESSION = `(${kHASH_ALGO})-(${kHASH_VALUE})`;
2020
const kOPTION_EXPRESSION = `(${kVCHAR}*)`;
2121
const kHASH_WITH_OPTIONS = `${kHASH_EXPRESSION}(?:[?](${kOPTION_EXPRESSION}))?`;
22-
const kSRIPattern = new RegExp(`(${kWSP}*)(?:${kHASH_WITH_OPTIONS})`, 'g');
22+
const kSRIPattern = RegExp(`(${kWSP}*)(?:${kHASH_WITH_OPTIONS})`, 'g');
2323
const { freeze } = Object;
2424
Object.seal(kSRIPattern);
25-
const kAllWSP = new RegExp(`^${kWSP}*$`);
25+
const kAllWSP = RegExp(`^${kWSP}*$`);
2626
Object.seal(kAllWSP);
2727

2828
const BufferFrom = require('buffer').Buffer.from;
@@ -34,10 +34,10 @@ const parse = (str) => {
3434
const entries = [];
3535
while (match = RegExpPrototype.exec(kSRIPattern, str)) {
3636
if (match.index !== prevIndex) {
37-
throw new ERR_SRI_PARSE(str, prevIndex);
37+
throw new ERR_SRI_PARSE(str, str.charAt(prevIndex), prevIndex);
3838
}
3939
if (entries.length > 0 && match[1] === '') {
40-
throw new ERR_SRI_PARSE(str, prevIndex);
40+
throw new ERR_SRI_PARSE(str, str.charAt(prevIndex), prevIndex);
4141
}
4242

4343
// Avoid setters being fired
@@ -56,7 +56,7 @@ const parse = (str) => {
5656

5757
if (prevIndex !== str.length) {
5858
if (!RegExpPrototype.test(kAllWSP, StringPrototype.slice(str, prevIndex))) {
59-
throw new ERR_SRI_PARSE(str, prevIndex);
59+
throw new ERR_SRI_PARSE(str, str.charAt(prevIndex), prevIndex);
6060
}
6161
}
6262
return entries;

lib/internal/process/policy.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,6 @@ module.exports = Object.freeze({
5252
},
5353

5454
assertIntegrity(moduleURL, content) {
55-
this.manifest.matchesIntegrity(moduleURL, content);
55+
this.manifest.assertIntegrity(moduleURL, content);
5656
}
5757
});

0 commit comments

Comments
 (0)