From b226bf889f0c4cb58d8492c3767db4f4da273064 Mon Sep 17 00:00:00 2001 From: uzlopak Date: Wed, 21 Aug 2024 21:41:19 +0200 Subject: [PATCH 1/2] url: use primordials in url.js --- lib/url.js | 224 +++++++++++++++++++++++++++++------------------------ 1 file changed, 123 insertions(+), 101 deletions(-) diff --git a/lib/url.js b/lib/url.js index 8fa8553c2b3f30..45047cbc6a137c 100644 --- a/lib/url.js +++ b/lib/url.js @@ -18,15 +18,25 @@ // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. - 'use strict'; const { + ArrayPrototypeJoin, + ArrayPrototypePop, + ArrayPrototypePush, + ArrayPrototypeShift, + ArrayPrototypeUnshift, Boolean, Int8Array, ObjectAssign, - ObjectKeys, + RegExpPrototypeExec, StringPrototypeCharCodeAt, + StringPrototypeIncludes, + StringPrototypeIndexOf, + StringPrototypeReplaceAll, + StringPrototypeSlice, + StringPrototypeSplit, + StringPrototypeToLowerCase, decodeURIComponent, } = primordials; @@ -103,8 +113,6 @@ const { CHAR_ZERO_WIDTH_NOBREAK_SPACE, CHAR_HASH, CHAR_FORWARD_SLASH, - CHAR_LEFT_SQUARE_BRACKET, - CHAR_RIGHT_SQUARE_BRACKET, CHAR_LEFT_ANGLE_BRACKET, CHAR_RIGHT_ANGLE_BRACKET, CHAR_LEFT_CURLY_BRACKET, @@ -145,9 +153,8 @@ function urlParse(url, parseQueryString, slashesDenoteHost) { function isIpv6Hostname(hostname) { return ( - StringPrototypeCharCodeAt(hostname, 0) === CHAR_LEFT_SQUARE_BRACKET && - StringPrototypeCharCodeAt(hostname, hostname.length - 1) === - CHAR_RIGHT_SQUARE_BRACKET + hostname[0] === '[' && + hostname[hostname.length - 1] === ']' ); } @@ -180,7 +187,7 @@ Url.prototype.parse = function parse(url, parseQueryString, slashesDenoteHost) { let rest = ''; let lastPos = 0; for (let i = 0, inWs = false, split = false; i < url.length; ++i) { - const code = url.charCodeAt(i); + const code = StringPrototypeCharCodeAt(url, i); // Find first and last non-whitespace characters for trimming const isWs = code < 33 || @@ -214,7 +221,7 @@ Url.prototype.parse = function parse(url, parseQueryString, slashesDenoteHost) { break; case CHAR_BACKWARD_SLASH: if (i - lastPos > 0) - rest += url.slice(lastPos, i); + rest += StringPrototypeSlice(url, lastPos, i); rest += '/'; lastPos = i + 1; break; @@ -233,22 +240,22 @@ Url.prototype.parse = function parse(url, parseQueryString, slashesDenoteHost) { if (start === 0) rest = url; else - rest = url.slice(start); + rest = StringPrototypeSlice(url, start); } else { - rest = url.slice(start, end); + rest = StringPrototypeSlice(url, start, end); } } else if (end === -1 && lastPos < url.length) { // We converted some backslashes and have only part of the entire string - rest += url.slice(lastPos); + rest += StringPrototypeSlice(url, lastPos); } else if (end !== -1 && lastPos < end) { // We converted some backslashes and have only part of the entire string - rest += url.slice(lastPos, end); + rest += StringPrototypeSlice(url, lastPos, end); } } if (!slashesDenoteHost && !hasHash && !hasAt) { // Try fast path regexp - const simplePath = simplePathPattern.exec(rest); + const simplePath = RegExpPrototypeExec(simplePathPattern, rest); if (simplePath) { this.path = rest; this.href = rest; @@ -256,9 +263,9 @@ Url.prototype.parse = function parse(url, parseQueryString, slashesDenoteHost) { if (simplePath[2]) { this.search = simplePath[2]; if (parseQueryString) { - this.query = querystring.parse(this.search.slice(1)); + this.query = querystring.parse(StringPrototypeSlice(this.search, 1)); } else { - this.query = this.search.slice(1); + this.query = StringPrototypeSlice(this.search, 1); } } else if (parseQueryString) { this.search = null; @@ -268,13 +275,13 @@ Url.prototype.parse = function parse(url, parseQueryString, slashesDenoteHost) { } } - let proto = protocolPattern.exec(rest); + let proto = RegExpPrototypeExec(protocolPattern, rest); let lowerProto; if (proto) { proto = proto[0]; - lowerProto = proto.toLowerCase(); + lowerProto = StringPrototypeToLowerCase(proto); this.protocol = lowerProto; - rest = rest.slice(proto.length); + rest = StringPrototypeSlice(rest, proto.length); } // Figure out if it's got a host @@ -282,11 +289,11 @@ Url.prototype.parse = function parse(url, parseQueryString, slashesDenoteHost) { // resolution will treat //foo/bar as host=foo,path=bar because that's // how the browser resolves relative URLs. let slashes; - if (slashesDenoteHost || proto || hostPattern.test(rest)) { - slashes = rest.charCodeAt(0) === CHAR_FORWARD_SLASH && - rest.charCodeAt(1) === CHAR_FORWARD_SLASH; + if (slashesDenoteHost || proto || RegExpPrototypeExec(hostPattern, rest) !== null) { + slashes = rest[0] === '/' && + rest[1] === '/'; if (slashes && !(proto && hostlessProtocol.has(lowerProto))) { - rest = rest.slice(2); + rest = StringPrototypeSlice(rest, 2); this.slashes = true; } } @@ -310,12 +317,12 @@ Url.prototype.parse = function parse(url, parseQueryString, slashesDenoteHost) { let atSign = -1; let nonHost = -1; for (let i = 0; i < rest.length; ++i) { - switch (rest.charCodeAt(i)) { + switch (StringPrototypeCharCodeAt(rest, i)) { case CHAR_TAB: case CHAR_LINE_FEED: case CHAR_CARRIAGE_RETURN: // WHATWG URL removes tabs, newlines, and carriage returns. Let's do that too. - rest = rest.slice(0, i) + rest.slice(i + 1); + rest = StringPrototypeSlice(rest, 0, i) + StringPrototypeSlice(rest, i + 1); i -= 1; break; case CHAR_SPACE: @@ -355,15 +362,15 @@ Url.prototype.parse = function parse(url, parseQueryString, slashesDenoteHost) { } start = 0; if (atSign !== -1) { - this.auth = decodeURIComponent(rest.slice(0, atSign)); + this.auth = decodeURIComponent(StringPrototypeSlice(rest, 0, atSign)); start = atSign + 1; } if (nonHost === -1) { - this.host = rest.slice(start); + this.host = StringPrototypeSlice(rest, start); rest = ''; } else { - this.host = rest.slice(start, nonHost); - rest = rest.slice(nonHost); + this.host = StringPrototypeSlice(rest, start, nonHost); + rest = StringPrototypeSlice(rest, nonHost); } // pull out port. @@ -389,12 +396,12 @@ Url.prototype.parse = function parse(url, parseQueryString, slashesDenoteHost) { this.hostname = ''; } else { // Hostnames are always lower case. - this.hostname = this.hostname.toLowerCase(); + this.hostname = StringPrototypeToLowerCase(this.hostname); } if (this.hostname !== '') { if (ipv6Hostname) { - if (forbiddenHostCharsIpv6.test(this.hostname)) { + if (RegExpPrototypeExec(forbiddenHostCharsIpv6, this.hostname) !== null) { throw new ERR_INVALID_URL(url); } } else { @@ -413,7 +420,7 @@ Url.prototype.parse = function parse(url, parseQueryString, slashesDenoteHost) { // Rather than trying to correct this by moving the non-host part into // the pathname as we've done in getHostname, throw an exception to // convey the severity of this issue. - if (this.hostname === '' || forbiddenHostChars.test(this.hostname)) { + if (this.hostname === '' || RegExpPrototypeExec(forbiddenHostChars, this.hostname) !== null) { throw new ERR_INVALID_URL(url); } } @@ -426,7 +433,7 @@ Url.prototype.parse = function parse(url, parseQueryString, slashesDenoteHost) { // strip [ and ] from the hostname // the host field still retains them, though if (ipv6Hostname) { - this.hostname = this.hostname.slice(1, -1); + this.hostname = StringPrototypeSlice(this.hostname, 1, -1); if (rest[0] !== '/') { rest = '/' + rest; } @@ -445,9 +452,9 @@ Url.prototype.parse = function parse(url, parseQueryString, slashesDenoteHost) { let questionIdx = -1; let hashIdx = -1; for (let i = 0; i < rest.length; ++i) { - const code = rest.charCodeAt(i); + const code = StringPrototypeCharCodeAt(rest, i); if (code === CHAR_HASH) { - this.hash = rest.slice(i); + this.hash = StringPrototypeSlice(rest, i); hashIdx = i; break; } else if (code === CHAR_QUESTION_MARK && questionIdx === -1) { @@ -457,11 +464,11 @@ Url.prototype.parse = function parse(url, parseQueryString, slashesDenoteHost) { if (questionIdx !== -1) { if (hashIdx === -1) { - this.search = rest.slice(questionIdx); - this.query = rest.slice(questionIdx + 1); + this.search = StringPrototypeSlice(rest, questionIdx); + this.query = StringPrototypeSlice(rest, questionIdx + 1); } else { - this.search = rest.slice(questionIdx, hashIdx); - this.query = rest.slice(questionIdx + 1, hashIdx); + this.search = StringPrototypeSlice(rest, questionIdx, hashIdx); + this.query = StringPrototypeSlice(rest, questionIdx + 1, hashIdx); } if (parseQueryString) { this.query = querystring.parse(this.query); @@ -479,7 +486,7 @@ Url.prototype.parse = function parse(url, parseQueryString, slashesDenoteHost) { if (rest.length > 0) this.pathname = rest; } else if (firstIdx > 0) { - this.pathname = rest.slice(0, firstIdx); + this.pathname = StringPrototypeSlice(rest, 0, firstIdx); } if (slashedProtocol.has(lowerProto) && this.hostname && !this.pathname) { @@ -501,7 +508,7 @@ Url.prototype.parse = function parse(url, parseQueryString, slashesDenoteHost) { let warnInvalidPort = true; function getHostname(self, rest, hostname, url) { for (let i = 0; i < hostname.length; ++i) { - const code = hostname.charCodeAt(i); + const code = StringPrototypeCharCodeAt(hostname, i); const isValid = (code !== CHAR_FORWARD_SLASH && code !== CHAR_BACKWARD_SLASH && code !== CHAR_HASH && @@ -517,8 +524,8 @@ function getHostname(self, rest, hostname, url) { process.emitWarning(detail, 'DeprecationWarning', 'DEP0170'); warnInvalidPort = false; } - self.hostname = hostname.slice(0, i); - return `/${hostname.slice(i)}${rest}`; + self.hostname = StringPrototypeSlice(hostname, 0, i); + return `/${StringPrototypeSlice(hostname, i)}${rest}`; } } return rest; @@ -550,11 +557,11 @@ function autoEscapeStr(rest) { let lastEscapedPos = 0; for (let i = 0; i < rest.length; ++i) { // `escaped` contains substring up to the last escaped character. - const escapedChar = escapedCodes[rest.charCodeAt(i)]; + const escapedChar = escapedCodes[StringPrototypeCharCodeAt(rest, i)]; if (escapedChar) { // Concat if there are ordinary characters in the middle. if (i > lastEscapedPos) - escaped += rest.slice(lastEscapedPos, i); + escaped += StringPrototypeSlice(rest, lastEscapedPos, i); escaped += escapedChar; lastEscapedPos = i + 1; } @@ -564,7 +571,7 @@ function autoEscapeStr(rest) { // There are ordinary characters at the end. if (lastEscapedPos < rest.length) - escaped += rest.slice(lastEscapedPos); + escaped += StringPrototypeSlice(rest, lastEscapedPos); return escaped; } @@ -646,7 +653,7 @@ Url.prototype.format = function format() { host = auth + this.host; } else if (this.hostname) { host = auth + ( - this.hostname.includes(':') && !isIpv6Hostname(this.hostname) ? + StringPrototypeIncludes(this.hostname, ':') && !isIpv6Hostname(this.hostname) ? '[' + this.hostname + ']' : this.hostname ); @@ -661,22 +668,22 @@ Url.prototype.format = function format() { let search = this.search || (query && ('?' + query)) || ''; - if (protocol && protocol.charCodeAt(protocol.length - 1) !== 58/* : */) + if (protocol && protocol[protocol.length - 1] !== ':') protocol += ':'; let newPathname = ''; let lastPos = 0; for (let i = 0; i < pathname.length; ++i) { - switch (pathname.charCodeAt(i)) { + switch (StringPrototypeCharCodeAt(pathname, i)) { case CHAR_HASH: if (i - lastPos > 0) - newPathname += pathname.slice(lastPos, i); + newPathname += StringPrototypeSlice(pathname, lastPos, i); newPathname += '%23'; lastPos = i + 1; break; case CHAR_QUESTION_MARK: if (i - lastPos > 0) - newPathname += pathname.slice(lastPos, i); + newPathname += StringPrototypeSlice(pathname, lastPos, i); newPathname += '%3F'; lastPos = i + 1; break; @@ -684,7 +691,7 @@ Url.prototype.format = function format() { } if (lastPos > 0) { if (lastPos !== pathname.length) - pathname = newPathname + pathname.slice(lastPos); + pathname = newPathname + StringPrototypeSlice(pathname, lastPos); else pathname = newPathname; } @@ -693,23 +700,23 @@ Url.prototype.format = function format() { // unless they had them to begin with. if (this.slashes || slashedProtocol.has(protocol)) { if (this.slashes || host) { - if (pathname && pathname.charCodeAt(0) !== CHAR_FORWARD_SLASH) + if (pathname && pathname[0] !== '/') pathname = '/' + pathname; host = '//' + host; } else if (protocol.length >= 4 && - protocol.charCodeAt(0) === 102/* f */ && - protocol.charCodeAt(1) === 105/* i */ && - protocol.charCodeAt(2) === 108/* l */ && - protocol.charCodeAt(3) === 101/* e */) { + protocol[0] === 'f' && + protocol[1] === 'i' && + protocol[2] === 'l' && + protocol[3] === 'e') { host = '//'; } } - search = search.replace(/#/g, '%23'); + search = StringPrototypeReplaceAll(search, '#', '%23'); - if (hash && hash.charCodeAt(0) !== CHAR_HASH) + if (hash && hash[0] !== '#') hash = '#' + hash; - if (search && search.charCodeAt(0) !== CHAR_QUESTION_MARK) + if (search && search[0] !== '?') search = '?' + search; return protocol + host + pathname + search + hash; @@ -751,12 +758,10 @@ Url.prototype.resolveObject = function resolveObject(relative) { // Hrefs like //foo/bar always cut to the protocol. if (relative.slashes && !relative.protocol) { // Take everything except the protocol from relative - const relativeWithoutProtocol = ObjectKeys(relative).reduce((acc, key) => { - if (key !== 'protocol') { - acc[key] = relative[key]; - } - return acc; - }, {}); + const { + protocol: protocolThrowAway, // eslint-disable-line no-unused-vars + ...relativeWithoutProtocol + } = relative; ObjectAssign(result, relativeWithoutProtocol); // urlParse appends trailing / to urls like http://www.example.com @@ -786,15 +791,15 @@ Url.prototype.resolveObject = function resolveObject(relative) { result.protocol = relative.protocol; if (!relative.host && - !/^file:?$/.test(relative.protocol) && + !isFileProtocol(relative.protocol) && !hostlessProtocol.has(relative.protocol)) { - const relPath = (relative.pathname || '').split('/'); - while (relPath.length && !(relative.host = relPath.shift())); + const relPath = relative.pathname ? StringPrototypeSplit(relative.pathname, '/') : ['']; + while (relPath.length && !(relative.host = ArrayPrototypeShift(relPath))); if (!relative.host) relative.host = ''; if (!relative.hostname) relative.hostname = ''; - if (relPath[0] !== '') relPath.unshift(''); - if (relPath.length < 2) relPath.unshift(''); - result.pathname = relPath.join('/'); + if (relPath[0] !== '') ArrayPrototypeUnshift(relPath, ''); + if (relPath.length < 2) ArrayPrototypeUnshift(relPath, ''); + result.pathname = ArrayPrototypeJoin(relPath, '/'); } else { result.pathname = relative.pathname; } @@ -815,15 +820,15 @@ Url.prototype.resolveObject = function resolveObject(relative) { return result; } - const isSourceAbs = (result.pathname && result.pathname.charAt(0) === '/'); + const isSourceAbs = (result.pathname && result.pathname[0] === '/'); const isRelAbs = ( - relative.host || (relative.pathname && relative.pathname.charAt(0) === '/') + relative.host || (relative.pathname && relative.pathname[0] === '/') ); let mustEndAbs = (isRelAbs || isSourceAbs || (result.host && relative.pathname)); const removeAllDots = mustEndAbs; - let srcPath = (result.pathname && result.pathname.split('/')) || []; - const relPath = (relative.pathname && relative.pathname.split('/')) || []; + let srcPath = (result.pathname && StringPrototypeSplit(result.pathname, '/')) || []; + const relPath = (relative.pathname && StringPrototypeSplit(relative.pathname, '/')) || []; const noLeadingSlashes = result.protocol && !slashedProtocol.has(result.protocol); @@ -837,7 +842,7 @@ Url.prototype.resolveObject = function resolveObject(relative) { result.port = null; if (result.host) { if (srcPath[0] === '') srcPath[0] = result.host; - else srcPath.unshift(result.host); + else ArrayPrototypeUnshift(srcPath, result.host); } result.host = ''; if (relative.protocol) { @@ -846,7 +851,7 @@ Url.prototype.resolveObject = function resolveObject(relative) { result.auth = null; if (relative.host) { if (relPath[0] === '') relPath[0] = relative.host; - else relPath.unshift(relative.host); + else ArrayPrototypeUnshift(relPath, relative.host); } relative.host = null; } @@ -872,8 +877,10 @@ Url.prototype.resolveObject = function resolveObject(relative) { // it's relative // throw away the existing file, and take the new path instead. if (!srcPath) srcPath = []; - srcPath.pop(); - srcPath = srcPath.concat(relPath); + ArrayPrototypePop(srcPath); + for (let i = 0; i < relPath.length; i++) { + ArrayPrototypePush(srcPath, relPath[i]); + } result.search = relative.search; result.query = relative.query; } else if (relative.search !== null && relative.search !== undefined) { @@ -881,15 +888,15 @@ Url.prototype.resolveObject = function resolveObject(relative) { // like href='?foo'. // Put this after the other two cases because it simplifies the booleans if (noLeadingSlashes) { - result.hostname = result.host = srcPath.shift(); + result.hostname = result.host = ArrayPrototypeShift(srcPath); // Occasionally the auth can get stuck only in host. // This especially happens in cases like // url.resolveObject('mailto:local1@domain1', 'local2@domain2') const authInHost = - result.host && result.host.indexOf('@') > 0 && result.host.split('@'); + result.host && StringPrototypeIndexOf(result.host, '@') > 0 && StringPrototypeSplit(result.host, '@'); if (authInHost) { - result.auth = authInHost.shift(); - result.host = result.hostname = authInHost.shift(); + result.auth = ArrayPrototypeShift(authInHost); + result.host = result.hostname = ArrayPrototypeShift(authInHost); } } result.search = relative.search; @@ -919,7 +926,7 @@ Url.prototype.resolveObject = function resolveObject(relative) { // If a url ENDs in . or .., then it must get a trailing slash. // however, if it ends in anything else non-slashy, // then it must NOT get a trailing slash. - let last = srcPath.slice(-1)[0]; + let last = srcPath[srcPath.length - 1]; const hasTrailingSlash = ( ((result.host || relative.host || srcPath.length > 1) && (last === '.' || last === '..')) || last === ''); @@ -943,48 +950,48 @@ Url.prototype.resolveObject = function resolveObject(relative) { // If the path is allowed to go above the root, restore leading ..s if (!mustEndAbs && !removeAllDots) { while (up--) { - srcPath.unshift('..'); + ArrayPrototypeUnshift(srcPath, '..'); } } if (mustEndAbs && srcPath[0] !== '' && - (!srcPath[0] || srcPath[0].charAt(0) !== '/')) { - srcPath.unshift(''); + (!srcPath[0] || srcPath[0][0] !== '/')) { + ArrayPrototypeUnshift(srcPath, ''); } - if (hasTrailingSlash && (srcPath.join('/').slice(-1) !== '/')) { - srcPath.push(''); + if (hasTrailingSlash && (StringPrototypeSlice(ArrayPrototypeJoin(srcPath, '/'), -1) !== '/')) { + ArrayPrototypePush(srcPath, ''); } const isAbsolute = srcPath[0] === '' || - (srcPath[0] && srcPath[0].charAt(0) === '/'); + (srcPath[0] && srcPath[0][0] === '/'); // put the host back if (noLeadingSlashes) { result.hostname = - result.host = isAbsolute ? '' : srcPath.length ? srcPath.shift() : ''; + result.host = isAbsolute ? '' : srcPath.length ? ArrayPrototypeShift(srcPath) : ''; // Occasionally the auth can get stuck only in host. // This especially happens in cases like // url.resolveObject('mailto:local1@domain1', 'local2@domain2') - const authInHost = result.host && result.host.indexOf('@') > 0 ? - result.host.split('@') : false; + const authInHost = result.host && StringPrototypeIndexOf(result.host, '@') > 0 ? + StringPrototypeSplit(result.host, '@') : false; if (authInHost) { - result.auth = authInHost.shift(); - result.host = result.hostname = authInHost.shift(); + result.auth = ArrayPrototypeShift(authInHost); + result.host = result.hostname = ArrayPrototypeShift(authInHost); } } mustEndAbs = mustEndAbs || (result.host && srcPath.length); if (mustEndAbs && !isAbsolute) { - srcPath.unshift(''); + ArrayPrototypeUnshift(srcPath, ''); } if (!srcPath.length) { result.pathname = null; result.path = null; } else { - result.pathname = srcPath.join('/'); + result.pathname = ArrayPrototypeJoin(srcPath, '/'); } // To support request.http @@ -1000,17 +1007,32 @@ Url.prototype.resolveObject = function resolveObject(relative) { Url.prototype.parseHost = function parseHost() { let host = this.host; - let port = portPattern.exec(host); + let port = RegExpPrototypeExec(portPattern, host); if (port) { port = port[0]; if (port !== ':') { - this.port = port.slice(1); + this.port = StringPrototypeSlice(port, 1); } - host = host.slice(0, host.length - port.length); + host = StringPrototypeSlice(host, 0, host.length - port.length); } if (host) this.hostname = host; }; +function isFileProtocol(protocol) { + switch (protocol.length) { + case 5: + if (protocol[4] !== ':') return false; + /** fallthrough */ + case 4: + return protocol[0] === 'f' && + protocol[1] === 'i' && + protocol[2] === 'l' && + protocol[3] === 'e'; + default: + return false; + } +} + // When used internally, we are not obligated to associate TypeError with // this function, so non-strings can be rejected by underlying implementation. // Public API has to validate input and throw appropriate error. From 1f986da5f1fa543193217663198663b8ccc924d7 Mon Sep 17 00:00:00 2001 From: uzlopak Date: Thu, 22 Aug 2024 01:37:03 +0200 Subject: [PATCH 2/2] adapt ljharbs suggestions + minor --- benchmark/url/url-parse.js | 3 +++ lib/url.js | 19 +++++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/benchmark/url/url-parse.js b/benchmark/url/url-parse.js index 163dd790f8142b..231d682cb63481 100644 --- a/benchmark/url/url-parse.js +++ b/benchmark/url/url-parse.js @@ -3,6 +3,9 @@ const common = require('../common.js'); const url = require('url'); const inputs = { + ipV6: 'http://[::1]:3000', + ipV4: 'http://127.0.0.1:3000', + withPort: 'http://foo.bar:3000', normal: 'http://foo.com/bar', escaped: 'https://foo.bar/{}^`/abcd', }; diff --git a/lib/url.js b/lib/url.js index 45047cbc6a137c..242540dc530464 100644 --- a/lib/url.js +++ b/lib/url.js @@ -18,12 +18,11 @@ // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. + 'use strict'; const { ArrayPrototypeJoin, - ArrayPrototypePop, - ArrayPrototypePush, ArrayPrototypeShift, ArrayPrototypeUnshift, Boolean, @@ -290,8 +289,7 @@ Url.prototype.parse = function parse(url, parseQueryString, slashesDenoteHost) { // how the browser resolves relative URLs. let slashes; if (slashesDenoteHost || proto || RegExpPrototypeExec(hostPattern, rest) !== null) { - slashes = rest[0] === '/' && - rest[1] === '/'; + slashes = rest[0] === '/' && rest[1] === '/'; if (slashes && !(proto && hostlessProtocol.has(lowerProto))) { rest = StringPrototypeSlice(rest, 2); this.slashes = true; @@ -876,11 +874,16 @@ Url.prototype.resolveObject = function resolveObject(relative) { } else if (relPath.length) { // it's relative // throw away the existing file, and take the new path instead. - if (!srcPath) srcPath = []; - ArrayPrototypePop(srcPath); + if (!srcPath) { + srcPath = []; + } else if (srcPath.length !== 0) { + srcPath.length = srcPath.length - 1; + } + for (let i = 0; i < relPath.length; i++) { - ArrayPrototypePush(srcPath, relPath[i]); + srcPath[srcPath.length] = relPath[i]; } + result.search = relative.search; result.query = relative.query; } else if (relative.search !== null && relative.search !== undefined) { @@ -960,7 +963,7 @@ Url.prototype.resolveObject = function resolveObject(relative) { } if (hasTrailingSlash && (StringPrototypeSlice(ArrayPrototypeJoin(srcPath, '/'), -1) !== '/')) { - ArrayPrototypePush(srcPath, ''); + srcPath[srcPath.length] = ''; } const isAbsolute = srcPath[0] === '' ||