diff --git a/.eslintrc.js b/.eslintrc.js index 2ad6cc199cedf7..260f53c4672b61 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -16,7 +16,9 @@ const ModuleFindPath = Module._findPath; const hacks = [ 'eslint-plugin-node-core', 'eslint-plugin-markdown', - 'babel-eslint', + '@babel/eslint-parser', + '@babel/plugin-syntax-class-properties', + '@babel/plugin-syntax-top-level-await', ]; Module._findPath = (request, paths, isMain) => { const r = ModuleFindPath(request, paths, isMain); @@ -37,8 +39,17 @@ Module._findPath = (request, paths, isMain) => { module.exports = { root: true, plugins: ['markdown', 'node-core'], - parser: 'babel-eslint', - parserOptions: { sourceType: 'script' }, + parser: '@babel/eslint-parser', + parserOptions: { + babelOptions: { + plugins: [ + Module._findPath('@babel/plugin-syntax-class-properties'), + Module._findPath('@babel/plugin-syntax-top-level-await'), + ], + }, + requireConfigFile: false, + sourceType: 'script', + }, overrides: [ { files: [ @@ -46,6 +57,7 @@ module.exports = { 'doc/api/module.md', 'doc/api/modules.md', 'doc/api/packages.md', + 'doc/api/wasi.md', 'test/es-module/test-esm-type-flag.js', 'test/es-module/test-esm-type-flag-alias.js', '*.mjs', @@ -275,13 +287,15 @@ module.exports = { 'template-curly-spacing': 'error', 'unicode-bom': 'error', 'use-isnan': 'error', - 'valid-typeof': 'error', + 'valid-typeof': ['error', { requireStringLiterals: true }], // Custom rules from eslint-plugin-node-core 'node-core/no-unescaped-regexp-dot': 'error', 'node-core/no-duplicate-requires': 'error', }, globals: { + AbortController: 'readable', + AbortSignal: 'readable', Atomics: 'readable', BigInt: 'readable', BigInt64Array: 'readable', diff --git a/.github/ISSUE_TEMPLATE/1-bug-report.md b/.github/ISSUE_TEMPLATE/1-bug-report.md index 2a2e94d411fe2a..84b6daf665e522 100644 --- a/.github/ISSUE_TEMPLATE/1-bug-report.md +++ b/.github/ISSUE_TEMPLATE/1-bug-report.md @@ -15,7 +15,7 @@ repo. https://github.com/nodejs/help Please fill in as much of the template below as you're able. Version: output of `node -v` -Platform: output of `uname -a` (UNIX), or version and 32 or 64-bit (Windows) +Platform: output of `uname -a` (UNIX), or output of `"$([Environment]::OSVersion | ForEach-Object VersionString) $(if ([Environment]::Is64BitOperatingSystem) { "x64" } else { "x86" })"` in PowerShell console (Windows) Subsystem: if known, please specify affected core module name --> diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 66efca5cd000e7..56632fda4c67eb 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,21 +1,15 @@ - -##### Checklist - +For code changes: +1. Include tests for any bug fixes or new features. +2. Update documentation if relevant. +3. Ensure that `make -j4 test` (UNIX), or `vcbuild test` (Windows) passes. -- [ ] `make -j4 test` (UNIX), or `vcbuild test` (Windows) passes -- [ ] tests and/or benchmarks are included -- [ ] documentation is changed or added -- [ ] commit message follows [commit guidelines](https://github.com/nodejs/node/blob/master/doc/guides/contributing/pull-requests.md#commit-message-guidelines) - - 2 meter to 6.56168 feet + // 2. convert the residual of 6.56168 feet (0.56168) to inches, which will be (6.74016 + // inches) + // 3. then, the final result will be (6 feet and 6.74016 inches) + for (int i = 0, n = units_.length(); i < n; i++) { + if (i == 0) { // first element + unitConverters_.emplaceBackAndCheckErrorCode(status, inputUnit, *units_[i], ratesInfo, + status); + } else { + unitConverters_.emplaceBackAndCheckErrorCode(status, *units_[i - 1], *units_[i], ratesInfo, + status); + } + + if (U_FAILURE(status)) { + return; + } + } +} + +UBool ComplexUnitsConverter::greaterThanOrEqual(double quantity, double limit) const { + U_ASSERT(unitConverters_.length() > 0); + + // First converter converts to the biggest quantity. + double newQuantity = unitConverters_[0]->convert(quantity); + return newQuantity >= limit; +} + +MaybeStackVector ComplexUnitsConverter::convert(double quantity, + icu::number::impl::RoundingImpl *rounder, + UErrorCode &status) const { + // TODO(hugovdm): return an error for "foot-and-foot"? + MaybeStackVector result; + int sign = 1; + if (quantity < 0) { + quantity *= -1; + sign = -1; + } + + // For N converters: + // - the first converter converts from the input unit to the largest unit, + // - N-1 converters convert to bigger units for which we want integers, + // - the Nth converter (index N-1) converts to the smallest unit, for which + // we keep a double. + MaybeStackArray intValues(unitConverters_.length() - 1, status); + if (U_FAILURE(status)) { + return result; + } + uprv_memset(intValues.getAlias(), 0, (unitConverters_.length() - 1) * sizeof(int64_t)); + + for (int i = 0, n = unitConverters_.length(); i < n; ++i) { + quantity = (*unitConverters_[i]).convert(quantity); + if (i < n - 1) { + // The double type has 15 decimal digits of precision. For choosing + // whether to use the current unit or the next smaller unit, we + // therefore nudge up the number with which the thresholding + // decision is made. However after the thresholding, we use the + // original values to ensure unbiased accuracy (to the extent of + // double's capabilities). + int64_t roundedQuantity = floor(quantity * (1 + DBL_EPSILON)); + intValues[i] = roundedQuantity; + + // Keep the residual of the quantity. + // For example: `3.6 feet`, keep only `0.6 feet` + // + // When the calculation is near enough +/- DBL_EPSILON, we round to + // zero. (We also ensure no negative values here.) + if ((quantity - roundedQuantity) / quantity < DBL_EPSILON) { + quantity = 0; + } else { + quantity -= roundedQuantity; + } + } else { // LAST ELEMENT + if (rounder == nullptr) { + // Nothing to do for the last element. + break; + } + + // Round the last value + // TODO(ICU-21288): get smarter about precision for mixed units. + number::impl::DecimalQuantity quant; + quant.setToDouble(quantity); + rounder->apply(quant, status); + if (U_FAILURE(status)) { + return result; + } + quantity = quant.toDouble(); + if (i == 0) { + // Last element is also the first element, so we're done + break; + } + + // Check if there's a carry, and bubble it back up the resulting intValues. + int64_t carry = floor(unitConverters_[i]->convertInverse(quantity) * (1 + DBL_EPSILON)); + if (carry <= 0) { + break; + } + quantity -= unitConverters_[i]->convert(carry); + intValues[i - 1] += carry; + + // We don't use the first converter: that one is for the input unit + for (int32_t j = i - 1; j > 0; j--) { + carry = floor(unitConverters_[j]->convertInverse(intValues[j]) * (1 + DBL_EPSILON)); + if (carry <= 0) { + break; + } + intValues[j] -= round(unitConverters_[j]->convert(carry)); + intValues[j - 1] += carry; + } + } + } + + // Package values into Measure instances in result: + for (int i = 0, n = unitConverters_.length(); i < n; ++i) { + if (i < n - 1) { + Formattable formattableQuantity(intValues[i] * sign); + // Measure takes ownership of the MeasureUnit* + MeasureUnit *type = new MeasureUnit(units_[i]->copy(status).build(status)); + if (result.emplaceBackAndCheckErrorCode(status, formattableQuantity, type, status) == + nullptr) { + // Ownership wasn't taken + U_ASSERT(U_FAILURE(status)); + delete type; + } + if (U_FAILURE(status)) { + return result; + } + } else { // LAST ELEMENT + // Add the last element, not an integer: + Formattable formattableQuantity(quantity * sign); + // Measure takes ownership of the MeasureUnit* + MeasureUnit *type = new MeasureUnit(units_[i]->copy(status).build(status)); + if (result.emplaceBackAndCheckErrorCode(status, formattableQuantity, type, status) == + nullptr) { + // Ownership wasn't taken + U_ASSERT(U_FAILURE(status)); + delete type; + } + if (U_FAILURE(status)) { + return result; + } + U_ASSERT(result.length() == i + 1); + U_ASSERT(result[i] != nullptr); + } + } + + MaybeStackVector orderedResult; + int32_t unitsCount = outputUnits_.length(); + U_ASSERT(unitsCount == units_.length()); + Measure **arr = result.getAlias(); + // O(N^2) is fine: mixed units' unitsCount is usually 2 or 3. + for (int32_t i = 0; i < unitsCount; i++) { + for (int32_t j = i; j < unitsCount; j++) { + // Find the next expected unit, and swap it into place. + U_ASSERT(result[j] != nullptr); + if (result[j]->getUnit() == *outputUnits_[i]) { + if (j != i) { + Measure *tmp = arr[j]; + arr[j] = arr[i]; + arr[i] = tmp; + } + } + } + } + + return result; +} + +} // namespace units +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/units_complexconverter.h b/deps/icu-small/source/i18n/units_complexconverter.h new file mode 100644 index 00000000000000..83c5b94342f373 --- /dev/null +++ b/deps/icu-small/source/i18n/units_complexconverter.h @@ -0,0 +1,93 @@ +// © 2020 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __UNITS_COMPLEXCONVERTER_H__ +#define __UNITS_COMPLEXCONVERTER_H__ + +#include "cmemory.h" +#include "measunit_impl.h" +#include "number_roundingutils.h" +#include "unicode/errorcode.h" +#include "unicode/measure.h" +#include "units_converter.h" +#include "units_data.h" + +U_NAMESPACE_BEGIN + +// Export explicit template instantiations of MaybeStackArray, MemoryPool and +// MaybeStackVector. This is required when building DLLs for Windows. (See +// datefmt.h, collationiterator.h, erarules.h and others for similar examples.) +// +// Note: These need to be outside of the units namespace, or Clang will generate +// a compile error. +#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN +template class U_I18N_API MaybeStackArray; +template class U_I18N_API MemoryPool; +template class U_I18N_API MaybeStackVector; +template class U_I18N_API MaybeStackArray; +template class U_I18N_API MemoryPool; +template class U_I18N_API MaybeStackVector; +template class U_I18N_API MaybeStackArray; +template class U_I18N_API MemoryPool; +template class U_I18N_API MaybeStackVector; +#endif + +namespace units { + +/** + * Converts from single or compound unit to single, compound or mixed units. + * For example, from `meter` to `foot+inch`. + * + * DESIGN: + * This class uses `UnitConverter` in order to perform the single converter (i.e. converters from a + * single unit to another single unit). Therefore, `ComplexUnitsConverter` class contains multiple + * instances of the `UnitConverter` to perform the conversion. + */ +class U_I18N_API ComplexUnitsConverter : public UMemory { + public: + /** + * Constructor of `ComplexUnitsConverter`. + * NOTE: + * - inputUnit and outputUnits must be under the same category + * - e.g. meter to feet and inches --> all of them are length units. + * + * @param inputUnit represents the source unit. (should be single or compound unit). + * @param outputUnits represents the output unit. could be any type. (single, compound or mixed). + * @param status + */ + ComplexUnitsConverter(const MeasureUnitImpl &inputUnit, const MeasureUnitImpl &outputUnits, + const ConversionRates &ratesInfo, UErrorCode &status); + + // Returns true if the specified `quantity` of the `inputUnit`, expressed in terms of the biggest + // unit in the MeasureUnit `outputUnit`, is greater than or equal to `limit`. + // For example, if the input unit is `meter` and the target unit is `foot+inch`. Therefore, this + // function will convert the `quantity` from `meter` to `foot`, then, it will compare the value in + // `foot` with the `limit`. + UBool greaterThanOrEqual(double quantity, double limit) const; + + // Returns outputMeasures which is an array with the corresponding values. + // - E.g. converting meters to feet and inches. + // 1 meter --> 3 feet, 3.3701 inches + // NOTE: + // the smallest element is the only element that could have fractional values. And all + // other elements are floored to the nearest integer + MaybeStackVector + convert(double quantity, icu::number::impl::RoundingImpl *rounder, UErrorCode &status) const; + + private: + MaybeStackVector unitConverters_; + // Individual units of mixed units, sorted big to small + MaybeStackVector units_; + // Individual units of mixed units, sorted in desired output order + MaybeStackVector outputUnits_; +}; + +} // namespace units +U_NAMESPACE_END + +#endif //__UNITS_COMPLEXCONVERTER_H__ + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/units_converter.cpp b/deps/icu-small/source/i18n/units_converter.cpp new file mode 100644 index 00000000000000..a777d026b98756 --- /dev/null +++ b/deps/icu-small/source/i18n/units_converter.cpp @@ -0,0 +1,546 @@ +// © 2020 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "charstr.h" +#include "cmemory.h" +#include "double-conversion-string-to-double.h" +#include "measunit_impl.h" +#include "uassert.h" +#include "unicode/errorcode.h" +#include "unicode/localpointer.h" +#include "unicode/stringpiece.h" +#include "units_converter.h" +#include +#include +#include +#include + +U_NAMESPACE_BEGIN +namespace units { + +void U_I18N_API Factor::multiplyBy(const Factor &rhs) { + factorNum *= rhs.factorNum; + factorDen *= rhs.factorDen; + for (int i = 0; i < CONSTANTS_COUNT; i++) { + constants[i] += rhs.constants[i]; + } + + // NOTE + // We need the offset when the source and the target are simple units. e.g. the source is + // celsius and the target is Fahrenheit. Therefore, we just keep the value using `std::max`. + offset = std::max(rhs.offset, offset); +} + +void U_I18N_API Factor::divideBy(const Factor &rhs) { + factorNum *= rhs.factorDen; + factorDen *= rhs.factorNum; + for (int i = 0; i < CONSTANTS_COUNT; i++) { + constants[i] -= rhs.constants[i]; + } + + // NOTE + // We need the offset when the source and the target are simple units. e.g. the source is + // celsius and the target is Fahrenheit. Therefore, we just keep the value using `std::max`. + offset = std::max(rhs.offset, offset); +} + +void U_I18N_API Factor::power(int32_t power) { + // multiply all the constant by the power. + for (int i = 0; i < CONSTANTS_COUNT; i++) { + constants[i] *= power; + } + + bool shouldFlip = power < 0; // This means that after applying the absolute power, we should flip + // the Numerator and Denominator. + + factorNum = std::pow(factorNum, std::abs(power)); + factorDen = std::pow(factorDen, std::abs(power)); + + if (shouldFlip) { + // Flip Numerator and Denominator. + std::swap(factorNum, factorDen); + } +} + +void U_I18N_API Factor::flip() { + std::swap(factorNum, factorDen); + + for (int i = 0; i < CONSTANTS_COUNT; i++) { + constants[i] *= -1; + } +} + +void U_I18N_API Factor::applySiPrefix(UMeasureSIPrefix siPrefix) { + if (siPrefix == UMeasureSIPrefix::UMEASURE_SI_PREFIX_ONE) return; // No need to do anything + + double siApplied = std::pow(10.0, std::abs(siPrefix)); + + if (siPrefix < 0) { + factorDen *= siApplied; + return; + } + + factorNum *= siApplied; +} + +void U_I18N_API Factor::substituteConstants() { + for (int i = 0; i < CONSTANTS_COUNT; i++) { + if (this->constants[i] == 0) { + continue; + } + + auto absPower = std::abs(this->constants[i]); + Signum powerSig = this->constants[i] < 0 ? Signum::NEGATIVE : Signum::POSITIVE; + double absConstantValue = std::pow(constantsValues[i], absPower); + + if (powerSig == Signum::NEGATIVE) { + this->factorDen *= absConstantValue; + } else { + this->factorNum *= absConstantValue; + } + + this->constants[i] = 0; + } +} + +namespace { + +/* Helpers */ + +using icu::double_conversion::StringToDoubleConverter; + +// TODO: Make this a shared-utility function. +// Returns `double` from a scientific number(i.e. "1", "2.01" or "3.09E+4") +double strToDouble(StringPiece strNum, UErrorCode &status) { + // We are processing well-formed input, so we don't need any special options to + // StringToDoubleConverter. + StringToDoubleConverter converter(0, 0, 0, "", ""); + int32_t count; + double result = converter.StringToDouble(strNum.data(), strNum.length(), &count); + if (count != strNum.length()) { + status = U_INVALID_FORMAT_ERROR; + } + + return result; +} + +// Returns `double` from a scientific number that could has a division sign (i.e. "1", "2.01", "3.09E+4" +// or "2E+2/3") +double strHasDivideSignToDouble(StringPiece strWithDivide, UErrorCode &status) { + int divisionSignInd = -1; + for (int i = 0, n = strWithDivide.length(); i < n; ++i) { + if (strWithDivide.data()[i] == '/') { + divisionSignInd = i; + break; + } + } + + if (divisionSignInd >= 0) { + return strToDouble(strWithDivide.substr(0, divisionSignInd), status) / + strToDouble(strWithDivide.substr(divisionSignInd + 1), status); + } + + return strToDouble(strWithDivide, status); +} + +/* + Adds single factor to a `Factor` object. Single factor means "23^2", "23.3333", "ft2m^3" ...etc. + However, complex factor are not included, such as "ft2m^3*200/3" +*/ +void addFactorElement(Factor &factor, StringPiece elementStr, Signum signum, UErrorCode &status) { + StringPiece baseStr; + StringPiece powerStr; + int32_t power = + 1; // In case the power is not written, then, the power is equal 1 ==> `ft2m^1` == `ft2m` + + // Search for the power part + int32_t powerInd = -1; + for (int32_t i = 0, n = elementStr.length(); i < n; ++i) { + if (elementStr.data()[i] == '^') { + powerInd = i; + break; + } + } + + if (powerInd > -1) { + // There is power + baseStr = elementStr.substr(0, powerInd); + powerStr = elementStr.substr(powerInd + 1); + + power = static_cast(strToDouble(powerStr, status)); + } else { + baseStr = elementStr; + } + + addSingleFactorConstant(baseStr, power, signum, factor, status); +} + +/* + * Extracts `Factor` from a complete string factor. e.g. "ft2m^3*1007/cup2m3*3" + */ +Factor extractFactorConversions(StringPiece stringFactor, UErrorCode &status) { + Factor result; + Signum signum = Signum::POSITIVE; + auto factorData = stringFactor.data(); + for (int32_t i = 0, start = 0, n = stringFactor.length(); i < n; i++) { + if (factorData[i] == '*' || factorData[i] == '/') { + StringPiece factorElement = stringFactor.substr(start, i - start); + addFactorElement(result, factorElement, signum, status); + + start = i + 1; // Set `start` to point to the start of the new element. + } else if (i == n - 1) { + // Last element + addFactorElement(result, stringFactor.substr(start, i + 1), signum, status); + } + + if (factorData[i] == '/') { + signum = Signum::NEGATIVE; // Change the signum because we reached the Denominator. + } + } + + return result; +} + +// Load factor for a single source +Factor loadSingleFactor(StringPiece source, const ConversionRates &ratesInfo, UErrorCode &status) { + const auto conversionUnit = ratesInfo.extractConversionInfo(source, status); + if (U_FAILURE(status)) return Factor(); + if (conversionUnit == nullptr) { + status = U_INTERNAL_PROGRAM_ERROR; + return Factor(); + } + + Factor result = extractFactorConversions(conversionUnit->factor.toStringPiece(), status); + result.offset = strHasDivideSignToDouble(conversionUnit->offset.toStringPiece(), status); + + return result; +} + +// Load Factor of a compound source unit. +Factor loadCompoundFactor(const MeasureUnitImpl &source, const ConversionRates &ratesInfo, + UErrorCode &status) { + + Factor result; + for (int32_t i = 0, n = source.units.length(); i < n; i++) { + SingleUnitImpl singleUnit = *source.units[i]; + + Factor singleFactor = loadSingleFactor(singleUnit.getSimpleUnitID(), ratesInfo, status); + if (U_FAILURE(status)) return result; + + // Apply SiPrefix before the power, because the power may be will flip the factor. + singleFactor.applySiPrefix(singleUnit.siPrefix); + + // Apply the power of the `dimensionality` + singleFactor.power(singleUnit.dimensionality); + + result.multiplyBy(singleFactor); + } + + return result; +} + +/** + * Checks if the source unit and the target unit are simple. For example celsius or fahrenheit. But not + * square-celsius or square-fahrenheit. + * + * NOTE: + * Empty unit means simple unit. + */ +UBool checkSimpleUnit(const MeasureUnitImpl &unit, UErrorCode &status) { + if (U_FAILURE(status)) return false; + + if (unit.complexity != UMEASURE_UNIT_SINGLE) { + return false; + } + if (unit.units.length() == 0) { + // Empty units means simple unit. + return true; + } + + auto singleUnit = *(unit.units[0]); + + if (singleUnit.dimensionality != 1 || singleUnit.siPrefix != UMEASURE_SI_PREFIX_ONE) { + return false; + } + + return true; +} + +/** + * Extract conversion rate from `source` to `target` + */ +void loadConversionRate(ConversionRate &conversionRate, const MeasureUnitImpl &source, + const MeasureUnitImpl &target, Convertibility unitsState, + const ConversionRates &ratesInfo, UErrorCode &status) { + // Represents the conversion factor from the source to the target. + Factor finalFactor; + + // Represents the conversion factor from the source to the base unit that specified in the conversion + // data which is considered as the root of the source and the target. + Factor sourceToBase = loadCompoundFactor(source, ratesInfo, status); + Factor targetToBase = loadCompoundFactor(target, ratesInfo, status); + + // Merger Factors + finalFactor.multiplyBy(sourceToBase); + if (unitsState == Convertibility::CONVERTIBLE) { + finalFactor.divideBy(targetToBase); + } else if (unitsState == Convertibility::RECIPROCAL) { + finalFactor.multiplyBy(targetToBase); + } else { + status = UErrorCode::U_ARGUMENT_TYPE_MISMATCH; + return; + } + + finalFactor.substituteConstants(); + + conversionRate.factorNum = finalFactor.factorNum; + conversionRate.factorDen = finalFactor.factorDen; + + // In case of simple units (such as: celsius or fahrenheit), offsets are considered. + if (checkSimpleUnit(source, status) && checkSimpleUnit(target, status)) { + conversionRate.sourceOffset = + sourceToBase.offset * sourceToBase.factorDen / sourceToBase.factorNum; + conversionRate.targetOffset = + targetToBase.offset * targetToBase.factorDen / targetToBase.factorNum; + } + + conversionRate.reciprocal = unitsState == Convertibility::RECIPROCAL; +} + +struct UnitIndexAndDimension : UMemory { + int32_t index = 0; + int32_t dimensionality = 0; + + UnitIndexAndDimension(const SingleUnitImpl &singleUnit, int32_t multiplier) { + index = singleUnit.index; + dimensionality = singleUnit.dimensionality * multiplier; + } +}; + +void mergeSingleUnitWithDimension(MaybeStackVector &unitIndicesWithDimension, + const SingleUnitImpl &shouldBeMerged, int32_t multiplier) { + for (int32_t i = 0; i < unitIndicesWithDimension.length(); i++) { + auto &unitWithIndex = *unitIndicesWithDimension[i]; + if (unitWithIndex.index == shouldBeMerged.index) { + unitWithIndex.dimensionality += shouldBeMerged.dimensionality * multiplier; + return; + } + } + + unitIndicesWithDimension.emplaceBack(shouldBeMerged, multiplier); +} + +void mergeUnitsAndDimensions(MaybeStackVector &unitIndicesWithDimension, + const MeasureUnitImpl &shouldBeMerged, int32_t multiplier) { + for (int32_t unit_i = 0; unit_i < shouldBeMerged.units.length(); unit_i++) { + auto singleUnit = *shouldBeMerged.units[unit_i]; + mergeSingleUnitWithDimension(unitIndicesWithDimension, singleUnit, multiplier); + } +} + +UBool checkAllDimensionsAreZeros(const MaybeStackVector &dimensionVector) { + for (int32_t i = 0; i < dimensionVector.length(); i++) { + if (dimensionVector[i]->dimensionality != 0) { + return false; + } + } + + return true; +} + +} // namespace + +// Conceptually, this modifies factor: factor *= baseStr^(signum*power). +// +// baseStr must be a known constant or a value that strToDouble() is able to +// parse. +void U_I18N_API addSingleFactorConstant(StringPiece baseStr, int32_t power, Signum signum, + Factor &factor, UErrorCode &status) { + if (baseStr == "ft_to_m") { + factor.constants[CONSTANT_FT2M] += power * signum; + } else if (baseStr == "ft2_to_m2") { + factor.constants[CONSTANT_FT2M] += 2 * power * signum; + } else if (baseStr == "ft3_to_m3") { + factor.constants[CONSTANT_FT2M] += 3 * power * signum; + } else if (baseStr == "in3_to_m3") { + factor.constants[CONSTANT_FT2M] += 3 * power * signum; + factor.factorDen *= 12 * 12 * 12; + } else if (baseStr == "gal_to_m3") { + factor.factorNum *= 231; + factor.constants[CONSTANT_FT2M] += 3 * power * signum; + factor.factorDen *= 12 * 12 * 12; + } else if (baseStr == "gal_imp_to_m3") { + factor.constants[CONSTANT_GAL_IMP2M3] += power * signum; + } else if (baseStr == "G") { + factor.constants[CONSTANT_G] += power * signum; + } else if (baseStr == "gravity") { + factor.constants[CONSTANT_GRAVITY] += power * signum; + } else if (baseStr == "lb_to_kg") { + factor.constants[CONSTANT_LB2KG] += power * signum; + } else if (baseStr == "PI") { + factor.constants[CONSTANT_PI] += power * signum; + } else { + if (signum == Signum::NEGATIVE) { + factor.factorDen *= std::pow(strToDouble(baseStr, status), power); + } else { + factor.factorNum *= std::pow(strToDouble(baseStr, status), power); + } + } +} + +/** + * Extracts the compound base unit of a compound unit (`source`). For example, if the source unit is + * `square-mile-per-hour`, the compound base unit will be `square-meter-per-second` + */ +MeasureUnitImpl U_I18N_API extractCompoundBaseUnit(const MeasureUnitImpl &source, + const ConversionRates &conversionRates, + UErrorCode &status) { + + MeasureUnitImpl result; + if (U_FAILURE(status)) return result; + + const auto &singleUnits = source.units; + for (int i = 0, count = singleUnits.length(); i < count; ++i) { + const auto &singleUnit = *singleUnits[i]; + // Extract `ConversionRateInfo` using the absolute unit. For example: in case of `square-meter`, + // we will use `meter` + const auto rateInfo = + conversionRates.extractConversionInfo(singleUnit.getSimpleUnitID(), status); + if (U_FAILURE(status)) { + return result; + } + if (rateInfo == nullptr) { + status = U_INTERNAL_PROGRAM_ERROR; + return result; + } + + // Multiply the power of the singleUnit by the power of the baseUnit. For example, square-hectare + // must be pow4-meter. (NOTE: hectare --> square-meter) + auto baseUnits = + MeasureUnitImpl::forIdentifier(rateInfo->baseUnit.toStringPiece(), status).units; + for (int32_t i = 0, baseUnitsCount = baseUnits.length(); i < baseUnitsCount; i++) { + baseUnits[i]->dimensionality *= singleUnit.dimensionality; + // TODO: Deal with SI-prefix + result.append(*baseUnits[i], status); + + if (U_FAILURE(status)) { + return result; + } + } + } + + return result; +} + +/** + * Determine the convertibility between `source` and `target`. + * For example: + * `meter` and `foot` are `CONVERTIBLE`. + * `meter-per-second` and `second-per-meter` are `RECIPROCAL`. + * `meter` and `pound` are `UNCONVERTIBLE`. + * + * NOTE: + * Only works with SINGLE and COMPOUND units. If one of the units is a + * MIXED unit, an error will occur. For more information, see UMeasureUnitComplexity. + */ +Convertibility U_I18N_API extractConvertibility(const MeasureUnitImpl &source, + const MeasureUnitImpl &target, + const ConversionRates &conversionRates, + UErrorCode &status) { + + if (source.complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED || + target.complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) { + status = U_INTERNAL_PROGRAM_ERROR; + return UNCONVERTIBLE; + } + + MeasureUnitImpl sourceBaseUnit = extractCompoundBaseUnit(source, conversionRates, status); + MeasureUnitImpl targetBaseUnit = extractCompoundBaseUnit(target, conversionRates, status); + if (U_FAILURE(status)) return UNCONVERTIBLE; + + MaybeStackVector convertible; + MaybeStackVector reciprocal; + + mergeUnitsAndDimensions(convertible, sourceBaseUnit, 1); + mergeUnitsAndDimensions(reciprocal, sourceBaseUnit, 1); + + mergeUnitsAndDimensions(convertible, targetBaseUnit, -1); + mergeUnitsAndDimensions(reciprocal, targetBaseUnit, 1); + + if (checkAllDimensionsAreZeros(convertible)) { + return CONVERTIBLE; + } + + if (checkAllDimensionsAreZeros(reciprocal)) { + return RECIPROCAL; + } + + return UNCONVERTIBLE; +} + +UnitConverter::UnitConverter(const MeasureUnitImpl &source, const MeasureUnitImpl &target, + const ConversionRates &ratesInfo, UErrorCode &status) + : conversionRate_(source.copy(status), target.copy(status)) { + if (source.complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED || + target.complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) { + status = U_INTERNAL_PROGRAM_ERROR; + return; + } + + Convertibility unitsState = extractConvertibility(source, target, ratesInfo, status); + if (U_FAILURE(status)) return; + if (unitsState == Convertibility::UNCONVERTIBLE) { + status = U_INTERNAL_PROGRAM_ERROR; + return; + } + + loadConversionRate(conversionRate_, conversionRate_.source, conversionRate_.target, unitsState, + ratesInfo, status); +} + +double UnitConverter::convert(double inputValue) const { + double result = + inputValue + conversionRate_.sourceOffset; // Reset the input to the target zero index. + // Convert the quantity to from the source scale to the target scale. + result *= conversionRate_.factorNum / conversionRate_.factorDen; + + result -= conversionRate_.targetOffset; // Set the result to its index. + + if (conversionRate_.reciprocal) { + if (result == 0) { + // TODO: demonstrate the resulting behaviour in tests... and figure + // out desired behaviour. (Theoretical result should be infinity, + // not 0.) + return 0.0; + } + result = 1.0 / result; + } + + return result; +} + +double UnitConverter::convertInverse(double inputValue) const { + double result = inputValue; + if (conversionRate_.reciprocal) { + if (result == 0) { + // TODO: demonstrate the resulting behaviour in tests... and figure + // out desired behaviour. (Theoretical result should be infinity, + // not 0.) + return 0.0; + } + result = 1.0 / result; + } + result += conversionRate_.targetOffset; + result *= conversionRate_.factorDen / conversionRate_.factorNum; + result -= conversionRate_.sourceOffset; + return result; +} + +} // namespace units +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/units_converter.h b/deps/icu-small/source/i18n/units_converter.h new file mode 100644 index 00000000000000..7650131b1f6975 --- /dev/null +++ b/deps/icu-small/source/i18n/units_converter.h @@ -0,0 +1,173 @@ +// © 2020 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __UNITS_CONVERTER_H__ +#define __UNITS_CONVERTER_H__ + +#include "cmemory.h" +#include "measunit_impl.h" +#include "unicode/errorcode.h" +#include "unicode/stringpiece.h" +#include "unicode/uobject.h" +#include "units_converter.h" +#include "units_data.h" + +U_NAMESPACE_BEGIN +namespace units { + +/* Internal Structure */ + +enum Constants { + CONSTANT_FT2M, // ft2m stands for foot to meter. + CONSTANT_PI, // PI + CONSTANT_GRAVITY, // Gravity + CONSTANT_G, + CONSTANT_GAL_IMP2M3, // Gallon imp to m3 + CONSTANT_LB2KG, // Pound to Kilogram + + // Must be the last element. + CONSTANTS_COUNT +}; + +// These values are a hard-coded subset of unitConstants in the units +// resources file. A unit test checks that all constants in the resource +// file are at least recognised by the code. Derived constants' values or +// hard-coded derivations are not checked. +static const double constantsValues[CONSTANTS_COUNT] = { + 0.3048, // CONSTANT_FT2M + 411557987.0 / 131002976.0, // CONSTANT_PI + 9.80665, // CONSTANT_GRAVITY + 6.67408E-11, // CONSTANT_G + 0.00454609, // CONSTANT_GAL_IMP2M3 + 0.45359237, // CONSTANT_LB2KG +}; + +typedef enum Signum { + NEGATIVE = -1, + POSITIVE = 1, +} Signum; + +/* Represents a conversion factor */ +struct U_I18N_API Factor { + double factorNum = 1; + double factorDen = 1; + double offset = 0; + bool reciprocal = false; + int32_t constants[CONSTANTS_COUNT] = {}; + + void multiplyBy(const Factor &rhs); + void divideBy(const Factor &rhs); + + // Apply the power to the factor. + void power(int32_t power); + + // Flip the `Factor`, for example, factor= 2/3, flippedFactor = 3/2 + void flip(); + + // Apply SI prefix to the `Factor` + void applySiPrefix(UMeasureSIPrefix siPrefix); + void substituteConstants(); +}; + +/* + * Adds a single factor element to the `Factor`. e.g "ft3m", "2.333" or "cup2m3". But not "cup2m3^3". + */ +void U_I18N_API addSingleFactorConstant(StringPiece baseStr, int32_t power, Signum sigNum, + Factor &factor, UErrorCode &status); + +/** + * Represents the conversion rate between `source` and `target`. + */ +struct U_I18N_API ConversionRate : public UMemory { + const MeasureUnitImpl source; + const MeasureUnitImpl target; + double factorNum = 1; + double factorDen = 1; + double sourceOffset = 0; + double targetOffset = 0; + bool reciprocal = false; + + ConversionRate(MeasureUnitImpl &&source, MeasureUnitImpl &&target) + : source(std::move(source)), target(std::move(target)) {} +}; + +enum Convertibility { + RECIPROCAL, + CONVERTIBLE, + UNCONVERTIBLE, +}; + +MeasureUnitImpl U_I18N_API extractCompoundBaseUnit(const MeasureUnitImpl &source, + const ConversionRates &conversionRates, + UErrorCode &status); + +/** + * Check if the convertibility between `source` and `target`. + * For example: + * `meter` and `foot` are `CONVERTIBLE`. + * `meter-per-second` and `second-per-meter` are `RECIPROCAL`. + * `meter` and `pound` are `UNCONVERTIBLE`. + * + * NOTE: + * Only works with SINGLE and COMPOUND units. If one of the units is a + * MIXED unit, an error will occur. For more information, see UMeasureUnitComplexity. + */ +Convertibility U_I18N_API extractConvertibility(const MeasureUnitImpl &source, + const MeasureUnitImpl &target, + const ConversionRates &conversionRates, + UErrorCode &status); + +/** + * Converts from a source `MeasureUnit` to a target `MeasureUnit`. + * + * NOTE: + * Only works with SINGLE and COMPOUND units. If one of the units is a + * MIXED unit, an error will occur. For more information, see UMeasureUnitComplexity. + */ +class U_I18N_API UnitConverter : public UMemory { + public: + /** + * Constructor of `UnitConverter`. + * NOTE: + * - source and target must be under the same category + * - e.g. meter to mile --> both of them are length units. + * + * @param source represents the source unit. + * @param target represents the target unit. + * @param ratesInfo Contains all the needed conversion rates. + * @param status + */ + UnitConverter(const MeasureUnitImpl &source, const MeasureUnitImpl &target, + const ConversionRates &ratesInfo, UErrorCode &status); + + /** + * Convert a measurement expressed in the source unit to a measurement + * expressed in the target unit. + * + * @param inputValue the value to be converted. + * @return the converted value. + */ + double convert(double inputValue) const; + + /** + * The inverse of convert(): convert a measurement expressed in the target + * unit to a measurement expressed in the source unit. + * + * @param inputValue the value to be converted. + * @return the converted value. + */ + double convertInverse(double inputValue) const; + + private: + ConversionRate conversionRate_; +}; + +} // namespace units +U_NAMESPACE_END + +#endif //__UNITS_CONVERTER_H__ + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/units_data.cpp b/deps/icu-small/source/i18n/units_data.cpp new file mode 100644 index 00000000000000..42bd6248b0b26d --- /dev/null +++ b/deps/icu-small/source/i18n/units_data.cpp @@ -0,0 +1,428 @@ +// © 2020 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "cstring.h" +#include "number_decimalquantity.h" +#include "resource.h" +#include "uassert.h" +#include "unicode/unistr.h" +#include "unicode/ures.h" +#include "units_data.h" +#include "uresimp.h" +#include "util.h" +#include + +U_NAMESPACE_BEGIN +namespace units { + +namespace { + +using icu::number::impl::DecimalQuantity; + +void trimSpaces(CharString& factor, UErrorCode& status){ + CharString trimmed; + for (int i = 0 ; i < factor.length(); i++) { + if (factor[i] == ' ') continue; + + trimmed.append(factor[i], status); + } + + factor = std::move(trimmed); +} + +/** + * A ResourceSink that collects conversion rate information. + * + * This class is for use by ures_getAllItemsWithFallback. + */ +class ConversionRateDataSink : public ResourceSink { + public: + /** + * Constructor. + * @param out The vector to which ConversionRateInfo instances are to be + * added. This vector must outlive the use of the ResourceSink. + */ + explicit ConversionRateDataSink(MaybeStackVector *out) : outVector(out) {} + + /** + * Method for use by `ures_getAllItemsWithFallback`. Adds the unit + * conversion rates that are found in `value` to the output vector. + * + * @param source This string must be "convertUnits": the resource that this + * class supports reading. + * @param value The "convertUnits" resource, containing unit conversion rate + * information. + * @param noFallback Ignored. + * @param status The standard ICU error code output parameter. + */ + void put(const char *source, ResourceValue &value, UBool /*noFallback*/, UErrorCode &status) { + if (U_FAILURE(status)) { return; } + if (uprv_strcmp(source, "convertUnits") != 0) { + // This is very strict, however it is the cheapest way to be sure + // that with `value`, we're looking at the convertUnits table. + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + ResourceTable conversionRateTable = value.getTable(status); + const char *srcUnit; + // We're reusing `value`, which seems to be a common pattern: + for (int32_t unit = 0; conversionRateTable.getKeyAndValue(unit, srcUnit, value); unit++) { + ResourceTable unitTable = value.getTable(status); + const char *key; + UnicodeString baseUnit = ICU_Utility::makeBogusString(); + UnicodeString factor = ICU_Utility::makeBogusString(); + UnicodeString offset = ICU_Utility::makeBogusString(); + for (int32_t i = 0; unitTable.getKeyAndValue(i, key, value); i++) { + if (uprv_strcmp(key, "target") == 0) { + baseUnit = value.getUnicodeString(status); + } else if (uprv_strcmp(key, "factor") == 0) { + factor = value.getUnicodeString(status); + } else if (uprv_strcmp(key, "offset") == 0) { + offset = value.getUnicodeString(status); + } + } + if (U_FAILURE(status)) { return; } + if (baseUnit.isBogus() || factor.isBogus()) { + // We could not find a usable conversion rate: bad resource. + status = U_MISSING_RESOURCE_ERROR; + return; + } + + // We don't have this ConversionRateInfo yet: add it. + ConversionRateInfo *cr = outVector->emplaceBack(); + if (!cr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } else { + cr->sourceUnit.append(srcUnit, status); + cr->baseUnit.appendInvariantChars(baseUnit, status); + cr->factor.appendInvariantChars(factor, status); + trimSpaces(cr->factor, status); + if (!offset.isBogus()) cr->offset.appendInvariantChars(offset, status); + } + } + return; + } + + private: + MaybeStackVector *outVector; +}; + +bool operator<(const UnitPreferenceMetadata &a, const UnitPreferenceMetadata &b) { + return a.compareTo(b) < 0; +} + +/** + * A ResourceSink that collects unit preferences information. + * + * This class is for use by ures_getAllItemsWithFallback. + */ +class UnitPreferencesSink : public ResourceSink { + public: + /** + * Constructor. + * @param outPrefs The vector to which UnitPreference instances are to be + * added. This vector must outlive the use of the ResourceSink. + * @param outMetadata The vector to which UnitPreferenceMetadata instances + * are to be added. This vector must outlive the use of the ResourceSink. + */ + explicit UnitPreferencesSink(MaybeStackVector *outPrefs, + MaybeStackVector *outMetadata) + : preferences(outPrefs), metadata(outMetadata) {} + + /** + * Method for use by `ures_getAllItemsWithFallback`. Adds the unit + * preferences info that are found in `value` to the output vector. + * + * @param source This string must be "unitPreferenceData": the resource that + * this class supports reading. + * @param value The "unitPreferenceData" resource, containing unit + * preferences data. + * @param noFallback Ignored. + * @param status The standard ICU error code output parameter. Note: if an + * error is returned, outPrefs and outMetadata may be inconsistent. + */ + void put(const char *key, ResourceValue &value, UBool /*noFallback*/, UErrorCode &status) { + if (U_FAILURE(status)) { return; } + if (uprv_strcmp(key, "unitPreferenceData") != 0) { + // This is very strict, however it is the cheapest way to be sure + // that with `value`, we're looking at the convertUnits table. + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + // The unitPreferenceData structure (see data/misc/units.txt) contains a + // hierarchy of category/usage/region, within which are a set of + // preferences. Hence three for-loops and another loop for the + // preferences themselves: + ResourceTable unitPreferenceDataTable = value.getTable(status); + const char *category; + for (int32_t i = 0; unitPreferenceDataTable.getKeyAndValue(i, category, value); i++) { + ResourceTable categoryTable = value.getTable(status); + const char *usage; + for (int32_t j = 0; categoryTable.getKeyAndValue(j, usage, value); j++) { + ResourceTable regionTable = value.getTable(status); + const char *region; + for (int32_t k = 0; regionTable.getKeyAndValue(k, region, value); k++) { + // `value` now contains the set of preferences for + // category/usage/region. + ResourceArray unitPrefs = value.getArray(status); + if (U_FAILURE(status)) { return; } + int32_t prefLen = unitPrefs.getSize(); + + // Update metadata for this set of preferences. + UnitPreferenceMetadata *meta = metadata->emplaceBack( + category, usage, region, preferences->length(), prefLen, status); + if (!meta) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + if (U_FAILURE(status)) { return; } + if (metadata->length() > 1) { + // Verify that unit preferences are sorted and + // without duplicates. + if (!(*(*metadata)[metadata->length() - 2] < + *(*metadata)[metadata->length() - 1])) { + status = U_INVALID_FORMAT_ERROR; + return; + } + } + + // Collect the individual preferences. + for (int32_t i = 0; unitPrefs.getValue(i, value); i++) { + UnitPreference *up = preferences->emplaceBack(); + if (!up) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + ResourceTable unitPref = value.getTable(status); + if (U_FAILURE(status)) { return; } + for (int32_t i = 0; unitPref.getKeyAndValue(i, key, value); ++i) { + if (uprv_strcmp(key, "unit") == 0) { + int32_t length; + const UChar *u = value.getString(length, status); + up->unit.appendInvariantChars(u, length, status); + } else if (uprv_strcmp(key, "geq") == 0) { + int32_t length; + const UChar *g = value.getString(length, status); + CharString geq; + geq.appendInvariantChars(g, length, status); + DecimalQuantity dq; + dq.setToDecNumber(geq.data(), status); + up->geq = dq.toDouble(); + } else if (uprv_strcmp(key, "skeleton") == 0) { + up->skeleton = value.getUnicodeString(status); + } + } + } + } + } + } + } + + private: + MaybeStackVector *preferences; + MaybeStackVector *metadata; +}; + +int32_t binarySearch(const MaybeStackVector *metadata, + const UnitPreferenceMetadata &desired, bool *foundCategory, bool *foundUsage, + bool *foundRegion, UErrorCode &status) { + if (U_FAILURE(status)) { return -1; } + int32_t start = 0; + int32_t end = metadata->length(); + *foundCategory = false; + *foundUsage = false; + *foundRegion = false; + while (start < end) { + int32_t mid = (start + end) / 2; + int32_t cmp = (*metadata)[mid]->compareTo(desired, foundCategory, foundUsage, foundRegion); + if (cmp < 0) { + start = mid + 1; + } else if (cmp > 0) { + end = mid; + } else { + return mid; + } + } + return -1; +} + +/** + * Finds the UnitPreferenceMetadata instance that matches the given category, + * usage and region: if missing, region falls back to "001", and usage + * repeatedly drops tailing components, eventually trying "default" + * ("land-agriculture-grain" -> "land-agriculture" -> "land" -> "default"). + * + * @param metadata The full list of UnitPreferenceMetadata instances. + * @param category The category to search for. See getUnitCategory(). + * @param usage The usage for which formatting preferences is needed. If the + * given usage is not known, automatic fallback occurs, see function description + * above. + * @param region The region for which preferences are needed. If there are no + * region-specific preferences, this function automatically falls back to the + * "001" region (global). + * @param status The standard ICU error code output parameter. + * * If an invalid category is given, status will be U_ILLEGAL_ARGUMENT_ERROR. + * * If fallback to "default" or "001" didn't resolve, status will be + * U_MISSING_RESOURCE. + * @return The index into the metadata vector which represents the appropriate + * preferences. If appropriate preferences are not found, -1 is returned. + */ +int32_t getPreferenceMetadataIndex(const MaybeStackVector *metadata, + StringPiece category, StringPiece usage, StringPiece region, + UErrorCode &status) { + if (U_FAILURE(status)) { return -1; } + bool foundCategory, foundUsage, foundRegion; + UnitPreferenceMetadata desired(category, usage, region, -1, -1, status); + int32_t idx = binarySearch(metadata, desired, &foundCategory, &foundUsage, &foundRegion, status); + if (U_FAILURE(status)) { return -1; } + if (idx >= 0) { return idx; } + if (!foundCategory) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return -1; + } + U_ASSERT(foundCategory); + while (!foundUsage) { + int32_t lastDashIdx = desired.usage.lastIndexOf('-'); + if (lastDashIdx > 0) { + desired.usage.truncate(lastDashIdx); + } else if (uprv_strcmp(desired.usage.data(), "default") != 0) { + desired.usage.truncate(0).append("default", status); + } else { + // "default" is not supposed to be missing for any valid category. + status = U_MISSING_RESOURCE_ERROR; + return -1; + } + idx = binarySearch(metadata, desired, &foundCategory, &foundUsage, &foundRegion, status); + if (U_FAILURE(status)) { return -1; } + } + U_ASSERT(foundCategory); + U_ASSERT(foundUsage); + if (!foundRegion) { + if (uprv_strcmp(desired.region.data(), "001") != 0) { + desired.region.truncate(0).append("001", status); + idx = binarySearch(metadata, desired, &foundCategory, &foundUsage, &foundRegion, status); + } + if (!foundRegion) { + // "001" is not supposed to be missing for any valid usage. + status = U_MISSING_RESOURCE_ERROR; + return -1; + } + } + U_ASSERT(foundCategory); + U_ASSERT(foundUsage); + U_ASSERT(foundRegion); + U_ASSERT(idx >= 0); + return idx; +} + +} // namespace + +UnitPreferenceMetadata::UnitPreferenceMetadata(StringPiece category, StringPiece usage, + StringPiece region, int32_t prefsOffset, + int32_t prefsCount, UErrorCode &status) { + this->category.append(category, status); + this->usage.append(usage, status); + this->region.append(region, status); + this->prefsOffset = prefsOffset; + this->prefsCount = prefsCount; +} + +int32_t UnitPreferenceMetadata::compareTo(const UnitPreferenceMetadata &other) const { + int32_t cmp = uprv_strcmp(category.data(), other.category.data()); + if (cmp == 0) { + cmp = uprv_strcmp(usage.data(), other.usage.data()); + } + if (cmp == 0) { + cmp = uprv_strcmp(region.data(), other.region.data()); + } + return cmp; +} + +int32_t UnitPreferenceMetadata::compareTo(const UnitPreferenceMetadata &other, bool *foundCategory, + bool *foundUsage, bool *foundRegion) const { + int32_t cmp = uprv_strcmp(category.data(), other.category.data()); + if (cmp == 0) { + *foundCategory = true; + cmp = uprv_strcmp(usage.data(), other.usage.data()); + } + if (cmp == 0) { + *foundUsage = true; + cmp = uprv_strcmp(region.data(), other.region.data()); + } + if (cmp == 0) { + *foundRegion = true; + } + return cmp; +} + +CharString U_I18N_API getUnitCategory(const char *baseUnitIdentifier, UErrorCode &status) { + CharString result; + LocalUResourceBundlePointer unitsBundle(ures_openDirect(NULL, "units", &status)); + LocalUResourceBundlePointer unitQuantities( + ures_getByKey(unitsBundle.getAlias(), "unitQuantities", NULL, &status)); + int32_t categoryLength; + if (U_FAILURE(status)) { return result; } + const UChar *uCategory = + ures_getStringByKey(unitQuantities.getAlias(), baseUnitIdentifier, &categoryLength, &status); + if (U_FAILURE(status)) { + // TODO(CLDR-13787,hugovdm): special-casing the consumption-inverse + // case. Once CLDR-13787 is clarified, this should be generalised (or + // possibly removed): + if (uprv_strcmp(baseUnitIdentifier, "meter-per-cubic-meter") == 0) { + status = U_ZERO_ERROR; + result.append("consumption-inverse", status); + return result; + } + } + result.appendInvariantChars(uCategory, categoryLength, status); + return result; +} + +// TODO: this may be unnecessary. Fold into ConversionRates class? Or move to anonymous namespace? +void U_I18N_API getAllConversionRates(MaybeStackVector &result, UErrorCode &status) { + LocalUResourceBundlePointer unitsBundle(ures_openDirect(NULL, "units", &status)); + ConversionRateDataSink sink(&result); + ures_getAllItemsWithFallback(unitsBundle.getAlias(), "convertUnits", sink, status); +} + +const ConversionRateInfo *ConversionRates::extractConversionInfo(StringPiece source, + UErrorCode &status) const { + for (size_t i = 0, n = conversionInfo_.length(); i < n; ++i) { + if (conversionInfo_[i]->sourceUnit.toStringPiece() == source) return conversionInfo_[i]; + } + + status = U_INTERNAL_PROGRAM_ERROR; + return nullptr; +} + +U_I18N_API UnitPreferences::UnitPreferences(UErrorCode &status) { + LocalUResourceBundlePointer unitsBundle(ures_openDirect(NULL, "units", &status)); + UnitPreferencesSink sink(&unitPrefs_, &metadata_); + ures_getAllItemsWithFallback(unitsBundle.getAlias(), "unitPreferenceData", sink, status); +} + +// TODO: make outPreferences const? +// +// TODO: consider replacing `UnitPreference **&outPreferences` with slice class +// of some kind. +void U_I18N_API UnitPreferences::getPreferencesFor(StringPiece category, StringPiece usage, + StringPiece region, + const UnitPreference *const *&outPreferences, + int32_t &preferenceCount, UErrorCode &status) const { + int32_t idx = getPreferenceMetadataIndex(&metadata_, category, usage, region, status); + if (U_FAILURE(status)) { return; } + U_ASSERT(idx >= 0); // Failures should have been taken care of by `status`. + const UnitPreferenceMetadata *m = metadata_[idx]; + outPreferences = unitPrefs_.getAlias() + m->prefsOffset; + preferenceCount = m->prefsCount; +} + +} // namespace units +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/units_data.h b/deps/icu-small/source/i18n/units_data.h new file mode 100644 index 00000000000000..b6fe8e88de3c2e --- /dev/null +++ b/deps/icu-small/source/i18n/units_data.h @@ -0,0 +1,229 @@ +// © 2020 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __UNITS_DATA_H__ +#define __UNITS_DATA_H__ + +#include + +#include "charstr.h" +#include "cmemory.h" +#include "unicode/stringpiece.h" +#include "unicode/uobject.h" + +U_NAMESPACE_BEGIN +namespace units { + +/** + * Looks up the unit category of a base unit identifier. + * + * Only supports base units, other units must be resolved to base units before + * passing to this function. + * + * Categories are found in `unitQuantities` in the `units` resource (see + * `units.txt`). + * + * TODO(hugovdm): if we give units_data.cpp access to the functionality of + * `extractCompoundBaseUnit` which is currently in units_converter.cpp, we could + * support all units for which there is a category. Does it make sense to move + * that function to units_data.cpp? + */ +CharString U_I18N_API getUnitCategory(const char *baseUnitIdentifier, UErrorCode &status); + +/** + * Encapsulates "convertUnits" information from units resources, specifying how + * to convert from one unit to another. + * + * Information in this class is still in the form of strings: symbolic constants + * need to be interpreted. Rationale: symbols can cancel out for higher + * precision conversion - going from feet to inches should cancel out the + * `ft_to_m` constant. + */ +class U_I18N_API ConversionRateInfo : public UMemory { + public: + ConversionRateInfo() {} + ConversionRateInfo(StringPiece sourceUnit, StringPiece baseUnit, StringPiece factor, + StringPiece offset, UErrorCode &status) + : sourceUnit(), baseUnit(), factor(), offset() { + this->sourceUnit.append(sourceUnit, status); + this->baseUnit.append(baseUnit, status); + this->factor.append(factor, status); + this->offset.append(offset, status); + } + CharString sourceUnit; + CharString baseUnit; + CharString factor; + CharString offset; +}; + +} // namespace units + +// Export explicit template instantiations of MaybeStackArray, MemoryPool and +// MaybeStackVector. This is required when building DLLs for Windows. (See +// datefmt.h, collationiterator.h, erarules.h and others for similar examples.) +// +// Note: These need to be outside of the units namespace, or Clang will generate +// a compile error. +#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN +template class U_I18N_API MaybeStackArray; +template class U_I18N_API MemoryPool; +template class U_I18N_API MaybeStackVector; +#endif + +namespace units { + +/** + * Returns ConversionRateInfo for all supported conversions. + * + * @param result Receives the set of conversion rates. + * @param status Receives status. + */ +void U_I18N_API getAllConversionRates(MaybeStackVector &result, UErrorCode &status); + +/** + * Contains all the supported conversion rates. + */ +class U_I18N_API ConversionRates { + public: + /** + * Constructor + * + * @param status Receives status. + */ + ConversionRates(UErrorCode &status) { getAllConversionRates(conversionInfo_, status); } + + /** + * Returns a pointer to the conversion rate info that match the `source`. + * + * @param source Contains the source. + * @param status Receives status. + */ + const ConversionRateInfo *extractConversionInfo(StringPiece source, UErrorCode &status) const; + + private: + MaybeStackVector conversionInfo_; +}; + +// Encapsulates unitPreferenceData information from units resources, specifying +// a sequence of output unit preferences. +struct U_I18N_API UnitPreference : public UMemory { + // Set geq to 1.0 by default + UnitPreference() : geq(1.0) {} + CharString unit; + double geq; + UnicodeString skeleton; +}; + +/** + * Metadata about the preferences in UnitPreferences::unitPrefs_. + * + * This class owns all of its data. + * + * UnitPreferenceMetadata lives in the anonymous namespace, because it should + * only be useful to internal code and unit testing code. + */ +class U_I18N_API UnitPreferenceMetadata : public UMemory { + public: + UnitPreferenceMetadata() {} + // Constructor, makes copies of the parameters passed to it. + UnitPreferenceMetadata(StringPiece category, StringPiece usage, StringPiece region, + int32_t prefsOffset, int32_t prefsCount, UErrorCode &status); + + // Unit category (e.g. "length", "mass", "electric-capacitance"). + CharString category; + // Usage (e.g. "road", "vehicle-fuel", "blood-glucose"). Every category + // should have an entry for "default" usage. TODO(hugovdm): add a test for + // this. + CharString usage; + // Region code (e.g. "US", "CZ", "001"). Every usage should have an entry + // for the "001" region ("world"). TODO(hugovdm): add a test for this. + CharString region; + // Offset into the UnitPreferences::unitPrefs_ list where the relevant + // preferences are found. + int32_t prefsOffset; + // The number of preferences that form this set. + int32_t prefsCount; + + int32_t compareTo(const UnitPreferenceMetadata &other) const; + int32_t compareTo(const UnitPreferenceMetadata &other, bool *foundCategory, bool *foundUsage, + bool *foundRegion) const; +}; + +} // namespace units + +// Export explicit template instantiations of MaybeStackArray, MemoryPool and +// MaybeStackVector. This is required when building DLLs for Windows. (See +// datefmt.h, collationiterator.h, erarules.h and others for similar examples.) +// +// Note: These need to be outside of the units namespace, or Clang will generate +// a compile error. +#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN +template class U_I18N_API MaybeStackArray; +template class U_I18N_API MemoryPool; +template class U_I18N_API MaybeStackVector; +template class U_I18N_API MaybeStackArray; +template class U_I18N_API MemoryPool; +template class U_I18N_API MaybeStackVector; +#endif + +namespace units { + +/** + * Unit Preferences information for various locales and usages. + */ +class U_I18N_API UnitPreferences { + public: + /** + * Constructor, loads all the preference data. + * + * @param status Receives status. + */ + UnitPreferences(UErrorCode &status); + + /** + * Returns the set of unit preferences in the particular category that best + * matches the specified usage and region. + * + * If region can't be found, falls back to global (001). If usage can't be + * found, falls back to "default". + * + * @param category The category within which to look up usage and region. + * (TODO(hugovdm): improve docs on how to find the category, once the lookup + * function is added.) + * @param usage The usage parameter. (TODO(hugovdm): improve this + * documentation. Add reference to some list of usages we support.) If the + * given usage is not found, the method automatically falls back to + * "default". + * @param region The region whose preferences are desired. If there are no + * specific preferences for the requested region, the method automatically + * falls back to region "001" ("world"). + * @param outPreferences A pointer into an array of preferences: essentially + * an array slice in combination with preferenceCount. + * @param preferenceCount The number of unit preferences that belong to the + * result set. + * @param status Receives status. + * + * TODO(hugovdm): maybe replace `UnitPreference **&outPreferences` with a slice class? + */ + void getPreferencesFor(StringPiece category, StringPiece usage, StringPiece region, + const UnitPreference *const *&outPreferences, int32_t &preferenceCount, + UErrorCode &status) const; + + protected: + // Metadata about the sets of preferences, this is the index for looking up + // preferences in the unitPrefs_ list. + MaybeStackVector metadata_; + // All the preferences as a flat list: which usage and region preferences + // are associated with is stored in `metadata_`. + MaybeStackVector unitPrefs_; +}; + +} // namespace units +U_NAMESPACE_END + +#endif //__UNITS_DATA_H__ + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/units_router.cpp b/deps/icu-small/source/i18n/units_router.cpp new file mode 100644 index 00000000000000..3158718fd22fba --- /dev/null +++ b/deps/icu-small/source/i18n/units_router.cpp @@ -0,0 +1,132 @@ +// © 2020 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "charstr.h" +#include "cmemory.h" +#include "cstring.h" +#include "measunit_impl.h" +#include "number_decimalquantity.h" +#include "number_roundingutils.h" +#include "resource.h" +#include "unicode/measure.h" +#include "units_data.h" +#include "units_router.h" +#include + +U_NAMESPACE_BEGIN +namespace units { + +using number::Precision; +using number::impl::parseIncrementOption; + +Precision UnitsRouter::parseSkeletonToPrecision(icu::UnicodeString precisionSkeleton, + UErrorCode &status) { + if (U_FAILURE(status)) { + // As a member of UsagePrefsHandler, which is a friend of Precision, we + // get access to the default constructor. + return {}; + } + constexpr int32_t kSkelPrefixLen = 20; + if (!precisionSkeleton.startsWith(UNICODE_STRING_SIMPLE("precision-increment/"))) { + status = U_INVALID_FORMAT_ERROR; + return {}; + } + U_ASSERT(precisionSkeleton[kSkelPrefixLen - 1] == u'/'); + StringSegment segment(precisionSkeleton, false); + segment.adjustOffset(kSkelPrefixLen); + Precision result; + parseIncrementOption(segment, result, status); + return result; +} + +UnitsRouter::UnitsRouter(MeasureUnit inputUnit, StringPiece region, StringPiece usage, + UErrorCode &status) { + // TODO: do we want to pass in ConversionRates and UnitPreferences instead + // of loading in each UnitsRouter instance? (Or make global?) + ConversionRates conversionRates(status); + UnitPreferences prefs(status); + + MeasureUnitImpl inputUnitImpl = MeasureUnitImpl::forMeasureUnitMaybeCopy(inputUnit, status); + MeasureUnit baseUnit = + (extractCompoundBaseUnit(inputUnitImpl, conversionRates, status)).build(status); + CharString category = getUnitCategory(baseUnit.getIdentifier(), status); + + const UnitPreference *const *unitPreferences; + int32_t preferencesCount; + prefs.getPreferencesFor(category.data(), usage, region, unitPreferences, preferencesCount, status); + + for (int i = 0; i < preferencesCount; ++i) { + const auto &preference = *unitPreferences[i]; + + MeasureUnitImpl complexTargetUnitImpl = + MeasureUnitImpl::forIdentifier(preference.unit.data(), status); + if (U_FAILURE(status)) { + return; + } + + UnicodeString precision = preference.skeleton; + + // For now, we only have "precision-increment" in Units Preferences skeleton. + // Therefore, we check if the skeleton starts with "precision-increment" and force the program to + // fail otherwise. + // NOTE: + // It is allowed to have an empty precision. + if (!precision.isEmpty() && !precision.startsWith(u"precision-increment", 19)) { + status = U_INTERNAL_PROGRAM_ERROR; + return; + } + + outputUnits_.emplaceBackAndCheckErrorCode(status, + complexTargetUnitImpl.copy(status).build(status)); + converterPreferences_.emplaceBackAndCheckErrorCode(status, inputUnitImpl, complexTargetUnitImpl, + preference.geq, std::move(precision), + conversionRates, status); + + if (U_FAILURE(status)) { + return; + } + } +} + +RouteResult UnitsRouter::route(double quantity, icu::number::impl::RoundingImpl *rounder, UErrorCode &status) const { + // Find the matching preference + const ConverterPreference *converterPreference = nullptr; + for (int32_t i = 0, n = converterPreferences_.length(); i < n; i++) { + converterPreference = converterPreferences_[i]; + if (converterPreference->converter.greaterThanOrEqual(std::abs(quantity) * (1 + DBL_EPSILON), + converterPreference->limit)) { + break; + } + } + U_ASSERT(converterPreference != nullptr); + + // Set up the rounder for this preference's precision + if (rounder != nullptr && rounder->fPrecision.isBogus()) { + if (converterPreference->precision.length() > 0) { + rounder->fPrecision = parseSkeletonToPrecision(converterPreference->precision, status); + } else { + // We use the same rounding mode as COMPACT notation: known to be a + // human-friendly rounding mode: integers, but add a decimal digit + // as needed to ensure we have at least 2 significant digits. + rounder->fPrecision = Precision::integer().withMinDigits(2); + } + } + + return RouteResult(converterPreference->converter.convert(quantity, rounder, status), + converterPreference->targetUnit.copy(status)); +} + +const MaybeStackVector *UnitsRouter::getOutputUnits() const { + // TODO: consider pulling this from converterPreferences_ and dropping + // outputUnits_? + return &outputUnits_; +} + +} // namespace units +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/units_router.h b/deps/icu-small/source/i18n/units_router.h new file mode 100644 index 00000000000000..bd7a93d2d8c531 --- /dev/null +++ b/deps/icu-small/source/i18n/units_router.h @@ -0,0 +1,162 @@ +// © 2020 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __UNITS_ROUTER_H__ +#define __UNITS_ROUTER_H__ + +#include + +#include "cmemory.h" +#include "measunit_impl.h" +#include "unicode/measunit.h" +#include "unicode/stringpiece.h" +#include "unicode/uobject.h" +#include "units_complexconverter.h" +#include "units_data.h" + +U_NAMESPACE_BEGIN + +// Forward declarations +class Measure; +namespace number { +class Precision; +} + +namespace units { + +struct RouteResult : UMemory { + // A list of measures: a single measure for single units, multiple measures + // for mixed units. + // + // TODO(icu-units/icu#21): figure out the right mixed unit API. + MaybeStackVector measures; + + // The output unit for this RouteResult. This may be a MIXED unit - for + // example: "yard-and-foot-and-inch", for which `measures` will have three + // elements. + MeasureUnitImpl outputUnit; + + RouteResult(MaybeStackVector measures, MeasureUnitImpl outputUnit) + : measures(std::move(measures)), outputUnit(std::move(outputUnit)) {} +}; + +/** + * Contains the complex unit converter and the limit which representing the smallest value that the + * converter should accept. For example, if the converter is converting to `foot+inch` and the limit + * equals 3.0, thus means the converter should not convert to a value less than `3.0 feet`. + * + * NOTE: + * if the limit doest not has a value `i.e. (std::numeric_limits::lowest())`, this mean there + * is no limit for the converter. + */ +struct ConverterPreference : UMemory { + ComplexUnitsConverter converter; + double limit; + UnicodeString precision; + + // The output unit for this ConverterPreference. This may be a MIXED unit - + // for example: "yard-and-foot-and-inch". + MeasureUnitImpl targetUnit; + + // In case there is no limit, the limit will be -inf. + ConverterPreference(const MeasureUnitImpl &source, const MeasureUnitImpl &complexTarget, + UnicodeString precision, const ConversionRates &ratesInfo, UErrorCode &status) + : ConverterPreference(source, complexTarget, std::numeric_limits::lowest(), precision, + ratesInfo, status) {} + + ConverterPreference(const MeasureUnitImpl &source, const MeasureUnitImpl &complexTarget, + double limit, UnicodeString precision, const ConversionRates &ratesInfo, + UErrorCode &status) + : converter(source, complexTarget, ratesInfo, status), limit(limit), + precision(std::move(precision)), targetUnit(complexTarget.copy(status)) {} +}; + +} // namespace units + +// Export explicit template instantiations of MaybeStackArray, MemoryPool and +// MaybeStackVector. This is required when building DLLs for Windows. (See +// datefmt.h, collationiterator.h, erarules.h and others for similar examples.) +// +// Note: These need to be outside of the units namespace, or Clang will generate +// a compile error. +#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN +template class U_I18N_API MaybeStackArray; +template class U_I18N_API MemoryPool; +template class U_I18N_API MaybeStackVector; +#endif + +namespace units { + +/** + * `UnitsRouter` responsible for converting from a single unit (such as `meter` or `meter-per-second`) to + * one of the complex units based on the limits. + * For example: + * if the input is `meter` and the output as following + * {`foot+inch`, limit: 3.0} + * {`inch` , limit: no value (-inf)} + * Thus means if the input in `meter` is greater than or equal to `3.0 feet`, the output will be in + * `foot+inch`, otherwise, the output will be in `inch`. + * + * NOTE: + * the output units and the their limits MUST BE in order, for example, if the output units, from the + * previous example, are the following: + * {`inch` , limit: no value (-inf)} + * {`foot+inch`, limit: 3.0} + * IN THIS CASE THE OUTPUT WILL BE ALWAYS IN `inch`. + * + * NOTE: + * the output units and their limits will be extracted from the units preferences database by knowing + * the followings: + * - input unit + * - locale + * - usage + * + * DESIGN: + * `UnitRouter` uses internally `ComplexUnitConverter` in order to convert the input units to the + * desired complex units and to check the limit too. + */ +class U_I18N_API UnitsRouter { + public: + UnitsRouter(MeasureUnit inputUnit, StringPiece locale, StringPiece usage, UErrorCode &status); + + /** + * Performs locale and usage sensitive unit conversion. + * @param quantity The quantity to convert, expressed in terms of inputUnit. + * @param rounder If not null, this RoundingImpl will be used to do rounding + * on the converted value. If the rounder lacks an fPrecision, the + * rounder will be modified to use the preferred precision for the usage + * and locale preference, alternatively with the default precision. + * @param status Receives status. + */ + RouteResult route(double quantity, icu::number::impl::RoundingImpl *rounder, UErrorCode &status) const; + + /** + * Returns the list of possible output units, i.e. the full set of + * preferences, for the localized, usage-specific unit preferences. + * + * The returned pointer should be valid for the lifetime of the + * UnitsRouter instance. + */ + const MaybeStackVector *getOutputUnits() const; + + private: + // List of possible output units. TODO: converterPreferences_ now also has + // this data available. Maybe drop outputUnits_ and have getOutputUnits + // construct a the list from data in converterPreferences_ instead? + MaybeStackVector outputUnits_; + + MaybeStackVector converterPreferences_; + + static number::Precision parseSkeletonToPrecision(icu::UnicodeString precisionSkeleton, + UErrorCode &status); +}; + +} // namespace units +U_NAMESPACE_END + +#endif //__UNITS_ROUTER_H__ + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/unum.cpp b/deps/icu-small/source/i18n/unum.cpp index ba3d519162ce54..de693fc7276244 100644 --- a/deps/icu-small/source/i18n/unum.cpp +++ b/deps/icu-small/source/i18n/unum.cpp @@ -898,7 +898,7 @@ unum_getContext(const UNumberFormat *fmt, UDisplayContextType type, UErrorCode* return ((const NumberFormat*)fmt)->getContext(type, *status); } -U_INTERNAL UFormattable * U_EXPORT2 +U_CAPI UFormattable * U_EXPORT2 unum_parseToUFormattable(const UNumberFormat* fmt, UFormattable *result, const UChar* text, @@ -922,7 +922,7 @@ unum_parseToUFormattable(const UNumberFormat* fmt, return result; } -U_INTERNAL int32_t U_EXPORT2 +U_CAPI int32_t U_EXPORT2 unum_formatUFormattable(const UNumberFormat* fmt, const UFormattable *number, UChar *result, diff --git a/deps/icu-small/source/i18n/upluralrules.cpp b/deps/icu-small/source/i18n/upluralrules.cpp index 5119257fd804f7..73e59a75c4d016 100644 --- a/deps/icu-small/source/i18n/upluralrules.cpp +++ b/deps/icu-small/source/i18n/upluralrules.cpp @@ -20,6 +20,7 @@ #include "unicode/unumberformatter.h" #include "number_decimalquantity.h" #include "number_utypes.h" +#include "numrange_impl.h" U_NAMESPACE_USE @@ -115,6 +116,25 @@ uplrules_selectFormatted(const UPluralRules *uplrules, return result.extract(keyword, capacity, *status); } +U_CAPI int32_t U_EXPORT2 +uplrules_selectForRange(const UPluralRules *uplrules, + const UFormattedNumberRange* urange, + UChar *keyword, int32_t capacity, + UErrorCode *status) +{ + if (U_FAILURE(*status)) { + return 0; + } + if (keyword == NULL ? capacity != 0 : capacity < 0) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + const number::impl::UFormattedNumberRangeData* impl = + number::impl::validateUFormattedNumberRange(urange, *status); + UnicodeString result = ((PluralRules*)uplrules)->select(impl, *status); + return result.extract(keyword, capacity, *status); +} + U_CAPI int32_t U_EXPORT2 uplrules_selectWithFormat(const UPluralRules *uplrules, double number, diff --git a/deps/icu-small/source/i18n/uspoof_impl.h b/deps/icu-small/source/i18n/uspoof_impl.h index 8844a96446e2b5..b0bd8cefc82ddc 100644 --- a/deps/icu-small/source/i18n/uspoof_impl.h +++ b/deps/icu-small/source/i18n/uspoof_impl.h @@ -222,7 +222,7 @@ class SpoofData: public UMemory { SpoofData(const void *serializedData, int32_t length, UErrorCode &status); // Check raw Spoof Data Version compatibility. - // Return TRUE it looks good. + // Return true it looks good. UBool validateDataVersion(UErrorCode &status) const; ~SpoofData(); // Destructor not normally used. diff --git a/deps/icu-small/source/i18n/usrchimp.h b/deps/icu-small/source/i18n/usrchimp.h index 5438417e7e60aa..e056337caecdef 100644 --- a/deps/icu-small/source/i18n/usrchimp.h +++ b/deps/icu-small/source/i18n/usrchimp.h @@ -206,7 +206,7 @@ struct UStringSearch { * the text "\u00e6" * @param strsrch string search data * @param status error status if any -* @return TRUE if an exact match is found, FALSE otherwise +* @return true if an exact match is found, false otherwise */ U_CFUNC UBool usearch_handleNextExact(UStringSearch *strsrch, UErrorCode *status); @@ -217,7 +217,7 @@ UBool usearch_handleNextExact(UStringSearch *strsrch, UErrorCode *status); * of beginning and ending accents if it overlaps that region. * @param strsrch string search data * @param status error status if any -* @return TRUE if a canonical match is found, FALSE otherwise +* @return true if a canonical match is found, false otherwise */ U_CFUNC UBool usearch_handleNextCanonical(UStringSearch *strsrch, UErrorCode *status); @@ -227,7 +227,7 @@ UBool usearch_handleNextCanonical(UStringSearch *strsrch, UErrorCode *status); * Comments follows from handleNextExact * @param strsrch string search data * @param status error status if any -* @return True if a exact math is found, FALSE otherwise. +* @return True if a exact math is found, false otherwise. */ U_CFUNC UBool usearch_handlePreviousExact(UStringSearch *strsrch, UErrorCode *status); @@ -238,7 +238,7 @@ UBool usearch_handlePreviousExact(UStringSearch *strsrch, UErrorCode *status); * of beginning and ending accents if it overlaps that region. * @param strsrch string search data * @param status error status if any -* @return TRUE if a canonical match is found, FALSE otherwise +* @return true if a canonical match is found, false otherwise */ U_CFUNC UBool usearch_handlePreviousCanonical(UStringSearch *strsrch, diff --git a/deps/icu-small/source/i18n/utf16collationiterator.h b/deps/icu-small/source/i18n/utf16collationiterator.h index fd3a05e9efab0b..6305d81c30c66b 100644 --- a/deps/icu-small/source/i18n/utf16collationiterator.h +++ b/deps/icu-small/source/i18n/utf16collationiterator.h @@ -125,7 +125,7 @@ class U_I18N_API FCDUTF16CollationIterator : public UTF16CollationIterator { /** * Extend the FCD text segment forward or normalize around pos. * To be called when checkDir > 0 && pos != limit. - * @return TRUE if success, checkDir == 0 and pos != limit + * @return true if success, checkDir == 0 and pos != limit */ UBool nextSegment(UErrorCode &errorCode); @@ -139,7 +139,7 @@ class U_I18N_API FCDUTF16CollationIterator : public UTF16CollationIterator { /** * Extend the FCD text segment backward or normalize around pos. * To be called when checkDir < 0 && pos != start. - * @return TRUE if success, checkDir == 0 and pos != start + * @return true if success, checkDir == 0 and pos != start */ UBool previousSegment(UErrorCode &errorCode); diff --git a/deps/icu-small/source/i18n/utf8collationiterator.h b/deps/icu-small/source/i18n/utf8collationiterator.h index 9a3ec45aeb41eb..9059e72dcf9e63 100644 --- a/deps/icu-small/source/i18n/utf8collationiterator.h +++ b/deps/icu-small/source/i18n/utf8collationiterator.h @@ -54,7 +54,7 @@ class U_I18N_API UTF8CollationIterator : public CollationIterator { * together with a bogus code point. The caller will ignore that code point. * * Special values may be returned for surrogate code points, which are also illegal in UTF-8, - * but the caller will treat them like U+FFFD because forbidSurrogateCodePoints() returns TRUE. + * but the caller will treat them like U+FFFD because forbidSurrogateCodePoints() returns true. * * Valid lead surrogates are returned from inside a normalized text segment, * where handleGetTrailSurrogate() will return the matching trail surrogate. @@ -117,7 +117,7 @@ class U_I18N_API FCDUTF8CollationIterator : public UTF8CollationIterator { /** * Extends the FCD text segment forward or normalizes around pos. - * @return TRUE if success + * @return true if success */ UBool nextSegment(UErrorCode &errorCode); @@ -128,7 +128,7 @@ class U_I18N_API FCDUTF8CollationIterator : public UTF8CollationIterator { /** * Extends the FCD text segment backward or normalizes around pos. - * @return TRUE if success + * @return true if success */ UBool previousSegment(UErrorCode &errorCode); diff --git a/deps/icu-small/source/i18n/vzone.h b/deps/icu-small/source/i18n/vzone.h index 700687e0cb76ab..9c83c1b7e660cb 100644 --- a/deps/icu-small/source/i18n/vzone.h +++ b/deps/icu-small/source/i18n/vzone.h @@ -91,7 +91,7 @@ vzone_equals(const VZone* zone1, const VZone* zone2); * @param zone, the vzone to use * @param url Receives the RFC2445 TZURL property value. * @param urlLength, length of the url - * @return TRUE if TZURL attribute is available and value is set. + * @return true if TZURL attribute is available and value is set. */ U_CAPI UBool U_EXPORT2 vzone_getTZURL(VZone* zone, UChar* & url, int32_t & urlLength); @@ -112,7 +112,7 @@ vzone_setTZURL(VZone* zone, UChar* url, int32_t urlLength); * is not set. * @param zone, the vzone to use * @param lastModified Receives the last modified date. - * @return TRUE if lastModified attribute is available and value is set. + * @return true if lastModified attribute is available and value is set. */ U_CAPI UBool U_EXPORT2 vzone_getLastModified(VZone* zone, UDate& lastModified); @@ -283,7 +283,7 @@ vzone_useDaylightTime(VZone* zone); * @return true if the given date is in daylight savings time, * false, otherwise. */ -U_INTERNAL UBool U_EXPORT2 +U_CAPI UBool U_EXPORT2 vzone_inDaylightTime(VZone* zone, UDate date, UErrorCode& status); /** @@ -303,7 +303,7 @@ vzone_hasSameRules(VZone* zone, const VZone* other); * @param base The base time. * @param inclusive Whether the base time is inclusive or not. * @param result Receives the first transition after the base time. - * @return TRUE if the transition is found. + * @return true if the transition is found. */ U_CAPI UBool U_EXPORT2 vzone_getNextTransition(VZone* zone, UDate base, UBool inclusive, ZTrans* result); @@ -314,7 +314,7 @@ vzone_getNextTransition(VZone* zone, UDate base, UBool inclusive, ZTrans* result * @param base The base time. * @param inclusive Whether the base time is inclusive or not. * @param result Receives the most recent transition before the base time. - * @return TRUE if the transition is found. + * @return true if the transition is found. */ U_CAPI UBool U_EXPORT2 vzone_getPreviousTransition(VZone* zone, UDate base, UBool inclusive, ZTrans* result); diff --git a/deps/icu-small/source/i18n/zonemeta.h b/deps/icu-small/source/i18n/zonemeta.h index 9dbcc878a22dc8..58724ea3b7d2e4 100644 --- a/deps/icu-small/source/i18n/zonemeta.h +++ b/deps/icu-small/source/i18n/zonemeta.h @@ -59,7 +59,7 @@ class U_I18N_API ZoneMeta { * is not associated with a country, return bogus string. * @param tzid Zone ID * @param country [output] Country code - * @param isPrimary [output] TRUE if the zone is the primary zone for the country + * @param isPrimary [output] true if the zone is the primary zone for the country * @return A reference to the result country */ static UnicodeString& U_EXPORT2 getCanonicalCountry(const UnicodeString &tzid, UnicodeString &country, UBool *isPrimary = NULL); diff --git a/deps/icu-small/source/python/icutools/databuilder/filtration_schema.json b/deps/icu-small/source/python/icutools/databuilder/filtration_schema.json index 2b7ff9989992a1..3aed41a3341f65 100644 --- a/deps/icu-small/source/python/icutools/databuilder/filtration_schema.json +++ b/deps/icu-small/source/python/icutools/databuilder/filtration_schema.json @@ -90,7 +90,7 @@ { "properties": { "filterType": { - "$ref": "#/definitions/blacklistWhitelistFilterTypes" + "$ref": "#/definitions/includeExcludeFilterTypes" }, "whitelist": { "$ref": "#/definitions/stringList" } }, @@ -100,13 +100,33 @@ { "properties": { "filterType": { - "$ref": "#/definitions/blacklistWhitelistFilterTypes" + "$ref": "#/definitions/includeExcludeFilterTypes" }, "blacklist": { "$ref": "#/definitions/stringList" } }, "required": ["blacklist"], "additionalProperties": false }, + { + "properties": { + "filterType": { + "$ref": "#/definitions/includeExcludeFilterTypes" + }, + "includelist": { "$ref": "#/definitions/stringList" } + }, + "required": ["includelist"], + "additionalProperties": false + }, + { + "properties": { + "filterType": { + "$ref": "#/definitions/includeExcludeFilterTypes" + }, + "excludelist": { "$ref": "#/definitions/stringList" } + }, + "required": ["excludelist"], + "additionalProperties": false + }, { "properties": { "filterType": { @@ -134,6 +154,23 @@ "required": ["filterType", "whitelist"], "additionalProperties": false }, + { + "properties": { + "filterType": { + "type": "string", + "enum": ["locale"] + }, + "includeChildren": { + "type": "boolean" + }, + "includeScripts": { + "type": "boolean" + }, + "includelist": { "$ref": "#/definitions/stringList" } + }, + "required": ["filterType", "includelist"], + "additionalProperties": false + }, { "properties": { "filterType": { @@ -150,7 +187,7 @@ } ] }, - "blacklistWhitelistFilterTypes": { + "includeExcludeFilterTypes": { "type": "string", "enum": [ "language", diff --git a/deps/icu-small/source/python/icutools/databuilder/test/sample_data/brkitr/LOCALE_DEPS.json b/deps/icu-small/source/python/icutools/databuilder/test/sample_data/brkitr/LOCALE_DEPS.json index 89329e87eea539..674db09278fc37 100644 --- a/deps/icu-small/source/python/icutools/databuilder/test/sample_data/brkitr/LOCALE_DEPS.json +++ b/deps/icu-small/source/python/icutools/databuilder/test/sample_data/brkitr/LOCALE_DEPS.json @@ -1,5 +1,5 @@ // © 2019 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html#License +// License & terms of use: http://www.unicode.org/copyright.html ////////////////////////////////////////////////////////////// // This is a sample LOCALE_DEPS.json file for testing only. // diff --git a/deps/icu-small/source/python/icutools/databuilder/test/sample_data/locales/LOCALE_DEPS.json b/deps/icu-small/source/python/icutools/databuilder/test/sample_data/locales/LOCALE_DEPS.json index fd28a741ef67c2..1456ea0d9acff4 100644 --- a/deps/icu-small/source/python/icutools/databuilder/test/sample_data/locales/LOCALE_DEPS.json +++ b/deps/icu-small/source/python/icutools/databuilder/test/sample_data/locales/LOCALE_DEPS.json @@ -1,5 +1,5 @@ // © 2019 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html#License +// License & terms of use: http://www.unicode.org/copyright.html ////////////////////////////////////////////////////////////// // This is a sample LOCALE_DEPS.json file for testing only. // diff --git a/deps/icu-small/source/python/icutools/databuilder/test/sample_data/rbnf/LOCALE_DEPS.json b/deps/icu-small/source/python/icutools/databuilder/test/sample_data/rbnf/LOCALE_DEPS.json index f079619a3685ba..c6ec208add2b41 100644 --- a/deps/icu-small/source/python/icutools/databuilder/test/sample_data/rbnf/LOCALE_DEPS.json +++ b/deps/icu-small/source/python/icutools/databuilder/test/sample_data/rbnf/LOCALE_DEPS.json @@ -1,5 +1,5 @@ // © 2019 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html#License +// License & terms of use: http://www.unicode.org/copyright.html ////////////////////////////////////////////////////////////// // This is a sample LOCALE_DEPS.json file for testing only. // diff --git a/deps/icu-small/source/tools/genrb/errmsg.c b/deps/icu-small/source/tools/genrb/errmsg.c index 603f26a174c4e9..91dfd3265e1149 100644 --- a/deps/icu-small/source/tools/genrb/errmsg.c +++ b/deps/icu-small/source/tools/genrb/errmsg.c @@ -21,6 +21,7 @@ #include #include "cstring.h" #include "errmsg.h" +#include "toolutil.h" U_CFUNC void error(uint32_t linenumber, const char *msg, ...) { diff --git a/deps/icu-small/source/tools/genrb/read.c b/deps/icu-small/source/tools/genrb/read.c index c20b4510a28ac4..141e285d25efb9 100644 --- a/deps/icu-small/source/tools/genrb/read.c +++ b/deps/icu-small/source/tools/genrb/read.c @@ -20,6 +20,7 @@ #include "read.h" #include "errmsg.h" +#include "toolutil.h" #include "unicode/ustring.h" #include "unicode/utf16.h" diff --git a/deps/icu-small/source/tools/genrb/reslist.cpp b/deps/icu-small/source/tools/genrb/reslist.cpp index 3186c781e934f3..4bc6adc128ecc3 100644 --- a/deps/icu-small/source/tools/genrb/reslist.cpp +++ b/deps/icu-small/source/tools/genrb/reslist.cpp @@ -39,6 +39,7 @@ #include "unicode/putil.h" #include "errmsg.h" #include "filterrb.h" +#include "toolutil.h" #include "uarrsort.h" #include "uelement.h" diff --git a/deps/icu-small/source/tools/genrb/reslist.h b/deps/icu-small/source/tools/genrb/reslist.h index 34b710c4232670..e7b10fa0961d18 100644 --- a/deps/icu-small/source/tools/genrb/reslist.h +++ b/deps/icu-small/source/tools/genrb/reslist.h @@ -355,7 +355,7 @@ class StringResource : public StringBaseResource { fNumCopies(0), fNumUnitsSaved(0), fNumCharsForLength(numCharsForLength) { // v3 pool string encoded as string-v2 with low offset fRes = URES_MAKE_RESOURCE(URES_STRING_V2, poolStringIndex); - fWritten = TRUE; + fWritten = true; } virtual ~StringResource(); diff --git a/deps/icu-small/source/tools/genrb/rle.c b/deps/icu-small/source/tools/genrb/rle.c index 3d034f78ca3881..c5d29ba1d24b6b 100644 --- a/deps/icu-small/source/tools/genrb/rle.c +++ b/deps/icu-small/source/tools/genrb/rle.c @@ -16,6 +16,7 @@ * 01/11/02 Ram Creation. ******************************************************************************* */ +#include #include "rle.h" /** * The ESCAPE character is used during run-length encoding. It signals @@ -297,7 +298,7 @@ int32_t rleStringToByteArray(uint16_t* src, int32_t srcLen, uint8_t* target, int32_t tgtLen, UErrorCode* status) { int32_t length = 0; - UBool nextChar = TRUE; + UBool nextChar = true; uint16_t c = 0; int32_t node = 0; int32_t runLength = 0; @@ -334,11 +335,11 @@ rleStringToByteArray(uint16_t* src, int32_t srcLen, uint8_t* target, int32_t tgt if (nextChar) { c = src[i++]; b = (uint8_t) (c >> 8); - nextChar = FALSE; + nextChar = false; } else { b = (uint8_t) (c & 0xFF); - nextChar = TRUE; + nextChar = true; } /* This part of the loop is a tiny state machine which handles diff --git a/deps/icu-small/source/tools/icupkg/icupkg.cpp b/deps/icu-small/source/tools/icupkg/icupkg.cpp index ea7be4a90923fd..39707946b0943c 100644 --- a/deps/icu-small/source/tools/icupkg/icupkg.cpp +++ b/deps/icu-small/source/tools/icupkg/icupkg.cpp @@ -59,6 +59,7 @@ printUsage(const char *pname, UBool isHelp) { "%csage: %s [-h|-?|--help ] [-tl|-tb|-te] [-c] [-C comment]\n" "\t[-a list] [-r list] [-x list] [-l [-o outputListFileName]]\n" "\t[-s path] [-d path] [-w] [-m mode]\n" + "\t[--ignore-deps]\n" "\t[--auto_toc_prefix] [--auto_toc_prefix_with_type] [--toc_prefix]\n" "\tinfilename [outfilename]\n", isHelp ? 'U' : 'u', pname); @@ -119,6 +120,10 @@ printUsage(const char *pname, UBool isHelp) { "\t-m mode or --matchmode mode set the matching mode for item names with\n" "\t wildcards\n" "\t noslash: the '*' wildcard does not match the '/' tree separator\n"); + fprintf(where, + "\n" + "\t--ignore-deps Do not fail if not all resource dependencies are met. Use this\n" + "\t option if the missing resources come from another source."); fprintf(where, "\n" "\tIn the .dat package, the Table of Contents (ToC) contains an entry\n" @@ -198,6 +203,8 @@ static UOption options[]={ UOPTION_DEF("matchmode", 'm', UOPT_REQUIRES_ARG), + UOPTION_DEF("ignore-deps", '\1', UOPT_NO_ARG), + UOPTION_DEF("add", 'a', UOPT_REQUIRES_ARG), UOPTION_DEF("remove", 'r', UOPT_REQUIRES_ARG), UOPTION_DEF("extract", 'x', UOPT_REQUIRES_ARG), @@ -225,6 +232,8 @@ enum { OPT_MATCHMODE, + OPT_IGNORE_DEPS, + OPT_ADD_LIST, OPT_REMOVE_LIST, OPT_EXTRACT_LIST, @@ -501,7 +510,7 @@ main(int argc, char *argv[]) { } /* check dependencies between items */ - if(!pkg->checkDependencies()) { + if(!options[OPT_IGNORE_DEPS].doesOccur && !pkg->checkDependencies()) { /* some dependencies are not fulfilled */ return U_MISSING_RESOURCE_ERROR; } diff --git a/deps/icu-small/source/tools/pkgdata/pkgtypes.c b/deps/icu-small/source/tools/pkgdata/pkgtypes.c index 43ee3dfb5e2c33..26bd945df73d48 100644 --- a/deps/icu-small/source/tools/pkgdata/pkgtypes.c +++ b/deps/icu-small/source/tools/pkgdata/pkgtypes.c @@ -17,6 +17,7 @@ * common types for pkgdata */ +#include #include #include #include "unicode/utypes.h" @@ -294,9 +295,9 @@ UBool pkg_listContains(CharList *l, const char *str) { for(;l;l=l->next){ if(!uprv_strcmp(l->str, str)) { - return TRUE; + return true; } } - return FALSE; + return false; } diff --git a/deps/icu-small/source/tools/toolutil/dbgutil.cpp b/deps/icu-small/source/tools/toolutil/dbgutil.cpp index 29bab927535e78..17fdfbb1444e29 100644 --- a/deps/icu-small/source/tools/toolutil/dbgutil.cpp +++ b/deps/icu-small/source/tools/toolutil/dbgutil.cpp @@ -29,7 +29,7 @@ static const UnicodeString& _fieldString(UDebugEnumType type, int32_t field, Un if(str == NULL) { return fillin.remove(); } else { - return fillin = UnicodeString(str, ""); // optimize? + return fillin = UnicodeString(str, -1, US_INV); } } diff --git a/deps/icu-small/source/tools/toolutil/filetools.h b/deps/icu-small/source/tools/toolutil/filetools.h index 6a25c3601c0178..40a606a7d49076 100644 --- a/deps/icu-small/source/tools/toolutil/filetools.h +++ b/deps/icu-small/source/tools/toolutil/filetools.h @@ -26,7 +26,7 @@ #include "unicode/utypes.h" U_CAPI UBool U_EXPORT2 -isFileModTimeLater(const char *filePath, const char *checkAgainst, UBool isDir=FALSE); +isFileModTimeLater(const char *filePath, const char *checkAgainst, UBool isDir=false); U_CAPI void U_EXPORT2 swapFileSepChar(char *filePath, const char oldFileSepChar, const char newFileSepChar); diff --git a/deps/icu-small/source/tools/toolutil/package.h b/deps/icu-small/source/tools/toolutil/package.h index 3263c84feb4c37..5f74eb73ef5c9a 100644 --- a/deps/icu-small/source/tools/toolutil/package.h +++ b/deps/icu-small/source/tools/toolutil/package.h @@ -57,13 +57,13 @@ class U_TOOLUTIL_API Package { * Uses the prefix of the first entry of the package in readPackage(), * rather than the package basename. */ - void setAutoPrefix() { doAutoPrefix=TRUE; } + void setAutoPrefix() { doAutoPrefix=true; } /** * Same as setAutoPrefix(), plus the prefix must end with the platform type letter. */ void setAutoPrefixWithType() { - doAutoPrefix=TRUE; - prefixEndsWithType=TRUE; + doAutoPrefix=true; + prefixEndsWithType=true; } void setPrefix(const char *p); @@ -128,7 +128,7 @@ class U_TOOLUTIL_API Package { const Item *getItem(int32_t idx) const; /* - * Check dependencies and return TRUE if all dependencies are fulfilled. + * Check dependencies and return true if all dependencies are fulfilled. */ UBool checkDependencies(); diff --git a/deps/icu-small/source/tools/toolutil/pkg_genc.cpp b/deps/icu-small/source/tools/toolutil/pkg_genc.cpp index 31db2e2184b3be..f9400c5058c206 100644 --- a/deps/icu-small/source/tools/toolutil/pkg_genc.cpp +++ b/deps/icu-small/source/tools/toolutil/pkg_genc.cpp @@ -738,8 +738,8 @@ getOutFilename( exit(U_ILLEGAL_ARGUMENT_ERROR); } - uprv_strcpy(outFilename, outFilenameBuilder.data()); - uprv_strcpy(entryName, entryNameBuilder.data()); + outFilenameBuilder.extract(outFilename, outFilenameCapacity, status); + entryNameBuilder.extract(entryName, entryNameCapacity, status); } #ifdef CAN_GENERATE_OBJECTS diff --git a/deps/icu-small/source/tools/toolutil/pkg_genc.h b/deps/icu-small/source/tools/toolutil/pkg_genc.h index b231aa6170c287..72d96870355020 100644 --- a/deps/icu-small/source/tools/toolutil/pkg_genc.h +++ b/deps/icu-small/source/tools/toolutil/pkg_genc.h @@ -68,13 +68,13 @@ -U_INTERNAL void U_EXPORT2 +U_CAPI void U_EXPORT2 printAssemblyHeadersToStdErr(void); -U_INTERNAL UBool U_EXPORT2 +U_CAPI UBool U_EXPORT2 checkAssemblyHeaderName(const char* optAssembly); -U_INTERNAL void U_EXPORT2 +U_CAPI void U_EXPORT2 writeCCode( const char *filename, const char *destdir, @@ -83,7 +83,7 @@ writeCCode( char *outFilePath, size_t outFilePathCapacity); -U_INTERNAL void U_EXPORT2 +U_CAPI void U_EXPORT2 writeAssemblyCode( const char *filename, const char *destdir, @@ -92,7 +92,7 @@ writeAssemblyCode( char *outFilePath, size_t outFilePathCapacity); -U_INTERNAL void U_EXPORT2 +U_CAPI void U_EXPORT2 writeObjectCode( const char *filename, const char *destdir, diff --git a/deps/icu-small/source/tools/toolutil/pkgitems.cpp b/deps/icu-small/source/tools/toolutil/pkgitems.cpp index b0ea980d605dcb..16b18a3e27345f 100644 --- a/deps/icu-small/source/tools/toolutil/pkgitems.cpp +++ b/deps/icu-small/source/tools/toolutil/pkgitems.cpp @@ -441,6 +441,7 @@ ures_enumDependencies(const char *itemName, const UDataInfo *pInfo, // get dependencies from conversion tables --------------------------------- *** +#if !UCONFIG_NO_CONVERSION /* code adapted from ucnv_swap() */ static void ucnv_enumDependencies(const UDataSwapper *ds, @@ -631,5 +632,6 @@ Package::enumDependencies(Item *pItem, void *context, CheckDependency check) { } } } +#endif /* UCONFIG_NO_CONVERSION */ U_NAMESPACE_END diff --git a/deps/icu-small/source/tools/toolutil/toolutil.cpp b/deps/icu-small/source/tools/toolutil/toolutil.cpp index f0d6be5cf5ee2a..0ce9b02115dd32 100644 --- a/deps/icu-small/source/tools/toolutil/toolutil.cpp +++ b/deps/icu-small/source/tools/toolutil/toolutil.cpp @@ -33,7 +33,7 @@ #include "unicode/utypes.h" #ifndef U_TOOLUTIL_IMPLEMENTATION -#error U_TOOLUTIL_IMPLEMENTATION not set - must be set for all ICU source files in common/ - see http://userguide.icu-project.org/howtouseicu +#error U_TOOLUTIL_IMPLEMENTATION not set - must be set for all ICU source files in common/ - see https://unicode-org.github.io/icu/userguide/howtouseicu #endif #if U_PLATFORM_USES_ONLY_WIN32_API @@ -166,14 +166,11 @@ findBasename(const char *filename) { const char *basename=uprv_strrchr(filename, U_FILE_SEP_CHAR); #if U_FILE_ALT_SEP_CHAR!=U_FILE_SEP_CHAR -#if !(U_PLATFORM == U_PF_CYGWIN && U_PLATFORM_USES_ONLY_WIN32_API) - if(basename==NULL) -#endif - { - /* Use lenient matching on Windows, which can accept either \ or / - This is useful for environments like Win32+CygWin which have both. - */ - basename=uprv_strrchr(filename, U_FILE_ALT_SEP_CHAR); + //be lenient about pathname separators on Windows, like official implementation of C++17 std::filesystem in MSVC + //would be convenient to merge this loop with the one above, but alas, there is no such solution in the standard library + const char *alt_basename=uprv_strrchr(filename, U_FILE_ALT_SEP_CHAR); + if(alt_basename>basename) { + basename=alt_basename; } #endif diff --git a/deps/icu-small/source/tools/toolutil/toolutil.h b/deps/icu-small/source/tools/toolutil/toolutil.h index be07787a9fa950..1d0d26c97477c5 100644 --- a/deps/icu-small/source/tools/toolutil/toolutil.h +++ b/deps/icu-small/source/tools/toolutil/toolutil.h @@ -23,6 +23,12 @@ #include "unicode/utypes.h" +#ifndef TRUE +# define TRUE 1 +#endif +#ifndef FALSE +# define FALSE 0 +#endif #ifdef __cplusplus diff --git a/deps/icu-small/source/tools/toolutil/udbgutil.cpp b/deps/icu-small/source/tools/toolutil/udbgutil.cpp index 285f68a0ec66a1..993694546f4a72 100644 --- a/deps/icu-small/source/tools/toolutil/udbgutil.cpp +++ b/deps/icu-small/source/tools/toolutil/udbgutil.cpp @@ -594,24 +594,11 @@ U_CAPI void udbg_writeIcuInfo(FILE *out) { fprintf(out, " \n"); } -#define ICU_TRAC_URL "http://bugs.icu-project.org/trac/ticket/" -#define CLDR_TRAC_URL "http://unicode.org/cldr/trac/ticket/" -#define CLDR_TICKET_PREFIX "cldrbug:" +#define UNICODE_BUG_URL "https://unicode-org.atlassian.net/browse/" +#define OLD_CLDR_PREFIX "cldrbug:" +#define CLDR_BUG_PREFIX "CLDR-" +#define ICU_BUG_PREFIX "ICU-" -U_CAPI char *udbg_knownIssueURLFrom(const char *ticket, char *buf) { - if( ticket==NULL ) { - return NULL; - } - - if( !strncmp(ticket, CLDR_TICKET_PREFIX, strlen(CLDR_TICKET_PREFIX)) ) { - strcpy( buf, CLDR_TRAC_URL ); - strcat( buf, ticket+strlen(CLDR_TICKET_PREFIX) ); - } else { - strcpy( buf, ICU_TRAC_URL ); - strcat( buf, ticket ); - } - return buf; -} #include @@ -641,8 +628,27 @@ KnownIssues::~KnownIssues() { } -void KnownIssues::add(const char *ticket, const char *where, const UChar *msg, UBool *firstForTicket, UBool *firstForWhere) +/** + * Map cldr:1234 to CLDR-1234 + * Map 1234 to ICU-1234 + */ +static std::string mapTicketId(const char *ticketStr) { + std::string ticket(ticketStr); + // TODO: Can remove this function once all logKnownIssue calls are switched over + // to the ICU-1234 and CLDR-1234 format. + if(ticket.rfind(OLD_CLDR_PREFIX) == 0) { + // map cldrbug:1234 to CLDR-1234 + ticket.replace(0, uprv_strlen(OLD_CLDR_PREFIX), CLDR_BUG_PREFIX); + } else if(::isdigit(ticket[0])) { + // map 1234 to ICU-1234 + ticket.insert(0, ICU_BUG_PREFIX); + } + return ticket; +} + +void KnownIssues::add(const char *ticketStr, const char *where, const UChar *msg, UBool *firstForTicket, UBool *firstForWhere) { + const std::string ticket = mapTicketId(ticketStr); if(fTable.find(ticket) == fTable.end()) { if(firstForTicket!=NULL) *firstForTicket = TRUE; fTable[ticket] = std::map < std::string, std::set < std::string > >(); @@ -664,8 +670,9 @@ void KnownIssues::add(const char *ticket, const char *where, const UChar *msg, U fTable[ticket][where].insert(std::string(icu::CStr(ustr)())); } -void KnownIssues::add(const char *ticket, const char *where, const char *msg, UBool *firstForTicket, UBool *firstForWhere) +void KnownIssues::add(const char *ticketStr, const char *where, const char *msg, UBool *firstForTicket, UBool *firstForWhere) { + const std::string ticket = mapTicketId(ticketStr); if(fTable.find(ticket) == fTable.end()) { if(firstForTicket!=NULL) *firstForTicket = TRUE; fTable[ticket] = std::map < std::string, std::set < std::string > >(); @@ -697,8 +704,13 @@ UBool KnownIssues::print() std::map < std::string, std::set < std::string > > >::iterator i = fTable.begin(); i != fTable.end(); i++ ) { - char URL[1024]; - std::cout << '#' << (*i).first << " <" << udbg_knownIssueURLFrom( (*i).first.c_str(), URL ) << ">" << std::endl; + const std::string ticketid = (*i).first; + std::cout << "[" << ticketid << "] "; + if(ticketid.rfind(ICU_BUG_PREFIX) == 0 || ticketid.rfind(CLDR_BUG_PREFIX) == 0) { + // If it's a unicode.org bug. + std::cout << UNICODE_BUG_URL << ticketid; + } // Else: some other kind of bug. Allow this, but without a URL. + std::cout << std::endl; for( std::map< std::string, std::set < std::string > >::iterator ii = (*i).second.begin(); ii != (*i).second.end(); diff --git a/deps/icu-small/source/tools/toolutil/udbgutil.h b/deps/icu-small/source/tools/toolutil/udbgutil.h index 2f186e6ed87f26..b9af132da5ba64 100644 --- a/deps/icu-small/source/tools/toolutil/udbgutil.h +++ b/deps/icu-small/source/tools/toolutil/udbgutil.h @@ -113,14 +113,6 @@ U_CAPI void udbg_writeIcuInfo(FILE *f); */ #define UDBG_KNOWNISSUE_LEN 255 -/** - * Convert a "known issue" string into a URL - * @param ticket ticket string such as "10245" or "cldrbug:5013" - * @param buf output buffer - must be UDBG_KNOWNISSUE_LEN in size - * @return pointer to output buffer, or NULL on err - */ -U_CAPI char *udbg_knownIssueURLFrom(const char *ticket, char *buf); - /** * Open (or reopen) a 'known issue' table. * @param ptr pointer to 'table'. Opaque. diff --git a/deps/icu-small/source/tools/toolutil/unewdata.cpp b/deps/icu-small/source/tools/toolutil/unewdata.cpp index 22d8540881f454..20f13be5b83ef1 100644 --- a/deps/icu-small/source/tools/toolutil/unewdata.cpp +++ b/deps/icu-small/source/tools/toolutil/unewdata.cpp @@ -57,6 +57,16 @@ udata_create(const char *dir, const char *type, const char *name, return NULL; } + char dirSepChar = U_FILE_SEP_CHAR; +#if (U_FILE_SEP_CHAR != U_FILE_ALT_SEP_CHAR) + // We may need to append a different directory separator when building for Cygwin or MSYS2. + if(dir && *dir) { + if(!uprv_strchr(dir, U_FILE_SEP_CHAR) && uprv_strchr(dir, U_FILE_ALT_SEP_CHAR)) { + dirSepChar = U_FILE_ALT_SEP_CHAR; + } + } +#endif + /* Check that the full path won't be too long */ length = 0; /* Start with nothing */ if(dir != NULL && *dir !=0) /* Add directory length if one was given */ @@ -64,7 +74,7 @@ udata_create(const char *dir, const char *type, const char *name, length += static_cast(strlen(dir)); /* Add 1 if dir doesn't end with path sep */ - if (dir[strlen(dir) - 1]!= U_FILE_SEP_CHAR) { + if (dir[strlen(dir) - 1]!= dirSepChar) { length++; } } @@ -87,8 +97,8 @@ udata_create(const char *dir, const char *type, const char *name, if(dir!=NULL && *dir!=0) { /* if dir has a value, we prepend it to the filename */ char *p=filename+strlen(dir); uprv_strcpy(filename, dir); - if (*(p-1)!=U_FILE_SEP_CHAR) { - *p++=U_FILE_SEP_CHAR; + if (*(p-1)!=dirSepChar) { + *p++=dirSepChar; *p=0; } } else { /* otherwise, we'll output to the current dir */ diff --git a/deps/nghttp2/lib/CMakeLists.txt b/deps/nghttp2/lib/CMakeLists.txt new file mode 100644 index 00000000000000..a02a534b1a8436 --- /dev/null +++ b/deps/nghttp2/lib/CMakeLists.txt @@ -0,0 +1,77 @@ +add_subdirectory(includes) + +include_directories( + "${CMAKE_CURRENT_SOURCE_DIR}/includes" + "${CMAKE_CURRENT_BINARY_DIR}/includes" +) + +add_definitions(-DBUILDING_NGHTTP2) + +set(NGHTTP2_SOURCES + nghttp2_pq.c nghttp2_map.c nghttp2_queue.c + nghttp2_frame.c + nghttp2_buf.c + nghttp2_stream.c nghttp2_outbound_item.c + nghttp2_session.c nghttp2_submit.c + nghttp2_helper.c + nghttp2_npn.c + nghttp2_hd.c nghttp2_hd_huffman.c nghttp2_hd_huffman_data.c + nghttp2_version.c + nghttp2_priority_spec.c + nghttp2_option.c + nghttp2_callbacks.c + nghttp2_mem.c + nghttp2_http.c + nghttp2_rcbuf.c + nghttp2_debug.c + nghttp2_ksl.c +) + +set(NGHTTP2_RES "") + +if(WIN32) + configure_file( + version.rc.in + ${CMAKE_CURRENT_BINARY_DIR}/version.rc + @ONLY) + + set(NGHTTP2_RES ${CMAKE_CURRENT_BINARY_DIR}/version.rc) +endif() + +# Public shared library +if(ENABLE_SHARED_LIB) + add_library(nghttp2 SHARED ${NGHTTP2_SOURCES} ${NGHTTP2_RES}) + set_target_properties(nghttp2 PROPERTIES + COMPILE_FLAGS "${WARNCFLAGS}" + VERSION ${LT_VERSION} SOVERSION ${LT_SOVERSION} + C_VISIBILITY_PRESET hidden + ) + target_include_directories(nghttp2 INTERFACE + "${CMAKE_CURRENT_BINARY_DIR}/includes" + "${CMAKE_CURRENT_SOURCE_DIR}/includes" + ) + + install(TARGETS nghttp2 + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") +endif() + +if(HAVE_CUNIT OR ENABLE_STATIC_LIB) + # Static library (for unittests because of symbol visibility) + add_library(nghttp2_static STATIC ${NGHTTP2_SOURCES}) + set_target_properties(nghttp2_static PROPERTIES + COMPILE_FLAGS "${WARNCFLAGS}" + VERSION ${LT_VERSION} SOVERSION ${LT_SOVERSION} + ARCHIVE_OUTPUT_NAME nghttp2${STATIC_LIB_SUFFIX} + ) + target_compile_definitions(nghttp2_static PUBLIC "-DNGHTTP2_STATICLIB") + if(ENABLE_STATIC_LIB) + install(TARGETS nghttp2_static + DESTINATION "${CMAKE_INSTALL_LIBDIR}") + endif() +endif() + + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libnghttp2.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") diff --git a/deps/nghttp2/lib/Makefile.am b/deps/nghttp2/lib/Makefile.am index 24a5bd62645054..63fa0fa8324d0a 100644 --- a/deps/nghttp2/lib/Makefile.am +++ b/deps/nghttp2/lib/Makefile.am @@ -49,7 +49,8 @@ OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \ nghttp2_mem.c \ nghttp2_http.c \ nghttp2_rcbuf.c \ - nghttp2_debug.c + nghttp2_debug.c \ + nghttp2_ksl.c HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \ nghttp2_frame.h \ @@ -65,7 +66,8 @@ HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \ nghttp2_mem.h \ nghttp2_http.h \ nghttp2_rcbuf.h \ - nghttp2_debug.h + nghttp2_debug.h \ + nghttp2_ksl.h libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS) libnghttp2_la_LDFLAGS = -no-undefined \ diff --git a/deps/nghttp2/lib/Makefile.in b/deps/nghttp2/lib/Makefile.in new file mode 100644 index 00000000000000..14ec74591ba3df --- /dev/null +++ b/deps/nghttp2/lib/Makefile.in @@ -0,0 +1,1028 @@ +# Makefile.in generated by automake 1.16.2 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = lib +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = libnghttp2.pc +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +libnghttp2_la_LIBADD = +am__objects_1 = +am__objects_2 = nghttp2_pq.lo nghttp2_map.lo nghttp2_queue.lo \ + nghttp2_frame.lo nghttp2_buf.lo nghttp2_stream.lo \ + nghttp2_outbound_item.lo nghttp2_session.lo nghttp2_submit.lo \ + nghttp2_helper.lo nghttp2_npn.lo nghttp2_hd.lo \ + nghttp2_hd_huffman.lo nghttp2_hd_huffman_data.lo \ + nghttp2_version.lo nghttp2_priority_spec.lo nghttp2_option.lo \ + nghttp2_callbacks.lo nghttp2_mem.lo nghttp2_http.lo \ + nghttp2_rcbuf.lo nghttp2_debug.lo nghttp2_ksl.lo +am_libnghttp2_la_OBJECTS = $(am__objects_1) $(am__objects_2) +libnghttp2_la_OBJECTS = $(am_libnghttp2_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libnghttp2_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libnghttp2_la_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/nghttp2_buf.Plo \ + ./$(DEPDIR)/nghttp2_callbacks.Plo \ + ./$(DEPDIR)/nghttp2_debug.Plo ./$(DEPDIR)/nghttp2_frame.Plo \ + ./$(DEPDIR)/nghttp2_hd.Plo ./$(DEPDIR)/nghttp2_hd_huffman.Plo \ + ./$(DEPDIR)/nghttp2_hd_huffman_data.Plo \ + ./$(DEPDIR)/nghttp2_helper.Plo ./$(DEPDIR)/nghttp2_http.Plo \ + ./$(DEPDIR)/nghttp2_ksl.Plo ./$(DEPDIR)/nghttp2_map.Plo \ + ./$(DEPDIR)/nghttp2_mem.Plo ./$(DEPDIR)/nghttp2_npn.Plo \ + ./$(DEPDIR)/nghttp2_option.Plo \ + ./$(DEPDIR)/nghttp2_outbound_item.Plo \ + ./$(DEPDIR)/nghttp2_pq.Plo \ + ./$(DEPDIR)/nghttp2_priority_spec.Plo \ + ./$(DEPDIR)/nghttp2_queue.Plo ./$(DEPDIR)/nghttp2_rcbuf.Plo \ + ./$(DEPDIR)/nghttp2_session.Plo ./$(DEPDIR)/nghttp2_stream.Plo \ + ./$(DEPDIR)/nghttp2_submit.Plo ./$(DEPDIR)/nghttp2_version.Plo +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libnghttp2_la_SOURCES) +DIST_SOURCES = $(libnghttp2_la_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +DATA = $(pkgconfig_DATA) +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/libnghttp2.pc.in \ + $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLDFLAGS = @APPLDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CUNIT_CFLAGS = @CUNIT_CFLAGS@ +CUNIT_LIBS = @CUNIT_LIBS@ +CXX = @CXX@ +CXX1XCXXFLAGS = @CXX1XCXXFLAGS@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYTHON = @CYTHON@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXTRACFLAG = @EXTRACFLAG@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX14 = @HAVE_CXX14@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBCARES_CFLAGS = @LIBCARES_CFLAGS@ +LIBCARES_LIBS = @LIBCARES_LIBS@ +LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ +LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@ +LIBMRUBY_LIBS = @LIBMRUBY_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ +LIBXML2_LIBS = @LIBXML2_LIBS@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_REVISION = @LT_REVISION@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@ +SYSTEMD_LIBS = @SYSTEMD_LIBS@ +TESTLDADD = @TESTLDADD@ +VERSION = @VERSION@ +WARNCFLAGS = @WARNCFLAGS@ +WARNCXXFLAGS = @WARNCXXFLAGS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, 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. +SUBDIRS = includes +EXTRA_DIST = Makefile.msvc CMakeLists.txt version.rc.in +AM_CFLAGS = $(WARNCFLAGS) $(EXTRACFLAG) +AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes -DBUILDING_NGHTTP2 \ + @DEFS@ + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libnghttp2.pc +DISTCLEANFILES = $(pkgconfig_DATA) +lib_LTLIBRARIES = libnghttp2.la +OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \ + nghttp2_frame.c \ + nghttp2_buf.c \ + nghttp2_stream.c nghttp2_outbound_item.c \ + nghttp2_session.c nghttp2_submit.c \ + nghttp2_helper.c \ + nghttp2_npn.c \ + nghttp2_hd.c nghttp2_hd_huffman.c nghttp2_hd_huffman_data.c \ + nghttp2_version.c \ + nghttp2_priority_spec.c \ + nghttp2_option.c \ + nghttp2_callbacks.c \ + nghttp2_mem.c \ + nghttp2_http.c \ + nghttp2_rcbuf.c \ + nghttp2_debug.c \ + nghttp2_ksl.c + +HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \ + nghttp2_frame.h \ + nghttp2_buf.h \ + nghttp2_session.h nghttp2_helper.h nghttp2_stream.h nghttp2_int.h \ + nghttp2_npn.h \ + nghttp2_submit.h nghttp2_outbound_item.h \ + nghttp2_net.h \ + nghttp2_hd.h nghttp2_hd_huffman.h \ + nghttp2_priority_spec.h \ + nghttp2_option.h \ + nghttp2_callbacks.h \ + nghttp2_mem.h \ + nghttp2_http.h \ + nghttp2_rcbuf.h \ + nghttp2_debug.h \ + nghttp2_ksl.h + +libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS) +libnghttp2_la_LDFLAGS = -no-undefined \ + -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu lib/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +libnghttp2.pc: $(top_builddir)/config.status $(srcdir)/libnghttp2.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libnghttp2.la: $(libnghttp2_la_OBJECTS) $(libnghttp2_la_DEPENDENCIES) $(EXTRA_libnghttp2_la_DEPENDENCIES) + $(AM_V_CCLD)$(libnghttp2_la_LINK) -rpath $(libdir) $(libnghttp2_la_OBJECTS) $(libnghttp2_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_buf.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_callbacks.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_debug.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_frame.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd_huffman.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd_huffman_data.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_helper.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_http.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_ksl.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_map.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_mem.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_npn.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_option.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_outbound_item.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_pq.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_priority_spec.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_queue.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_rcbuf.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_session.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_stream.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_submit.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_version.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkgconfigDATA: $(pkgconfig_DATA) + @$(NORMAL_INSTALL) + @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \ + done + +uninstall-pkgconfigDATA: + @$(NORMAL_UNINSTALL) + @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir) + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LTLIBRARIES) $(DATA) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/nghttp2_buf.Plo + -rm -f ./$(DEPDIR)/nghttp2_callbacks.Plo + -rm -f ./$(DEPDIR)/nghttp2_debug.Plo + -rm -f ./$(DEPDIR)/nghttp2_frame.Plo + -rm -f ./$(DEPDIR)/nghttp2_hd.Plo + -rm -f ./$(DEPDIR)/nghttp2_hd_huffman.Plo + -rm -f ./$(DEPDIR)/nghttp2_hd_huffman_data.Plo + -rm -f ./$(DEPDIR)/nghttp2_helper.Plo + -rm -f ./$(DEPDIR)/nghttp2_http.Plo + -rm -f ./$(DEPDIR)/nghttp2_ksl.Plo + -rm -f ./$(DEPDIR)/nghttp2_map.Plo + -rm -f ./$(DEPDIR)/nghttp2_mem.Plo + -rm -f ./$(DEPDIR)/nghttp2_npn.Plo + -rm -f ./$(DEPDIR)/nghttp2_option.Plo + -rm -f ./$(DEPDIR)/nghttp2_outbound_item.Plo + -rm -f ./$(DEPDIR)/nghttp2_pq.Plo + -rm -f ./$(DEPDIR)/nghttp2_priority_spec.Plo + -rm -f ./$(DEPDIR)/nghttp2_queue.Plo + -rm -f ./$(DEPDIR)/nghttp2_rcbuf.Plo + -rm -f ./$(DEPDIR)/nghttp2_session.Plo + -rm -f ./$(DEPDIR)/nghttp2_stream.Plo + -rm -f ./$(DEPDIR)/nghttp2_submit.Plo + -rm -f ./$(DEPDIR)/nghttp2_version.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: install-pkgconfigDATA + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f ./$(DEPDIR)/nghttp2_buf.Plo + -rm -f ./$(DEPDIR)/nghttp2_callbacks.Plo + -rm -f ./$(DEPDIR)/nghttp2_debug.Plo + -rm -f ./$(DEPDIR)/nghttp2_frame.Plo + -rm -f ./$(DEPDIR)/nghttp2_hd.Plo + -rm -f ./$(DEPDIR)/nghttp2_hd_huffman.Plo + -rm -f ./$(DEPDIR)/nghttp2_hd_huffman_data.Plo + -rm -f ./$(DEPDIR)/nghttp2_helper.Plo + -rm -f ./$(DEPDIR)/nghttp2_http.Plo + -rm -f ./$(DEPDIR)/nghttp2_ksl.Plo + -rm -f ./$(DEPDIR)/nghttp2_map.Plo + -rm -f ./$(DEPDIR)/nghttp2_mem.Plo + -rm -f ./$(DEPDIR)/nghttp2_npn.Plo + -rm -f ./$(DEPDIR)/nghttp2_option.Plo + -rm -f ./$(DEPDIR)/nghttp2_outbound_item.Plo + -rm -f ./$(DEPDIR)/nghttp2_pq.Plo + -rm -f ./$(DEPDIR)/nghttp2_priority_spec.Plo + -rm -f ./$(DEPDIR)/nghttp2_queue.Plo + -rm -f ./$(DEPDIR)/nghttp2_rcbuf.Plo + -rm -f ./$(DEPDIR)/nghttp2_session.Plo + -rm -f ./$(DEPDIR)/nghttp2_stream.Plo + -rm -f ./$(DEPDIR)/nghttp2_submit.Plo + -rm -f ./$(DEPDIR)/nghttp2_version.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-libLTLIBRARIES uninstall-pkgconfigDATA + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--depfiles check check-am clean clean-generic \ + clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-libLTLIBRARIES install-man install-pdf \ + install-pdf-am install-pkgconfigDATA install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-libLTLIBRARIES uninstall-pkgconfigDATA + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/deps/nghttp2/lib/includes/Makefile.in b/deps/nghttp2/lib/includes/Makefile.in new file mode 100644 index 00000000000000..e1948dfeaa3f1c --- /dev/null +++ b/deps/nghttp2/lib/includes/Makefile.in @@ -0,0 +1,658 @@ +# Makefile.in generated by automake 1.16.2 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, 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. + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = lib/includes +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(nobase_include_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(includedir)" +HEADERS = $(nobase_include_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLDFLAGS = @APPLDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CUNIT_CFLAGS = @CUNIT_CFLAGS@ +CUNIT_LIBS = @CUNIT_LIBS@ +CXX = @CXX@ +CXX1XCXXFLAGS = @CXX1XCXXFLAGS@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYTHON = @CYTHON@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXTRACFLAG = @EXTRACFLAG@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX14 = @HAVE_CXX14@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBCARES_CFLAGS = @LIBCARES_CFLAGS@ +LIBCARES_LIBS = @LIBCARES_LIBS@ +LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ +LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@ +LIBMRUBY_LIBS = @LIBMRUBY_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ +LIBXML2_LIBS = @LIBXML2_LIBS@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_REVISION = @LT_REVISION@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@ +SYSTEMD_LIBS = @SYSTEMD_LIBS@ +TESTLDADD = @TESTLDADD@ +VERSION = @VERSION@ +WARNCFLAGS = @WARNCFLAGS@ +WARNCXXFLAGS = @WARNCXXFLAGS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +EXTRA_DIST = CMakeLists.txt +nobase_include_HEADERS = nghttp2/nghttp2.h nghttp2/nghttp2ver.h +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu lib/includes/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/includes/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-nobase_includeHEADERS: $(nobase_include_HEADERS) + @$(NORMAL_INSTALL) + @list='$(nobase_include_HEADERS)'; test -n "$(includedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ + fi; \ + $(am__nobase_list) | while read dir files; do \ + xfiles=; for file in $$files; do \ + if test -f "$$file"; then xfiles="$$xfiles $$file"; \ + else xfiles="$$xfiles $(srcdir)/$$file"; fi; done; \ + test -z "$$xfiles" || { \ + test "x$$dir" = x. || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(includedir)/$$dir'"; \ + $(MKDIR_P) "$(DESTDIR)$(includedir)/$$dir"; }; \ + echo " $(INSTALL_HEADER) $$xfiles '$(DESTDIR)$(includedir)/$$dir'"; \ + $(INSTALL_HEADER) $$xfiles "$(DESTDIR)$(includedir)/$$dir" || exit $$?; }; \ + done + +uninstall-nobase_includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(nobase_include_HEADERS)'; test -n "$(includedir)" || list=; \ + $(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \ + dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(includedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-nobase_includeHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-nobase_includeHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool cscopelist-am ctags ctags-am distclean \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man \ + install-nobase_includeHEADERS install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-nobase_includeHEADERS + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/deps/nghttp2/lib/includes/nghttp2/nghttp2.h b/deps/nghttp2/lib/includes/nghttp2/nghttp2.h index 9be6eea5c02257..d9fae5bf0c138e 100644 --- a/deps/nghttp2/lib/includes/nghttp2/nghttp2.h +++ b/deps/nghttp2/lib/includes/nghttp2/nghttp2.h @@ -2851,9 +2851,11 @@ NGHTTP2_EXTERN void nghttp2_session_del(nghttp2_session *session); * * This function retrieves the highest prioritized frame from the * outbound queue and sends it to the remote peer. It does this as - * many as possible until the user callback + * many times as possible until the user callback * :type:`nghttp2_send_callback` returns - * :enum:`NGHTTP2_ERR_WOULDBLOCK` or the outbound queue becomes empty. + * :enum:`NGHTTP2_ERR_WOULDBLOCK`, the outbound queue becomes empty + * or flow control is triggered (remote window size becomes depleted + * or maximum number of concurrent streams is reached). * This function calls several callback functions which are passed * when initializing the |session|. Here is the simple time chart * which tells when each callback is invoked: diff --git a/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h b/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h index 795a44c1e86863..ff6bf3d3b22e4f 100644 --- a/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h +++ b/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h @@ -29,7 +29,7 @@ * @macro * Version number of the nghttp2 library release */ -#define NGHTTP2_VERSION "1.41.0" +#define NGHTTP2_VERSION "1.42.0" /** * @macro @@ -37,6 +37,6 @@ * release. This is a 24 bit number with 8 bits for major number, 8 bits * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203. */ -#define NGHTTP2_VERSION_NUM 0x012900 +#define NGHTTP2_VERSION_NUM 0x012a00 #endif /* NGHTTP2VER_H */ diff --git a/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h.in b/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h.in new file mode 100644 index 00000000000000..7717a647f75581 --- /dev/null +++ b/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h.in @@ -0,0 +1,42 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, 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. + */ +#ifndef NGHTTP2VER_H +#define NGHTTP2VER_H + +/** + * @macro + * Version number of the nghttp2 library release + */ +#define NGHTTP2_VERSION "@PACKAGE_VERSION@" + +/** + * @macro + * Numerical representation of the version number of the nghttp2 library + * release. This is a 24 bit number with 8 bits for major number, 8 bits + * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203. + */ +#define NGHTTP2_VERSION_NUM @PACKAGE_VERSION_NUM@ + +#endif /* NGHTTP2VER_H */ diff --git a/deps/nghttp2/lib/nghttp2_buf.c b/deps/nghttp2/lib/nghttp2_buf.c index 2a435bebf927bd..a32844712e4493 100644 --- a/deps/nghttp2/lib/nghttp2_buf.c +++ b/deps/nghttp2/lib/nghttp2_buf.c @@ -82,8 +82,10 @@ void nghttp2_buf_reset(nghttp2_buf *buf) { } void nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len) { - buf->begin = buf->pos = buf->last = buf->mark = begin; - buf->end = begin + len; + buf->begin = buf->pos = buf->last = buf->mark = buf->end = begin; + if (len) { + buf->end += len; + } } static int buf_chain_new(nghttp2_buf_chain **chain, size_t chunk_length, diff --git a/deps/nghttp2/lib/nghttp2_frame.c b/deps/nghttp2/lib/nghttp2_frame.c index 4821de40885736..382a26c818dd7b 100644 --- a/deps/nghttp2/lib/nghttp2_frame.c +++ b/deps/nghttp2/lib/nghttp2_frame.c @@ -818,8 +818,10 @@ int nghttp2_frame_unpack_origin_payload(nghttp2_extension *frame, size_t len = 0; origin = frame->payload; - p = payload; - end = p + payloadlen; + p = end = payload; + if (payloadlen) { + end += payloadlen; + } for (; p != end;) { if (end - p < 2) { @@ -897,9 +899,25 @@ nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv, } int nghttp2_nv_equal(const nghttp2_nv *a, const nghttp2_nv *b) { - return a->namelen == b->namelen && a->valuelen == b->valuelen && - memcmp(a->name, b->name, a->namelen) == 0 && - memcmp(a->value, b->value, a->valuelen) == 0; + if (a->namelen != b->namelen || a->valuelen != b->valuelen) { + return 0; + } + + if (a->name == NULL || b->name == NULL) { + assert(a->namelen == 0); + assert(b->namelen == 0); + } else if (memcmp(a->name, b->name, a->namelen) != 0) { + return 0; + } + + if (a->value == NULL || b->value == NULL) { + assert(a->valuelen == 0); + assert(b->valuelen == 0); + } else if (memcmp(a->value, b->value, a->valuelen) != 0) { + return 0; + } + + return 1; } void nghttp2_nv_array_del(nghttp2_nv *nva, nghttp2_mem *mem) { diff --git a/deps/nghttp2/lib/nghttp2_ksl.c b/deps/nghttp2/lib/nghttp2_ksl.c new file mode 100644 index 00000000000000..dffd1cbcffc484 --- /dev/null +++ b/deps/nghttp2/lib/nghttp2_ksl.c @@ -0,0 +1,707 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2020 nghttp2 contributors + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, 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. + */ +#include "nghttp2_ksl.h" + +#include +#include +#include +#include + +#include "nghttp2_mem.h" + +static size_t ksl_nodelen(size_t keylen) { + return (sizeof(nghttp2_ksl_node) + keylen - sizeof(uint64_t) + 0xf) & + (size_t)~0xf; +} + +static size_t ksl_blklen(size_t nodelen) { + return sizeof(nghttp2_ksl_blk) + nodelen * NGHTTP2_KSL_MAX_NBLK - + sizeof(uint64_t); +} + +/* + * ksl_node_set_key sets |key| to |node|. + */ +static void ksl_node_set_key(nghttp2_ksl *ksl, nghttp2_ksl_node *node, + const void *key) { + memcpy(node->key, key, ksl->keylen); +} + +int nghttp2_ksl_init(nghttp2_ksl *ksl, nghttp2_ksl_compar compar, size_t keylen, + nghttp2_mem *mem) { + size_t nodelen = ksl_nodelen(keylen); + size_t blklen = ksl_blklen(nodelen); + nghttp2_ksl_blk *head; + + ksl->head = nghttp2_mem_malloc(mem, blklen); + if (!ksl->head) { + return NGHTTP2_ERR_NOMEM; + } + ksl->front = ksl->back = ksl->head; + ksl->compar = compar; + ksl->keylen = keylen; + ksl->nodelen = nodelen; + ksl->n = 0; + ksl->mem = mem; + + head = ksl->head; + head->next = head->prev = NULL; + head->n = 0; + head->leaf = 1; + + return 0; +} + +/* + * ksl_free_blk frees |blk| recursively. + */ +static void ksl_free_blk(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk) { + size_t i; + + if (!blk->leaf) { + for (i = 0; i < blk->n; ++i) { + ksl_free_blk(ksl, nghttp2_ksl_nth_node(ksl, blk, i)->blk); + } + } + + nghttp2_mem_free(ksl->mem, blk); +} + +void nghttp2_ksl_free(nghttp2_ksl *ksl) { + if (!ksl) { + return; + } + + ksl_free_blk(ksl, ksl->head); +} + +/* + * ksl_split_blk splits |blk| into 2 nghttp2_ksl_blk objects. The new + * nghttp2_ksl_blk is always the "right" block. + * + * It returns the pointer to the nghttp2_ksl_blk created which is the + * located at the right of |blk|, or NULL which indicates out of + * memory error. + */ +static nghttp2_ksl_blk *ksl_split_blk(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk) { + nghttp2_ksl_blk *rblk; + + rblk = nghttp2_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen)); + if (rblk == NULL) { + return NULL; + } + + rblk->next = blk->next; + blk->next = rblk; + if (rblk->next) { + rblk->next->prev = rblk; + } else if (ksl->back == blk) { + ksl->back = rblk; + } + rblk->prev = blk; + rblk->leaf = blk->leaf; + + rblk->n = blk->n / 2; + + memcpy(rblk->nodes, blk->nodes + ksl->nodelen * (blk->n - rblk->n), + ksl->nodelen * rblk->n); + + blk->n -= rblk->n; + + assert(blk->n >= NGHTTP2_KSL_MIN_NBLK); + assert(rblk->n >= NGHTTP2_KSL_MIN_NBLK); + + return rblk; +} + +/* + * ksl_split_node splits a node included in |blk| at the position |i| + * into 2 adjacent nodes. The new node is always inserted at the + * position |i+1|. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +static int ksl_split_node(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk, size_t i) { + nghttp2_ksl_node *node; + nghttp2_ksl_blk *lblk = nghttp2_ksl_nth_node(ksl, blk, i)->blk, *rblk; + + rblk = ksl_split_blk(ksl, lblk); + if (rblk == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + memmove(blk->nodes + (i + 2) * ksl->nodelen, + blk->nodes + (i + 1) * ksl->nodelen, + ksl->nodelen * (blk->n - (i + 1))); + + node = nghttp2_ksl_nth_node(ksl, blk, i + 1); + node->blk = rblk; + ++blk->n; + ksl_node_set_key(ksl, node, + nghttp2_ksl_nth_node(ksl, rblk, rblk->n - 1)->key); + + node = nghttp2_ksl_nth_node(ksl, blk, i); + ksl_node_set_key(ksl, node, + nghttp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key); + + return 0; +} + +/* + * ksl_split_head splits a head (root) block. It increases the height + * of skip list by 1. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +static int ksl_split_head(nghttp2_ksl *ksl) { + nghttp2_ksl_blk *rblk = NULL, *lblk, *nhead = NULL; + nghttp2_ksl_node *node; + + rblk = ksl_split_blk(ksl, ksl->head); + if (rblk == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + lblk = ksl->head; + + nhead = nghttp2_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen)); + if (nhead == NULL) { + nghttp2_mem_free(ksl->mem, rblk); + return NGHTTP2_ERR_NOMEM; + } + nhead->next = nhead->prev = NULL; + nhead->n = 2; + nhead->leaf = 0; + + node = nghttp2_ksl_nth_node(ksl, nhead, 0); + ksl_node_set_key(ksl, node, + nghttp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key); + node->blk = lblk; + + node = nghttp2_ksl_nth_node(ksl, nhead, 1); + ksl_node_set_key(ksl, node, + nghttp2_ksl_nth_node(ksl, rblk, rblk->n - 1)->key); + node->blk = rblk; + + ksl->head = nhead; + + return 0; +} + +/* + * insert_node inserts a node whose key is |key| with the associated + * |data| at the index of |i|. This function assumes that the number + * of nodes contained by |blk| is strictly less than + * NGHTTP2_KSL_MAX_NBLK. + */ +static void ksl_insert_node(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk, size_t i, + const nghttp2_ksl_key *key, void *data) { + nghttp2_ksl_node *node; + + assert(blk->n < NGHTTP2_KSL_MAX_NBLK); + + memmove(blk->nodes + (i + 1) * ksl->nodelen, blk->nodes + i * ksl->nodelen, + ksl->nodelen * (blk->n - i)); + + node = nghttp2_ksl_nth_node(ksl, blk, i); + ksl_node_set_key(ksl, node, key); + node->data = data; + + ++blk->n; +} + +static size_t ksl_bsearch(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk, + const nghttp2_ksl_key *key, + nghttp2_ksl_compar compar) { + ssize_t left = -1, right = (ssize_t)blk->n, mid; + nghttp2_ksl_node *node; + + while (right - left > 1) { + mid = (left + right) / 2; + node = nghttp2_ksl_nth_node(ksl, blk, (size_t)mid); + if (compar((nghttp2_ksl_key *)node->key, key)) { + left = mid; + } else { + right = mid; + } + } + + return (size_t)right; +} + +int nghttp2_ksl_insert(nghttp2_ksl *ksl, nghttp2_ksl_it *it, + const nghttp2_ksl_key *key, void *data) { + nghttp2_ksl_blk *blk = ksl->head; + nghttp2_ksl_node *node; + size_t i; + int rv; + + if (blk->n == NGHTTP2_KSL_MAX_NBLK) { + rv = ksl_split_head(ksl); + if (rv != 0) { + return rv; + } + blk = ksl->head; + } + + for (;;) { + i = ksl_bsearch(ksl, blk, key, ksl->compar); + + if (blk->leaf) { + if (i < blk->n && + !ksl->compar(key, nghttp2_ksl_nth_node(ksl, blk, i)->key)) { + if (it) { + *it = nghttp2_ksl_end(ksl); + } + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + ksl_insert_node(ksl, blk, i, key, data); + ++ksl->n; + if (it) { + nghttp2_ksl_it_init(it, ksl, blk, i); + } + return 0; + } + + if (i == blk->n) { + /* This insertion extends the largest key in this subtree. */ + for (; !blk->leaf;) { + node = nghttp2_ksl_nth_node(ksl, blk, blk->n - 1); + if (node->blk->n == NGHTTP2_KSL_MAX_NBLK) { + rv = ksl_split_node(ksl, blk, blk->n - 1); + if (rv != 0) { + return rv; + } + node = nghttp2_ksl_nth_node(ksl, blk, blk->n - 1); + } + ksl_node_set_key(ksl, node, key); + blk = node->blk; + } + ksl_insert_node(ksl, blk, blk->n, key, data); + ++ksl->n; + if (it) { + nghttp2_ksl_it_init(it, ksl, blk, blk->n - 1); + } + return 0; + } + + node = nghttp2_ksl_nth_node(ksl, blk, i); + + if (node->blk->n == NGHTTP2_KSL_MAX_NBLK) { + rv = ksl_split_node(ksl, blk, i); + if (rv != 0) { + return rv; + } + if (ksl->compar((nghttp2_ksl_key *)node->key, key)) { + node = nghttp2_ksl_nth_node(ksl, blk, i + 1); + if (ksl->compar((nghttp2_ksl_key *)node->key, key)) { + ksl_node_set_key(ksl, node, key); + } + } + } + + blk = node->blk; + } +} + +/* + * ksl_remove_node removes the node included in |blk| at the index of + * |i|. + */ +static void ksl_remove_node(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk, size_t i) { + memmove(blk->nodes + i * ksl->nodelen, blk->nodes + (i + 1) * ksl->nodelen, + ksl->nodelen * (blk->n - (i + 1))); + + --blk->n; +} + +/* + * ksl_merge_node merges 2 nodes which are the nodes at the index of + * |i| and |i + 1|. + * + * If |blk| is the direct descendant of head (root) block and the head + * block contains just 2 nodes, the merged block becomes head block, + * which decreases the height of |ksl| by 1. + * + * This function returns the pointer to the merged block. + */ +static nghttp2_ksl_blk *ksl_merge_node(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk, + size_t i) { + nghttp2_ksl_blk *lblk, *rblk; + + assert(i + 1 < blk->n); + + lblk = nghttp2_ksl_nth_node(ksl, blk, i)->blk; + rblk = nghttp2_ksl_nth_node(ksl, blk, i + 1)->blk; + + assert(lblk->n + rblk->n < NGHTTP2_KSL_MAX_NBLK); + + memcpy(lblk->nodes + ksl->nodelen * lblk->n, rblk->nodes, + ksl->nodelen * rblk->n); + + lblk->n += rblk->n; + lblk->next = rblk->next; + if (lblk->next) { + lblk->next->prev = lblk; + } else if (ksl->back == rblk) { + ksl->back = lblk; + } + + nghttp2_mem_free(ksl->mem, rblk); + + if (ksl->head == blk && blk->n == 2) { + nghttp2_mem_free(ksl->mem, ksl->head); + ksl->head = lblk; + } else { + ksl_remove_node(ksl, blk, i + 1); + ksl_node_set_key(ksl, nghttp2_ksl_nth_node(ksl, blk, i), + nghttp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key); + } + + return lblk; +} + +/* + * ksl_shift_left moves the first node in blk->nodes[i]->blk->nodes to + * blk->nodes[i - 1]->blk->nodes. + */ +static void ksl_shift_left(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk, size_t i) { + nghttp2_ksl_node *lnode, *rnode, *dest, *src; + + assert(i > 0); + + lnode = nghttp2_ksl_nth_node(ksl, blk, i - 1); + rnode = nghttp2_ksl_nth_node(ksl, blk, i); + + assert(lnode->blk->n < NGHTTP2_KSL_MAX_NBLK); + assert(rnode->blk->n > NGHTTP2_KSL_MIN_NBLK); + + dest = nghttp2_ksl_nth_node(ksl, lnode->blk, lnode->blk->n); + src = nghttp2_ksl_nth_node(ksl, rnode->blk, 0); + + memcpy(dest, src, ksl->nodelen); + ksl_node_set_key(ksl, lnode, dest->key); + ++lnode->blk->n; + + --rnode->blk->n; + memmove(rnode->blk->nodes, rnode->blk->nodes + ksl->nodelen, + ksl->nodelen * rnode->blk->n); +} + +/* + * ksl_shift_right moves the last node in blk->nodes[i]->blk->nodes to + * blk->nodes[i + 1]->blk->nodes. + */ +static void ksl_shift_right(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk, size_t i) { + nghttp2_ksl_node *lnode, *rnode, *dest, *src; + + assert(i < blk->n - 1); + + lnode = nghttp2_ksl_nth_node(ksl, blk, i); + rnode = nghttp2_ksl_nth_node(ksl, blk, i + 1); + + assert(lnode->blk->n > NGHTTP2_KSL_MIN_NBLK); + assert(rnode->blk->n < NGHTTP2_KSL_MAX_NBLK); + + memmove(rnode->blk->nodes + ksl->nodelen, rnode->blk->nodes, + ksl->nodelen * rnode->blk->n); + ++rnode->blk->n; + + dest = nghttp2_ksl_nth_node(ksl, rnode->blk, 0); + src = nghttp2_ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1); + + memcpy(dest, src, ksl->nodelen); + + --lnode->blk->n; + ksl_node_set_key( + ksl, lnode, + nghttp2_ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1)->key); +} + +/* + * key_equal returns nonzero if |lhs| and |rhs| are equal using the + * function |compar|. + */ +static int key_equal(nghttp2_ksl_compar compar, const nghttp2_ksl_key *lhs, + const nghttp2_ksl_key *rhs) { + return !compar(lhs, rhs) && !compar(rhs, lhs); +} + +int nghttp2_ksl_remove(nghttp2_ksl *ksl, nghttp2_ksl_it *it, + const nghttp2_ksl_key *key) { + nghttp2_ksl_blk *blk = ksl->head; + nghttp2_ksl_node *node; + size_t i; + + if (!blk->leaf && blk->n == 2 && + nghttp2_ksl_nth_node(ksl, blk, 0)->blk->n == NGHTTP2_KSL_MIN_NBLK && + nghttp2_ksl_nth_node(ksl, blk, 1)->blk->n == NGHTTP2_KSL_MIN_NBLK) { + blk = ksl_merge_node(ksl, ksl->head, 0); + } + + for (;;) { + i = ksl_bsearch(ksl, blk, key, ksl->compar); + + if (i == blk->n) { + if (it) { + *it = nghttp2_ksl_end(ksl); + } + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (blk->leaf) { + if (ksl->compar(key, nghttp2_ksl_nth_node(ksl, blk, i)->key)) { + if (it) { + *it = nghttp2_ksl_end(ksl); + } + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + ksl_remove_node(ksl, blk, i); + --ksl->n; + if (it) { + if (blk->n == i && blk->next) { + nghttp2_ksl_it_init(it, ksl, blk->next, 0); + } else { + nghttp2_ksl_it_init(it, ksl, blk, i); + } + } + return 0; + } + + node = nghttp2_ksl_nth_node(ksl, blk, i); + + if (node->blk->n == NGHTTP2_KSL_MIN_NBLK) { + if (i > 0 && nghttp2_ksl_nth_node(ksl, blk, i - 1)->blk->n > + NGHTTP2_KSL_MIN_NBLK) { + ksl_shift_right(ksl, blk, i - 1); + blk = node->blk; + } else if (i + 1 < blk->n && + nghttp2_ksl_nth_node(ksl, blk, i + 1)->blk->n > + NGHTTP2_KSL_MIN_NBLK) { + ksl_shift_left(ksl, blk, i + 1); + blk = node->blk; + } else if (i > 0) { + blk = ksl_merge_node(ksl, blk, i - 1); + } else { + assert(i + 1 < blk->n); + blk = ksl_merge_node(ksl, blk, i); + } + } else { + blk = node->blk; + } + } +} + +nghttp2_ksl_it nghttp2_ksl_lower_bound(nghttp2_ksl *ksl, + const nghttp2_ksl_key *key) { + nghttp2_ksl_blk *blk = ksl->head; + nghttp2_ksl_it it; + size_t i; + + for (;;) { + i = ksl_bsearch(ksl, blk, key, ksl->compar); + + if (blk->leaf) { + if (i == blk->n && blk->next) { + blk = blk->next; + i = 0; + } + nghttp2_ksl_it_init(&it, ksl, blk, i); + return it; + } + + if (i == blk->n) { + /* This happens if descendant has smaller key. Fast forward to + find last node in this subtree. */ + for (; !blk->leaf; blk = nghttp2_ksl_nth_node(ksl, blk, blk->n - 1)->blk) + ; + if (blk->next) { + blk = blk->next; + i = 0; + } else { + i = blk->n; + } + nghttp2_ksl_it_init(&it, ksl, blk, i); + return it; + } + blk = nghttp2_ksl_nth_node(ksl, blk, i)->blk; + } +} + +nghttp2_ksl_it nghttp2_ksl_lower_bound_compar(nghttp2_ksl *ksl, + const nghttp2_ksl_key *key, + nghttp2_ksl_compar compar) { + nghttp2_ksl_blk *blk = ksl->head; + nghttp2_ksl_it it; + size_t i; + + for (;;) { + i = ksl_bsearch(ksl, blk, key, compar); + + if (blk->leaf) { + if (i == blk->n && blk->next) { + blk = blk->next; + i = 0; + } + nghttp2_ksl_it_init(&it, ksl, blk, i); + return it; + } + + if (i == blk->n) { + /* This happens if descendant has smaller key. Fast forward to + find last node in this subtree. */ + for (; !blk->leaf; blk = nghttp2_ksl_nth_node(ksl, blk, blk->n - 1)->blk) + ; + if (blk->next) { + blk = blk->next; + i = 0; + } else { + i = blk->n; + } + nghttp2_ksl_it_init(&it, ksl, blk, i); + return it; + } + blk = nghttp2_ksl_nth_node(ksl, blk, i)->blk; + } +} + +void nghttp2_ksl_update_key(nghttp2_ksl *ksl, const nghttp2_ksl_key *old_key, + const nghttp2_ksl_key *new_key) { + nghttp2_ksl_blk *blk = ksl->head; + nghttp2_ksl_node *node; + size_t i; + + for (;;) { + i = ksl_bsearch(ksl, blk, old_key, ksl->compar); + + assert(i < blk->n); + node = nghttp2_ksl_nth_node(ksl, blk, i); + + if (blk->leaf) { + assert(key_equal(ksl->compar, (nghttp2_ksl_key *)node->key, old_key)); + ksl_node_set_key(ksl, node, new_key); + return; + } + + if (key_equal(ksl->compar, (nghttp2_ksl_key *)node->key, old_key) || + ksl->compar((nghttp2_ksl_key *)node->key, new_key)) { + ksl_node_set_key(ksl, node, new_key); + } + + blk = node->blk; + } +} + +static void ksl_print(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk, size_t level) { + size_t i; + nghttp2_ksl_node *node; + + fprintf(stderr, "LV=%zu n=%zu\n", level, blk->n); + + if (blk->leaf) { + for (i = 0; i < blk->n; ++i) { + node = nghttp2_ksl_nth_node(ksl, blk, i); + fprintf(stderr, " %" PRId64, *(int64_t *)(void *)node->key); + } + fprintf(stderr, "\n"); + return; + } + + for (i = 0; i < blk->n; ++i) { + ksl_print(ksl, nghttp2_ksl_nth_node(ksl, blk, i)->blk, level + 1); + } +} + +size_t nghttp2_ksl_len(nghttp2_ksl *ksl) { return ksl->n; } + +void nghttp2_ksl_clear(nghttp2_ksl *ksl) { + size_t i; + nghttp2_ksl_blk *head; + + if (!ksl->head->leaf) { + for (i = 0; i < ksl->head->n; ++i) { + ksl_free_blk(ksl, nghttp2_ksl_nth_node(ksl, ksl->head, i)->blk); + } + } + + ksl->front = ksl->back = ksl->head; + ksl->n = 0; + + head = ksl->head; + + head->next = head->prev = NULL; + head->n = 0; + head->leaf = 1; +} + +void nghttp2_ksl_print(nghttp2_ksl *ksl) { ksl_print(ksl, ksl->head, 0); } + +nghttp2_ksl_it nghttp2_ksl_begin(const nghttp2_ksl *ksl) { + nghttp2_ksl_it it; + nghttp2_ksl_it_init(&it, ksl, ksl->front, 0); + return it; +} + +nghttp2_ksl_it nghttp2_ksl_end(const nghttp2_ksl *ksl) { + nghttp2_ksl_it it; + nghttp2_ksl_it_init(&it, ksl, ksl->back, ksl->back->n); + return it; +} + +void nghttp2_ksl_it_init(nghttp2_ksl_it *it, const nghttp2_ksl *ksl, + nghttp2_ksl_blk *blk, size_t i) { + it->ksl = ksl; + it->blk = blk; + it->i = i; +} + +void *nghttp2_ksl_it_get(const nghttp2_ksl_it *it) { + assert(it->i < it->blk->n); + return nghttp2_ksl_nth_node(it->ksl, it->blk, it->i)->data; +} + +void nghttp2_ksl_it_prev(nghttp2_ksl_it *it) { + assert(!nghttp2_ksl_it_begin(it)); + + if (it->i == 0) { + it->blk = it->blk->prev; + it->i = it->blk->n - 1; + } else { + --it->i; + } +} + +int nghttp2_ksl_it_begin(const nghttp2_ksl_it *it) { + return it->i == 0 && it->blk->prev == NULL; +} diff --git a/deps/nghttp2/lib/nghttp2_ksl.h b/deps/nghttp2/lib/nghttp2_ksl.h new file mode 100644 index 00000000000000..39d900ffbf5bd6 --- /dev/null +++ b/deps/nghttp2/lib/nghttp2_ksl.h @@ -0,0 +1,315 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2020 nghttp2 contributors + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, 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. + */ +#ifndef NGHTTP2_KSL_H +#define NGHTTP2_KSL_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include + +/* + * Skip List using single key instead of range. + */ + +#define NGHTTP2_KSL_DEGR 16 +/* NGHTTP2_KSL_MAX_NBLK is the maximum number of nodes which a single + block can contain. */ +#define NGHTTP2_KSL_MAX_NBLK (2 * NGHTTP2_KSL_DEGR - 1) +/* NGHTTP2_KSL_MIN_NBLK is the minimum number of nodes which a single + block other than root must contains. */ +#define NGHTTP2_KSL_MIN_NBLK (NGHTTP2_KSL_DEGR - 1) + +/* + * nghttp2_ksl_key represents key in nghttp2_ksl. + */ +typedef void nghttp2_ksl_key; + +struct nghttp2_ksl_node; +typedef struct nghttp2_ksl_node nghttp2_ksl_node; + +struct nghttp2_ksl_blk; +typedef struct nghttp2_ksl_blk nghttp2_ksl_blk; + +/* + * nghttp2_ksl_node is a node which contains either nghttp2_ksl_blk or + * opaque data. If a node is an internal node, it contains + * nghttp2_ksl_blk. Otherwise, it has data. The key is stored at the + * location starting at key. + */ +struct nghttp2_ksl_node { + union { + nghttp2_ksl_blk *blk; + void *data; + }; + union { + uint64_t align; + /* key is a buffer to include key associated to this node. + Because the length of key is unknown until nghttp2_ksl_init is + called, the actual buffer will be allocated after this + field. */ + uint8_t key[1]; + }; +}; + +/* + * nghttp2_ksl_blk contains nghttp2_ksl_node objects. + */ +struct nghttp2_ksl_blk { + /* next points to the next block if leaf field is nonzero. */ + nghttp2_ksl_blk *next; + /* prev points to the previous block if leaf field is nonzero. */ + nghttp2_ksl_blk *prev; + /* n is the number of nodes this object contains in nodes. */ + size_t n; + /* leaf is nonzero if this block contains leaf nodes. */ + int leaf; + union { + uint64_t align; + /* nodes is a buffer to contain NGHTTP2_KSL_MAX_NBLK + nghttp2_ksl_node objects. Because nghttp2_ksl_node object is + allocated along with the additional variable length key + storage, the size of buffer is unknown until nghttp2_ksl_init is + called. */ + uint8_t nodes[1]; + }; +}; + +/* + * nghttp2_ksl_compar is a function type which returns nonzero if key + * |lhs| should be placed before |rhs|. It returns 0 otherwise. + */ +typedef int (*nghttp2_ksl_compar)(const nghttp2_ksl_key *lhs, + const nghttp2_ksl_key *rhs); + +struct nghttp2_ksl; +typedef struct nghttp2_ksl nghttp2_ksl; + +struct nghttp2_ksl_it; +typedef struct nghttp2_ksl_it nghttp2_ksl_it; + +/* + * nghttp2_ksl_it is a forward iterator to iterate nodes. + */ +struct nghttp2_ksl_it { + const nghttp2_ksl *ksl; + nghttp2_ksl_blk *blk; + size_t i; +}; + +/* + * nghttp2_ksl is a deterministic paged skip list. + */ +struct nghttp2_ksl { + /* head points to the root block. */ + nghttp2_ksl_blk *head; + /* front points to the first leaf block. */ + nghttp2_ksl_blk *front; + /* back points to the last leaf block. */ + nghttp2_ksl_blk *back; + nghttp2_ksl_compar compar; + size_t n; + /* keylen is the size of key */ + size_t keylen; + /* nodelen is the actual size of nghttp2_ksl_node including key + storage. */ + size_t nodelen; + nghttp2_mem *mem; +}; + +/* + * nghttp2_ksl_init initializes |ksl|. |compar| specifies compare + * function. |keylen| is the length of key. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_ksl_init(nghttp2_ksl *ksl, nghttp2_ksl_compar compar, size_t keylen, + nghttp2_mem *mem); + +/* + * nghttp2_ksl_free frees resources allocated for |ksl|. If |ksl| is + * NULL, this function does nothing. It does not free the memory + * region pointed by |ksl| itself. + */ +void nghttp2_ksl_free(nghttp2_ksl *ksl); + +/* + * nghttp2_ksl_insert inserts |key| with its associated |data|. On + * successful insertion, the iterator points to the inserted node is + * stored in |*it|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_INVALID_ARGUMENT + * |key| already exists. + */ +int nghttp2_ksl_insert(nghttp2_ksl *ksl, nghttp2_ksl_it *it, + const nghttp2_ksl_key *key, void *data); + +/* + * nghttp2_ksl_remove removes the |key| from |ksl|. + * + * This function assigns the iterator to |*it|, which points to the + * node which is located at the right next of the removed node if |it| + * is not NULL. If |key| is not found, no deletion takes place and + * the return value of nghttp2_ksl_end(ksl) is assigned to |*it|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_INVALID_ARGUMENT + * |key| does not exist. + */ +int nghttp2_ksl_remove(nghttp2_ksl *ksl, nghttp2_ksl_it *it, + const nghttp2_ksl_key *key); + +/* + * nghttp2_ksl_lower_bound returns the iterator which points to the + * first node which has the key which is equal to |key| or the last + * node which satisfies !compar(&node->key, key). If there is no such + * node, it returns the iterator which satisfies nghttp2_ksl_it_end(it) + * != 0. + */ +nghttp2_ksl_it nghttp2_ksl_lower_bound(nghttp2_ksl *ksl, + const nghttp2_ksl_key *key); + +/* + * nghttp2_ksl_lower_bound_compar works like nghttp2_ksl_lower_bound, + * but it takes custom function |compar| to do lower bound search. + */ +nghttp2_ksl_it nghttp2_ksl_lower_bound_compar(nghttp2_ksl *ksl, + const nghttp2_ksl_key *key, + nghttp2_ksl_compar compar); + +/* + * nghttp2_ksl_update_key replaces the key of nodes which has |old_key| + * with |new_key|. |new_key| must be strictly greater than the + * previous node and strictly smaller than the next node. + */ +void nghttp2_ksl_update_key(nghttp2_ksl *ksl, const nghttp2_ksl_key *old_key, + const nghttp2_ksl_key *new_key); + +/* + * nghttp2_ksl_begin returns the iterator which points to the first + * node. If there is no node in |ksl|, it returns the iterator which + * satisfies nghttp2_ksl_it_end(it) != 0. + */ +nghttp2_ksl_it nghttp2_ksl_begin(const nghttp2_ksl *ksl); + +/* + * nghttp2_ksl_end returns the iterator which points to the node + * following the last node. The returned object satisfies + * nghttp2_ksl_it_end(). If there is no node in |ksl|, it returns the + * iterator which satisfies nghttp2_ksl_it_begin(it) != 0. + */ +nghttp2_ksl_it nghttp2_ksl_end(const nghttp2_ksl *ksl); + +/* + * nghttp2_ksl_len returns the number of elements stored in |ksl|. + */ +size_t nghttp2_ksl_len(nghttp2_ksl *ksl); + +/* + * nghttp2_ksl_clear removes all elements stored in |ksl|. + */ +void nghttp2_ksl_clear(nghttp2_ksl *ksl); + +/* + * nghttp2_ksl_nth_node returns the |n|th node under |blk|. + */ +#define nghttp2_ksl_nth_node(KSL, BLK, N) \ + ((nghttp2_ksl_node *)(void *)((BLK)->nodes + (KSL)->nodelen * (N))) + +/* + * nghttp2_ksl_print prints its internal state in stderr. It assumes + * that the key is of type int64_t. This function should be used for + * the debugging purpose only. + */ +void nghttp2_ksl_print(nghttp2_ksl *ksl); + +/* + * nghttp2_ksl_it_init initializes |it|. + */ +void nghttp2_ksl_it_init(nghttp2_ksl_it *it, const nghttp2_ksl *ksl, + nghttp2_ksl_blk *blk, size_t i); + +/* + * nghttp2_ksl_it_get returns the data associated to the node which + * |it| points to. It is undefined to call this function when + * nghttp2_ksl_it_end(it) returns nonzero. + */ +void *nghttp2_ksl_it_get(const nghttp2_ksl_it *it); + +/* + * nghttp2_ksl_it_next advances the iterator by one. It is undefined + * if this function is called when nghttp2_ksl_it_end(it) returns + * nonzero. + */ +#define nghttp2_ksl_it_next(IT) \ + (++(IT)->i == (IT)->blk->n && (IT)->blk->next \ + ? ((IT)->blk = (IT)->blk->next, (IT)->i = 0) \ + : 0) + +/* + * nghttp2_ksl_it_prev moves backward the iterator by one. It is + * undefined if this function is called when nghttp2_ksl_it_begin(it) + * returns nonzero. + */ +void nghttp2_ksl_it_prev(nghttp2_ksl_it *it); + +/* + * nghttp2_ksl_it_end returns nonzero if |it| points to the beyond the + * last node. + */ +#define nghttp2_ksl_it_end(IT) \ + ((IT)->blk->n == (IT)->i && (IT)->blk->next == NULL) + +/* + * nghttp2_ksl_it_begin returns nonzero if |it| points to the first + * node. |it| might satisfy both nghttp2_ksl_it_begin(&it) and + * nghttp2_ksl_it_end(&it) if the skip list has no node. + */ +int nghttp2_ksl_it_begin(const nghttp2_ksl_it *it); + +/* + * nghttp2_ksl_key returns the key of the node which |it| points to. + * It is undefined to call this function when nghttp2_ksl_it_end(it) + * returns nonzero. + */ +#define nghttp2_ksl_it_key(IT) \ + ((nghttp2_ksl_key *)nghttp2_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->key) + +#endif /* NGHTTP2_KSL_H */ diff --git a/deps/nghttp2/lib/nghttp2_map.c b/deps/nghttp2/lib/nghttp2_map.c index 4d9f97b47e2f54..d12a1f4757b725 100644 --- a/deps/nghttp2/lib/nghttp2_map.c +++ b/deps/nghttp2/lib/nghttp2_map.c @@ -1,7 +1,8 @@ /* * nghttp2 - HTTP/2 C Library * - * Copyright (c) 2012 Tatsuhiro Tsujikawa + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -25,6 +26,9 @@ #include "nghttp2_map.h" #include +#include + +#include "nghttp2_helper.h" #define INITIAL_TABLE_LENGTH 256 @@ -32,7 +36,7 @@ int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) { map->mem = mem; map->tablelen = INITIAL_TABLE_LENGTH; map->table = - nghttp2_mem_calloc(mem, map->tablelen, sizeof(nghttp2_map_entry *)); + nghttp2_mem_calloc(mem, map->tablelen, sizeof(nghttp2_map_bucket)); if (map->table == NULL) { return NGHTTP2_ERR_NOMEM; } @@ -43,6 +47,21 @@ int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) { } void nghttp2_map_free(nghttp2_map *map) { + size_t i; + nghttp2_map_bucket *bkt; + + if (!map) { + return; + } + + for (i = 0; i < map->tablelen; ++i) { + bkt = &map->table[i]; + if (bkt->ksl) { + nghttp2_ksl_free(bkt->ksl); + nghttp2_mem_free(map->mem, bkt->ksl); + } + } + nghttp2_mem_free(map->mem, map->table); } @@ -50,14 +69,29 @@ void nghttp2_map_each_free(nghttp2_map *map, int (*func)(nghttp2_map_entry *entry, void *ptr), void *ptr) { uint32_t i; + nghttp2_map_bucket *bkt; + nghttp2_ksl_it it; + for (i = 0; i < map->tablelen; ++i) { - nghttp2_map_entry *entry; - for (entry = map->table[i]; entry;) { - nghttp2_map_entry *next = entry->next; - func(entry, ptr); - entry = next; + bkt = &map->table[i]; + + if (bkt->ptr) { + func(bkt->ptr, ptr); + bkt->ptr = NULL; + assert(bkt->ksl == NULL || nghttp2_ksl_len(bkt->ksl) == 0); + continue; + } + + if (bkt->ksl) { + for (it = nghttp2_ksl_begin(bkt->ksl); !nghttp2_ksl_it_end(&it); + nghttp2_ksl_it_next(&it)) { + func(nghttp2_ksl_it_get(&it), ptr); + } + + nghttp2_ksl_free(bkt->ksl); + nghttp2_mem_free(map->mem, bkt->ksl); + bkt->ksl = NULL; } - map->table[i] = NULL; } } @@ -66,13 +100,29 @@ int nghttp2_map_each(nghttp2_map *map, void *ptr) { int rv; uint32_t i; + nghttp2_map_bucket *bkt; + nghttp2_ksl_it it; + for (i = 0; i < map->tablelen; ++i) { - nghttp2_map_entry *entry; - for (entry = map->table[i]; entry; entry = entry->next) { - rv = func(entry, ptr); + bkt = &map->table[i]; + + if (bkt->ptr) { + rv = func(bkt->ptr, ptr); if (rv != 0) { return rv; } + assert(bkt->ksl == NULL || nghttp2_ksl_len(bkt->ksl) == 0); + continue; + } + + if (bkt->ksl) { + for (it = nghttp2_ksl_begin(bkt->ksl); !nghttp2_ksl_it_end(&it); + nghttp2_ksl_it_next(&it)) { + rv = func(nghttp2_ksl_it_get(&it), ptr); + if (rv != 0) { + return rv; + } + } } } return 0; @@ -83,72 +133,133 @@ void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key) { entry->next = NULL; } -/* Same hash function in android HashMap source code. */ -/* The |mod| must be power of 2 */ -static uint32_t hash(int32_t key, uint32_t mod) { - uint32_t h = (uint32_t)key; - h ^= (h >> 20) ^ (h >> 12); - h ^= (h >> 7) ^ (h >> 4); +/* FNV1a hash */ +static uint32_t hash(key_type key, uint32_t mod) { + uint8_t *p, *end; + uint32_t h = 0x811C9DC5u; + + p = (uint8_t *)&key; + end = p + sizeof(key_type); + + for (; p != end;) { + h ^= *p++; + h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24); + } + return h & (mod - 1); } -static int insert(nghttp2_map_entry **table, uint32_t tablelen, - nghttp2_map_entry *entry) { +static int less(const nghttp2_ksl_key *lhs, const nghttp2_ksl_key *rhs) { + return *(key_type *)lhs < *(key_type *)rhs; +} + +static int map_insert(nghttp2_map *map, nghttp2_map_bucket *table, + uint32_t tablelen, nghttp2_map_entry *entry) { uint32_t h = hash(entry->key, tablelen); - if (table[h] == NULL) { - table[h] = entry; - } else { - nghttp2_map_entry *p; - /* We won't allow duplicated key, so check it out. */ - for (p = table[h]; p; p = p->next) { - if (p->key == entry->key) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } + nghttp2_map_bucket *bkt = &table[h]; + nghttp2_mem *mem = map->mem; + int rv; + + if (bkt->ptr == NULL && + (bkt->ksl == NULL || nghttp2_ksl_len(bkt->ksl) == 0)) { + bkt->ptr = entry; + return 0; + } + + if (!bkt->ksl) { + bkt->ksl = nghttp2_mem_malloc(mem, sizeof(*bkt->ksl)); + if (bkt->ksl == NULL) { + return NGHTTP2_ERR_NOMEM; } - entry->next = table[h]; - table[h] = entry; + nghttp2_ksl_init(bkt->ksl, less, sizeof(key_type), mem); } - return 0; + + if (bkt->ptr) { + rv = nghttp2_ksl_insert(bkt->ksl, NULL, &bkt->ptr->key, bkt->ptr); + if (rv != 0) { + return rv; + } + + bkt->ptr = NULL; + } + + return nghttp2_ksl_insert(bkt->ksl, NULL, &entry->key, entry); } /* new_tablelen must be power of 2 */ -static int resize(nghttp2_map *map, uint32_t new_tablelen) { +static int map_resize(nghttp2_map *map, uint32_t new_tablelen) { uint32_t i; - nghttp2_map_entry **new_table; + nghttp2_map_bucket *new_table; + nghttp2_map_bucket *bkt; + nghttp2_ksl_it it; + int rv; new_table = - nghttp2_mem_calloc(map->mem, new_tablelen, sizeof(nghttp2_map_entry *)); + nghttp2_mem_calloc(map->mem, new_tablelen, sizeof(nghttp2_map_bucket)); if (new_table == NULL) { return NGHTTP2_ERR_NOMEM; } for (i = 0; i < map->tablelen; ++i) { - nghttp2_map_entry *entry; - for (entry = map->table[i]; entry;) { - nghttp2_map_entry *next = entry->next; - entry->next = NULL; - /* This function must succeed */ - insert(new_table, new_tablelen, entry); - entry = next; + bkt = &map->table[i]; + + if (bkt->ptr) { + rv = map_insert(map, new_table, new_tablelen, bkt->ptr); + if (rv != 0) { + goto fail; + } + assert(bkt->ksl == NULL || nghttp2_ksl_len(bkt->ksl) == 0); + continue; + } + + if (bkt->ksl) { + for (it = nghttp2_ksl_begin(bkt->ksl); !nghttp2_ksl_it_end(&it); + nghttp2_ksl_it_next(&it)) { + rv = map_insert(map, new_table, new_tablelen, nghttp2_ksl_it_get(&it)); + if (rv != 0) { + goto fail; + } + } + } + } + + for (i = 0; i < map->tablelen; ++i) { + bkt = &map->table[i]; + if (bkt->ksl) { + nghttp2_ksl_free(bkt->ksl); + nghttp2_mem_free(map->mem, bkt->ksl); } } + nghttp2_mem_free(map->mem, map->table); map->tablelen = new_tablelen; map->table = new_table; return 0; + +fail: + for (i = 0; i < new_tablelen; ++i) { + bkt = &new_table[i]; + if (bkt->ksl) { + nghttp2_ksl_free(bkt->ksl); + nghttp2_mem_free(map->mem, bkt->ksl); + } + } + + return rv; } int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *new_entry) { int rv; + /* Load factor is 0.75 */ if ((map->size + 1) * 4 > map->tablelen * 3) { - rv = resize(map, map->tablelen * 2); + rv = map_resize(map, map->tablelen * 2); if (rv != 0) { return rv; } } - rv = insert(map->table, map->tablelen, new_entry); + rv = map_insert(map, map->table, map->tablelen, new_entry); if (rv != 0) { return rv; } @@ -157,33 +268,68 @@ int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *new_entry) { } nghttp2_map_entry *nghttp2_map_find(nghttp2_map *map, key_type key) { - uint32_t h; - nghttp2_map_entry *entry; - h = hash(key, map->tablelen); - for (entry = map->table[h]; entry; entry = entry->next) { - if (entry->key == key) { - return entry; + nghttp2_map_bucket *bkt = &map->table[hash(key, map->tablelen)]; + nghttp2_ksl_it it; + + if (bkt->ptr) { + if (bkt->ptr->key == key) { + return bkt->ptr; + } + return NULL; + } + + if (bkt->ksl) { + it = nghttp2_ksl_lower_bound(bkt->ksl, &key); + if (nghttp2_ksl_it_end(&it) || + *(key_type *)nghttp2_ksl_it_key(&it) != key) { + return NULL; } + return nghttp2_ksl_it_get(&it); } + return NULL; } int nghttp2_map_remove(nghttp2_map *map, key_type key) { - uint32_t h; - nghttp2_map_entry **dst; - - h = hash(key, map->tablelen); + nghttp2_map_bucket *bkt = &map->table[hash(key, map->tablelen)]; + int rv; - for (dst = &map->table[h]; *dst; dst = &(*dst)->next) { - if ((*dst)->key != key) { - continue; + if (bkt->ptr) { + if (bkt->ptr->key == key) { + bkt->ptr = NULL; + --map->size; + return 0; } + return NGHTTP2_ERR_INVALID_ARGUMENT; + } - *dst = (*dst)->next; + if (bkt->ksl) { + rv = nghttp2_ksl_remove(bkt->ksl, NULL, &key); + if (rv != 0) { + return rv; + } --map->size; return 0; } + return NGHTTP2_ERR_INVALID_ARGUMENT; } +void nghttp2_map_clear(nghttp2_map *map) { + uint32_t i; + nghttp2_map_bucket *bkt; + + for (i = 0; i < map->tablelen; ++i) { + bkt = &map->table[i]; + bkt->ptr = NULL; + if (bkt->ksl) { + nghttp2_ksl_free(bkt->ksl); + nghttp2_mem_free(map->mem, bkt->ksl); + bkt->ksl = NULL; + } + } + + map->size = 0; +} + size_t nghttp2_map_size(nghttp2_map *map) { return map->size; } diff --git a/deps/nghttp2/lib/nghttp2_map.h b/deps/nghttp2/lib/nghttp2_map.h index f6e29e35f2de3f..2cf59f698e35bb 100644 --- a/deps/nghttp2/lib/nghttp2_map.h +++ b/deps/nghttp2/lib/nghttp2_map.h @@ -1,7 +1,8 @@ /* * nghttp2 - HTTP/2 C Library * - * Copyright (c) 2012 Tatsuhiro Tsujikawa + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -30,8 +31,9 @@ #endif /* HAVE_CONFIG_H */ #include -#include "nghttp2_int.h" + #include "nghttp2_mem.h" +#include "nghttp2_ksl.h" /* Implementation of unordered map */ @@ -46,8 +48,13 @@ typedef struct nghttp2_map_entry { #endif } nghttp2_map_entry; +typedef struct nghttp2_map_bucket { + nghttp2_map_entry *ptr; + nghttp2_ksl *ksl; +} nghttp2_map_bucket; + typedef struct { - nghttp2_map_entry **table; + nghttp2_map_bucket *table; nghttp2_mem *mem; size_t size; uint32_t tablelen; @@ -118,6 +125,11 @@ nghttp2_map_entry *nghttp2_map_find(nghttp2_map *map, key_type key); */ int nghttp2_map_remove(nghttp2_map *map, key_type key); +/* + * Removes all entries from |map|. + */ +void nghttp2_map_clear(nghttp2_map *map); + /* * Returns the number of items stored in the map |map|. */ diff --git a/deps/nghttp2/lib/nghttp2_session.c b/deps/nghttp2/lib/nghttp2_session.c index 39f81f498cda79..2e7e907f25e199 100644 --- a/deps/nghttp2/lib/nghttp2_session.c +++ b/deps/nghttp2/lib/nghttp2_session.c @@ -959,6 +959,18 @@ int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id, return 0; } + /* Sending RST_STREAM to an idle stream is subject to protocol + violation. Historically, nghttp2 allows this. In order not to + disrupt the existing applications, we don't error out this case + and simply ignore it. */ + if (nghttp2_session_is_my_stream_id(session, stream_id)) { + if ((uint32_t)stream_id >= session->next_stream_id) { + return 0; + } + } else if (session->last_recv_stream_id < stream_id) { + return 0; + } + /* Cancel pending request HEADERS in ob_syn if this RST_STREAM refers to that stream. */ if (!session->server && nghttp2_session_is_my_stream_id(session, stream_id) && @@ -969,8 +981,7 @@ int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id, headers_frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame; assert(headers_frame->hd.type == NGHTTP2_HEADERS); - if (headers_frame->hd.stream_id <= stream_id && - (uint32_t)stream_id < session->next_stream_id) { + if (headers_frame->hd.stream_id <= stream_id) { for (item = session->ob_syn.head; item; item = item->qnext) { aux_data = &item->aux_data.headers; @@ -5353,9 +5364,11 @@ static ssize_t inbound_frame_effective_readlen(nghttp2_inbound_frame *iframe, return (ssize_t)(readlen); } +static const uint8_t static_in[] = {0}; + ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, size_t inlen) { - const uint8_t *first = in, *last = in + inlen; + const uint8_t *first, *last; nghttp2_inbound_frame *iframe = &session->iframe; size_t readlen; ssize_t padlen; @@ -5366,6 +5379,14 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, size_t pri_fieldlen; nghttp2_mem *mem; + if (in == NULL) { + assert(inlen == 0); + in = static_in; + } + + first = in; + last = in + inlen; + DEBUGF("recv: connection recv_window_size=%d, local_window=%d\n", session->recv_window_size, session->local_window_size); @@ -7448,8 +7469,8 @@ static int nghttp2_session_upgrade_internal(nghttp2_session *session, return NGHTTP2_ERR_INVALID_ARGUMENT; } /* SETTINGS frame contains too many settings */ - if (settings_payloadlen / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH - > session->max_settings) { + if (settings_payloadlen / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH > + session->max_settings) { return NGHTTP2_ERR_TOO_MANY_SETTINGS; } rv = nghttp2_frame_unpack_settings_payload2(&iv, &niv, settings_payload, diff --git a/deps/nghttp2/nghttp2.gyp b/deps/nghttp2/nghttp2.gyp index 0dcd034b8169da..21fd6ba5ecf46d 100644 --- a/deps/nghttp2/nghttp2.gyp +++ b/deps/nghttp2/nghttp2.gyp @@ -44,6 +44,7 @@ 'lib/nghttp2_hd_huffman_data.c', 'lib/nghttp2_helper.c', 'lib/nghttp2_http.c', + 'lib/nghttp2_ksl.c', 'lib/nghttp2_map.c', 'lib/nghttp2_mem.c', 'lib/nghttp2_npn.c', diff --git a/deps/npm/.travis.yml b/deps/npm/.travis.yml index 9fb0d51133692c..16165cd643da85 100644 --- a/deps/npm/.travis.yml +++ b/deps/npm/.travis.yml @@ -5,6 +5,7 @@ os: - windows node_js: + - 14 - 12 - 10 - 8 diff --git a/deps/npm/CHANGELOG.md b/deps/npm/CHANGELOG.md index ee1f7410f2b867..fc2934d6abd5c1 100644 --- a/deps/npm/CHANGELOG.md +++ b/deps/npm/CHANGELOG.md @@ -1,3 +1,12 @@ +## 6.14.13 (2021-04-08) + +### DEPENDENCIES + +* [`285ab3f65`](https://github.com/npm/cli/commit/285ab3f654882860246f729eb52e2c8c6a6d6e01) + `hosted-git-info@2.8.9` +* [`63b5c56c5`](https://github.com/npm/cli/commit/63b5c56c5203c8965c8ddeff28f2a65010b40b7c) + `ssri@6.0.2` + ## 6.14.12 (2021-03-25) ### DEPENDENCIES diff --git a/deps/npm/docs/public/cli-commands/npm-access/index.html b/deps/npm/docs/public/cli-commands/npm-access/index.html index 16cbf9e6daf1c8..c3fdda6d4582de 100644 --- a/deps/npm/docs/public/cli-commands/npm-access/index.html +++ b/deps/npm/docs/public/cli-commands/npm-access/index.html @@ -74,7 +74,7 @@ } } }) -

npm access

+

npm access

Set access level on published packages

Synopsis

npm access public [<package>]
@@ -148,4 +148,4 @@ 

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-adduser/index.html b/deps/npm/docs/public/cli-commands/npm-adduser/index.html index 88004562dbbc64..e1c45b50c87ed9 100644 --- a/deps/npm/docs/public/cli-commands/npm-adduser/index.html +++ b/deps/npm/docs/public/cli-commands/npm-adduser/index.html @@ -74,7 +74,7 @@ } } }) -

+

section: cli-commands title: npm-adduser description: Set access level on published packages

@@ -143,4 +143,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-audit/index.html b/deps/npm/docs/public/cli-commands/npm-audit/index.html index 8f69c3148f703c..bbb7365b861d5a 100644 --- a/deps/npm/docs/public/cli-commands/npm-audit/index.html +++ b/deps/npm/docs/public/cli-commands/npm-audit/index.html @@ -74,7 +74,7 @@ } } }) -

npm audit

+

npm audit

Run a security audit

Synopsis

npm audit [--json|--parseable|--audit-level=(low|moderate|high|critical)]
@@ -165,4 +165,4 @@ 

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-bin/index.html b/deps/npm/docs/public/cli-commands/npm-bin/index.html index 14bf501255602d..46d9ab4a1dcd7e 100644 --- a/deps/npm/docs/public/cli-commands/npm-bin/index.html +++ b/deps/npm/docs/public/cli-commands/npm-bin/index.html @@ -74,7 +74,7 @@ } } }) -

npm bin

+

npm bin

Display npm bin folder

Synopsis

npm bin [-g|--global]
@@ -94,4 +94,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-bugs/index.html b/deps/npm/docs/public/cli-commands/npm-bugs/index.html index 4b847d35e81eca..77b542fbec79b3 100644 --- a/deps/npm/docs/public/cli-commands/npm-bugs/index.html +++ b/deps/npm/docs/public/cli-commands/npm-bugs/index.html @@ -74,7 +74,7 @@ } } }) -

npm bugs

+

npm bugs

Bugs for a package in a web browser maybe

Synopsis

npm bugs [<pkgname>]
@@ -114,4 +114,4 @@ 

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-build/index.html b/deps/npm/docs/public/cli-commands/npm-build/index.html index 5a70a446864e07..66c7a2966d4ea0 100644 --- a/deps/npm/docs/public/cli-commands/npm-build/index.html +++ b/deps/npm/docs/public/cli-commands/npm-build/index.html @@ -74,7 +74,7 @@ } } }) -

npm build

+

npm build

Build a package

Synopsis

npm build [<package-folder>]
@@ -100,4 +100,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-bundle/index.html b/deps/npm/docs/public/cli-commands/npm-bundle/index.html index 2b0b4b8c248eaf..24c3e8adec48c4 100644 --- a/deps/npm/docs/public/cli-commands/npm-bundle/index.html +++ b/deps/npm/docs/public/cli-commands/npm-bundle/index.html @@ -74,7 +74,7 @@ } } }) -

npm bundle

+

npm bundle

REMOVED

Description

The npm bundle command has been removed in 1.0, for the simple reason @@ -91,4 +91,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-cache/index.html b/deps/npm/docs/public/cli-commands/npm-cache/index.html index 98d2e17fb90e5e..52f89546b17899 100644 --- a/deps/npm/docs/public/cli-commands/npm-cache/index.html +++ b/deps/npm/docs/public/cli-commands/npm-cache/index.html @@ -74,7 +74,7 @@ } } }) -

npm cache

+

npm cache

Manipulates packages cache

Synopsis

npm cache add <tarball file>
@@ -145,4 +145,4 @@ 

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-ci/index.html b/deps/npm/docs/public/cli-commands/npm-ci/index.html index 56fefe0db8cc99..f709366bbf4e1c 100644 --- a/deps/npm/docs/public/cli-commands/npm-ci/index.html +++ b/deps/npm/docs/public/cli-commands/npm-ci/index.html @@ -74,7 +74,7 @@ } } }) -

npm ci

+

npm ci

Install a project with a clean slate

Synopsis

npm ci
@@ -122,4 +122,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-completion/index.html b/deps/npm/docs/public/cli-commands/npm-completion/index.html index 9307fa56a645b1..454799f398cde8 100644 --- a/deps/npm/docs/public/cli-commands/npm-completion/index.html +++ b/deps/npm/docs/public/cli-commands/npm-completion/index.html @@ -74,7 +74,7 @@ } } }) -

npm completion

+

npm completion

Tab Completion for npm

Synopsis

source <(npm completion)
@@ -104,4 +104,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-config/index.html b/deps/npm/docs/public/cli-commands/npm-config/index.html index 523708e8fa7598..0c3cfe5706d465 100644 --- a/deps/npm/docs/public/cli-commands/npm-config/index.html +++ b/deps/npm/docs/public/cli-commands/npm-config/index.html @@ -74,7 +74,7 @@ } } }) -

npm config

+

npm config

Manage the npm configuration files

Synopsis

npm config set <key> <value> [-g|--global]
@@ -128,4 +128,4 @@ 

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-dedupe/index.html b/deps/npm/docs/public/cli-commands/npm-dedupe/index.html index b0552c8c980dd2..f217f1a6086006 100644 --- a/deps/npm/docs/public/cli-commands/npm-dedupe/index.html +++ b/deps/npm/docs/public/cli-commands/npm-dedupe/index.html @@ -74,7 +74,7 @@ } } }) -

npm dedupe

+

npm dedupe

Reduce duplication

Synopsis

npm dedupe
@@ -121,4 +121,4 @@ 

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-deprecate/index.html b/deps/npm/docs/public/cli-commands/npm-deprecate/index.html index e6f214afba4216..2ed7c56b493392 100644 --- a/deps/npm/docs/public/cli-commands/npm-deprecate/index.html +++ b/deps/npm/docs/public/cli-commands/npm-deprecate/index.html @@ -74,7 +74,7 @@ } } }) -

npm deprecate

+

npm deprecate

Deprecate a version of a package

Synopsis

npm deprecate <pkg>[@<version>] <message>
@@ -100,4 +100,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-dist-tag/index.html b/deps/npm/docs/public/cli-commands/npm-dist-tag/index.html index 5fb176fe0300ce..92949d90545559 100644 --- a/deps/npm/docs/public/cli-commands/npm-dist-tag/index.html +++ b/deps/npm/docs/public/cli-commands/npm-dist-tag/index.html @@ -74,7 +74,7 @@ } } }) -

+

section: cli-commands title: npm-dist-tag description: Modify package distribution tags

@@ -149,4 +149,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-docs/index.html b/deps/npm/docs/public/cli-commands/npm-docs/index.html index 656d040aad09fc..d9ad3ae081213c 100644 --- a/deps/npm/docs/public/cli-commands/npm-docs/index.html +++ b/deps/npm/docs/public/cli-commands/npm-docs/index.html @@ -74,7 +74,7 @@ } } }) -

npm docs

+

npm docs

Docs for a package in a web browser maybe

Synopsis

npm docs [<pkgname> [<pkgname> ...]]
@@ -115,4 +115,4 @@ 

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-doctor/index.html b/deps/npm/docs/public/cli-commands/npm-doctor/index.html index 861e3cf9fd3046..60accac3187f89 100644 --- a/deps/npm/docs/public/cli-commands/npm-doctor/index.html +++ b/deps/npm/docs/public/cli-commands/npm-doctor/index.html @@ -74,7 +74,7 @@ } } }) -

npm doctor

+

npm doctor

Check your environments

Synopsis

npm doctor
@@ -163,4 +163,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-edit/index.html b/deps/npm/docs/public/cli-commands/npm-edit/index.html index 6b01eef6755a7b..eb6a55d887206f 100644 --- a/deps/npm/docs/public/cli-commands/npm-edit/index.html +++ b/deps/npm/docs/public/cli-commands/npm-edit/index.html @@ -74,7 +74,7 @@ } } }) -

npm edit

+

npm edit

Edit an installed package

Synopsis

npm edit <pkg>[/<subpkg>...]
@@ -110,4 +110,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-explore/index.html b/deps/npm/docs/public/cli-commands/npm-explore/index.html index b8492cee1f8d78..20733949f29de6 100644 --- a/deps/npm/docs/public/cli-commands/npm-explore/index.html +++ b/deps/npm/docs/public/cli-commands/npm-explore/index.html @@ -74,7 +74,7 @@ } } }) -

+

section: cli-commands title: npm-explore description: Browse an installed package

@@ -114,4 +114,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-fund/index.html b/deps/npm/docs/public/cli-commands/npm-fund/index.html index 1fb763e15f411c..8f5942a05ddcef 100644 --- a/deps/npm/docs/public/cli-commands/npm-fund/index.html +++ b/deps/npm/docs/public/cli-commands/npm-fund/index.html @@ -74,7 +74,7 @@ } } }) -

npm fund

+

npm fund

Retrieve funding information

Synopsis

    npm fund [<pkg>]
@@ -128,4 +128,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-help-search/index.html b/deps/npm/docs/public/cli-commands/npm-help-search/index.html index 56cfc5fef00fc6..7111f96694d9b1 100644 --- a/deps/npm/docs/public/cli-commands/npm-help-search/index.html +++ b/deps/npm/docs/public/cli-commands/npm-help-search/index.html @@ -74,7 +74,7 @@ } } }) -

npm help-search

+

npm help-search

Search npm help documentation

Synopsis

npm help-search <text>
@@ -105,4 +105,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-help/index.html b/deps/npm/docs/public/cli-commands/npm-help/index.html index 64c4774351fa58..7799d34a9c2a65 100644 --- a/deps/npm/docs/public/cli-commands/npm-help/index.html +++ b/deps/npm/docs/public/cli-commands/npm-help/index.html @@ -74,7 +74,7 @@ } } }) -

npm help

+

npm help

Get help on npm

Synopsis

npm help <term> [<terms..>]
@@ -107,4 +107,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-hook/index.html b/deps/npm/docs/public/cli-commands/npm-hook/index.html index 949fbafa114dc9..368525482bada1 100644 --- a/deps/npm/docs/public/cli-commands/npm-hook/index.html +++ b/deps/npm/docs/public/cli-commands/npm-hook/index.html @@ -74,7 +74,7 @@ } } }) -

npm hook

+

npm hook

Manage registry hooks

Synopsis

npm hook ls [pkg]
@@ -119,4 +119,4 @@ 

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-init/index.html b/deps/npm/docs/public/cli-commands/npm-init/index.html index 790563ba6c57ca..bab1afc5176333 100644 --- a/deps/npm/docs/public/cli-commands/npm-init/index.html +++ b/deps/npm/docs/public/cli-commands/npm-init/index.html @@ -74,7 +74,7 @@ } } }) -

npm init

+

npm init

create a package.json file

Synopsis

npm init [--force|-f|--yes|-y|--scope]
@@ -126,4 +126,4 @@ 

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-install-ci-test/index.html b/deps/npm/docs/public/cli-commands/npm-install-ci-test/index.html index 054c86c5801505..4647260c6f01f6 100644 --- a/deps/npm/docs/public/cli-commands/npm-install-ci-test/index.html +++ b/deps/npm/docs/public/cli-commands/npm-install-ci-test/index.html @@ -74,7 +74,7 @@ } } }) -

npm install-ci-test

+

npm install-ci-test

Install a project with a clean slate and run tests

Synopsis

npm install-ci-test
@@ -93,4 +93,4 @@ 

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-install-test/index.html b/deps/npm/docs/public/cli-commands/npm-install-test/index.html index b006ab9488cdf5..da6b621a6b2190 100644 --- a/deps/npm/docs/public/cli-commands/npm-install-test/index.html +++ b/deps/npm/docs/public/cli-commands/npm-install-test/index.html @@ -74,7 +74,7 @@ } } }) -

npm install-test

+

npm install-test

Install package(s) and run tests

Synopsis

npm install-test (with no args, in package dir)
@@ -102,4 +102,4 @@ 

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-install/index.html b/deps/npm/docs/public/cli-commands/npm-install/index.html index f6749ce75b0776..9d5f3cd9583a7f 100644 --- a/deps/npm/docs/public/cli-commands/npm-install/index.html +++ b/deps/npm/docs/public/cli-commands/npm-install/index.html @@ -74,7 +74,7 @@ } } }) -

npm install

+

npm install

Install a package

Synopsis

npm install (with no args, in package dir)
@@ -468,4 +468,4 @@ 

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-link/index.html b/deps/npm/docs/public/cli-commands/npm-link/index.html index 5c6e89095720b4..d1c964802e63d6 100644 --- a/deps/npm/docs/public/cli-commands/npm-link/index.html +++ b/deps/npm/docs/public/cli-commands/npm-link/index.html @@ -74,7 +74,7 @@ } } }) -

npm link

+

npm link

Synopsis

npm link (in package dir)
@@ -134,4 +134,4 @@ 

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-logout/index.html b/deps/npm/docs/public/cli-commands/npm-logout/index.html index 23ba50b01fbf84..d6bee8ec101e40 100644 --- a/deps/npm/docs/public/cli-commands/npm-logout/index.html +++ b/deps/npm/docs/public/cli-commands/npm-logout/index.html @@ -74,7 +74,7 @@ } } }) -

npm logout

+

npm logout

Log out of the registry

Synopsis

npm logout [--registry=<url>] [--scope=<@scope>]
@@ -109,4 +109,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-ls/index.html b/deps/npm/docs/public/cli-commands/npm-ls/index.html index 2b4843eba80d71..9c168c7232931b 100644 --- a/deps/npm/docs/public/cli-commands/npm-ls/index.html +++ b/deps/npm/docs/public/cli-commands/npm-ls/index.html @@ -74,7 +74,7 @@ } } }) -

npm ls

+
\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-org/index.html b/deps/npm/docs/public/cli-commands/npm-org/index.html index a34a86a46f0904..6ea1f1bbe52a2c 100644 --- a/deps/npm/docs/public/cli-commands/npm-org/index.html +++ b/deps/npm/docs/public/cli-commands/npm-org/index.html @@ -74,7 +74,7 @@ } } }) -

npm org

+

npm org

Manage orgs

Synopsis

npm org set <orgname> <username> [developer | admin | owner]
@@ -107,4 +107,4 @@ 

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-outdated/index.html b/deps/npm/docs/public/cli-commands/npm-outdated/index.html index ac6418ae0a797f..0f5f2a2923faa8 100644 --- a/deps/npm/docs/public/cli-commands/npm-outdated/index.html +++ b/deps/npm/docs/public/cli-commands/npm-outdated/index.html @@ -74,7 +74,7 @@ } } }) -

npm outdated

+

npm outdated

Check for outdated packages

Synopsis

npm outdated [[<@scope>/]<pkg> ...]
@@ -178,4 +178,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-owner/index.html b/deps/npm/docs/public/cli-commands/npm-owner/index.html index dc6ae0d7ae172b..8f8abdec660d5e 100644 --- a/deps/npm/docs/public/cli-commands/npm-owner/index.html +++ b/deps/npm/docs/public/cli-commands/npm-owner/index.html @@ -74,7 +74,7 @@ } } }) -

npm owner

+

npm owner

Manage package owners

Synopsis

npm owner add <user> [<@scope>/]<pkg>
@@ -114,4 +114,4 @@ 

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-pack/index.html b/deps/npm/docs/public/cli-commands/npm-pack/index.html index f0063827000d05..e449cd022345a0 100644 --- a/deps/npm/docs/public/cli-commands/npm-pack/index.html +++ b/deps/npm/docs/public/cli-commands/npm-pack/index.html @@ -74,7 +74,7 @@ } } }) -

npm pack

+

npm pack

Create a tarball from a package

Synopsis

npm pack [[<@scope>/]<pkg>...] [--dry-run]
@@ -102,4 +102,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-ping/index.html b/deps/npm/docs/public/cli-commands/npm-ping/index.html index 031e4ef47c9664..858969ac62dc63 100644 --- a/deps/npm/docs/public/cli-commands/npm-ping/index.html +++ b/deps/npm/docs/public/cli-commands/npm-ping/index.html @@ -74,7 +74,7 @@ } } }) -

npm ping

+

npm ping

Ping npm registry

Synopsis

npm ping [--registry <registry>]
@@ -95,4 +95,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-prefix/index.html b/deps/npm/docs/public/cli-commands/npm-prefix/index.html index 53f571111e5ff5..07e9442b7a3fc5 100644 --- a/deps/npm/docs/public/cli-commands/npm-prefix/index.html +++ b/deps/npm/docs/public/cli-commands/npm-prefix/index.html @@ -74,7 +74,7 @@ } } }) -

npm prefix

+

npm prefix

Display prefix

Synopsis

npm prefix [-g]
@@ -98,4 +98,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-profile/index.html b/deps/npm/docs/public/cli-commands/npm-profile/index.html index 6e6fdd273d35c4..2c36ad083382b9 100644 --- a/deps/npm/docs/public/cli-commands/npm-profile/index.html +++ b/deps/npm/docs/public/cli-commands/npm-profile/index.html @@ -74,7 +74,7 @@ } } }) -

npm profile

+

npm profile

Change settings on your registry profile

Synopsis

npm profile get [--json|--parseable] [<property>]
@@ -148,4 +148,4 @@ 

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-prune/index.html b/deps/npm/docs/public/cli-commands/npm-prune/index.html index 9a180aba248dce..7a0f48ef14899b 100644 --- a/deps/npm/docs/public/cli-commands/npm-prune/index.html +++ b/deps/npm/docs/public/cli-commands/npm-prune/index.html @@ -74,7 +74,7 @@ } } }) -

npm prune

+

npm prune

Remove extraneous packages

Synopsis

npm prune [[<@scope>/]<pkg>...] [--production] [--dry-run] [--json]
@@ -108,4 +108,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-publish/index.html b/deps/npm/docs/public/cli-commands/npm-publish/index.html index ed3e6f8b7e500d..18c74758311645 100644 --- a/deps/npm/docs/public/cli-commands/npm-publish/index.html +++ b/deps/npm/docs/public/cli-commands/npm-publish/index.html @@ -74,7 +74,7 @@ } } }) -

npm publish

+

npm publish

Publish a package

Synopsis

npm publish [<tarball>|<folder>] [--tag <tag>] [--access <public|restricted>] [--otp otpcode] [--dry-run]
@@ -140,4 +140,4 @@ 

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-rebuild/index.html b/deps/npm/docs/public/cli-commands/npm-rebuild/index.html index 1f644d8d2c01b8..c145200bb6b4fc 100644 --- a/deps/npm/docs/public/cli-commands/npm-rebuild/index.html +++ b/deps/npm/docs/public/cli-commands/npm-rebuild/index.html @@ -74,7 +74,7 @@ } } }) -

npm rebuild

+

npm rebuild

Rebuild a package

Synopsis

npm rebuild [[<@scope>/<name>]...]
@@ -93,4 +93,4 @@ 

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-repo/index.html b/deps/npm/docs/public/cli-commands/npm-repo/index.html index 7252105f28d7b4..6d241dc8406eb3 100644 --- a/deps/npm/docs/public/cli-commands/npm-repo/index.html +++ b/deps/npm/docs/public/cli-commands/npm-repo/index.html @@ -74,7 +74,7 @@ } } }) -

npm repo

+

npm repo

Open package repository page in the browser

Synopsis

npm repo [<pkg>]
@@ -101,4 +101,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-restart/index.html b/deps/npm/docs/public/cli-commands/npm-restart/index.html index 3901385a9a4daa..478c33ee4d64cf 100644 --- a/deps/npm/docs/public/cli-commands/npm-restart/index.html +++ b/deps/npm/docs/public/cli-commands/npm-restart/index.html @@ -74,7 +74,7 @@ } } }) -

npm restart

+

npm restart

Restart a package

Synopsis

npm restart [-- <args>]
@@ -113,4 +113,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-root/index.html b/deps/npm/docs/public/cli-commands/npm-root/index.html index bf843de2829cb7..6f6243e6ea3361 100644 --- a/deps/npm/docs/public/cli-commands/npm-root/index.html +++ b/deps/npm/docs/public/cli-commands/npm-root/index.html @@ -74,7 +74,7 @@ } } }) -

npm root

+

npm root

Display npm root

Synopsis

npm root [-g]
@@ -94,4 +94,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-run-script/index.html b/deps/npm/docs/public/cli-commands/npm-run-script/index.html index 8b2341899f9d3d..76ce2f73fe2bea 100644 --- a/deps/npm/docs/public/cli-commands/npm-run-script/index.html +++ b/deps/npm/docs/public/cli-commands/npm-run-script/index.html @@ -74,7 +74,7 @@ } } }) -

npm run-script

+

npm run-script

Run arbitrary package scripts

Synopsis

npm run-script <command> [--silent] [-- <args>...]
@@ -143,4 +143,4 @@ 

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-search/index.html b/deps/npm/docs/public/cli-commands/npm-search/index.html index 841520f6706c28..9aebe632a1eeac 100644 --- a/deps/npm/docs/public/cli-commands/npm-search/index.html +++ b/deps/npm/docs/public/cli-commands/npm-search/index.html @@ -74,7 +74,7 @@ } } }) -

npm search

+

npm search

Search for packages

Synopsis

npm search [-l|--long] [--json] [--parseable] [--no-description] [search terms ...]
@@ -168,4 +168,4 @@ 

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-shrinkwrap/index.html b/deps/npm/docs/public/cli-commands/npm-shrinkwrap/index.html index b62309b09c72b6..f808ef3f5787f5 100644 --- a/deps/npm/docs/public/cli-commands/npm-shrinkwrap/index.html +++ b/deps/npm/docs/public/cli-commands/npm-shrinkwrap/index.html @@ -74,7 +74,7 @@ } } }) -

npm shrinkwrap

+

npm shrinkwrap

Lock down dependency versions for publication

Synopsis

npm shrinkwrap
@@ -101,4 +101,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-star/index.html b/deps/npm/docs/public/cli-commands/npm-star/index.html index 98a6127a8b3182..91a5dd5b33414e 100644 --- a/deps/npm/docs/public/cli-commands/npm-star/index.html +++ b/deps/npm/docs/public/cli-commands/npm-star/index.html @@ -74,7 +74,7 @@ } } }) -

npm star

+

npm star

Mark your favorite packages

Synopsis

npm star [<pkg>...]
@@ -96,4 +96,4 @@ 

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-stars/index.html b/deps/npm/docs/public/cli-commands/npm-stars/index.html index d6c2a2225cbd90..94d3f1f2e7305c 100644 --- a/deps/npm/docs/public/cli-commands/npm-stars/index.html +++ b/deps/npm/docs/public/cli-commands/npm-stars/index.html @@ -74,7 +74,7 @@ } } }) -

npm stars

+

npm stars

View packages marked as favorites

Synopsis

npm stars [<user>]
@@ -96,4 +96,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-start/index.html b/deps/npm/docs/public/cli-commands/npm-start/index.html index 5cb9f446cf6d9d..074e259471d607 100644 --- a/deps/npm/docs/public/cli-commands/npm-start/index.html +++ b/deps/npm/docs/public/cli-commands/npm-start/index.html @@ -74,7 +74,7 @@ } } }) -

npm start

+

npm start

Start a package

Synopsis

npm start [-- <args>]
@@ -98,4 +98,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-stop/index.html b/deps/npm/docs/public/cli-commands/npm-stop/index.html index f6f2e5cd7ecdf4..198d75c0734175 100644 --- a/deps/npm/docs/public/cli-commands/npm-stop/index.html +++ b/deps/npm/docs/public/cli-commands/npm-stop/index.html @@ -74,7 +74,7 @@ } } }) -

npm stop

+

npm stop

Stop a package

Synopsis

npm stop [-- <args>]
@@ -94,4 +94,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-team/index.html b/deps/npm/docs/public/cli-commands/npm-team/index.html index 2b4f69a37f5c7b..9358b34e13ffd1 100644 --- a/deps/npm/docs/public/cli-commands/npm-team/index.html +++ b/deps/npm/docs/public/cli-commands/npm-team/index.html @@ -74,7 +74,7 @@ } } }) -

npm team

+

npm team

Manage organization teams and team memberships

Synopsis

npm team create <scope:team>
@@ -125,4 +125,4 @@ 

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-test/index.html b/deps/npm/docs/public/cli-commands/npm-test/index.html index 383e87cc6ad966..cba112ee4b2880 100644 --- a/deps/npm/docs/public/cli-commands/npm-test/index.html +++ b/deps/npm/docs/public/cli-commands/npm-test/index.html @@ -74,7 +74,7 @@ } } }) -

npm test

+

npm test

Test a package

Synopsis

npm test [-- <args>]
@@ -96,4 +96,4 @@ 

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-token/index.html b/deps/npm/docs/public/cli-commands/npm-token/index.html index 24985fbae9a5f5..bcf7c8524c6770 100644 --- a/deps/npm/docs/public/cli-commands/npm-token/index.html +++ b/deps/npm/docs/public/cli-commands/npm-token/index.html @@ -74,7 +74,7 @@ } } }) -

npm token

+

npm token

Manage your authentication tokens

Synopsis

  npm token list [--json|--parseable]
@@ -133,4 +133,4 @@ 

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-uninstall/index.html b/deps/npm/docs/public/cli-commands/npm-uninstall/index.html index 72406836e5d638..130526718c437c 100644 --- a/deps/npm/docs/public/cli-commands/npm-uninstall/index.html +++ b/deps/npm/docs/public/cli-commands/npm-uninstall/index.html @@ -74,7 +74,7 @@ } } }) -

npm uninstall

+

npm uninstall

Remove a package

Synopsis

npm uninstall [<@scope>/]<pkg>[@<version>]... [-S|--save|-D|--save-dev|-O|--save-optional|--no-save]
@@ -118,4 +118,4 @@ 

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-unpublish/index.html b/deps/npm/docs/public/cli-commands/npm-unpublish/index.html index 7ed977e59bc686..1528eb93179946 100644 --- a/deps/npm/docs/public/cli-commands/npm-unpublish/index.html +++ b/deps/npm/docs/public/cli-commands/npm-unpublish/index.html @@ -74,7 +74,7 @@ } } }) -

npm unpublish

+

npm unpublish

Remove a package from the registry

Synopsis

Unpublishing a single version of a package

@@ -106,4 +106,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-update/index.html b/deps/npm/docs/public/cli-commands/npm-update/index.html index e35894709eeb89..f8af25c3a34387 100644 --- a/deps/npm/docs/public/cli-commands/npm-update/index.html +++ b/deps/npm/docs/public/cli-commands/npm-update/index.html @@ -74,7 +74,7 @@ } } }) -

npm update

+

npm update

Update a package

Synopsis

npm update [-g] [<pkg>...]
@@ -167,4 +167,4 @@ 

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-version/index.html b/deps/npm/docs/public/cli-commands/npm-version/index.html index 2c26fa06697509..ece85b0026c103 100644 --- a/deps/npm/docs/public/cli-commands/npm-version/index.html +++ b/deps/npm/docs/public/cli-commands/npm-version/index.html @@ -74,7 +74,7 @@ } } }) -

npm version

+

npm version

Bump a package version

Synopsis

npm version [<newversion> | major | minor | patch | premajor | preminor | prepatch | prerelease [--preid=<prerelease-id>] | from-git]
@@ -180,4 +180,4 @@ 

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-view/index.html b/deps/npm/docs/public/cli-commands/npm-view/index.html index d6e894d34ad729..0de3004f71ca93 100644 --- a/deps/npm/docs/public/cli-commands/npm-view/index.html +++ b/deps/npm/docs/public/cli-commands/npm-view/index.html @@ -74,7 +74,7 @@ } } }) -

npm view

+

npm view

View registry info

Synopsis

npm view [<@scope>/]<name>[@<version>] [<field>[.<subfield>]...]
@@ -145,4 +145,4 @@ 

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm-whoami/index.html b/deps/npm/docs/public/cli-commands/npm-whoami/index.html index bde541ce6be896..2d760fcd58add7 100644 --- a/deps/npm/docs/public/cli-commands/npm-whoami/index.html +++ b/deps/npm/docs/public/cli-commands/npm-whoami/index.html @@ -74,7 +74,7 @@ } } }) -

npm whoami

+

npm whoami

Display npm username

Synopsis

npm whoami [--registry <registry>]
@@ -92,4 +92,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/cli-commands/npm/index.html b/deps/npm/docs/public/cli-commands/npm/index.html index 8539f9f3d08174..f22b64bc7e2e8c 100644 --- a/deps/npm/docs/public/cli-commands/npm/index.html +++ b/deps/npm/docs/public/cli-commands/npm/index.html @@ -74,12 +74,12 @@ } } }) -

npm

+

npm

javascript package manager

Synopsis

npm <command> [args]

Version

-

6.14.12

+

6.14.13

Description

npm is the package manager for the Node JavaScript platform. It puts modules in place so that node can find them, and manages dependency @@ -210,4 +210,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/configuring-npm/folders/index.html b/deps/npm/docs/public/configuring-npm/folders/index.html index 671afbc258f602..ba7f8824b00d48 100644 --- a/deps/npm/docs/public/configuring-npm/folders/index.html +++ b/deps/npm/docs/public/configuring-npm/folders/index.html @@ -74,7 +74,7 @@ } } }) -

folders

+

folders

Folder Structures Used by npm

Description

npm puts various things on your computer. That's its job.

@@ -240,4 +240,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/configuring-npm/install/index.html b/deps/npm/docs/public/configuring-npm/install/index.html index 4845c8e665c1b8..254b2ebfa95d51 100644 --- a/deps/npm/docs/public/configuring-npm/install/index.html +++ b/deps/npm/docs/public/configuring-npm/install/index.html @@ -74,7 +74,7 @@ } } }) -

install

+

install

Download and Install npm

Description

To publish and install packages to and from the public npm registry, you must install Node.js and the npm command line interface using either a Node version manager or a Node installer. We strongly recommend using a Node version manager to install Node.js and npm. We do not recommend using a Node installer, since the Node installation process installs npm in a directory with local permissions and can cause permissions errors when you run npm packages globally.

@@ -123,4 +123,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/configuring-npm/npmrc/index.html b/deps/npm/docs/public/configuring-npm/npmrc/index.html index d5453a238de306..a88518836cb20d 100644 --- a/deps/npm/docs/public/configuring-npm/npmrc/index.html +++ b/deps/npm/docs/public/configuring-npm/npmrc/index.html @@ -74,7 +74,7 @@ } } }) -

npmrc

+

npmrc

The npm config files

Description

npm gets its config settings from the command line, environment @@ -145,4 +145,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/configuring-npm/package-json/index.html b/deps/npm/docs/public/configuring-npm/package-json/index.html index fb39106fbb02a0..b4ce8d8ecd6ca3 100644 --- a/deps/npm/docs/public/configuring-npm/package-json/index.html +++ b/deps/npm/docs/public/configuring-npm/package-json/index.html @@ -74,7 +74,7 @@ } } }) -

package.json

+

package.json

Specifics of npm's package.json handling

Description

This document is all you need to know about what's required in your package.json @@ -711,4 +711,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/configuring-npm/package-lock-json/index.html b/deps/npm/docs/public/configuring-npm/package-lock-json/index.html index bbe38a7b7223c9..236b4204d31541 100644 --- a/deps/npm/docs/public/configuring-npm/package-lock-json/index.html +++ b/deps/npm/docs/public/configuring-npm/package-lock-json/index.html @@ -74,7 +74,7 @@ } } }) -

package-lock.json

+

package-lock.json

A manifestation of the manifest

Description

package-lock.json is automatically generated for any operations where npm @@ -186,4 +186,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/configuring-npm/package-locks/index.html b/deps/npm/docs/public/configuring-npm/package-locks/index.html index b35808ed6b1dd9..9868c85d21dfd4 100644 --- a/deps/npm/docs/public/configuring-npm/package-locks/index.html +++ b/deps/npm/docs/public/configuring-npm/package-locks/index.html @@ -74,7 +74,7 @@ } } }) -

package-locks

+

package-locks

An explanation of npm lockfiles

Description

Conceptually, the "input" to npm install is a package.json, while its @@ -214,4 +214,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/configuring-npm/shrinkwrap-json/index.html b/deps/npm/docs/public/configuring-npm/shrinkwrap-json/index.html index 7fe1537066c9ea..3a7635b505c3e9 100644 --- a/deps/npm/docs/public/configuring-npm/shrinkwrap-json/index.html +++ b/deps/npm/docs/public/configuring-npm/shrinkwrap-json/index.html @@ -74,7 +74,7 @@ } } }) -

npm shrinkwrap.json

+

npm shrinkwrap.json

A publishable lockfile

Description

npm-shrinkwrap.json is a file created by npm shrinkwrap. It is identical to @@ -102,4 +102,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/index.html b/deps/npm/docs/public/index.html index f9ce682312423a..a5537316e3f21c 100644 --- a/deps/npm/docs/public/index.html +++ b/deps/npm/docs/public/index.html @@ -128,4 +128,4 @@ } } }) -
npm cli _
The intelligent package manager for the Node Javascript Platform. Install stuff and get coding!
npm cli _
The intelligent package manager for the Node Javascript Platform. Install stuff and get coding!
npm cli _
The intelligent package manager for the Node Javascript Platform. Install stuff and get coding!

The current stable version of npm is available on GitHub.

To upgrade, run: npm install npm@latest -g

\ No newline at end of file +
npm cli _
The intelligent package manager for the Node Javascript Platform. Install stuff and get coding!
npm cli _
The intelligent package manager for the Node Javascript Platform. Install stuff and get coding!
npm cli _
The intelligent package manager for the Node Javascript Platform. Install stuff and get coding!

The current stable version of npm is available on GitHub.

To upgrade, run: npm install npm@latest -g

\ No newline at end of file diff --git a/deps/npm/docs/public/using-npm/config/index.html b/deps/npm/docs/public/using-npm/config/index.html index 36e47c60edcea1..3a43f4e02eac7d 100644 --- a/deps/npm/docs/public/using-npm/config/index.html +++ b/deps/npm/docs/public/using-npm/config/index.html @@ -74,7 +74,7 @@ } } }) -

config

+

config

More than you probably want to know about npm configuration

Description

npm gets its configuration values from the following sources, sorted by priority:

@@ -1161,4 +1161,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/using-npm/developers/index.html b/deps/npm/docs/public/using-npm/developers/index.html index b9477decc663c1..c3714d450c0ab5 100644 --- a/deps/npm/docs/public/using-npm/developers/index.html +++ b/deps/npm/docs/public/using-npm/developers/index.html @@ -74,7 +74,7 @@ } } }) -

developers

+

developers

Developer Guide

Description

So, you've decided to use npm to develop (and maybe publish/deploy) @@ -259,4 +259,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/using-npm/disputes/index.html b/deps/npm/docs/public/using-npm/disputes/index.html index e35932e24cc105..216c1cdeb8cc1c 100644 --- a/deps/npm/docs/public/using-npm/disputes/index.html +++ b/deps/npm/docs/public/using-npm/disputes/index.html @@ -74,7 +74,7 @@ } } }) -

disputes

+

disputes

Handling Module Name Disputes

This document describes the steps that you should take to resolve module name disputes with other npm publishers. It also describes special steps you should @@ -192,4 +192,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/using-npm/orgs/index.html b/deps/npm/docs/public/using-npm/orgs/index.html index 8a6cd4ed2704ae..86c34b532c0404 100644 --- a/deps/npm/docs/public/using-npm/orgs/index.html +++ b/deps/npm/docs/public/using-npm/orgs/index.html @@ -74,7 +74,7 @@ } } }) -

orgs

+

orgs

Working with Teams & Orgs

Description

There are three levels of org users:

@@ -144,4 +144,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/using-npm/registry/index.html b/deps/npm/docs/public/using-npm/registry/index.html index e9417d197bdb43..52aed9bf32ff12 100644 --- a/deps/npm/docs/public/using-npm/registry/index.html +++ b/deps/npm/docs/public/using-npm/registry/index.html @@ -74,7 +74,7 @@ } } }) -

registry

+

registry

The JavaScript Package Registry

Description

To resolve packages by name and version, npm talks to a registry website @@ -156,4 +156,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/using-npm/removal/index.html b/deps/npm/docs/public/using-npm/removal/index.html index 0000cf66131558..a00ea9bb243561 100644 --- a/deps/npm/docs/public/using-npm/removal/index.html +++ b/deps/npm/docs/public/using-npm/removal/index.html @@ -74,7 +74,7 @@ } } }) -

removal

+

removal

Cleaning the Slate

Synopsis

So sad to see you go.

@@ -116,4 +116,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/using-npm/scope/index.html b/deps/npm/docs/public/using-npm/scope/index.html index ebc7f6c40c4ced..c3f636e7eb915f 100644 --- a/deps/npm/docs/public/using-npm/scope/index.html +++ b/deps/npm/docs/public/using-npm/scope/index.html @@ -74,7 +74,7 @@ } } }) -

scope

+

scope

Scoped packages

Description

All npm packages have a name. Some package names also have a scope. A scope @@ -159,4 +159,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/using-npm/scripts/index.html b/deps/npm/docs/public/using-npm/scripts/index.html index 69b051ccaa3e74..563eebd58678b7 100644 --- a/deps/npm/docs/public/using-npm/scripts/index.html +++ b/deps/npm/docs/public/using-npm/scripts/index.html @@ -74,7 +74,7 @@ } } }) -

scripts

+

scripts

How npm handles the "scripts" field

Description

The "scripts" property of of your package.json file supports a number of built-in scripts and their preset life cycle events as well as arbitrary scripts. These all can be executed by running npm run-script <stage> or npm run <stage> for short. Pre and post commands with matching names will be run for those as well (e.g. premyscript, myscript, postmyscript). Scripts from dependencies can be run with npm explore <pkg> -- npm run <stage>.

@@ -323,4 +323,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/docs/public/using-npm/semver/index.html b/deps/npm/docs/public/using-npm/semver/index.html index 69546595e42554..e2ca8e40a3fea5 100644 --- a/deps/npm/docs/public/using-npm/semver/index.html +++ b/deps/npm/docs/public/using-npm/semver/index.html @@ -74,7 +74,7 @@ } } }) -

semver(7) -- The semantic versioner for npm

+

semver(7) -- The semantic versioner for npm

Install

npm install --save semver

Usage

@@ -435,4 +435,4 @@

\ No newline at end of file +
\ No newline at end of file diff --git a/deps/npm/man/man1/npm-README.1 b/deps/npm/man/man1/npm-README.1 index 05bed92fb853f0..34ac97c4a2478c 100644 --- a/deps/npm/man/man1/npm-README.1 +++ b/deps/npm/man/man1/npm-README.1 @@ -1,4 +1,4 @@ -.TH "NPM" "1" "March 2021" "" "" +.TH "NPM" "1" "April 2021" "" "" .SH "NAME" \fBnpm\fR \- a JavaScript package manager .P diff --git a/deps/npm/man/man1/npm-access.1 b/deps/npm/man/man1/npm-access.1 index e8c1ccadb9b185..473092afbe7e5e 100644 --- a/deps/npm/man/man1/npm-access.1 +++ b/deps/npm/man/man1/npm-access.1 @@ -1,4 +1,4 @@ -.TH "NPM\-ACCESS" "1" "March 2021" "" "" +.TH "NPM\-ACCESS" "1" "April 2021" "" "" .SH "NAME" \fBnpm-access\fR \- Set access level on published packages .SS Synopsis diff --git a/deps/npm/man/man1/npm-adduser.1 b/deps/npm/man/man1/npm-adduser.1 index 5797c04df12049..de1625d5252812 100644 --- a/deps/npm/man/man1/npm-adduser.1 +++ b/deps/npm/man/man1/npm-adduser.1 @@ -4,7 +4,7 @@ section: cli\-commands title: npm\-adduser description: Set access level on published packages .HR -.TH "NPM\-ADDUSER" "1" "March 2021" "" "" +.TH "NPM\-ADDUSER" "1" "April 2021" "" "" .SH "NAME" \fBnpm-adduser\fR \- Add a registry user account .SS Synopsis diff --git a/deps/npm/man/man1/npm-audit.1 b/deps/npm/man/man1/npm-audit.1 index 838340eb71d37f..dde323b05aa793 100644 --- a/deps/npm/man/man1/npm-audit.1 +++ b/deps/npm/man/man1/npm-audit.1 @@ -1,4 +1,4 @@ -.TH "NPM\-AUDIT" "1" "March 2021" "" "" +.TH "NPM\-AUDIT" "1" "April 2021" "" "" .SH "NAME" \fBnpm-audit\fR \- Run a security audit .SS Synopsis diff --git a/deps/npm/man/man1/npm-bin.1 b/deps/npm/man/man1/npm-bin.1 index cf1692e8943f12..8b826f5b5fb090 100644 --- a/deps/npm/man/man1/npm-bin.1 +++ b/deps/npm/man/man1/npm-bin.1 @@ -1,4 +1,4 @@ -.TH "NPM\-BIN" "1" "March 2021" "" "" +.TH "NPM\-BIN" "1" "April 2021" "" "" .SH "NAME" \fBnpm-bin\fR \- Display npm bin folder .SS Synopsis diff --git a/deps/npm/man/man1/npm-bugs.1 b/deps/npm/man/man1/npm-bugs.1 index 42ba349d1d3f1b..9d666fb08460d7 100644 --- a/deps/npm/man/man1/npm-bugs.1 +++ b/deps/npm/man/man1/npm-bugs.1 @@ -1,4 +1,4 @@ -.TH "NPM\-BUGS" "1" "March 2021" "" "" +.TH "NPM\-BUGS" "1" "April 2021" "" "" .SH "NAME" \fBnpm-bugs\fR \- Bugs for a package in a web browser maybe .SS Synopsis diff --git a/deps/npm/man/man1/npm-build.1 b/deps/npm/man/man1/npm-build.1 index 5a604defa56376..d7325bafa08c8b 100644 --- a/deps/npm/man/man1/npm-build.1 +++ b/deps/npm/man/man1/npm-build.1 @@ -1,4 +1,4 @@ -.TH "NPM\-BUILD" "1" "March 2021" "" "" +.TH "NPM\-BUILD" "1" "April 2021" "" "" .SH "NAME" \fBnpm-build\fR \- Build a package .SS Synopsis diff --git a/deps/npm/man/man1/npm-bundle.1 b/deps/npm/man/man1/npm-bundle.1 index 37a504a44a52a5..7e4d3e107e10db 100644 --- a/deps/npm/man/man1/npm-bundle.1 +++ b/deps/npm/man/man1/npm-bundle.1 @@ -1,4 +1,4 @@ -.TH "NPM\-BUNDLE" "1" "March 2021" "" "" +.TH "NPM\-BUNDLE" "1" "April 2021" "" "" .SH "NAME" \fBnpm-bundle\fR \- REMOVED .SS Description diff --git a/deps/npm/man/man1/npm-cache.1 b/deps/npm/man/man1/npm-cache.1 index a9a0128ebfe7ae..e2a570fd8fdf98 100644 --- a/deps/npm/man/man1/npm-cache.1 +++ b/deps/npm/man/man1/npm-cache.1 @@ -1,4 +1,4 @@ -.TH "NPM\-CACHE" "1" "March 2021" "" "" +.TH "NPM\-CACHE" "1" "April 2021" "" "" .SH "NAME" \fBnpm-cache\fR \- Manipulates packages cache .SS Synopsis diff --git a/deps/npm/man/man1/npm-ci.1 b/deps/npm/man/man1/npm-ci.1 index e9ae68b5e11db8..d2eebbe4db897c 100644 --- a/deps/npm/man/man1/npm-ci.1 +++ b/deps/npm/man/man1/npm-ci.1 @@ -1,4 +1,4 @@ -.TH "NPM\-CI" "1" "March 2021" "" "" +.TH "NPM\-CI" "1" "April 2021" "" "" .SH "NAME" \fBnpm-ci\fR \- Install a project with a clean slate .SS Synopsis diff --git a/deps/npm/man/man1/npm-completion.1 b/deps/npm/man/man1/npm-completion.1 index bc206f52421c5b..39de0cea8290d1 100644 --- a/deps/npm/man/man1/npm-completion.1 +++ b/deps/npm/man/man1/npm-completion.1 @@ -1,4 +1,4 @@ -.TH "NPM\-COMPLETION" "1" "March 2021" "" "" +.TH "NPM\-COMPLETION" "1" "April 2021" "" "" .SH "NAME" \fBnpm-completion\fR \- Tab Completion for npm .SS Synopsis diff --git a/deps/npm/man/man1/npm-config.1 b/deps/npm/man/man1/npm-config.1 index aae8bed370b9e9..0217e2a7dba350 100644 --- a/deps/npm/man/man1/npm-config.1 +++ b/deps/npm/man/man1/npm-config.1 @@ -1,4 +1,4 @@ -.TH "NPM\-CONFIG" "1" "March 2021" "" "" +.TH "NPM\-CONFIG" "1" "April 2021" "" "" .SH "NAME" \fBnpm-config\fR \- Manage the npm configuration files .SS Synopsis diff --git a/deps/npm/man/man1/npm-dedupe.1 b/deps/npm/man/man1/npm-dedupe.1 index 952a65603d3f1d..ae52835429a07e 100644 --- a/deps/npm/man/man1/npm-dedupe.1 +++ b/deps/npm/man/man1/npm-dedupe.1 @@ -1,4 +1,4 @@ -.TH "NPM\-DEDUPE" "1" "March 2021" "" "" +.TH "NPM\-DEDUPE" "1" "April 2021" "" "" .SH "NAME" \fBnpm-dedupe\fR \- Reduce duplication .SS Synopsis diff --git a/deps/npm/man/man1/npm-deprecate.1 b/deps/npm/man/man1/npm-deprecate.1 index 96d84fa4bf92a4..d6066b21ad7f39 100644 --- a/deps/npm/man/man1/npm-deprecate.1 +++ b/deps/npm/man/man1/npm-deprecate.1 @@ -1,4 +1,4 @@ -.TH "NPM\-DEPRECATE" "1" "March 2021" "" "" +.TH "NPM\-DEPRECATE" "1" "April 2021" "" "" .SH "NAME" \fBnpm-deprecate\fR \- Deprecate a version of a package .SS Synopsis diff --git a/deps/npm/man/man1/npm-dist-tag.1 b/deps/npm/man/man1/npm-dist-tag.1 index 3f6019429d54ea..21f86f7adb9a54 100644 --- a/deps/npm/man/man1/npm-dist-tag.1 +++ b/deps/npm/man/man1/npm-dist-tag.1 @@ -4,7 +4,7 @@ section: cli\-commands title: npm\-dist\-tag description: Modify package distribution tags .HR -.TH "NPM\-DIST\-TAG" "1" "March 2021" "" "" +.TH "NPM\-DIST\-TAG" "1" "April 2021" "" "" .SH "NAME" \fBnpm-dist-tag\fR \- Modify package distribution tags .SS Synopsis diff --git a/deps/npm/man/man1/npm-docs.1 b/deps/npm/man/man1/npm-docs.1 index 60770b0e0097a3..c5e719a551d03c 100644 --- a/deps/npm/man/man1/npm-docs.1 +++ b/deps/npm/man/man1/npm-docs.1 @@ -1,4 +1,4 @@ -.TH "NPM\-DOCS" "1" "March 2021" "" "" +.TH "NPM\-DOCS" "1" "April 2021" "" "" .SH "NAME" \fBnpm-docs\fR \- Docs for a package in a web browser maybe .SS Synopsis diff --git a/deps/npm/man/man1/npm-doctor.1 b/deps/npm/man/man1/npm-doctor.1 index c6e30223b575d9..a0dddad3b99eda 100644 --- a/deps/npm/man/man1/npm-doctor.1 +++ b/deps/npm/man/man1/npm-doctor.1 @@ -1,4 +1,4 @@ -.TH "NPM\-DOCTOR" "1" "March 2021" "" "" +.TH "NPM\-DOCTOR" "1" "April 2021" "" "" .SH "NAME" \fBnpm-doctor\fR \- Check your environments .SS Synopsis diff --git a/deps/npm/man/man1/npm-edit.1 b/deps/npm/man/man1/npm-edit.1 index 9c02493104f26e..94f2f02bc0081f 100644 --- a/deps/npm/man/man1/npm-edit.1 +++ b/deps/npm/man/man1/npm-edit.1 @@ -1,4 +1,4 @@ -.TH "NPM\-EDIT" "1" "March 2021" "" "" +.TH "NPM\-EDIT" "1" "April 2021" "" "" .SH "NAME" \fBnpm-edit\fR \- Edit an installed package .SS Synopsis diff --git a/deps/npm/man/man1/npm-explore.1 b/deps/npm/man/man1/npm-explore.1 index 8a6bbd6cbc61ce..4a86992997cb39 100644 --- a/deps/npm/man/man1/npm-explore.1 +++ b/deps/npm/man/man1/npm-explore.1 @@ -4,7 +4,7 @@ section: cli\-commands title: npm\-explore description: Browse an installed package .HR -.TH "NPM\-EXPLORE" "1" "March 2021" "" "" +.TH "NPM\-EXPLORE" "1" "April 2021" "" "" .SH "NAME" \fBnpm-explore\fR \- Browse an installed package .SS Synopsis diff --git a/deps/npm/man/man1/npm-fund.1 b/deps/npm/man/man1/npm-fund.1 index 37085552645351..467b52f79b2ed3 100644 --- a/deps/npm/man/man1/npm-fund.1 +++ b/deps/npm/man/man1/npm-fund.1 @@ -1,4 +1,4 @@ -.TH "NPM\-FUND" "1" "March 2021" "" "" +.TH "NPM\-FUND" "1" "April 2021" "" "" .SH "NAME" \fBnpm-fund\fR \- Retrieve funding information .SS Synopsis diff --git a/deps/npm/man/man1/npm-help-search.1 b/deps/npm/man/man1/npm-help-search.1 index 15c3ddafd2ea74..bf3da9fda88056 100644 --- a/deps/npm/man/man1/npm-help-search.1 +++ b/deps/npm/man/man1/npm-help-search.1 @@ -1,4 +1,4 @@ -.TH "NPM\-HELP\-SEARCH" "1" "March 2021" "" "" +.TH "NPM\-HELP\-SEARCH" "1" "April 2021" "" "" .SH "NAME" \fBnpm-help-search\fR \- Search npm help documentation .SS Synopsis diff --git a/deps/npm/man/man1/npm-help.1 b/deps/npm/man/man1/npm-help.1 index b2d03f7aa5125c..a28856ce5dc03c 100644 --- a/deps/npm/man/man1/npm-help.1 +++ b/deps/npm/man/man1/npm-help.1 @@ -1,4 +1,4 @@ -.TH "NPM\-HELP" "1" "March 2021" "" "" +.TH "NPM\-HELP" "1" "April 2021" "" "" .SH "NAME" \fBnpm-help\fR \- Get help on npm .SS Synopsis diff --git a/deps/npm/man/man1/npm-hook.1 b/deps/npm/man/man1/npm-hook.1 index ebb4696ad96fd4..fe6f738f3a2bdc 100644 --- a/deps/npm/man/man1/npm-hook.1 +++ b/deps/npm/man/man1/npm-hook.1 @@ -1,4 +1,4 @@ -.TH "NPM\-HOOK" "1" "March 2021" "" "" +.TH "NPM\-HOOK" "1" "April 2021" "" "" .SH "NAME" \fBnpm-hook\fR \- Manage registry hooks .SS Synopsis diff --git a/deps/npm/man/man1/npm-init.1 b/deps/npm/man/man1/npm-init.1 index c25102c9855853..87b68832e473db 100644 --- a/deps/npm/man/man1/npm-init.1 +++ b/deps/npm/man/man1/npm-init.1 @@ -1,4 +1,4 @@ -.TH "NPM\-INIT" "1" "March 2021" "" "" +.TH "NPM\-INIT" "1" "April 2021" "" "" .SH "NAME" \fBnpm-init\fR \- create a package\.json file .SS Synopsis diff --git a/deps/npm/man/man1/npm-install-ci-test.1 b/deps/npm/man/man1/npm-install-ci-test.1 index d9c06769a57a12..445dcfdafe289b 100644 --- a/deps/npm/man/man1/npm-install-ci-test.1 +++ b/deps/npm/man/man1/npm-install-ci-test.1 @@ -1,4 +1,4 @@ -.TH "NPM" "" "March 2021" "" "" +.TH "NPM" "" "April 2021" "" "" .SH "NAME" \fBnpm\fR .SS Synopsis diff --git a/deps/npm/man/man1/npm-install-test.1 b/deps/npm/man/man1/npm-install-test.1 index a55053d50bba67..dc26fded726173 100644 --- a/deps/npm/man/man1/npm-install-test.1 +++ b/deps/npm/man/man1/npm-install-test.1 @@ -1,4 +1,4 @@ -.TH "NPM" "" "March 2021" "" "" +.TH "NPM" "" "April 2021" "" "" .SH "NAME" \fBnpm\fR .SS Synopsis diff --git a/deps/npm/man/man1/npm-install.1 b/deps/npm/man/man1/npm-install.1 index 2c58fdfdfbd5db..f888d8fcc03a6d 100644 --- a/deps/npm/man/man1/npm-install.1 +++ b/deps/npm/man/man1/npm-install.1 @@ -1,4 +1,4 @@ -.TH "NPM\-INSTALL" "1" "March 2021" "" "" +.TH "NPM\-INSTALL" "1" "April 2021" "" "" .SH "NAME" \fBnpm-install\fR \- Install a package .SS Synopsis diff --git a/deps/npm/man/man1/npm-link.1 b/deps/npm/man/man1/npm-link.1 index c37c8a93b21cf8..c9ac984047d23d 100644 --- a/deps/npm/man/man1/npm-link.1 +++ b/deps/npm/man/man1/npm-link.1 @@ -1,4 +1,4 @@ -.TH "NPM\-LINK" "1" "March 2021" "" "" +.TH "NPM\-LINK" "1" "April 2021" "" "" .SH "NAME" \fBnpm-link\fR \- Symlink a package folder .SS Synopsis diff --git a/deps/npm/man/man1/npm-logout.1 b/deps/npm/man/man1/npm-logout.1 index 79c1e1e7891d69..27309a6205cd6c 100644 --- a/deps/npm/man/man1/npm-logout.1 +++ b/deps/npm/man/man1/npm-logout.1 @@ -1,4 +1,4 @@ -.TH "NPM\-LOGOUT" "1" "March 2021" "" "" +.TH "NPM\-LOGOUT" "1" "April 2021" "" "" .SH "NAME" \fBnpm-logout\fR \- Log out of the registry .SS Synopsis diff --git a/deps/npm/man/man1/npm-ls.1 b/deps/npm/man/man1/npm-ls.1 index 2ef4b0a4a6faf2..0135d55588c743 100644 --- a/deps/npm/man/man1/npm-ls.1 +++ b/deps/npm/man/man1/npm-ls.1 @@ -1,4 +1,4 @@ -.TH "NPM\-LS" "1" "March 2021" "" "" +.TH "NPM\-LS" "1" "April 2021" "" "" .SH "NAME" \fBnpm-ls\fR \- List installed packages .SS Synopsis @@ -22,7 +22,7 @@ For example, running \fBnpm ls promzard\fP in npm's source tree will show: .P .RS 2 .nf - npm@6\.14\.12 /path/to/npm + npm@6\.14\.13 /path/to/npm └─┬ init\-package\-json@0\.0\.4 └── promzard@0\.1\.5 .fi diff --git a/deps/npm/man/man1/npm-org.1 b/deps/npm/man/man1/npm-org.1 index 1c8cb08d179024..bb2aa177494be6 100644 --- a/deps/npm/man/man1/npm-org.1 +++ b/deps/npm/man/man1/npm-org.1 @@ -1,4 +1,4 @@ -.TH "NPM\-ORG" "1" "March 2021" "" "" +.TH "NPM\-ORG" "1" "April 2021" "" "" .SH "NAME" \fBnpm-org\fR \- Manage orgs .SS Synopsis diff --git a/deps/npm/man/man1/npm-outdated.1 b/deps/npm/man/man1/npm-outdated.1 index c76840ee505946..7898383b4255a5 100644 --- a/deps/npm/man/man1/npm-outdated.1 +++ b/deps/npm/man/man1/npm-outdated.1 @@ -1,4 +1,4 @@ -.TH "NPM\-OUTDATED" "1" "March 2021" "" "" +.TH "NPM\-OUTDATED" "1" "April 2021" "" "" .SH "NAME" \fBnpm-outdated\fR \- Check for outdated packages .SS Synopsis diff --git a/deps/npm/man/man1/npm-owner.1 b/deps/npm/man/man1/npm-owner.1 index 8618b2ff5da2b0..ebf847ed41e4ee 100644 --- a/deps/npm/man/man1/npm-owner.1 +++ b/deps/npm/man/man1/npm-owner.1 @@ -1,4 +1,4 @@ -.TH "NPM\-OWNER" "1" "March 2021" "" "" +.TH "NPM\-OWNER" "1" "April 2021" "" "" .SH "NAME" \fBnpm-owner\fR \- Manage package owners .SS Synopsis diff --git a/deps/npm/man/man1/npm-pack.1 b/deps/npm/man/man1/npm-pack.1 index 6fb39636c5184a..1545c55b6d4a71 100644 --- a/deps/npm/man/man1/npm-pack.1 +++ b/deps/npm/man/man1/npm-pack.1 @@ -1,4 +1,4 @@ -.TH "NPM\-PACK" "1" "March 2021" "" "" +.TH "NPM\-PACK" "1" "April 2021" "" "" .SH "NAME" \fBnpm-pack\fR \- Create a tarball from a package .SS Synopsis diff --git a/deps/npm/man/man1/npm-ping.1 b/deps/npm/man/man1/npm-ping.1 index bbc8d251566247..1c921719e617a3 100644 --- a/deps/npm/man/man1/npm-ping.1 +++ b/deps/npm/man/man1/npm-ping.1 @@ -1,4 +1,4 @@ -.TH "NPM\-PING" "1" "March 2021" "" "" +.TH "NPM\-PING" "1" "April 2021" "" "" .SH "NAME" \fBnpm-ping\fR \- Ping npm registry .SS Synopsis diff --git a/deps/npm/man/man1/npm-prefix.1 b/deps/npm/man/man1/npm-prefix.1 index f970ca52405c24..42700b59b37488 100644 --- a/deps/npm/man/man1/npm-prefix.1 +++ b/deps/npm/man/man1/npm-prefix.1 @@ -1,4 +1,4 @@ -.TH "NPM\-PREFIX" "1" "March 2021" "" "" +.TH "NPM\-PREFIX" "1" "April 2021" "" "" .SH "NAME" \fBnpm-prefix\fR \- Display prefix .SS Synopsis diff --git a/deps/npm/man/man1/npm-profile.1 b/deps/npm/man/man1/npm-profile.1 index b8c6aed8a3bcf2..7a81dd119c785f 100644 --- a/deps/npm/man/man1/npm-profile.1 +++ b/deps/npm/man/man1/npm-profile.1 @@ -1,4 +1,4 @@ -.TH "NPM\-PROFILE" "1" "March 2021" "" "" +.TH "NPM\-PROFILE" "1" "April 2021" "" "" .SH "NAME" \fBnpm-profile\fR \- Change settings on your registry profile .SS Synopsis diff --git a/deps/npm/man/man1/npm-prune.1 b/deps/npm/man/man1/npm-prune.1 index c48891c3ddbaf1..d1ef403c1e45b0 100644 --- a/deps/npm/man/man1/npm-prune.1 +++ b/deps/npm/man/man1/npm-prune.1 @@ -1,4 +1,4 @@ -.TH "NPM\-PRUNE" "1" "March 2021" "" "" +.TH "NPM\-PRUNE" "1" "April 2021" "" "" .SH "NAME" \fBnpm-prune\fR \- Remove extraneous packages .SS Synopsis diff --git a/deps/npm/man/man1/npm-publish.1 b/deps/npm/man/man1/npm-publish.1 index f5a480e49a7e7a..986d11e936990a 100644 --- a/deps/npm/man/man1/npm-publish.1 +++ b/deps/npm/man/man1/npm-publish.1 @@ -1,4 +1,4 @@ -.TH "NPM\-PUBLISH" "1" "March 2021" "" "" +.TH "NPM\-PUBLISH" "1" "April 2021" "" "" .SH "NAME" \fBnpm-publish\fR \- Publish a package .SS Synopsis diff --git a/deps/npm/man/man1/npm-rebuild.1 b/deps/npm/man/man1/npm-rebuild.1 index 60f3e1d4b7b6c8..019c7dc74d8e31 100644 --- a/deps/npm/man/man1/npm-rebuild.1 +++ b/deps/npm/man/man1/npm-rebuild.1 @@ -1,4 +1,4 @@ -.TH "NPM\-REBUILD" "1" "March 2021" "" "" +.TH "NPM\-REBUILD" "1" "April 2021" "" "" .SH "NAME" \fBnpm-rebuild\fR \- Rebuild a package .SS Synopsis diff --git a/deps/npm/man/man1/npm-repo.1 b/deps/npm/man/man1/npm-repo.1 index 1865aee62ef1ec..49d7dc95abcaaf 100644 --- a/deps/npm/man/man1/npm-repo.1 +++ b/deps/npm/man/man1/npm-repo.1 @@ -1,4 +1,4 @@ -.TH "NPM\-REPO" "1" "March 2021" "" "" +.TH "NPM\-REPO" "1" "April 2021" "" "" .SH "NAME" \fBnpm-repo\fR \- Open package repository page in the browser .SS Synopsis diff --git a/deps/npm/man/man1/npm-restart.1 b/deps/npm/man/man1/npm-restart.1 index 226486b942a589..af8962dcfa19cf 100644 --- a/deps/npm/man/man1/npm-restart.1 +++ b/deps/npm/man/man1/npm-restart.1 @@ -1,4 +1,4 @@ -.TH "NPM\-RESTART" "1" "March 2021" "" "" +.TH "NPM\-RESTART" "1" "April 2021" "" "" .SH "NAME" \fBnpm-restart\fR \- Restart a package .SS Synopsis diff --git a/deps/npm/man/man1/npm-root.1 b/deps/npm/man/man1/npm-root.1 index 0fdd5258a91690..f72d9b2c3e4acc 100644 --- a/deps/npm/man/man1/npm-root.1 +++ b/deps/npm/man/man1/npm-root.1 @@ -1,4 +1,4 @@ -.TH "NPM\-ROOT" "1" "March 2021" "" "" +.TH "NPM\-ROOT" "1" "April 2021" "" "" .SH "NAME" \fBnpm-root\fR \- Display npm root .SS Synopsis diff --git a/deps/npm/man/man1/npm-run-script.1 b/deps/npm/man/man1/npm-run-script.1 index 105c4f7f0aa20c..f9d2229c537eae 100644 --- a/deps/npm/man/man1/npm-run-script.1 +++ b/deps/npm/man/man1/npm-run-script.1 @@ -1,4 +1,4 @@ -.TH "NPM\-RUN\-SCRIPT" "1" "March 2021" "" "" +.TH "NPM\-RUN\-SCRIPT" "1" "April 2021" "" "" .SH "NAME" \fBnpm-run-script\fR \- Run arbitrary package scripts .SS Synopsis diff --git a/deps/npm/man/man1/npm-search.1 b/deps/npm/man/man1/npm-search.1 index 81724515c0bd3f..48670f171e260d 100644 --- a/deps/npm/man/man1/npm-search.1 +++ b/deps/npm/man/man1/npm-search.1 @@ -1,4 +1,4 @@ -.TH "NPM\-SEARCH" "1" "March 2021" "" "" +.TH "NPM\-SEARCH" "1" "April 2021" "" "" .SH "NAME" \fBnpm-search\fR \- Search for packages .SS Synopsis diff --git a/deps/npm/man/man1/npm-shrinkwrap.1 b/deps/npm/man/man1/npm-shrinkwrap.1 index 9328e8242466fc..8e718c48a4ab89 100644 --- a/deps/npm/man/man1/npm-shrinkwrap.1 +++ b/deps/npm/man/man1/npm-shrinkwrap.1 @@ -1,4 +1,4 @@ -.TH "NPM\-SHRINKWRAP" "1" "March 2021" "" "" +.TH "NPM\-SHRINKWRAP" "1" "April 2021" "" "" .SH "NAME" \fBnpm-shrinkwrap\fR \- Lock down dependency versions for publication .SS Synopsis diff --git a/deps/npm/man/man1/npm-star.1 b/deps/npm/man/man1/npm-star.1 index 19f60cdf5d49e4..0ab693071811c0 100644 --- a/deps/npm/man/man1/npm-star.1 +++ b/deps/npm/man/man1/npm-star.1 @@ -1,4 +1,4 @@ -.TH "NPM\-STAR" "1" "March 2021" "" "" +.TH "NPM\-STAR" "1" "April 2021" "" "" .SH "NAME" \fBnpm-star\fR \- Mark your favorite packages .SS Synopsis diff --git a/deps/npm/man/man1/npm-stars.1 b/deps/npm/man/man1/npm-stars.1 index 8319d8a3fa5910..72d731766efe5e 100644 --- a/deps/npm/man/man1/npm-stars.1 +++ b/deps/npm/man/man1/npm-stars.1 @@ -1,4 +1,4 @@ -.TH "NPM\-STARS" "1" "March 2021" "" "" +.TH "NPM\-STARS" "1" "April 2021" "" "" .SH "NAME" \fBnpm-stars\fR \- View packages marked as favorites .SS Synopsis diff --git a/deps/npm/man/man1/npm-start.1 b/deps/npm/man/man1/npm-start.1 index 30dbe0900f3d49..55f2d7ab9ab797 100644 --- a/deps/npm/man/man1/npm-start.1 +++ b/deps/npm/man/man1/npm-start.1 @@ -1,4 +1,4 @@ -.TH "NPM\-START" "1" "March 2021" "" "" +.TH "NPM\-START" "1" "April 2021" "" "" .SH "NAME" \fBnpm-start\fR \- Start a package .SS Synopsis diff --git a/deps/npm/man/man1/npm-stop.1 b/deps/npm/man/man1/npm-stop.1 index 10feb06ae8d9cd..d29ec3df62a2cf 100644 --- a/deps/npm/man/man1/npm-stop.1 +++ b/deps/npm/man/man1/npm-stop.1 @@ -1,4 +1,4 @@ -.TH "NPM\-STOP" "1" "March 2021" "" "" +.TH "NPM\-STOP" "1" "April 2021" "" "" .SH "NAME" \fBnpm-stop\fR \- Stop a package .SS Synopsis diff --git a/deps/npm/man/man1/npm-team.1 b/deps/npm/man/man1/npm-team.1 index 1a3cc6cb94b4e5..bb3c9899216cdd 100644 --- a/deps/npm/man/man1/npm-team.1 +++ b/deps/npm/man/man1/npm-team.1 @@ -1,4 +1,4 @@ -.TH "NPM\-TEAM" "1" "March 2021" "" "" +.TH "NPM\-TEAM" "1" "April 2021" "" "" .SH "NAME" \fBnpm-team\fR \- Manage organization teams and team memberships .SS Synopsis diff --git a/deps/npm/man/man1/npm-test.1 b/deps/npm/man/man1/npm-test.1 index 8330e82889199c..959d5bc04fe680 100644 --- a/deps/npm/man/man1/npm-test.1 +++ b/deps/npm/man/man1/npm-test.1 @@ -1,4 +1,4 @@ -.TH "NPM\-TEST" "1" "March 2021" "" "" +.TH "NPM\-TEST" "1" "April 2021" "" "" .SH "NAME" \fBnpm-test\fR \- Test a package .SS Synopsis diff --git a/deps/npm/man/man1/npm-token.1 b/deps/npm/man/man1/npm-token.1 index 895d507c9e9c2a..d71fa08a7d6fbe 100644 --- a/deps/npm/man/man1/npm-token.1 +++ b/deps/npm/man/man1/npm-token.1 @@ -1,4 +1,4 @@ -.TH "NPM\-TOKEN" "1" "March 2021" "" "" +.TH "NPM\-TOKEN" "1" "April 2021" "" "" .SH "NAME" \fBnpm-token\fR \- Manage your authentication tokens .SS Synopsis diff --git a/deps/npm/man/man1/npm-uninstall.1 b/deps/npm/man/man1/npm-uninstall.1 index 746d8815ac1041..1015227d814b66 100644 --- a/deps/npm/man/man1/npm-uninstall.1 +++ b/deps/npm/man/man1/npm-uninstall.1 @@ -1,4 +1,4 @@ -.TH "NPM\-UNINSTALL" "1" "March 2021" "" "" +.TH "NPM\-UNINSTALL" "1" "April 2021" "" "" .SH "NAME" \fBnpm-uninstall\fR \- Remove a package .SS Synopsis diff --git a/deps/npm/man/man1/npm-unpublish.1 b/deps/npm/man/man1/npm-unpublish.1 index 76098f9fcd65fd..3b98db936c672d 100644 --- a/deps/npm/man/man1/npm-unpublish.1 +++ b/deps/npm/man/man1/npm-unpublish.1 @@ -1,4 +1,4 @@ -.TH "NPM\-UNPUBLISH" "1" "March 2021" "" "" +.TH "NPM\-UNPUBLISH" "1" "April 2021" "" "" .SH "NAME" \fBnpm-unpublish\fR \- Remove a package from the registry .SS Synopsis diff --git a/deps/npm/man/man1/npm-update.1 b/deps/npm/man/man1/npm-update.1 index 5e5b5f67e2305f..2bfdbebaab46c8 100644 --- a/deps/npm/man/man1/npm-update.1 +++ b/deps/npm/man/man1/npm-update.1 @@ -1,4 +1,4 @@ -.TH "NPM\-UPDATE" "1" "March 2021" "" "" +.TH "NPM\-UPDATE" "1" "April 2021" "" "" .SH "NAME" \fBnpm-update\fR \- Update a package .SS Synopsis diff --git a/deps/npm/man/man1/npm-version.1 b/deps/npm/man/man1/npm-version.1 index e2a0f1f9b24f59..8934af52a3206c 100644 --- a/deps/npm/man/man1/npm-version.1 +++ b/deps/npm/man/man1/npm-version.1 @@ -1,4 +1,4 @@ -.TH "NPM\-VERSION" "1" "March 2021" "" "" +.TH "NPM\-VERSION" "1" "April 2021" "" "" .SH "NAME" \fBnpm-version\fR \- Bump a package version .SS Synopsis diff --git a/deps/npm/man/man1/npm-view.1 b/deps/npm/man/man1/npm-view.1 index b7965e4fe18bee..a03cf52b5c08c9 100644 --- a/deps/npm/man/man1/npm-view.1 +++ b/deps/npm/man/man1/npm-view.1 @@ -1,4 +1,4 @@ -.TH "NPM\-VIEW" "1" "March 2021" "" "" +.TH "NPM\-VIEW" "1" "April 2021" "" "" .SH "NAME" \fBnpm-view\fR \- View registry info .SS Synopsis diff --git a/deps/npm/man/man1/npm-whoami.1 b/deps/npm/man/man1/npm-whoami.1 index 6ade4045c0ff90..3c5c5b12b73e26 100644 --- a/deps/npm/man/man1/npm-whoami.1 +++ b/deps/npm/man/man1/npm-whoami.1 @@ -1,4 +1,4 @@ -.TH "NPM\-WHOAMI" "1" "March 2021" "" "" +.TH "NPM\-WHOAMI" "1" "April 2021" "" "" .SH "NAME" \fBnpm-whoami\fR \- Display npm username .SS Synopsis diff --git a/deps/npm/man/man1/npm.1 b/deps/npm/man/man1/npm.1 index 1624845fcec0a1..fec4abfdb0c3bf 100644 --- a/deps/npm/man/man1/npm.1 +++ b/deps/npm/man/man1/npm.1 @@ -1,4 +1,4 @@ -.TH "NPM" "1" "March 2021" "" "" +.TH "NPM" "1" "April 2021" "" "" .SH "NAME" \fBnpm\fR \- javascript package manager .SS Synopsis @@ -10,7 +10,7 @@ npm [args] .RE .SS Version .P -6\.14\.12 +6\.14\.13 .SS Description .P npm is the package manager for the Node JavaScript platform\. It puts diff --git a/deps/npm/man/man5/folders.5 b/deps/npm/man/man5/folders.5 index 698c975880fbe2..cacb710a03b030 100644 --- a/deps/npm/man/man5/folders.5 +++ b/deps/npm/man/man5/folders.5 @@ -1,4 +1,4 @@ -.TH "FOLDERS" "5" "March 2021" "" "" +.TH "FOLDERS" "5" "April 2021" "" "" .SH "NAME" \fBfolders\fR \- Folder Structures Used by npm .SS Description diff --git a/deps/npm/man/man5/install.5 b/deps/npm/man/man5/install.5 index 26392cc2a50f33..81da688e5d8e0f 100644 --- a/deps/npm/man/man5/install.5 +++ b/deps/npm/man/man5/install.5 @@ -1,4 +1,4 @@ -.TH "INSTALL" "5" "March 2021" "" "" +.TH "INSTALL" "5" "April 2021" "" "" .SH "NAME" \fBinstall\fR \- Download and Install npm .SS Description diff --git a/deps/npm/man/man5/npmrc.5 b/deps/npm/man/man5/npmrc.5 index c5c7465cafd040..975a95f7964e2a 100644 --- a/deps/npm/man/man5/npmrc.5 +++ b/deps/npm/man/man5/npmrc.5 @@ -1,4 +1,4 @@ -.TH "NPMRC" "5" "March 2021" "" "" +.TH "NPMRC" "5" "April 2021" "" "" .SH "NAME" \fBnpmrc\fR \- The npm config files .SS Description diff --git a/deps/npm/man/man5/package-json.5 b/deps/npm/man/man5/package-json.5 index 6ca9df3aa8834c..958d5a09ca9c1c 100644 --- a/deps/npm/man/man5/package-json.5 +++ b/deps/npm/man/man5/package-json.5 @@ -1,4 +1,4 @@ -.TH "PACKAGE\.JSON" "5" "March 2021" "" "" +.TH "PACKAGE\.JSON" "5" "April 2021" "" "" .SH "NAME" \fBpackage.json\fR \- Specifics of npm's package\.json handling .SS Description diff --git a/deps/npm/man/man5/package-lock-json.5 b/deps/npm/man/man5/package-lock-json.5 index 91ece0004d1f1b..f30bd9d8315b34 100644 --- a/deps/npm/man/man5/package-lock-json.5 +++ b/deps/npm/man/man5/package-lock-json.5 @@ -1,4 +1,4 @@ -.TH "PACKAGE\-LOCK\.JSON" "5" "March 2021" "" "" +.TH "PACKAGE\-LOCK\.JSON" "5" "April 2021" "" "" .SH "NAME" \fBpackage-lock.json\fR \- A manifestation of the manifest .SS Description diff --git a/deps/npm/man/man5/package-locks.5 b/deps/npm/man/man5/package-locks.5 index 4e6c5ffaef0f0e..381b13632dda21 100644 --- a/deps/npm/man/man5/package-locks.5 +++ b/deps/npm/man/man5/package-locks.5 @@ -1,4 +1,4 @@ -.TH "PACKAGE\-LOCKS" "5" "March 2021" "" "" +.TH "PACKAGE\-LOCKS" "5" "April 2021" "" "" .SH "NAME" \fBpackage-locks\fR \- An explanation of npm lockfiles .SS Description diff --git a/deps/npm/man/man5/shrinkwrap-json.5 b/deps/npm/man/man5/shrinkwrap-json.5 index 11a9cf10173561..c58891de299198 100644 --- a/deps/npm/man/man5/shrinkwrap-json.5 +++ b/deps/npm/man/man5/shrinkwrap-json.5 @@ -1,4 +1,4 @@ -.TH "NPM\-SHRINKWRAP\.JSON" "5" "March 2021" "" "" +.TH "NPM\-SHRINKWRAP\.JSON" "5" "April 2021" "" "" .SH "NAME" \fBnpm-shrinkwrap.json\fR \- A publishable lockfile .SS Description diff --git a/deps/npm/man/man7/config.7 b/deps/npm/man/man7/config.7 index f7047616e61512..70b4536013ce32 100644 --- a/deps/npm/man/man7/config.7 +++ b/deps/npm/man/man7/config.7 @@ -1,4 +1,4 @@ -.TH "CONFIG" "7" "March 2021" "" "" +.TH "CONFIG" "7" "April 2021" "" "" .SH "NAME" \fBconfig\fR \- More than you probably want to know about npm configuration .SS Description diff --git a/deps/npm/man/man7/developers.7 b/deps/npm/man/man7/developers.7 index bdd4261c85a15a..d951e45e25ae53 100644 --- a/deps/npm/man/man7/developers.7 +++ b/deps/npm/man/man7/developers.7 @@ -1,4 +1,4 @@ -.TH "DEVELOPERS" "7" "March 2021" "" "" +.TH "DEVELOPERS" "7" "April 2021" "" "" .SH "NAME" \fBdevelopers\fR \- Developer Guide .SS Description diff --git a/deps/npm/man/man7/disputes.7 b/deps/npm/man/man7/disputes.7 index 99046a37bf8201..5d30d819f1712b 100644 --- a/deps/npm/man/man7/disputes.7 +++ b/deps/npm/man/man7/disputes.7 @@ -1,4 +1,4 @@ -.TH "DISPUTES" "7" "March 2021" "" "" +.TH "DISPUTES" "7" "April 2021" "" "" .SH "NAME" \fBdisputes\fR \- Handling Module Name Disputes .P diff --git a/deps/npm/man/man7/orgs.7 b/deps/npm/man/man7/orgs.7 index 9765fa675d5b3d..7f29cf2c239730 100644 --- a/deps/npm/man/man7/orgs.7 +++ b/deps/npm/man/man7/orgs.7 @@ -1,4 +1,4 @@ -.TH "ORGS" "7" "March 2021" "" "" +.TH "ORGS" "7" "April 2021" "" "" .SH "NAME" \fBorgs\fR \- Working with Teams & Orgs .SS Description diff --git a/deps/npm/man/man7/registry.7 b/deps/npm/man/man7/registry.7 index 7ff2a5687e02c3..b805471d7c4989 100644 --- a/deps/npm/man/man7/registry.7 +++ b/deps/npm/man/man7/registry.7 @@ -1,4 +1,4 @@ -.TH "REGISTRY" "7" "March 2021" "" "" +.TH "REGISTRY" "7" "April 2021" "" "" .SH "NAME" \fBregistry\fR \- The JavaScript Package Registry .SS Description diff --git a/deps/npm/man/man7/removal.7 b/deps/npm/man/man7/removal.7 index 80354169a0df8d..06cb3db2632421 100644 --- a/deps/npm/man/man7/removal.7 +++ b/deps/npm/man/man7/removal.7 @@ -1,4 +1,4 @@ -.TH "REMOVAL" "7" "March 2021" "" "" +.TH "REMOVAL" "7" "April 2021" "" "" .SH "NAME" \fBremoval\fR \- Cleaning the Slate .SS Synopsis diff --git a/deps/npm/man/man7/scope.7 b/deps/npm/man/man7/scope.7 index 40f5d289f98571..2a0fa9e1c8ea7f 100644 --- a/deps/npm/man/man7/scope.7 +++ b/deps/npm/man/man7/scope.7 @@ -1,4 +1,4 @@ -.TH "SCOPE" "7" "March 2021" "" "" +.TH "SCOPE" "7" "April 2021" "" "" .SH "NAME" \fBscope\fR \- Scoped packages .SS Description diff --git a/deps/npm/man/man7/scripts.7 b/deps/npm/man/man7/scripts.7 index d21800d9501f15..05ea360d6ffcd5 100644 --- a/deps/npm/man/man7/scripts.7 +++ b/deps/npm/man/man7/scripts.7 @@ -1,4 +1,4 @@ -.TH "SCRIPTS" "7" "March 2021" "" "" +.TH "SCRIPTS" "7" "April 2021" "" "" .SH "NAME" \fBscripts\fR \- How npm handles the "scripts" field .SS Description diff --git a/deps/npm/man/man7/semver.7 b/deps/npm/man/man7/semver.7 index 500a75854547d5..06707dfa93111d 100644 --- a/deps/npm/man/man7/semver.7 +++ b/deps/npm/man/man7/semver.7 @@ -1,4 +1,4 @@ -.TH "SEMVER" "7" "March 2021" "" "" +.TH "SEMVER" "7" "April 2021" "" "" .SH "NAME" \fBsemver\fR \- The semantic versioner for npm .SH Install diff --git a/deps/npm/node_modules/hosted-git-info/CHANGELOG.md b/deps/npm/node_modules/hosted-git-info/CHANGELOG.md index 4f86601e029e95..6987fb4aebb578 100644 --- a/deps/npm/node_modules/hosted-git-info/CHANGELOG.md +++ b/deps/npm/node_modules/hosted-git-info/CHANGELOG.md @@ -2,6 +2,16 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. + +## [2.8.9](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9) (2021-04-07) + + +### Bug Fixes + +* backport regex fix from [#76](https://github.com/npm/hosted-git-info/issues/76) ([29adfe5](https://github.com/npm/hosted-git-info/commit/29adfe5)), closes [#84](https://github.com/npm/hosted-git-info/issues/84) + + + ## [2.8.8](https://github.com/npm/hosted-git-info/compare/v2.8.7...v2.8.8) (2020-02-29) diff --git a/deps/npm/node_modules/hosted-git-info/index.js b/deps/npm/node_modules/hosted-git-info/index.js index 21e53fe3724be1..08857722563627 100644 --- a/deps/npm/node_modules/hosted-git-info/index.js +++ b/deps/npm/node_modules/hosted-git-info/index.js @@ -41,7 +41,7 @@ function fromUrl (giturl, opts) { isGitHubShorthand(giturl) ? 'github:' + giturl : giturl ) var parsed = parseGitUrl(url) - var shortcutMatch = url.match(new RegExp('^([^:]+):(?:(?:[^@:]+(?:[^@]+)?@)?([^/]*))[/](.+?)(?:[.]git)?($|#)')) + var shortcutMatch = url.match(/^([^:]+):(?:[^@]+@)?(?:([^/]*)\/)?([^#]+)/) var matches = Object.keys(gitHosts).map(function (gitHostName) { try { var gitHostInfo = gitHosts[gitHostName] @@ -55,7 +55,7 @@ function fromUrl (giturl, opts) { var defaultRepresentation = null if (shortcutMatch && shortcutMatch[1] === gitHostName) { user = shortcutMatch[2] && decodeURIComponent(shortcutMatch[2]) - project = decodeURIComponent(shortcutMatch[3]) + project = decodeURIComponent(shortcutMatch[3].replace(/\.git$/, '')) defaultRepresentation = 'shortcut' } else { if (parsed.host && parsed.host !== gitHostInfo.domain && parsed.host.replace(/^www[.]/, '') !== gitHostInfo.domain) return diff --git a/deps/npm/node_modules/hosted-git-info/package.json b/deps/npm/node_modules/hosted-git-info/package.json index 8d9c2b2046c742..d85189b536cec5 100644 --- a/deps/npm/node_modules/hosted-git-info/package.json +++ b/deps/npm/node_modules/hosted-git-info/package.json @@ -1,19 +1,19 @@ { - "_from": "hosted-git-info@2.8.8", - "_id": "hosted-git-info@2.8.8", + "_from": "hosted-git-info@2.8.9", + "_id": "hosted-git-info@2.8.9", "_inBundle": false, - "_integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "_integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "_location": "/hosted-git-info", "_phantomChildren": {}, "_requested": { "type": "version", "registry": true, - "raw": "hosted-git-info@2.8.8", + "raw": "hosted-git-info@2.8.9", "name": "hosted-git-info", "escapedName": "hosted-git-info", - "rawSpec": "2.8.8", + "rawSpec": "2.8.9", "saveSpec": null, - "fetchSpec": "2.8.8" + "fetchSpec": "2.8.9" }, "_requiredBy": [ "#USER", @@ -21,10 +21,10 @@ "/normalize-package-data", "/npm-package-arg" ], - "_resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "_shasum": "7539bd4bc1e0e0a895815a2e0262420b12858488", - "_spec": "hosted-git-info@2.8.8", - "_where": "/Users/darcyclarke/Documents/Repos/npm/cli", + "_resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "_shasum": "dffc0bf9a21c02209090f2aa69429e1414daf3f9", + "_spec": "hosted-git-info@2.8.9", + "_where": "/Users/ruyadorno/Documents/workspace/cli/legacy", "author": { "name": "Rebecca Turner", "email": "me@re-becca.org", @@ -68,5 +68,5 @@ "test": "tap -J --coverage=90 --no-esm test/*.js", "test:coverage": "tap --coverage-report=html -J --coverage=90 --no-esm test/*.js" }, - "version": "2.8.8" + "version": "2.8.9" } diff --git a/deps/npm/node_modules/ssri/CHANGELOG.md b/deps/npm/node_modules/ssri/CHANGELOG.md index d4c5897902d12e..15c930b844d028 100644 --- a/deps/npm/node_modules/ssri/CHANGELOG.md +++ b/deps/npm/node_modules/ssri/CHANGELOG.md @@ -2,6 +2,16 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. + +## [6.0.2](https://github.com/zkat/ssri/compare/v6.0.1...v6.0.2) (2021-04-07) + + +### Bug Fixes + +* backport regex change from 8.0.1 ([b30dfdb](https://github.com/zkat/ssri/commit/b30dfdb)), closes [#19](https://github.com/zkat/ssri/issues/19) + + + ## [6.0.1](https://github.com/zkat/ssri/compare/v6.0.0...v6.0.1) (2018-08-27) diff --git a/deps/npm/node_modules/ssri/index.js b/deps/npm/node_modules/ssri/index.js index e102892b0bcd08..673ed2ad249ceb 100644 --- a/deps/npm/node_modules/ssri/index.js +++ b/deps/npm/node_modules/ssri/index.js @@ -8,7 +8,7 @@ const SPEC_ALGORITHMS = ['sha256', 'sha384', 'sha512'] const BASE64_REGEX = /^[a-z0-9+/]+(?:=?=?)$/i const SRI_REGEX = /^([^-]+)-([^?]+)([?\S*]*)$/ -const STRICT_SRI_REGEX = /^([^-]+)-([A-Za-z0-9+/=]{44,88})(\?[\x21-\x7E]*)*$/ +const STRICT_SRI_REGEX = /^([^-]+)-([A-Za-z0-9+/=]{44,88})(\?[\x21-\x7E]*)?$/ const VCHAR_REGEX = /^[\x21-\x7E]+$/ const SsriOpts = figgyPudding({ diff --git a/deps/npm/node_modules/ssri/package.json b/deps/npm/node_modules/ssri/package.json index 5dd740daa27827..c181f2b40f25fb 100644 --- a/deps/npm/node_modules/ssri/package.json +++ b/deps/npm/node_modules/ssri/package.json @@ -1,31 +1,32 @@ { - "_from": "ssri@latest", - "_id": "ssri@6.0.1", + "_from": "ssri@6.0.2", + "_id": "ssri@6.0.2", "_inBundle": false, - "_integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "_integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", "_location": "/ssri", "_phantomChildren": {}, "_requested": { - "type": "tag", + "type": "version", "registry": true, - "raw": "ssri@latest", + "raw": "ssri@6.0.2", "name": "ssri", "escapedName": "ssri", - "rawSpec": "latest", + "rawSpec": "6.0.2", "saveSpec": null, - "fetchSpec": "latest" + "fetchSpec": "6.0.2" }, "_requiredBy": [ "#USER", "/", "/cacache", + "/libnpmpublish", "/make-fetch-happen", "/pacote" ], - "_resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "_shasum": "2a3c41b28dd45b62b63676ecb74001265ae9edd8", - "_spec": "ssri@latest", - "_where": "/Users/zkat/Documents/code/work/npm", + "_resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", + "_shasum": "157939134f20464e7301ddba3e90ffa8f7728ac5", + "_spec": "ssri@6.0.2", + "_where": "/Users/ruyadorno/Documents/workspace/cli/legacy", "author": { "name": "Kat Marchán", "email": "kzm@sykosomatic.org" @@ -89,5 +90,5 @@ "update-coc": "weallbehave -o . && git add CODE_OF_CONDUCT.md && git commit -m 'docs(coc): updated CODE_OF_CONDUCT.md'", "update-contrib": "weallcontribute -o . && git add CONTRIBUTING.md && git commit -m 'docs(contributing): updated CONTRIBUTING.md'" }, - "version": "6.0.1" + "version": "6.0.2" } diff --git a/deps/npm/package.json b/deps/npm/package.json index ace575f874cb1b..138dce95204f86 100644 --- a/deps/npm/package.json +++ b/deps/npm/package.json @@ -1,5 +1,5 @@ { - "version": "6.14.12", + "version": "6.14.13", "name": "npm", "description": "a package manager for JavaScript", "keywords": [ @@ -63,7 +63,7 @@ "glob": "^7.1.6", "graceful-fs": "^4.2.4", "has-unicode": "~2.0.1", - "hosted-git-info": "^2.8.8", + "hosted-git-info": "^2.8.9", "iferr": "^1.0.2", "infer-owner": "^1.0.4", "inflight": "~1.0.6", @@ -132,7 +132,7 @@ "slide": "~1.1.6", "sorted-object": "~2.0.1", "sorted-union-stream": "~2.1.3", - "ssri": "^6.0.1", + "ssri": "^6.0.2", "stringify-package": "^1.0.1", "tar": "^4.4.13", "text-table": "~0.2.0", diff --git a/deps/npm/test/tap/git-npmignore.js b/deps/npm/test/tap/git-npmignore.js index c01f7aa50acc18..ba022b69892d6d 100644 --- a/deps/npm/test/tap/git-npmignore.js +++ b/deps/npm/test/tap/git-npmignore.js @@ -7,6 +7,7 @@ var rimraf = require('rimraf') var test = require('tap').test var which = require('which') +var GITHUB_ACTIONS = process.env.GITHUB_ACTIONS var common = require('../common-tap.js') var escapeArg = require('../../lib/utils/escape-arg.js') var Tacks = require('tacks') @@ -151,8 +152,9 @@ function setup (cb) { if (er) return cb(er) var git = escapeArg(gitPath) + var extraOpts = GITHUB_ACTIONS ? ' --initial-branch=main' : '' - exec(git + ' init --initial-branch=main', {cwd: dep}, init) + exec(git + ' init' + extraOpts, {cwd: dep}, init) function init (er, _, stderr) { if (er) return cb(er) diff --git a/deps/uv/.mailmap b/deps/uv/.mailmap index 56a80f586b3c17..045b7702d958de 100644 --- a/deps/uv/.mailmap +++ b/deps/uv/.mailmap @@ -32,6 +32,7 @@ Nicholas Vavilov Nick Logan Rasmus Christian Pedersen Rasmus Christian Pedersen +Richard Lau Robert Mustacchi Ryan Dahl Ryan Emery @@ -47,6 +48,7 @@ Timothy J. Fontaine Yasuhiro Matsumoto Yazhong Liu Yuki Okumura +cjihrig gengjiawen jBarz jBarz diff --git a/deps/uv/AUTHORS b/deps/uv/AUTHORS index e7c789cfd1b81f..9f327af9f8303f 100644 --- a/deps/uv/AUTHORS +++ b/deps/uv/AUTHORS @@ -212,7 +212,7 @@ guworks RossBencina Roger A. Light chenttuuvv -Richard Lau +Richard Lau ronkorving Corbin Simpson Zachary Hamm @@ -448,3 +448,14 @@ Aleksej Lebedev Nikolay Mitev Ulrik Strid Elad Lahav +Elad Nachmias +Darshan Sen +Simon Kadisch +Momtchil Momtchev +Ethel Weston <66453757+ethelweston@users.noreply.github.com> +Drew DeVault +Mark Klein +schamberg97 <50446906+schamberg97@users.noreply.github.com> +Bob Weinand +Issam E. Maghni +Juan Pablo Canepa diff --git a/deps/uv/CMakeLists.txt b/deps/uv/CMakeLists.txt index e648b00be6432f..c8e881d18f503e 100644 --- a/deps/uv/CMakeLists.txt +++ b/deps/uv/CMakeLists.txt @@ -30,6 +30,13 @@ if(QEMU) add_definitions(-D__QEMU__=1) endif() +option(ASAN "Enable AddressSanitizer (ASan)" OFF) +if(ASAN AND CMAKE_C_COMPILER_ID MATCHES "AppleClang|GNU|Clang") + add_definitions(-D__ASAN__=1) + set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") +endif() + # Compiler check string(CONCAT is-msvc $, @@ -95,6 +102,9 @@ list(APPEND uv_cflags ${lint-no-conditional-assignment-msvc}) list(APPEND uv_cflags ${lint-no-unsafe-msvc}) list(APPEND uv_cflags ${lint-utf8-msvc} ) +check_c_compiler_flag(-fno-strict-aliasing UV_F_STRICT_ALIASING) +list(APPEND uv_cflags $<$:-fno-strict-aliasing>) + set(uv_sources src/fs-poll.c src/idna.c @@ -108,7 +118,7 @@ set(uv_sources src/version.c) if(WIN32) - list(APPEND uv_defines WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0600) + list(APPEND uv_defines WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0602) list(APPEND uv_libraries psapi user32 @@ -318,7 +328,6 @@ if(CMAKE_SYSTEM_NAME STREQUAL "QNX") src/unix/bsd-ifaddrs.c src/unix/no-proctitle.c src/unix/no-fsevents.c) - list(APPEND uv_cflags -fno-strict-aliasing) list(APPEND uv_libraries socket) endif() @@ -466,6 +475,7 @@ if(LIBUV_BUILD_TESTS) test/test-poll-close-doesnt-corrupt-stack.c test/test-poll-close.c test/test-poll-closesocket.c + test/test-poll-multiple-handles.c test/test-poll-oob.c test/test-poll.c test/test-process-priority.c diff --git a/deps/uv/ChangeLog b/deps/uv/ChangeLog index 055dcaf9f18b4e..d0eaf9fe34a20f 100644 --- a/deps/uv/ChangeLog +++ b/deps/uv/ChangeLog @@ -1,3 +1,82 @@ +2021.02.14, Version 1.41.0 (Stable), 1dff88e5161cba5c59276d2070d2e304e4dcb242 + +Changes since version 1.40.0: + +* mailmap: update contact information for richardlau (Richard Lau) + +* build: add asan checks (gengjiawen) + +* unix: report bind error in uv_tcp_connect() (Ben Noordhuis) + +* doc: uv_tcp_bind() never returns UV_EADDRINUSE (Ben Noordhuis) + +* test: fix pump and tcp_write_batch benchmarks (Santiago Gimeno) + +* doc: mark IBM i as Tier 2 support (Jesse Gorzinski) + +* doc,poll: add notes (repeated cb & cancel pending cb) (Elad Nachmias) + +* linux: fix -Wincompatible-pointer-types warning (Ben Noordhuis) + +* linux: fix -Wsign-compare warning (Ben Noordhuis) + +* android: add system call api guards (Ben Noordhuis) + +* unix,win: harmonize uv_read_start() error handling (Ben Noordhuis) + +* unix,win: more uv_read_start() argument validation (Ben Noordhuis) + +* build: turn on -fno-strict-aliasing (Ben Noordhuis) + +* stream: add uv_pipe and uv_socketpair to the API (Jameson Nash) + +* unix,win: initialize timer `timeout` field (Ben Noordhuis) + +* bsd-ifaddrs: improve comments (Darshan Sen) + +* test: remove unnecessary uv_fs_stat() calls (Ben Noordhuis) + +* fs: fix utime/futime timestamp rounding errors (Ben Noordhuis) + +* test: ensure reliable floating point comparison (Jameson Nash) + +* unix,fs: fix uv_fs_sendfile() (Santiago Gimeno) + +* unix: fix uv_fs_stat when using statx (Simon Kadisch) + +* linux,macos: fix uv_set_process_title regression (Momtchil Momtchev) + +* doc: clarify UDP errors and recvmmsg (Ethel Weston) + +* test-getaddrinfo: use example.invalid (Drew DeVault) + +* Revert "build: fix android autotools build" (Bernardo Ramos) + +* unix,fs: on DVS fs, statx returns EOPNOTSUPP (Mark Klein) + +* win, fs: mkdir really return UV_EINVAL for invalid names (Nicholas Vavilov) + +* tools: migrate tools/make_dist_html.py to python3 (Dominique Dumont) + +* unix: fix uv_uptime() on linux (schamberg97) + +* unix: check for partial copy_file_range support (Momtchil Momtchev) + +* win: bump minimum supported version to windows 8 (Ben Noordhuis) + +* poll,unix: ensure safety of rapid fd reuse (Bob Weinand) + +* test: fix some warnings (Issam E. Maghni) + +* unix: fix uv_uptime() regression (Santiago Gimeno) + +* doc: fix versionadded metadata (cjihrig) + +* test: fix 'incompatible pointer types' warnings (cjihrig) + +* unix: check for EXDEV in uv__fs_sendfile() (Darshan Sen) + + 2020.09.26, Version 1.40.0 (Stable), 4e69e333252693bd82d6338d6124f0416538dbfc Changes since version 1.39.0: diff --git a/deps/uv/Makefile.am b/deps/uv/Makefile.am index 46308eaae28ee4..e8bab4963dda78 100644 --- a/deps/uv/Makefile.am +++ b/deps/uv/Makefile.am @@ -56,7 +56,7 @@ if WINNT uvinclude_HEADERS += include/uv/win.h include/uv/tree.h AM_CPPFLAGS += -I$(top_srcdir)/src/win \ -DWIN32_LEAN_AND_MEAN \ - -D_WIN32_WINNT=0x0600 + -D_WIN32_WINNT=0x0602 libuv_la_SOURCES += src/win/async.c \ src/win/atomicops-inl.h \ src/win/core.c \ @@ -225,6 +225,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-poll-close.c \ test/test-poll-close-doesnt-corrupt-stack.c \ test/test-poll-closesocket.c \ + test/test-poll-multiple-handles.c \ test/test-poll-oob.c \ test/test-process-priority.c \ test/test-process-title.c \ @@ -385,10 +386,6 @@ if ANDROID uvinclude_HEADERS += include/uv/android-ifaddrs.h libuv_la_CFLAGS += -D_GNU_SOURCE libuv_la_SOURCES += src/unix/android-ifaddrs.c \ - src/unix/linux-core.c \ - src/unix/linux-inotify.c \ - src/unix/linux-syscalls.c \ - src/unix/procfs-exepath.c \ src/unix/pthread-fixes.c \ src/unix/random-getrandom.c \ src/unix/random-sysctl-linux.c diff --git a/deps/uv/README.md b/deps/uv/README.md index 98007c5e7d21de..f6c73709cc5b82 100644 --- a/deps/uv/README.md +++ b/deps/uv/README.md @@ -286,6 +286,16 @@ listed in `test/benchmark-list.h`. Check the [SUPPORTED_PLATFORMS file](SUPPORTED_PLATFORMS.md). +### `-fno-strict-aliasing` + +It is recommended to turn on the `-fno-strict-aliasing` compiler flag in +projects that use libuv. The use of ad hoc "inheritance" in the libuv API +may not be safe in the presence of compiler optimizations that depend on +strict aliasing. + +MSVC does not have an equivalent flag but it also does not appear to need it +at the time of writing (December 2019.) + ### AIX Notes AIX compilation using IBM XL C/C++ requires version 12.1 or greater. diff --git a/deps/uv/SUPPORTED_PLATFORMS.md b/deps/uv/SUPPORTED_PLATFORMS.md index 72e054eba067ec..30e0ea617a6fca 100644 --- a/deps/uv/SUPPORTED_PLATFORMS.md +++ b/deps/uv/SUPPORTED_PLATFORMS.md @@ -4,14 +4,14 @@ |---|---|---|---| | GNU/Linux | Tier 1 | Linux >= 2.6.32 with glibc >= 2.12 | | | macOS | Tier 1 | macOS >= 10.7 | | -| Windows | Tier 1 | >= Windows 7 | MSVC 2008 and later are supported | +| Windows | Tier 1 | >= Windows 8 | VS 2015 and later are supported | | FreeBSD | Tier 1 | >= 10 | | | AIX | Tier 2 | >= 6 | Maintainers: @libuv/aix | +| IBM i | Tier 2 | >= IBM i 7.2 | Maintainers: @libuv/ibmi | | z/OS | Tier 2 | >= V2R2 | Maintainers: @libuv/zos | | Linux with musl | Tier 2 | musl >= 1.0 | | | SmartOS | Tier 2 | >= 14.4 | Maintainers: @libuv/smartos | | Android | Tier 3 | NDK >= r15b | | -| IBM i | Tier 3 | >= IBM i 7.2 | Maintainers: @libuv/ibmi | | MinGW | Tier 3 | MinGW32 and MinGW-w64 | | | SunOS | Tier 3 | Solaris 121 and later | | | Other | Tier 3 | N/A | | diff --git a/deps/uv/configure.ac b/deps/uv/configure.ac index 1a66b74d28357a..4bdc7fd3f532b8 100644 --- a/deps/uv/configure.ac +++ b/deps/uv/configure.ac @@ -13,7 +13,7 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. AC_PREREQ(2.57) -AC_INIT([libuv], [1.40.0], [https://github.com/libuv/libuv/issues]) +AC_INIT([libuv], [1.41.0], [https://github.com/libuv/libuv/issues]) AC_CONFIG_MACRO_DIR([m4]) m4_include([m4/libuv-extra-automake-flags.m4]) m4_include([m4/as_case.m4]) @@ -25,6 +25,7 @@ AC_ENABLE_STATIC AC_PROG_CC AM_PROG_CC_C_O CC_FLAG_VISIBILITY #[-fvisibility=hidden] +CC_CHECK_CFLAGS_APPEND([-fno-strict-aliasing]) CC_CHECK_CFLAGS_APPEND([-g]) CC_CHECK_CFLAGS_APPEND([-std=gnu89]) CC_CHECK_CFLAGS_APPEND([-Wall]) diff --git a/deps/uv/docs/src/pipe.rst b/deps/uv/docs/src/pipe.rst index 6437a9d9948148..5fa83b80d36543 100644 --- a/deps/uv/docs/src/pipe.rst +++ b/deps/uv/docs/src/pipe.rst @@ -118,3 +118,21 @@ API function is blocking. .. versionadded:: 1.16.0 + +.. c:function:: int uv_pipe(uv_file fds[2], int read_flags, int write_flags) + + Create a pair of connected pipe handles. + Data may be written to `fds[1]` and read from `fds[0]`. + The resulting handles can be passed to `uv_pipe_open`, used with `uv_spawn`, + or for any other purpose. + + Valid values for `flags` are: + + - UV_NONBLOCK_PIPE: Opens the specified socket handle for `OVERLAPPED` + or `FIONBIO`/`O_NONBLOCK` I/O usage. + This is recommended for handles that will be used by libuv, + and not usually recommended otherwise. + + Equivalent to :man:`pipe(2)` with the `O_CLOEXEC` flag set. + + .. versionadded:: 1.41.0 diff --git a/deps/uv/docs/src/poll.rst b/deps/uv/docs/src/poll.rst index aba8915886bb5f..93a101ec686c53 100644 --- a/deps/uv/docs/src/poll.rst +++ b/deps/uv/docs/src/poll.rst @@ -86,36 +86,63 @@ API .. c:function:: int uv_poll_start(uv_poll_t* handle, int events, uv_poll_cb cb) Starts polling the file descriptor. `events` is a bitmask made up of - UV_READABLE, UV_WRITABLE, UV_PRIORITIZED and UV_DISCONNECT. As soon as an - event is detected the callback will be called with `status` set to 0, and the - detected events set on the `events` field. + `UV_READABLE`, `UV_WRITABLE`, `UV_PRIORITIZED` and `UV_DISCONNECT`. As soon + as an event is detected the callback will be called with `status` set to 0, + and the detected events set on the `events` field. - The UV_PRIORITIZED event is used to watch for sysfs interrupts or TCP out-of-band - messages. + The `UV_PRIORITIZED` event is used to watch for sysfs interrupts or TCP + out-of-band messages. - The UV_DISCONNECT event is optional in the sense that it may not be - reported and the user is free to ignore it, but it can help optimize the shutdown - path because an extra read or write call might be avoided. + The `UV_DISCONNECT` event is optional in the sense that it may not be + reported and the user is free to ignore it, but it can help optimize the + shutdown path because an extra read or write call might be avoided. If an error happens while polling, `status` will be < 0 and corresponds - with one of the UV_E* error codes (see :ref:`errors`). The user should + with one of the `UV_E*` error codes (see :ref:`errors`). The user should not close the socket while the handle is active. If the user does that - anyway, the callback *may* be called reporting an error status, but this - is **not** guaranteed. + anyway, the callback *may* be called reporting an error status, but this is + **not** guaranteed. .. note:: - Calling :c:func:`uv_poll_start` on a handle that is already active is fine. Doing so - will update the events mask that is being watched for. + Calling :c:func:`uv_poll_start` on a handle that is already active is + fine. Doing so will update the events mask that is being watched for. .. note:: - Though UV_DISCONNECT can be set, it is unsupported on AIX and as such will not be set - on the `events` field in the callback. + Though `UV_DISCONNECT` can be set, it is unsupported on AIX and as such + will not be set on the `events` field in the callback. - .. versionchanged:: 1.9.0 Added the UV_DISCONNECT event. - .. versionchanged:: 1.14.0 Added the UV_PRIORITIZED event. + .. note:: + If one of the events `UV_READABLE` or `UV_WRITABLE` are set, the + callback will be called again, as long as the given fd/socket remains + readable or writable accordingly. Particularly in each of the following + scenarios: + + * The callback has been called because the socket became + readable/writable and the callback did not conduct a read/write on + this socket at all. + * The callback committed a read on the socket, and has not read all the + available data (when `UV_READABLE` is set). + * The callback committed a write on the socket, but it remained + writable afterwards (when `UV_WRITABLE` is set). + * The socket has already became readable/writable before calling + :c:func:`uv_poll_start` on a poll handle associated with this socket, + and since then the state of the socket did not changed. + + In all of the above listed scenarios, the socket remains readable or + writable and hence the callback will be called again (depending on the + events set in the bitmask). This behaviour is known as level + triggering. + + .. versionchanged:: 1.9.0 Added the `UV_DISCONNECT` event. + .. versionchanged:: 1.14.0 Added the `UV_PRIORITIZED` event. .. c:function:: int uv_poll_stop(uv_poll_t* poll) Stop polling the file descriptor, the callback will no longer be called. + .. note:: + Calling :c:func:`uv_poll_stop` is effective immediately: any pending + callback is also canceled, even if the socket state change notification + was already pending. + .. seealso:: The :c:type:`uv_handle_t` API functions also apply. diff --git a/deps/uv/docs/src/process.rst b/deps/uv/docs/src/process.rst index 8ff19add57849f..ea6c4b9ad2811d 100644 --- a/deps/uv/docs/src/process.rst +++ b/deps/uv/docs/src/process.rst @@ -119,12 +119,14 @@ Data types * flags may be specified to create a duplex data stream. */ UV_READABLE_PIPE = 0x10, - UV_WRITABLE_PIPE = 0x20 + UV_WRITABLE_PIPE = 0x20, /* - * Open the child pipe handle in overlapped mode on Windows. - * On Unix it is silently ignored. - */ - UV_OVERLAPPED_PIPE = 0x40 + * When UV_CREATE_PIPE is specified, specifying UV_NONBLOCK_PIPE opens the + * handle in non-blocking mode in the child. This may cause loss of data, + * if the child is not designed to handle to encounter this mode, + * but can also be significantly more efficient. + */ + UV_NONBLOCK_PIPE = 0x40 } uv_stdio_flags; diff --git a/deps/uv/docs/src/stream.rst b/deps/uv/docs/src/stream.rst index 2ccb59b51cb432..429ebdab28f5f1 100644 --- a/deps/uv/docs/src/stream.rst +++ b/deps/uv/docs/src/stream.rst @@ -139,6 +139,11 @@ API be made several times until there is no more data to read or :c:func:`uv_read_stop` is called. + .. versionchanged:: 1.38.0 :c:func:`uv_read_start()` now consistently + returns `UV_EALREADY` when called twice, and `UV_EINVAL` when the + stream is closing. With older libuv versions, it returns `UV_EALREADY` + on Windows but not UNIX, and `UV_EINVAL` on UNIX but not Windows. + .. c:function:: int uv_read_stop(uv_stream_t*) Stop reading data from the stream. The :c:type:`uv_read_cb` callback will diff --git a/deps/uv/docs/src/tcp.rst b/deps/uv/docs/src/tcp.rst index 3cc8efaac10bf6..cccc86bbfc0335 100644 --- a/deps/uv/docs/src/tcp.rst +++ b/deps/uv/docs/src/tcp.rst @@ -81,10 +81,9 @@ API initialized ``struct sockaddr_in`` or ``struct sockaddr_in6``. When the port is already taken, you can expect to see an ``UV_EADDRINUSE`` - error from either :c:func:`uv_tcp_bind`, :c:func:`uv_listen` or - :c:func:`uv_tcp_connect`. That is, a successful call to this function does - not guarantee that the call to :c:func:`uv_listen` or :c:func:`uv_tcp_connect` - will succeed as well. + error from :c:func:`uv_listen` or :c:func:`uv_tcp_connect`. That is, + a successful call to this function does not guarantee that the call + to :c:func:`uv_listen` or :c:func:`uv_tcp_connect` will succeed as well. `flags` can contain ``UV_TCP_IPV6ONLY``, in which case dual-stack support is disabled and only IPv6 is used. @@ -128,3 +127,20 @@ API :c:func:`uv_tcp_close_reset` calls is not allowed. .. versionadded:: 1.32.0 + +.. c:function:: int uv_socketpair(int type, int protocol, uv_os_sock_t socket_vector[2], int flags0, int flags1) + + Create a pair of connected sockets with the specified properties. + The resulting handles can be passed to `uv_tcp_open`, used with `uv_spawn`, + or for any other purpose. + + Valid values for `flags0` and `flags1` are: + + - UV_NONBLOCK_PIPE: Opens the specified socket handle for `OVERLAPPED` + or `FIONBIO`/`O_NONBLOCK` I/O usage. + This is recommended for handles that will be used by libuv, + and not usually recommended otherwise. + + Equivalent to :man:`socketpair(2)` with a domain of AF_UNIX. + + .. versionadded:: 1.41.0 diff --git a/deps/uv/docs/src/udp.rst b/deps/uv/docs/src/udp.rst index 30aa4593f01936..827fbaad6c0476 100644 --- a/deps/uv/docs/src/udp.rst +++ b/deps/uv/docs/src/udp.rst @@ -73,7 +73,8 @@ Data types * `nread`: Number of bytes that have been received. 0 if there is no more data to read. Note that 0 may also mean that an empty datagram was received (in this case `addr` is not NULL). < 0 if - a transmission error was detected. + a transmission error was detected; if using :man:`recvmmsg(2)` no more + chunks will be received and the buffer can be freed safely. * `buf`: :c:type:`uv_buf_t` with the received data. * `addr`: ``struct sockaddr*`` containing the address of the sender. Can be NULL. Valid for the duration of the callback only. @@ -84,10 +85,11 @@ Data types on error. When using :man:`recvmmsg(2)`, chunks will have the `UV_UDP_MMSG_CHUNK` flag set, - those must not be freed. There will be a final callback with `nread` set to 0, - `addr` set to NULL and the buffer pointing at the initially allocated data with - the `UV_UDP_MMSG_CHUNK` flag cleared and the `UV_UDP_MMSG_FREE` flag set. - The callee can now safely free the provided buffer. + those must not be freed. If no errors occur, there will be a final callback with + `nread` set to 0, `addr` set to NULL and the buffer pointing at the initially + allocated data with the `UV_UDP_MMSG_CHUNK` flag cleared and the `UV_UDP_MMSG_FREE` + flag set. If a UDP socket error occurs, `nread` will be < 0. In either scenario, + the callee can now safely free the provided buffer. .. versionchanged:: 1.40.0 added the `UV_UDP_MMSG_FREE` flag. diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h index 2557961eedba7f..1e1fc94bfcc3dc 100644 --- a/deps/uv/include/uv.h +++ b/deps/uv/include/uv.h @@ -475,6 +475,12 @@ UV_EXTERN int uv_fileno(const uv_handle_t* handle, uv_os_fd_t* fd); UV_EXTERN uv_buf_t uv_buf_init(char* base, unsigned int len); +UV_EXTERN int uv_pipe(uv_file fds[2], int read_flags, int write_flags); +UV_EXTERN int uv_socketpair(int type, + int protocol, + uv_os_sock_t socket_vector[2], + int flags0, + int flags1); #define UV_STREAM_FIELDS \ /* number of bytes queued for writing */ \ @@ -933,10 +939,13 @@ typedef enum { UV_WRITABLE_PIPE = 0x20, /* - * Open the child pipe handle in overlapped mode on Windows. - * On Unix it is silently ignored. + * When UV_CREATE_PIPE is specified, specifying UV_NONBLOCK_PIPE opens the + * handle in non-blocking mode in the child. This may cause loss of data, + * if the child is not designed to handle to encounter this mode, + * but can also be significantly more efficient. */ - UV_OVERLAPPED_PIPE = 0x40 + UV_NONBLOCK_PIPE = 0x40, + UV_OVERLAPPED_PIPE = 0x40 /* old name, for compatibility */ } uv_stdio_flags; typedef struct uv_stdio_container_s { diff --git a/deps/uv/include/uv/version.h b/deps/uv/include/uv/version.h index 5272008a3434b5..e94f1e02e15354 100644 --- a/deps/uv/include/uv/version.h +++ b/deps/uv/include/uv/version.h @@ -31,7 +31,7 @@ */ #define UV_VERSION_MAJOR 1 -#define UV_VERSION_MINOR 40 +#define UV_VERSION_MINOR 41 #define UV_VERSION_PATCH 0 #define UV_VERSION_IS_RELEASE 1 #define UV_VERSION_SUFFIX "" diff --git a/deps/uv/src/timer.c b/deps/uv/src/timer.c index 1bea2a8bd29cdf..bc680e71a9ef04 100644 --- a/deps/uv/src/timer.c +++ b/deps/uv/src/timer.c @@ -58,6 +58,7 @@ static int timer_less_than(const struct heap_node* ha, int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle) { uv__handle_init(loop, (uv_handle_t*)handle, UV_TIMER); handle->timer_cb = NULL; + handle->timeout = 0; handle->repeat = 0; return 0; } diff --git a/deps/uv/src/unix/async.c b/deps/uv/src/unix/async.c index 5f58fb88d628ec..e1805c323795e5 100644 --- a/deps/uv/src/unix/async.c +++ b/deps/uv/src/unix/async.c @@ -214,7 +214,7 @@ static int uv__async_start(uv_loop_t* loop) { pipefd[0] = err; pipefd[1] = -1; #else - err = uv__make_pipe(pipefd, UV__F_NONBLOCK); + err = uv__make_pipe(pipefd, UV_NONBLOCK_PIPE); if (err < 0) return err; #endif diff --git a/deps/uv/src/unix/bsd-ifaddrs.c b/deps/uv/src/unix/bsd-ifaddrs.c index 5223ab4879677e..e48934bce2b65d 100644 --- a/deps/uv/src/unix/bsd-ifaddrs.c +++ b/deps/uv/src/unix/bsd-ifaddrs.c @@ -42,8 +42,8 @@ static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) { return 1; #if !defined(__CYGWIN__) && !defined(__MSYS__) /* - * If `exclude_type` is `UV__EXCLUDE_IFPHYS`, just see whether `sa_family` - * equals to `AF_LINK` or not. Otherwise, the result depends on the operation + * If `exclude_type` is `UV__EXCLUDE_IFPHYS`, return whether `sa_family` + * equals `AF_LINK`. Otherwise, the result depends on the operating * system with `AF_LINK` or `PF_INET`. */ if (exclude_type == UV__EXCLUDE_IFPHYS) @@ -53,7 +53,7 @@ static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) { defined(__HAIKU__) /* * On BSD getifaddrs returns information related to the raw underlying - * devices. We're not interested in this information. + * devices. We're not interested in this information. */ if (ent->ifa_addr->sa_family == AF_LINK) return 1; diff --git a/deps/uv/src/unix/core.c b/deps/uv/src/unix/core.c index 1597828c868b38..63f268f795f100 100644 --- a/deps/uv/src/unix/core.c +++ b/deps/uv/src/unix/core.c @@ -925,13 +925,12 @@ void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events) { if (w->pevents == 0) { QUEUE_REMOVE(&w->watcher_queue); QUEUE_INIT(&w->watcher_queue); + w->events = 0; - if (loop->watchers[w->fd] != NULL) { - assert(loop->watchers[w->fd] == w); + if (w == loop->watchers[w->fd]) { assert(loop->nfds > 0); loop->watchers[w->fd] = NULL; loop->nfds--; - w->events = 0; } } else if (QUEUE_EMPTY(&w->watcher_queue)) diff --git a/deps/uv/src/unix/fs.c b/deps/uv/src/unix/fs.c index 556fd103c3a954..fd7ae08755f519 100644 --- a/deps/uv/src/unix/fs.c +++ b/deps/uv/src/unix/fs.c @@ -58,6 +58,7 @@ #if defined(__linux__) || defined(__sun) # include +# include #endif #if defined(__APPLE__) @@ -212,14 +213,30 @@ static ssize_t uv__fs_fdatasync(uv_fs_t* req) { UV_UNUSED(static struct timespec uv__fs_to_timespec(double time)) { struct timespec ts; ts.tv_sec = time; - ts.tv_nsec = (uint64_t)(time * 1000000) % 1000000 * 1000; + ts.tv_nsec = (time - ts.tv_sec) * 1e9; + + /* TODO(bnoordhuis) Remove this. utimesat() has nanosecond resolution but we + * stick to microsecond resolution for the sake of consistency with other + * platforms. I'm the original author of this compatibility hack but I'm + * less convinced it's useful nowadays. + */ + ts.tv_nsec -= ts.tv_nsec % 1000; + + if (ts.tv_nsec < 0) { + ts.tv_nsec += 1e9; + ts.tv_sec -= 1; + } return ts; } UV_UNUSED(static struct timeval uv__fs_to_timeval(double time)) { struct timeval tv; tv.tv_sec = time; - tv.tv_usec = (uint64_t)(time * 1000000) % 1000000; + tv.tv_usec = (time - tv.tv_sec) * 1e6; + if (tv.tv_usec < 0) { + tv.tv_usec += 1e6; + tv.tv_sec -= 1; + } return tv; } @@ -227,9 +244,6 @@ static ssize_t uv__fs_futime(uv_fs_t* req) { #if defined(__linux__) \ || defined(_AIX71) \ || defined(__HAIKU__) - /* utimesat() has nanosecond resolution but we stick to microseconds - * for the sake of consistency with other platforms. - */ struct timespec ts[2]; ts[0] = uv__fs_to_timespec(req->atime); ts[1] = uv__fs_to_timespec(req->mtime); @@ -906,11 +920,17 @@ static ssize_t uv__fs_sendfile(uv_fs_t* req) { static int copy_file_range_support = 1; if (copy_file_range_support) { - r = uv__fs_copy_file_range(in_fd, NULL, out_fd, &off, req->bufsml[0].len, 0); + r = uv__fs_copy_file_range(in_fd, &off, out_fd, NULL, req->bufsml[0].len, 0); if (r == -1 && errno == ENOSYS) { + /* ENOSYS - it will never work */ errno = 0; copy_file_range_support = 0; + } else if (r == -1 && (errno == ENOTSUP || errno == EXDEV)) { + /* ENOTSUP - it could work on another file system type */ + /* EXDEV - it will not work when in_fd and out_fd are not on the same + mounted filesystem (pre Linux 5.3) */ + errno = 0; } else { goto ok; } @@ -1010,9 +1030,6 @@ static ssize_t uv__fs_utime(uv_fs_t* req) { || defined(_AIX71) \ || defined(__sun) \ || defined(__HAIKU__) - /* utimesat() has nanosecond resolution but we stick to microseconds - * for the sake of consistency with other platforms. - */ struct timespec ts[2]; ts[0] = uv__fs_to_timespec(req->atime); ts[1] = uv__fs_to_timespec(req->mtime); @@ -1220,7 +1237,7 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) { if (fstatfs(dstfd, &s) == -1) goto out; - if (s.f_type != /* CIFS */ 0xFF534D42u) + if ((unsigned) s.f_type != /* CIFS */ 0xFF534D42u) goto out; } @@ -1420,8 +1437,9 @@ static int uv__fs_statx(int fd, case -1: /* EPERM happens when a seccomp filter rejects the system call. * Has been observed with libseccomp < 2.3.3 and docker < 18.04. + * EOPNOTSUPP is used on DVS exported filesystems */ - if (errno != EINVAL && errno != EPERM && errno != ENOSYS) + if (errno != EINVAL && errno != EPERM && errno != ENOSYS && errno != EOPNOTSUPP) return -1; /* Fall through. */ default: @@ -1434,12 +1452,12 @@ static int uv__fs_statx(int fd, return UV_ENOSYS; } - buf->st_dev = 256 * statxbuf.stx_dev_major + statxbuf.stx_dev_minor; + buf->st_dev = makedev(statxbuf.stx_dev_major, statxbuf.stx_dev_minor); buf->st_mode = statxbuf.stx_mode; buf->st_nlink = statxbuf.stx_nlink; buf->st_uid = statxbuf.stx_uid; buf->st_gid = statxbuf.stx_gid; - buf->st_rdev = statxbuf.stx_rdev_major; + buf->st_rdev = makedev(statxbuf.stx_rdev_major, statxbuf.stx_rdev_minor); buf->st_ino = statxbuf.stx_ino; buf->st_size = statxbuf.stx_size; buf->st_blksize = statxbuf.stx_blksize; diff --git a/deps/uv/src/unix/internal.h b/deps/uv/src/unix/internal.h index 570274ed60bebc..3bdf7283bd4fd8 100644 --- a/deps/uv/src/unix/internal.h +++ b/deps/uv/src/unix/internal.h @@ -282,12 +282,6 @@ int uv___stream_fd(const uv_stream_t* handle); #define uv__stream_fd(handle) ((handle)->io_watcher.fd) #endif /* defined(__APPLE__) */ -#ifdef O_NONBLOCK -# define UV__F_NONBLOCK O_NONBLOCK -#else -# define UV__F_NONBLOCK 1 -#endif - int uv__make_pipe(int fds[2], int flags); #if defined(__APPLE__) diff --git a/deps/uv/src/unix/linux-core.c b/deps/uv/src/unix/linux-core.c index 4db2f05053a1cc..c356e96d2dec5b 100644 --- a/deps/uv/src/unix/linux-core.c +++ b/deps/uv/src/unix/linux-core.c @@ -602,22 +602,53 @@ int uv_resident_set_memory(size_t* rss) { return UV_EINVAL; } +static int uv__slurp(const char* filename, char* buf, size_t len) { + ssize_t n; + int fd; + + assert(len > 0); + + fd = uv__open_cloexec(filename, O_RDONLY); + if (fd < 0) + return fd; + + do + n = read(fd, buf, len - 1); + while (n == -1 && errno == EINTR); + + if (uv__close_nocheckstdio(fd)) + abort(); + + if (n < 0) + return UV__ERR(errno); + + buf[n] = '\0'; + + return 0; +} int uv_uptime(double* uptime) { static volatile int no_clock_boottime; + char buf[128]; struct timespec now; int r; + /* Try /proc/uptime first, then fallback to clock_gettime(). */ + + if (0 == uv__slurp("/proc/uptime", buf, sizeof(buf))) + if (1 == sscanf(buf, "%lf", uptime)) + return 0; + /* Try CLOCK_BOOTTIME first, fall back to CLOCK_MONOTONIC if not available * (pre-2.6.39 kernels). CLOCK_MONOTONIC doesn't increase when the system * is suspended. */ if (no_clock_boottime) { - retry: r = clock_gettime(CLOCK_MONOTONIC, &now); + retry_clock_gettime: r = clock_gettime(CLOCK_MONOTONIC, &now); } else if ((r = clock_gettime(CLOCK_BOOTTIME, &now)) && errno == EINVAL) { no_clock_boottime = 1; - goto retry; + goto retry_clock_gettime; } if (r) @@ -1025,32 +1056,6 @@ void uv__set_process_title(const char* title) { } -static int uv__slurp(const char* filename, char* buf, size_t len) { - ssize_t n; - int fd; - - assert(len > 0); - - fd = uv__open_cloexec(filename, O_RDONLY); - if (fd < 0) - return fd; - - do - n = read(fd, buf, len - 1); - while (n == -1 && errno == EINTR); - - if (uv__close_nocheckstdio(fd)) - abort(); - - if (n < 0) - return UV__ERR(errno); - - buf[n] = '\0'; - - return 0; -} - - static uint64_t uv__read_proc_meminfo(const char* what) { uint64_t rc; char* p; diff --git a/deps/uv/src/unix/linux-syscalls.c b/deps/uv/src/unix/linux-syscalls.c index 44daaf12d49810..5071cd56d1fcb2 100644 --- a/deps/uv/src/unix/linux-syscalls.c +++ b/deps/uv/src/unix/linux-syscalls.c @@ -194,37 +194,37 @@ int uv__recvmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) { ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset) { -#if defined(__NR_preadv) - return syscall(__NR_preadv, fd, iov, iovcnt, (long)offset, (long)(offset >> 32)); -#else +#if !defined(__NR_preadv) || defined(__ANDROID_API__) && __ANDROID_API__ < 24 return errno = ENOSYS, -1; +#else + return syscall(__NR_preadv, fd, iov, iovcnt, (long)offset, (long)(offset >> 32)); #endif } ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset) { -#if defined(__NR_pwritev) - return syscall(__NR_pwritev, fd, iov, iovcnt, (long)offset, (long)(offset >> 32)); -#else +#if !defined(__NR_pwritev) || defined(__ANDROID_API__) && __ANDROID_API__ < 24 return errno = ENOSYS, -1; +#else + return syscall(__NR_pwritev, fd, iov, iovcnt, (long)offset, (long)(offset >> 32)); #endif } int uv__dup3(int oldfd, int newfd, int flags) { -#if defined(__NR_dup3) - return syscall(__NR_dup3, oldfd, newfd, flags); -#else +#if !defined(__NR_dup3) || defined(__ANDROID_API__) && __ANDROID_API__ < 21 return errno = ENOSYS, -1; +#else + return syscall(__NR_dup3, oldfd, newfd, flags); #endif } ssize_t uv__fs_copy_file_range(int fd_in, - ssize_t* off_in, + off_t* off_in, int fd_out, - ssize_t* off_out, + off_t* off_out, size_t len, unsigned int flags) { @@ -247,21 +247,18 @@ int uv__statx(int dirfd, int flags, unsigned int mask, struct uv__statx* statxbuf) { - /* __NR_statx make Android box killed by SIGSYS. - * That looks like a seccomp2 sandbox filter rejecting the system call. - */ -#if defined(__NR_statx) && !defined(__ANDROID__) - return syscall(__NR_statx, dirfd, path, flags, mask, statxbuf); -#else +#if !defined(__NR_statx) || defined(__ANDROID_API__) && __ANDROID_API__ < 30 return errno = ENOSYS, -1; +#else + return syscall(__NR_statx, dirfd, path, flags, mask, statxbuf); #endif } ssize_t uv__getrandom(void* buf, size_t buflen, unsigned flags) { -#if defined(__NR_getrandom) - return syscall(__NR_getrandom, buf, buflen, flags); -#else +#if !defined(__NR_getrandom) || defined(__ANDROID_API__) && __ANDROID_API__ < 28 return errno = ENOSYS, -1; +#else + return syscall(__NR_getrandom, buf, buflen, flags); #endif } diff --git a/deps/uv/src/unix/linux-syscalls.h b/deps/uv/src/unix/linux-syscalls.h index 761ff32e21bc53..c85231f6bf4436 100644 --- a/deps/uv/src/unix/linux-syscalls.h +++ b/deps/uv/src/unix/linux-syscalls.h @@ -66,9 +66,9 @@ ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset) int uv__dup3(int oldfd, int newfd, int flags); ssize_t uv__fs_copy_file_range(int fd_in, - ssize_t* off_in, + off_t* off_in, int fd_out, - ssize_t* off_out, + off_t* off_out, size_t len, unsigned int flags); int uv__statx(int dirfd, diff --git a/deps/uv/src/unix/pipe.c b/deps/uv/src/unix/pipe.c index 040d57817fa5b1..788e038e8aaae9 100644 --- a/deps/uv/src/unix/pipe.c +++ b/deps/uv/src/unix/pipe.c @@ -379,3 +379,57 @@ int uv_pipe_chmod(uv_pipe_t* handle, int mode) { return r != -1 ? 0 : UV__ERR(errno); } + + +int uv_pipe(uv_os_fd_t fds[2], int read_flags, int write_flags) { + uv_os_fd_t temp[2]; + int err; +#if defined(__FreeBSD__) || defined(__linux__) + int flags = O_CLOEXEC; + + if ((read_flags & UV_NONBLOCK_PIPE) && (write_flags & UV_NONBLOCK_PIPE)) + flags |= UV_FS_O_NONBLOCK; + + if (pipe2(temp, flags)) + return UV__ERR(errno); + + if (flags & UV_FS_O_NONBLOCK) { + fds[0] = temp[0]; + fds[1] = temp[1]; + return 0; + } +#else + if (pipe(temp)) + return UV__ERR(errno); + + if ((err = uv__cloexec(temp[0], 1))) + goto fail; + + if ((err = uv__cloexec(temp[1], 1))) + goto fail; +#endif + + if (read_flags & UV_NONBLOCK_PIPE) + if ((err = uv__nonblock(temp[0], 1))) + goto fail; + + if (write_flags & UV_NONBLOCK_PIPE) + if ((err = uv__nonblock(temp[1], 1))) + goto fail; + + fds[0] = temp[0]; + fds[1] = temp[1]; + return 0; + +fail: + uv__close(temp[0]); + uv__close(temp[1]); + return err; +} + + +int uv__make_pipe(int fds[2], int flags) { + return uv_pipe(fds, + flags & UV_NONBLOCK_PIPE, + flags & UV_NONBLOCK_PIPE); +} diff --git a/deps/uv/src/unix/poll.c b/deps/uv/src/unix/poll.c index 3d5022b22e85b6..7a1bc7b9dd58d8 100644 --- a/deps/uv/src/unix/poll.c +++ b/deps/uv/src/unix/poll.c @@ -116,12 +116,21 @@ int uv_poll_stop(uv_poll_t* handle) { int uv_poll_start(uv_poll_t* handle, int pevents, uv_poll_cb poll_cb) { + uv__io_t** watchers; + uv__io_t* w; int events; assert((pevents & ~(UV_READABLE | UV_WRITABLE | UV_DISCONNECT | UV_PRIORITIZED)) == 0); assert(!uv__is_closing(handle)); + watchers = handle->loop->watchers; + w = &handle->io_watcher; + + if (uv__fd_exists(handle->loop, w->fd)) + if (watchers[w->fd] != w) + return UV_EEXIST; + uv__poll_stop(handle); if (pevents == 0) diff --git a/deps/uv/src/unix/process.c b/deps/uv/src/unix/process.c index b021aaeba87d0b..8f94c53b249978 100644 --- a/deps/uv/src/unix/process.c +++ b/deps/uv/src/unix/process.c @@ -111,68 +111,6 @@ static void uv__chld(uv_signal_t* handle, int signum) { assert(QUEUE_EMPTY(&pending)); } - -static int uv__make_socketpair(int fds[2]) { -#if defined(__FreeBSD__) || defined(__linux__) - if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds)) - return UV__ERR(errno); - - return 0; -#else - int err; - - if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) - return UV__ERR(errno); - - err = uv__cloexec(fds[0], 1); - if (err == 0) - err = uv__cloexec(fds[1], 1); - - if (err != 0) { - uv__close(fds[0]); - uv__close(fds[1]); - return UV__ERR(errno); - } - - return 0; -#endif -} - - -int uv__make_pipe(int fds[2], int flags) { -#if defined(__FreeBSD__) || defined(__linux__) - if (pipe2(fds, flags | O_CLOEXEC)) - return UV__ERR(errno); - - return 0; -#else - if (pipe(fds)) - return UV__ERR(errno); - - if (uv__cloexec(fds[0], 1)) - goto fail; - - if (uv__cloexec(fds[1], 1)) - goto fail; - - if (flags & UV__F_NONBLOCK) { - if (uv__nonblock(fds[0], 1)) - goto fail; - - if (uv__nonblock(fds[1], 1)) - goto fail; - } - - return 0; - -fail: - uv__close(fds[0]); - uv__close(fds[1]); - return UV__ERR(errno); -#endif -} - - /* * Used for initializing stdio streams like options.stdin_stream. Returns * zero on success. See also the cleanup section in uv_spawn(). @@ -192,7 +130,7 @@ static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2]) { if (container->data.stream->type != UV_NAMED_PIPE) return UV_EINVAL; else - return uv__make_socketpair(fds); + return uv_socketpair(SOCK_STREAM, 0, fds, 0, 0); case UV_INHERIT_FD: case UV_INHERIT_STREAM: diff --git a/deps/uv/src/unix/proctitle.c b/deps/uv/src/unix/proctitle.c index 9ffe5b629c2554..9e39545e44a0ff 100644 --- a/deps/uv/src/unix/proctitle.c +++ b/deps/uv/src/unix/proctitle.c @@ -119,6 +119,7 @@ int uv_set_process_title(const char* title) { memcpy(pt->str, title, len); memset(pt->str + len, '\0', pt->cap - len); pt->len = len; + uv__set_process_title(pt->str); uv_mutex_unlock(&process_title_mutex); diff --git a/deps/uv/src/unix/signal.c b/deps/uv/src/unix/signal.c index f40a3e54ebb74e..1133c73a955525 100644 --- a/deps/uv/src/unix/signal.c +++ b/deps/uv/src/unix/signal.c @@ -265,7 +265,7 @@ static int uv__signal_loop_once_init(uv_loop_t* loop) { if (loop->signal_pipefd[0] != -1) return 0; - err = uv__make_pipe(loop->signal_pipefd, UV__F_NONBLOCK); + err = uv__make_pipe(loop->signal_pipefd, UV_NONBLOCK_PIPE); if (err) return err; diff --git a/deps/uv/src/unix/stream.c b/deps/uv/src/unix/stream.c index 8327f9ccfcea75..106785e4574ea0 100644 --- a/deps/uv/src/unix/stream.c +++ b/deps/uv/src/unix/stream.c @@ -1552,18 +1552,12 @@ int uv_try_write(uv_stream_t* stream, } -int uv_read_start(uv_stream_t* stream, - uv_alloc_cb alloc_cb, - uv_read_cb read_cb) { +int uv__read_start(uv_stream_t* stream, + uv_alloc_cb alloc_cb, + uv_read_cb read_cb) { assert(stream->type == UV_TCP || stream->type == UV_NAMED_PIPE || stream->type == UV_TTY); - if (stream->flags & UV_HANDLE_CLOSING) - return UV_EINVAL; - - if (!(stream->flags & UV_HANDLE_READABLE)) - return UV_ENOTCONN; - /* The UV_HANDLE_READING flag is irrelevant of the state of the tcp - it just * expresses the desired state of the user. */ diff --git a/deps/uv/src/unix/tcp.c b/deps/uv/src/unix/tcp.c index 18acd20df14e79..bc0fb661f1c520 100644 --- a/deps/uv/src/unix/tcp.c +++ b/deps/uv/src/unix/tcp.c @@ -214,14 +214,15 @@ int uv__tcp_connect(uv_connect_t* req, if (handle->connect_req != NULL) return UV_EALREADY; /* FIXME(bnoordhuis) UV_EINVAL or maybe UV_EBUSY. */ + if (handle->delayed_error != 0) + goto out; + err = maybe_new_socket(handle, addr->sa_family, UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); if (err) return err; - handle->delayed_error = 0; - do { errno = 0; r = connect(uv__stream_fd(handle), addr, addrlen); @@ -249,6 +250,8 @@ int uv__tcp_connect(uv_connect_t* req, return UV__ERR(errno); } +out: + uv__req_init(handle->loop, req, UV_CONNECT); req->cb = cb; req->handle = (uv_stream_t*) handle; @@ -459,3 +462,49 @@ int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) { void uv__tcp_close(uv_tcp_t* handle) { uv__stream_close((uv_stream_t*)handle); } + + +int uv_socketpair(int type, int protocol, uv_os_sock_t fds[2], int flags0, int flags1) { + uv_os_sock_t temp[2]; + int err; +#if defined(__FreeBSD__) || defined(__linux__) + int flags; + + flags = type | SOCK_CLOEXEC; + if ((flags0 & UV_NONBLOCK_PIPE) && (flags1 & UV_NONBLOCK_PIPE)) + flags |= SOCK_NONBLOCK; + + if (socketpair(AF_UNIX, flags, protocol, temp)) + return UV__ERR(errno); + + if (flags & UV_FS_O_NONBLOCK) { + fds[0] = temp[0]; + fds[1] = temp[1]; + return 0; + } +#else + if (socketpair(AF_UNIX, type, protocol, temp)) + return UV__ERR(errno); + + if ((err = uv__cloexec(temp[0], 1))) + goto fail; + if ((err = uv__cloexec(temp[1], 1))) + goto fail; +#endif + + if (flags0 & UV_NONBLOCK_PIPE) + if ((err = uv__nonblock(temp[0], 1))) + goto fail; + if (flags1 & UV_NONBLOCK_PIPE) + if ((err = uv__nonblock(temp[1], 1))) + goto fail; + + fds[0] = temp[0]; + fds[1] = temp[1]; + return 0; + +fail: + uv__close(temp[0]); + uv__close(temp[1]); + return err; +} diff --git a/deps/uv/src/uv-common.c b/deps/uv/src/uv-common.c index 602e5f492fd2be..dd559a11d11b6a 100644 --- a/deps/uv/src/uv-common.c +++ b/deps/uv/src/uv-common.c @@ -832,6 +832,25 @@ void uv_loop_delete(uv_loop_t* loop) { } +int uv_read_start(uv_stream_t* stream, + uv_alloc_cb alloc_cb, + uv_read_cb read_cb) { + if (stream == NULL || alloc_cb == NULL || read_cb == NULL) + return UV_EINVAL; + + if (stream->flags & UV_HANDLE_CLOSING) + return UV_EINVAL; + + if (stream->flags & UV_HANDLE_READING) + return UV_EALREADY; + + if (!(stream->flags & UV_HANDLE_READABLE)) + return UV_ENOTCONN; + + return uv__read_start(stream, alloc_cb, read_cb); +} + + void uv_os_free_environ(uv_env_item_t* envitems, int count) { int i; diff --git a/deps/uv/src/uv-common.h b/deps/uv/src/uv-common.h index e851291cc06e01..a92912fdb1a72e 100644 --- a/deps/uv/src/uv-common.h +++ b/deps/uv/src/uv-common.h @@ -136,6 +136,10 @@ int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap); void uv__loop_close(uv_loop_t* loop); +int uv__read_start(uv_stream_t* stream, + uv_alloc_cb alloc_cb, + uv_read_cb read_cb); + int uv__tcp_bind(uv_tcp_t* tcp, const struct sockaddr* addr, unsigned int addrlen, diff --git a/deps/uv/src/win/fs.c b/deps/uv/src/win/fs.c index 8a801749d472b0..a083b5e82c8c42 100644 --- a/deps/uv/src/win/fs.c +++ b/deps/uv/src/win/fs.c @@ -92,30 +92,24 @@ return; \ } -#define MILLIONu (1000U * 1000U) -#define BILLIONu (1000U * 1000U * 1000U) +#define MILLION ((int64_t) 1000 * 1000) +#define BILLION ((int64_t) 1000 * 1000 * 1000) -#define FILETIME_TO_UINT(filetime) \ - (*((uint64_t*) &(filetime)) - (uint64_t) 116444736 * BILLIONu) - -#define FILETIME_TO_TIME_T(filetime) \ - (FILETIME_TO_UINT(filetime) / (10u * MILLIONu)) - -#define FILETIME_TO_TIME_NS(filetime, secs) \ - ((FILETIME_TO_UINT(filetime) - (secs * (uint64_t) 10 * MILLIONu)) * 100U) - -#define FILETIME_TO_TIMESPEC(ts, filetime) \ - do { \ - (ts).tv_sec = (long) FILETIME_TO_TIME_T(filetime); \ - (ts).tv_nsec = (long) FILETIME_TO_TIME_NS(filetime, (ts).tv_sec); \ - } while(0) +static void uv__filetime_to_timespec(uv_timespec_t *ts, int64_t filetime) { + filetime -= 116444736 * BILLION; + ts->tv_sec = (long) (filetime / (10 * MILLION)); + ts->tv_nsec = (long) ((filetime - ts->tv_sec * 10 * MILLION) * 100U); + if (ts->tv_nsec < 0) { + ts->tv_sec -= 1; + ts->tv_nsec += 1e9; + } +} #define TIME_T_TO_FILETIME(time, filetime_ptr) \ do { \ - uint64_t bigtime = ((uint64_t) ((time) * (uint64_t) 10 * MILLIONu)) + \ - (uint64_t) 116444736 * BILLIONu; \ - (filetime_ptr)->dwLowDateTime = bigtime & 0xFFFFFFFF; \ - (filetime_ptr)->dwHighDateTime = bigtime >> 32; \ + int64_t bigtime = ((time) * 10 * MILLION + 116444736 * BILLION); \ + (filetime_ptr)->dwLowDateTime = (uint64_t) bigtime & 0xFFFFFFFF; \ + (filetime_ptr)->dwHighDateTime = (uint64_t) bigtime >> 32; \ } while(0) #define IS_SLASH(c) ((c) == L'\\' || (c) == L'/') @@ -1224,7 +1218,8 @@ void fs__mkdir(uv_fs_t* req) { SET_REQ_RESULT(req, 0); } else { SET_REQ_WIN32_ERROR(req, GetLastError()); - if (req->sys_errno_ == ERROR_INVALID_NAME) + if (req->sys_errno_ == ERROR_INVALID_NAME || + req->sys_errno_ == ERROR_DIRECTORY) req->result = UV_EINVAL; } } @@ -1791,10 +1786,14 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf, statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) | ((_S_IREAD | _S_IWRITE) >> 6); - FILETIME_TO_TIMESPEC(statbuf->st_atim, file_info.BasicInformation.LastAccessTime); - FILETIME_TO_TIMESPEC(statbuf->st_ctim, file_info.BasicInformation.ChangeTime); - FILETIME_TO_TIMESPEC(statbuf->st_mtim, file_info.BasicInformation.LastWriteTime); - FILETIME_TO_TIMESPEC(statbuf->st_birthtim, file_info.BasicInformation.CreationTime); + uv__filetime_to_timespec(&statbuf->st_atim, + file_info.BasicInformation.LastAccessTime.QuadPart); + uv__filetime_to_timespec(&statbuf->st_ctim, + file_info.BasicInformation.ChangeTime.QuadPart); + uv__filetime_to_timespec(&statbuf->st_mtim, + file_info.BasicInformation.LastWriteTime.QuadPart); + uv__filetime_to_timespec(&statbuf->st_birthtim, + file_info.BasicInformation.CreationTime.QuadPart); statbuf->st_ino = file_info.InternalInformation.IndexNumber.QuadPart; diff --git a/deps/uv/src/win/internal.h b/deps/uv/src/win/internal.h index b096255e4d63e0..b1b25b4c786bb7 100644 --- a/deps/uv/src/win/internal.h +++ b/deps/uv/src/win/internal.h @@ -115,8 +115,8 @@ void uv_udp_endgame(uv_loop_t* loop, uv_udp_t* handle); /* * Pipes */ -int uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access, - char* name, size_t nameSize); +int uv__create_stdio_pipe_pair(uv_loop_t* loop, + uv_pipe_t* parent_pipe, HANDLE* child_pipe_ptr, unsigned int flags); int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb); int uv_pipe_accept(uv_pipe_t* server, uv_stream_t* client); diff --git a/deps/uv/src/win/pipe.c b/deps/uv/src/win/pipe.c index f81245ec606fcb..88ba99bbc0a49a 100644 --- a/deps/uv/src/win/pipe.c +++ b/deps/uv/src/win/pipe.c @@ -202,17 +202,17 @@ static void close_pipe(uv_pipe_t* pipe) { } -int uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access, - char* name, size_t nameSize) { +static int uv__pipe_server( + HANDLE* pipeHandle_ptr, DWORD access, + char* name, size_t nameSize, char* random) { HANDLE pipeHandle; int err; - char* ptr = (char*)handle; for (;;) { - uv_unique_pipe_name(ptr, name, nameSize); + uv_unique_pipe_name(random, name, nameSize); pipeHandle = CreateNamedPipeA(name, - access | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE | WRITE_DAC, + access | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 65536, 65536, 0, NULL); @@ -226,26 +226,225 @@ int uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access, goto error; } - /* Pipe name collision. Increment the pointer and try again. */ - ptr++; + /* Pipe name collision. Increment the random number and try again. */ + random++; } - if (CreateIoCompletionPort(pipeHandle, + *pipeHandle_ptr = pipeHandle; + + return 0; + + error: + if (pipeHandle != INVALID_HANDLE_VALUE) + CloseHandle(pipeHandle); + + return err; +} + + +static int uv__create_pipe_pair( + HANDLE* server_pipe_ptr, HANDLE* client_pipe_ptr, + unsigned int server_flags, unsigned int client_flags, + int inherit_client, char* random) { + /* allowed flags are: UV_READABLE_PIPE | UV_WRITABLE_PIPE | UV_NONBLOCK_PIPE */ + char pipe_name[64]; + SECURITY_ATTRIBUTES sa; + DWORD server_access; + DWORD client_access; + HANDLE server_pipe; + HANDLE client_pipe; + int err; + + server_pipe = INVALID_HANDLE_VALUE; + client_pipe = INVALID_HANDLE_VALUE; + + server_access = 0; + if (server_flags & UV_READABLE_PIPE) + server_access |= PIPE_ACCESS_INBOUND; + if (server_flags & UV_WRITABLE_PIPE) + server_access |= PIPE_ACCESS_OUTBOUND; + if (server_flags & UV_NONBLOCK_PIPE) + server_access |= FILE_FLAG_OVERLAPPED; + server_access |= WRITE_DAC; + + client_access = 0; + if (client_flags & UV_READABLE_PIPE) + client_access |= GENERIC_READ; + else + client_access |= FILE_READ_ATTRIBUTES; + if (client_flags & UV_WRITABLE_PIPE) + client_access |= GENERIC_WRITE; + else + client_access |= FILE_WRITE_ATTRIBUTES; + client_access |= WRITE_DAC; + + /* Create server pipe handle. */ + err = uv__pipe_server(&server_pipe, + server_access, + pipe_name, + sizeof(pipe_name), + random); + if (err) + goto error; + + /* Create client pipe handle. */ + sa.nLength = sizeof sa; + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = inherit_client; + + client_pipe = CreateFileA(pipe_name, + client_access, + 0, + &sa, + OPEN_EXISTING, + (client_flags & UV_NONBLOCK_PIPE) ? FILE_FLAG_OVERLAPPED : 0, + NULL); + if (client_pipe == INVALID_HANDLE_VALUE) { + err = GetLastError(); + goto error; + } + +#ifndef NDEBUG + /* Validate that the pipe was opened in the right mode. */ + { + DWORD mode; + BOOL r; + r = GetNamedPipeHandleState(client_pipe, &mode, NULL, NULL, NULL, NULL, 0); + if (r == TRUE) { + assert(mode == (PIPE_READMODE_BYTE | PIPE_WAIT)); + } else { + fprintf(stderr, "libuv assertion failure: GetNamedPipeHandleState failed\n"); + } + } +#endif + + /* Do a blocking ConnectNamedPipe. This should not block because we have + * both ends of the pipe created. */ + if (!ConnectNamedPipe(server_pipe, NULL)) { + if (GetLastError() != ERROR_PIPE_CONNECTED) { + err = GetLastError(); + goto error; + } + } + + *client_pipe_ptr = client_pipe; + *server_pipe_ptr = server_pipe; + return 0; + + error: + if (server_pipe != INVALID_HANDLE_VALUE) + CloseHandle(server_pipe); + + if (client_pipe != INVALID_HANDLE_VALUE) + CloseHandle(client_pipe); + + return err; +} + + +int uv_pipe(uv_file fds[2], int read_flags, int write_flags) { + uv_file temp[2]; + int err; + HANDLE readh; + HANDLE writeh; + + /* Make the server side the inbound (read) end, */ + /* so that both ends will have FILE_READ_ATTRIBUTES permission. */ + /* TODO: better source of local randomness than &fds? */ + read_flags |= UV_READABLE_PIPE; + write_flags |= UV_WRITABLE_PIPE; + err = uv__create_pipe_pair(&readh, &writeh, read_flags, write_flags, 0, (char*) &fds[0]); + if (err != 0) + return err; + temp[0] = _open_osfhandle((intptr_t) readh, 0); + if (temp[0] == -1) { + if (errno == UV_EMFILE) + err = UV_EMFILE; + else + err = UV_UNKNOWN; + CloseHandle(readh); + CloseHandle(writeh); + return err; + } + temp[1] = _open_osfhandle((intptr_t) writeh, 0); + if (temp[1] == -1) { + if (errno == UV_EMFILE) + err = UV_EMFILE; + else + err = UV_UNKNOWN; + _close(temp[0]); + CloseHandle(writeh); + return err; + } + fds[0] = temp[0]; + fds[1] = temp[1]; + return 0; +} + + +int uv__create_stdio_pipe_pair(uv_loop_t* loop, + uv_pipe_t* parent_pipe, HANDLE* child_pipe_ptr, unsigned int flags) { + /* The parent_pipe is always the server_pipe and kept by libuv. + * The child_pipe is always the client_pipe and is passed to the child. + * The flags are specified with respect to their usage in the child. */ + HANDLE server_pipe; + HANDLE client_pipe; + unsigned int server_flags; + unsigned int client_flags; + int err; + + server_pipe = INVALID_HANDLE_VALUE; + client_pipe = INVALID_HANDLE_VALUE; + + server_flags = 0; + client_flags = 0; + if (flags & UV_READABLE_PIPE) { + /* The server needs inbound (read) access too, otherwise CreateNamedPipe() + * won't give us the FILE_READ_ATTRIBUTES permission. We need that to probe + * the state of the write buffer when we're trying to shutdown the pipe. */ + server_flags |= UV_READABLE_PIPE | UV_WRITABLE_PIPE; + client_flags |= UV_READABLE_PIPE; + } + if (flags & UV_WRITABLE_PIPE) { + server_flags |= UV_READABLE_PIPE; + client_flags |= UV_WRITABLE_PIPE; + } + server_flags |= UV_NONBLOCK_PIPE; + if (flags & UV_NONBLOCK_PIPE || parent_pipe->ipc) { + client_flags |= UV_NONBLOCK_PIPE; + } + + err = uv__create_pipe_pair(&server_pipe, &client_pipe, + server_flags, client_flags, 1, (char*) server_pipe); + if (err) + goto error; + + if (CreateIoCompletionPort(server_pipe, loop->iocp, - (ULONG_PTR)handle, + (ULONG_PTR) parent_pipe, 0) == NULL) { err = GetLastError(); goto error; } - uv_pipe_connection_init(handle); - handle->handle = pipeHandle; + uv_pipe_connection_init(parent_pipe); + parent_pipe->handle = server_pipe; + *child_pipe_ptr = client_pipe; + + /* The server end is now readable and/or writable. */ + if (flags & UV_READABLE_PIPE) + parent_pipe->flags |= UV_HANDLE_WRITABLE; + if (flags & UV_WRITABLE_PIPE) + parent_pipe->flags |= UV_HANDLE_READABLE; return 0; error: - if (pipeHandle != INVALID_HANDLE_VALUE) - CloseHandle(pipeHandle); + if (server_pipe != INVALID_HANDLE_VALUE) + CloseHandle(server_pipe); + + if (client_pipe != INVALID_HANDLE_VALUE) + CloseHandle(client_pipe); return err; } @@ -712,9 +911,8 @@ void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, handle->name = NULL; } - if (pipeHandle != INVALID_HANDLE_VALUE) { + if (pipeHandle != INVALID_HANDLE_VALUE) CloseHandle(pipeHandle); - } /* Make this req pending reporting an error. */ SET_REQ_ERROR(req, err); diff --git a/deps/uv/src/win/process-stdio.c b/deps/uv/src/win/process-stdio.c index 355d6188088b4a..0db35723731505 100644 --- a/deps/uv/src/win/process-stdio.c +++ b/deps/uv/src/win/process-stdio.c @@ -95,102 +95,6 @@ void uv_disable_stdio_inheritance(void) { } -static int uv__create_stdio_pipe_pair(uv_loop_t* loop, - uv_pipe_t* server_pipe, HANDLE* child_pipe_ptr, unsigned int flags) { - char pipe_name[64]; - SECURITY_ATTRIBUTES sa; - DWORD server_access = 0; - DWORD client_access = 0; - HANDLE child_pipe = INVALID_HANDLE_VALUE; - int err; - int overlap; - - if (flags & UV_READABLE_PIPE) { - /* The server needs inbound access too, otherwise CreateNamedPipe() won't - * give us the FILE_READ_ATTRIBUTES permission. We need that to probe the - * state of the write buffer when we're trying to shutdown the pipe. */ - server_access |= PIPE_ACCESS_OUTBOUND | PIPE_ACCESS_INBOUND; - client_access |= GENERIC_READ | FILE_WRITE_ATTRIBUTES; - } - if (flags & UV_WRITABLE_PIPE) { - server_access |= PIPE_ACCESS_INBOUND; - client_access |= GENERIC_WRITE | FILE_READ_ATTRIBUTES; - } - - /* Create server pipe handle. */ - err = uv_stdio_pipe_server(loop, - server_pipe, - server_access, - pipe_name, - sizeof(pipe_name)); - if (err) - goto error; - - /* Create child pipe handle. */ - sa.nLength = sizeof sa; - sa.lpSecurityDescriptor = NULL; - sa.bInheritHandle = TRUE; - - overlap = server_pipe->ipc || (flags & UV_OVERLAPPED_PIPE); - child_pipe = CreateFileA(pipe_name, - client_access, - 0, - &sa, - OPEN_EXISTING, - overlap ? FILE_FLAG_OVERLAPPED : 0, - NULL); - if (child_pipe == INVALID_HANDLE_VALUE) { - err = GetLastError(); - goto error; - } - -#ifndef NDEBUG - /* Validate that the pipe was opened in the right mode. */ - { - DWORD mode; - BOOL r = GetNamedPipeHandleState(child_pipe, - &mode, - NULL, - NULL, - NULL, - NULL, - 0); - assert(r == TRUE); - assert(mode == (PIPE_READMODE_BYTE | PIPE_WAIT)); - } -#endif - - /* Do a blocking ConnectNamedPipe. This should not block because we have both - * ends of the pipe created. */ - if (!ConnectNamedPipe(server_pipe->handle, NULL)) { - if (GetLastError() != ERROR_PIPE_CONNECTED) { - err = GetLastError(); - goto error; - } - } - - /* The server end is now readable and/or writable. */ - if (flags & UV_READABLE_PIPE) - server_pipe->flags |= UV_HANDLE_WRITABLE; - if (flags & UV_WRITABLE_PIPE) - server_pipe->flags |= UV_HANDLE_READABLE; - - *child_pipe_ptr = child_pipe; - return 0; - - error: - if (server_pipe->handle != INVALID_HANDLE_VALUE) { - uv_pipe_cleanup(loop, server_pipe); - } - - if (child_pipe != INVALID_HANDLE_VALUE) { - CloseHandle(child_pipe); - } - - return err; -} - - static int uv__duplicate_handle(uv_loop_t* loop, HANDLE handle, HANDLE* dup) { HANDLE current_process; diff --git a/deps/uv/src/win/stream.c b/deps/uv/src/win/stream.c index 46a0709a38e3bd..ebb5fe5cb79d04 100644 --- a/deps/uv/src/win/stream.c +++ b/deps/uv/src/win/stream.c @@ -65,18 +65,11 @@ int uv_accept(uv_stream_t* server, uv_stream_t* client) { } -int uv_read_start(uv_stream_t* handle, uv_alloc_cb alloc_cb, - uv_read_cb read_cb) { +int uv__read_start(uv_stream_t* handle, + uv_alloc_cb alloc_cb, + uv_read_cb read_cb) { int err; - if (handle->flags & UV_HANDLE_READING) { - return UV_EALREADY; - } - - if (!(handle->flags & UV_HANDLE_READABLE)) { - return UV_ENOTCONN; - } - err = ERROR_INVALID_PARAMETER; switch (handle->type) { case UV_TCP: diff --git a/deps/uv/src/win/tcp.c b/deps/uv/src/win/tcp.c index 0dcaa97df70821..517700e66d81b9 100644 --- a/deps/uv/src/win/tcp.c +++ b/deps/uv/src/win/tcp.c @@ -800,9 +800,8 @@ static int uv_tcp_try_connect(uv_connect_t* req, if (err) return err; - if (handle->delayed_error) { - return handle->delayed_error; - } + if (handle->delayed_error != 0) + goto out; if (!(handle->flags & UV_HANDLE_BOUND)) { if (addrlen == sizeof(uv_addr_ip4_any_)) { @@ -815,8 +814,8 @@ static int uv_tcp_try_connect(uv_connect_t* req, err = uv_tcp_try_bind(handle, bind_addr, addrlen, 0); if (err) return err; - if (handle->delayed_error) - return handle->delayed_error; + if (handle->delayed_error != 0) + goto out; } if (!handle->tcp.conn.func_connectex) { @@ -844,11 +843,21 @@ static int uv_tcp_try_connect(uv_connect_t* req, NULL); } +out: + UV_REQ_INIT(req, UV_CONNECT); req->handle = (uv_stream_t*) handle; req->cb = cb; memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped)); + if (handle->delayed_error != 0) { + /* Process the req without IOCP. */ + handle->reqs_pending++; + REGISTER_HANDLE_REQ(loop, handle, req); + uv_insert_pending_req(loop, (uv_req_t*)req); + return 0; + } + success = handle->tcp.conn.func_connectex(handle->socket, (const struct sockaddr*) &converted, addrlen, @@ -1215,7 +1224,14 @@ void uv_process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle, UNREGISTER_HANDLE_REQ(loop, handle, req); err = 0; - if (REQ_SUCCESS(req)) { + if (handle->delayed_error) { + /* To smooth over the differences between unixes errors that + * were reported synchronously on the first connect can be delayed + * until the next tick--which is now. + */ + err = handle->delayed_error; + handle->delayed_error = 0; + } else if (REQ_SUCCESS(req)) { if (handle->flags & UV_HANDLE_CLOSING) { /* use UV_ECANCELED for consistency with Unix */ err = ERROR_OPERATION_ABORTED; @@ -1571,3 +1587,118 @@ int uv__tcp_connect(uv_connect_t* req, return 0; } + +#ifndef WSA_FLAG_NO_HANDLE_INHERIT +/* Added in Windows 7 SP1. Specify this to avoid race conditions, */ +/* but also manually clear the inherit flag in case this failed. */ +#define WSA_FLAG_NO_HANDLE_INHERIT 0x80 +#endif + +int uv_socketpair(int type, int protocol, uv_os_sock_t fds[2], int flags0, int flags1) { + SOCKET server = INVALID_SOCKET; + SOCKET client0 = INVALID_SOCKET; + SOCKET client1 = INVALID_SOCKET; + SOCKADDR_IN name; + LPFN_ACCEPTEX func_acceptex; + WSAOVERLAPPED overlap; + char accept_buffer[sizeof(struct sockaddr_storage) * 2 + 32]; + int namelen; + int err; + DWORD bytes; + DWORD flags; + DWORD client0_flags = WSA_FLAG_NO_HANDLE_INHERIT; + DWORD client1_flags = WSA_FLAG_NO_HANDLE_INHERIT; + + if (flags0 & UV_NONBLOCK_PIPE) + client0_flags |= WSA_FLAG_OVERLAPPED; + if (flags1 & UV_NONBLOCK_PIPE) + client1_flags |= WSA_FLAG_OVERLAPPED; + + server = WSASocketW(AF_INET, type, protocol, NULL, 0, + WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT); + if (server == INVALID_SOCKET) + goto wsaerror; + if (!SetHandleInformation((HANDLE) server, HANDLE_FLAG_INHERIT, 0)) + goto error; + name.sin_family = AF_INET; + name.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + name.sin_port = 0; + if (bind(server, (SOCKADDR*) &name, sizeof(name)) != 0) + goto wsaerror; + if (listen(server, 1) != 0) + goto wsaerror; + namelen = sizeof(name); + if (getsockname(server, (SOCKADDR*) &name, &namelen) != 0) + goto wsaerror; + client0 = WSASocketW(AF_INET, type, protocol, NULL, 0, client0_flags); + if (client0 == INVALID_SOCKET) + goto wsaerror; + if (!SetHandleInformation((HANDLE) client0, HANDLE_FLAG_INHERIT, 0)) + goto error; + if (connect(client0, (SOCKADDR*) &name, sizeof(name)) != 0) + goto wsaerror; + client1 = WSASocketW(AF_INET, type, protocol, NULL, 0, client1_flags); + if (client1 == INVALID_SOCKET) + goto wsaerror; + if (!SetHandleInformation((HANDLE) client1, HANDLE_FLAG_INHERIT, 0)) + goto error; + if (!uv_get_acceptex_function(server, &func_acceptex)) { + err = WSAEAFNOSUPPORT; + goto cleanup; + } + memset(&overlap, 0, sizeof(overlap)); + if (!func_acceptex(server, + client1, + accept_buffer, + 0, + sizeof(struct sockaddr_storage), + sizeof(struct sockaddr_storage), + &bytes, + &overlap)) { + err = WSAGetLastError(); + if (err == ERROR_IO_PENDING) { + /* Result should complete immediately, since we already called connect, + * but emperically, we sometimes have to poll the kernel a couple times + * until it notices that. */ + while (!WSAGetOverlappedResult(client1, &overlap, &bytes, FALSE, &flags)) { + err = WSAGetLastError(); + if (err != WSA_IO_INCOMPLETE) + goto cleanup; + SwitchToThread(); + } + } + else { + goto cleanup; + } + } + if (setsockopt(client1, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, + (char*) &server, sizeof(server)) != 0) { + goto wsaerror; + } + + closesocket(server); + + fds[0] = client0; + fds[1] = client1; + + return 0; + + wsaerror: + err = WSAGetLastError(); + goto cleanup; + + error: + err = GetLastError(); + goto cleanup; + + cleanup: + if (server != INVALID_SOCKET) + closesocket(server); + if (client0 != INVALID_SOCKET) + closesocket(client0); + if (client1 != INVALID_SOCKET) + closesocket(client1); + + assert(err); + return uv_translate_sys_error(err); +} diff --git a/deps/uv/test/benchmark-pump.c b/deps/uv/test/benchmark-pump.c index 8685258e052793..7d3977dfc32d0d 100644 --- a/deps/uv/test/benchmark-pump.c +++ b/deps/uv/test/benchmark-pump.c @@ -390,6 +390,7 @@ HELPER_IMPL(tcp_pump_server) { r = uv_listen((uv_stream_t*)&tcpServer, MAX_WRITE_HANDLES, connection_cb); ASSERT(r == 0); + notify_parent_process(); uv_run(loop, UV_RUN_DEFAULT); return 0; @@ -411,6 +412,7 @@ HELPER_IMPL(pipe_pump_server) { r = uv_listen((uv_stream_t*)&pipeServer, MAX_WRITE_HANDLES, connection_cb); ASSERT(r == 0); + notify_parent_process(); uv_run(loop, UV_RUN_DEFAULT); MAKE_VALGRIND_HAPPY(); diff --git a/deps/uv/test/blackhole-server.c b/deps/uv/test/blackhole-server.c index ad878b35c61a3e..9b21d1e0e07279 100644 --- a/deps/uv/test/blackhole-server.c +++ b/deps/uv/test/blackhole-server.c @@ -114,6 +114,7 @@ HELPER_IMPL(tcp4_blackhole_server) { r = uv_listen((uv_stream_t*)&tcp_server, 128, connection_cb); ASSERT(r == 0); + notify_parent_process(); r = uv_run(loop, UV_RUN_DEFAULT); ASSERT(0 && "Blackhole server dropped out of event loop."); diff --git a/deps/uv/test/echo-server.c b/deps/uv/test/echo-server.c index c65142ff901794..69d0abc980bc32 100644 --- a/deps/uv/test/echo-server.c +++ b/deps/uv/test/echo-server.c @@ -209,6 +209,7 @@ static void on_recv(uv_udp_t* handle, const struct sockaddr* addr, unsigned flags) { uv_buf_t sndbuf; + uv_udp_send_t* req; if (nread == 0) { /* Everything OK, but nothing read. */ @@ -218,7 +219,7 @@ static void on_recv(uv_udp_t* handle, ASSERT(nread > 0); ASSERT(addr->sa_family == AF_INET); - uv_udp_send_t* req = send_alloc(); + req = send_alloc(); ASSERT(req != NULL); sndbuf = uv_buf_init(rcvbuf->base, nread); ASSERT(0 <= uv_udp_send(req, handle, &sndbuf, 1, addr, on_send)); @@ -228,7 +229,7 @@ static int tcp4_echo_start(int port) { struct sockaddr_in addr; int r; - ASSERT(0 == uv_ip4_addr("0.0.0.0", port, &addr)); + ASSERT(0 == uv_ip4_addr("127.0.0.1", port, &addr)); server = (uv_handle_t*)&tcpServer; serverType = TCP; diff --git a/deps/uv/test/run-tests.c b/deps/uv/test/run-tests.c index e5e75e17c87e22..fc8a79a9eb9019 100644 --- a/deps/uv/test/run-tests.c +++ b/deps/uv/test/run-tests.c @@ -50,6 +50,10 @@ int spawn_tcp_server_helper(void); static int maybe_run_test(int argc, char **argv); +#ifdef _WIN32 +typedef BOOL (WINAPI *sCompareObjectHandles)(_In_ HANDLE, _In_ HANDLE); +#endif + int main(int argc, char **argv) { #ifndef _WIN32 @@ -202,22 +206,36 @@ static int maybe_run_test(int argc, char **argv) { return 1; } -#ifndef _WIN32 if (strcmp(argv[1], "spawn_helper8") == 0) { - int fd; - + uv_os_fd_t closed_fd; + uv_os_fd_t open_fd; +#ifdef _WIN32 + DWORD flags; + HMODULE kernelbase_module; + sCompareObjectHandles pCompareObjectHandles; /* function introduced in Windows 10 */ +#endif notify_parent_process(); - ASSERT(sizeof(fd) == read(0, &fd, sizeof(fd))); - ASSERT(fd > 2); + ASSERT(sizeof(closed_fd) == read(0, &closed_fd, sizeof(closed_fd))); + ASSERT(sizeof(open_fd) == read(0, &open_fd, sizeof(open_fd))); +#ifdef _WIN32 + ASSERT((intptr_t) closed_fd > 0); + ASSERT((intptr_t) open_fd > 0); + ASSERT(0 != GetHandleInformation(open_fd, &flags)); + kernelbase_module = GetModuleHandleA("kernelbase.dll"); + pCompareObjectHandles = (sCompareObjectHandles) + GetProcAddress(kernelbase_module, "CompareObjectHandles"); + ASSERT(pCompareObjectHandles == NULL || !pCompareObjectHandles(open_fd, closed_fd)); +#else + ASSERT(open_fd > 2); + ASSERT(closed_fd > 2); # if defined(__PASE__) /* On IBMi PASE, write() returns 1 */ - ASSERT(1 == write(fd, "x", 1)); + ASSERT(1 == write(closed_fd, "x", 1)); # else - ASSERT(-1 == write(fd, "x", 1)); + ASSERT(-1 == write(closed_fd, "x", 1)); # endif /* !__PASE__ */ - +#endif return 1; } -#endif /* !_WIN32 */ if (strcmp(argv[1], "spawn_helper9") == 0) { notify_parent_process(); diff --git a/deps/uv/test/task.h b/deps/uv/test/task.h index 8250f949b2bfbc..4e7e2f07f570d3 100644 --- a/deps/uv/test/task.h +++ b/deps/uv/test/task.h @@ -113,8 +113,8 @@ typedef enum { #define ASSERT_BASE(a, operator, b, type, conv) \ do { \ - type eval_a = (type) (a); \ - type eval_b = (type) (b); \ + volatile type eval_a = (type) (a); \ + volatile type eval_b = (type) (b); \ if (!(eval_a operator eval_b)) { \ fprintf(stderr, \ "Assertion failed in %s on line %d: `%s %s %s` " \ @@ -196,22 +196,26 @@ typedef enum { } \ } while (0) -#define ASSERT_INT_BASE(a, operator, b, type, conv) \ - ASSERT_BASE(a, operator, b, type, conv) - -#define ASSERT_EQ(a, b) ASSERT_INT_BASE(a, ==, b, int64_t, PRId64) -#define ASSERT_GE(a, b) ASSERT_INT_BASE(a, >=, b, int64_t, PRId64) -#define ASSERT_GT(a, b) ASSERT_INT_BASE(a, >, b, int64_t, PRId64) -#define ASSERT_LE(a, b) ASSERT_INT_BASE(a, <=, b, int64_t, PRId64) -#define ASSERT_LT(a, b) ASSERT_INT_BASE(a, <, b, int64_t, PRId64) -#define ASSERT_NE(a, b) ASSERT_INT_BASE(a, !=, b, int64_t, PRId64) - -#define ASSERT_UINT64_EQ(a, b) ASSERT_INT_BASE(a, ==, b, uint64_t, PRIu64) -#define ASSERT_UINT64_GE(a, b) ASSERT_INT_BASE(a, >=, b, uint64_t, PRIu64) -#define ASSERT_UINT64_GT(a, b) ASSERT_INT_BASE(a, >, b, uint64_t, PRIu64) -#define ASSERT_UINT64_LE(a, b) ASSERT_INT_BASE(a, <=, b, uint64_t, PRIu64) -#define ASSERT_UINT64_LT(a, b) ASSERT_INT_BASE(a, <, b, uint64_t, PRIu64) -#define ASSERT_UINT64_NE(a, b) ASSERT_INT_BASE(a, !=, b, uint64_t, PRIu64) +#define ASSERT_EQ(a, b) ASSERT_BASE(a, ==, b, int64_t, PRId64) +#define ASSERT_GE(a, b) ASSERT_BASE(a, >=, b, int64_t, PRId64) +#define ASSERT_GT(a, b) ASSERT_BASE(a, >, b, int64_t, PRId64) +#define ASSERT_LE(a, b) ASSERT_BASE(a, <=, b, int64_t, PRId64) +#define ASSERT_LT(a, b) ASSERT_BASE(a, <, b, int64_t, PRId64) +#define ASSERT_NE(a, b) ASSERT_BASE(a, !=, b, int64_t, PRId64) + +#define ASSERT_UINT64_EQ(a, b) ASSERT_BASE(a, ==, b, uint64_t, PRIu64) +#define ASSERT_UINT64_GE(a, b) ASSERT_BASE(a, >=, b, uint64_t, PRIu64) +#define ASSERT_UINT64_GT(a, b) ASSERT_BASE(a, >, b, uint64_t, PRIu64) +#define ASSERT_UINT64_LE(a, b) ASSERT_BASE(a, <=, b, uint64_t, PRIu64) +#define ASSERT_UINT64_LT(a, b) ASSERT_BASE(a, <, b, uint64_t, PRIu64) +#define ASSERT_UINT64_NE(a, b) ASSERT_BASE(a, !=, b, uint64_t, PRIu64) + +#define ASSERT_DOUBLE_EQ(a, b) ASSERT_BASE(a, ==, b, double, "f") +#define ASSERT_DOUBLE_GE(a, b) ASSERT_BASE(a, >=, b, double, "f") +#define ASSERT_DOUBLE_GT(a, b) ASSERT_BASE(a, >, b, double, "f") +#define ASSERT_DOUBLE_LE(a, b) ASSERT_BASE(a, <=, b, double, "f") +#define ASSERT_DOUBLE_LT(a, b) ASSERT_BASE(a, <, b, double, "f") +#define ASSERT_DOUBLE_NE(a, b) ASSERT_BASE(a, !=, b, double, "f") #define ASSERT_STR_EQ(a, b) \ ASSERT_BASE_STR(strcmp(a, b) == 0, a, == , b, char*, "s") diff --git a/deps/uv/test/test-close-fd.c b/deps/uv/test/test-close-fd.c index cea4a1b0b80bb4..0d3927f652ede0 100644 --- a/deps/uv/test/test-close-fd.c +++ b/deps/uv/test/test-close-fd.c @@ -19,12 +19,11 @@ * IN THE SOFTWARE. */ -#if !defined(_WIN32) - #include "uv.h" #include "task.h" -#include +#ifndef _WIN32 #include +#endif static unsigned int read_cb_called; @@ -51,14 +50,25 @@ static void read_cb(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { TEST_IMPL(close_fd) { uv_pipe_t pipe_handle; - int fd[2]; + uv_fs_t req; + uv_buf_t bufs[1]; + uv_file fd[2]; + bufs[0] = uv_buf_init("", 1); - ASSERT(0 == pipe(fd)); + ASSERT(0 == uv_pipe(fd, 0, 0)); ASSERT(0 == uv_pipe_init(uv_default_loop(), &pipe_handle, 0)); ASSERT(0 == uv_pipe_open(&pipe_handle, fd[0])); - fd[0] = -1; /* uv_pipe_open() takes ownership of the file descriptor. */ - ASSERT(1 == write(fd[1], "", 1)); + /* uv_pipe_open() takes ownership of the file descriptor. */ + fd[0] = -1; + + ASSERT(1 == uv_fs_write(NULL, &req, fd[1], bufs, 1, -1, NULL)); + ASSERT(1 == req.result); + uv_fs_req_cleanup(&req); +#ifdef _WIN32 + ASSERT(0 == _close(fd[1])); +#else ASSERT(0 == close(fd[1])); +#endif fd[1] = -1; ASSERT(0 == uv_read_start((uv_stream_t *) &pipe_handle, alloc_cb, read_cb)); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); @@ -72,9 +82,3 @@ TEST_IMPL(close_fd) { MAKE_VALGRIND_HAPPY(); return 0; } - -#else - -typedef int file_has_no_tests; /* ISO C forbids an empty translation unit. */ - -#endif /* !_WIN32 */ diff --git a/deps/uv/test/test-error.c b/deps/uv/test/test-error.c index 7f44f4a1bc606d..35d108a4a1f768 100644 --- a/deps/uv/test/test-error.c +++ b/deps/uv/test/test-error.c @@ -37,6 +37,9 @@ * See https://github.com/joyent/libuv/issues/210 */ TEST_IMPL(error_message) { +#if defined(__ASAN__) + RETURN_SKIP("Test does not currently work in ASAN"); +#endif char buf[32]; /* Cop out. Can't do proper checks on systems with diff --git a/deps/uv/test/test-fs-copyfile.c b/deps/uv/test/test-fs-copyfile.c index c785a4b51fbb10..fa00fe4ee89c87 100644 --- a/deps/uv/test/test-fs-copyfile.c +++ b/deps/uv/test/test-fs-copyfile.c @@ -96,6 +96,9 @@ static void touch_file(const char* name, unsigned int size) { TEST_IMPL(fs_copyfile) { +#if defined(__ASAN__) + RETURN_SKIP("Test does not currently work in ASAN"); +#endif const char src[] = "test_file_src"; uv_loop_t* loop; uv_fs_t req; diff --git a/deps/uv/test/test-fs-event.c b/deps/uv/test/test-fs-event.c index 28a6a1ebb3f096..0992d5989e421b 100644 --- a/deps/uv/test/test-fs-event.c +++ b/deps/uv/test/test-fs-event.c @@ -673,6 +673,9 @@ TEST_IMPL(fs_event_watch_file_exact_path) { TEST_IMPL(fs_event_watch_file_twice) { #if defined(NO_FS_EVENTS) RETURN_SKIP(NO_FS_EVENTS); +#endif +#if defined(__ASAN__) + RETURN_SKIP("Test does not currently work in ASAN"); #endif const char path[] = "test/fixtures/empty_file"; uv_fs_event_t watchers[2]; diff --git a/deps/uv/test/test-fs-readdir.c b/deps/uv/test/test-fs-readdir.c index 5efc853cc67f0f..cccaa7438baabc 100644 --- a/deps/uv/test/test-fs-readdir.c +++ b/deps/uv/test/test-fs-readdir.c @@ -230,6 +230,9 @@ static void file_opendir_cb(uv_fs_t* req) { } TEST_IMPL(fs_readdir_file) { +#if defined(__ASAN__) + RETURN_SKIP("Test does not currently work in ASAN"); +#endif const char* path; int r; diff --git a/deps/uv/test/test-fs.c b/deps/uv/test/test-fs.c index 63189d01d5adc1..0292a96b4802ca 100644 --- a/deps/uv/test/test-fs.c +++ b/deps/uv/test/test-fs.c @@ -673,7 +673,7 @@ static void stat_cb(uv_fs_t* req) { static void sendfile_cb(uv_fs_t* req) { ASSERT(req == &sendfile_req); ASSERT(req->fs_type == UV_FS_SENDFILE); - ASSERT(req->result == 65546); + ASSERT(req->result == 65545); sendfile_cb_count++; uv_fs_req_cleanup(req); } @@ -816,13 +816,44 @@ static void check_utime(const char* path, else r = uv_fs_stat(loop, &req, path, NULL); - ASSERT(r == 0); + ASSERT_EQ(r, 0); - ASSERT(req.result == 0); + ASSERT_EQ(req.result, 0); s = &req.statbuf; - ASSERT(s->st_atim.tv_sec + (s->st_atim.tv_nsec / 1000000000.0) == atime); - ASSERT(s->st_mtim.tv_sec + (s->st_mtim.tv_nsec / 1000000000.0) == mtime); + if (s->st_atim.tv_nsec == 0 && s->st_mtim.tv_nsec == 0) { + /* + * Test sub-second timestamps only when supported (such as Windows with + * NTFS). Some other platforms support sub-second timestamps, but that + * support is filesystem-dependent. Notably OS X (HFS Plus) does NOT + * support sub-second timestamps. But kernels may round or truncate in + * either direction, so we may accept either possible answer. + */ +#ifdef _WIN32 + ASSERT_DOUBLE_EQ(atime, (long) atime); + ASSERT_DOUBLE_EQ(mtime, (long) atime); +#endif + if (atime > 0 || (long) atime == atime) + ASSERT_EQ(s->st_atim.tv_sec, (long) atime); + if (mtime > 0 || (long) mtime == mtime) + ASSERT_EQ(s->st_mtim.tv_sec, (long) mtime); + ASSERT_GE(s->st_atim.tv_sec, (long) atime - 1); + ASSERT_GE(s->st_mtim.tv_sec, (long) mtime - 1); + ASSERT_LE(s->st_atim.tv_sec, (long) atime); + ASSERT_LE(s->st_mtim.tv_sec, (long) mtime); + } else { + double st_atim; + double st_mtim; +#ifndef __APPLE__ + /* TODO(vtjnash): would it be better to normalize this? */ + ASSERT_DOUBLE_GE(s->st_atim.tv_nsec, 0); + ASSERT_DOUBLE_GE(s->st_mtim.tv_nsec, 0); +#endif + st_atim = s->st_atim.tv_sec + s->st_atim.tv_nsec / 1e9; + st_mtim = s->st_mtim.tv_sec + s->st_mtim.tv_nsec / 1e9; + ASSERT_DOUBLE_EQ(st_atim, atime); + ASSERT_DOUBLE_EQ(st_mtim, mtime); + } uv_fs_req_cleanup(&req); } @@ -1159,6 +1190,8 @@ TEST_IMPL(fs_async_dir) { static int test_sendfile(void (*setup)(int), uv_fs_cb cb, off_t expected_size) { int f, r; struct stat s1, s2; + uv_fs_t req; + char buf1[1]; loop = uv_default_loop(); @@ -1188,7 +1221,7 @@ static int test_sendfile(void (*setup)(int), uv_fs_cb cb, off_t expected_size) { uv_fs_req_cleanup(&open_req2); r = uv_fs_sendfile(loop, &sendfile_req, open_req2.result, open_req1.result, - 0, 131072, cb); + 1, 131072, cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); @@ -1203,9 +1236,26 @@ static int test_sendfile(void (*setup)(int), uv_fs_cb cb, off_t expected_size) { ASSERT(0 == stat("test_file", &s1)); ASSERT(0 == stat("test_file2", &s2)); - ASSERT(s1.st_size == s2.st_size); ASSERT(s2.st_size == expected_size); + if (expected_size > 0) { + ASSERT_UINT64_EQ(s1.st_size, s2.st_size + 1); + r = uv_fs_open(NULL, &open_req1, "test_file2", O_RDWR, 0, NULL); + ASSERT(r >= 0); + ASSERT(open_req1.result >= 0); + uv_fs_req_cleanup(&open_req1); + + memset(buf1, 0, sizeof(buf1)); + iov = uv_buf_init(buf1, sizeof(buf1)); + r = uv_fs_read(NULL, &req, open_req1.result, &iov, 1, -1, NULL); + ASSERT(r >= 0); + ASSERT(req.result >= 0); + ASSERT_EQ(buf1[0], 'e'); /* 'e' from begin */ + uv_fs_req_cleanup(&req); + } else { + ASSERT_UINT64_EQ(s1.st_size, s2.st_size); + } + /* Cleanup. */ unlink("test_file"); unlink("test_file2"); @@ -1223,7 +1273,7 @@ static void sendfile_setup(int f) { TEST_IMPL(fs_async_sendfile) { - return test_sendfile(sendfile_setup, sendfile_cb, 65546); + return test_sendfile(sendfile_setup, sendfile_cb, 65545); } @@ -2523,29 +2573,16 @@ TEST_IMPL(fs_utime) { uv_fs_req_cleanup(&req); uv_fs_close(loop, &req, r, NULL); - atime = mtime = 400497753; /* 1982-09-10 11:22:33 */ - - /* - * Test sub-second timestamps only on Windows (assuming NTFS). Some other - * platforms support sub-second timestamps, but that support is filesystem- - * dependent. Notably OS X (HFS Plus) does NOT support sub-second timestamps. - */ -#ifdef _WIN32 - mtime += 0.444; /* 1982-09-10 11:22:33.444 */ -#endif + atime = mtime = 400497753.25; /* 1982-09-10 11:22:33.25 */ r = uv_fs_utime(NULL, &req, path, atime, mtime, NULL); ASSERT(r == 0); ASSERT(req.result == 0); uv_fs_req_cleanup(&req); - r = uv_fs_stat(NULL, &req, path, NULL); - ASSERT(r == 0); - ASSERT(req.result == 0); check_utime(path, atime, mtime, /* test_lutime */ 0); - uv_fs_req_cleanup(&req); - atime = mtime = 1291404900; /* 2010-12-03 20:35:00 - mees <3 */ + atime = mtime = 1291404900.25; /* 2010-12-03 20:35:00.25 - mees <3 */ checkme.path = path; checkme.atime = atime; checkme.mtime = mtime; @@ -2565,6 +2602,45 @@ TEST_IMPL(fs_utime) { } +TEST_IMPL(fs_utime_round) { + const char path[] = "test_file"; + double atime; + double mtime; + uv_fs_t req; + int r; + + loop = uv_default_loop(); + unlink(path); + r = uv_fs_open(NULL, &req, path, O_RDWR | O_CREAT, S_IWUSR | S_IRUSR, NULL); + ASSERT_GE(r, 0); + ASSERT_GE(req.result, 0); + uv_fs_req_cleanup(&req); + ASSERT_EQ(0, uv_fs_close(loop, &req, r, NULL)); + + atime = mtime = -14245440.25; /* 1969-07-20T02:56:00.25Z */ + + r = uv_fs_utime(NULL, &req, path, atime, mtime, NULL); +#if !defined(__linux__) && \ + !defined(_WIN32) && \ + !defined(__APPLE__) && \ + !defined(__FreeBSD__) && \ + !defined(__sun) + if (r != 0) { + ASSERT_EQ(r, UV_EINVAL); + RETURN_SKIP("utime on some OS (z/OS, IBM i PASE, AIX) or filesystems may reject pre-epoch timestamps"); + } +#endif + ASSERT_EQ(0, r); + ASSERT_EQ(0, req.result); + uv_fs_req_cleanup(&req); + check_utime(path, atime, mtime, /* test_lutime */ 0); + unlink(path); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + #ifdef _WIN32 TEST_IMPL(fs_stat_root) { int r; @@ -2618,16 +2694,7 @@ TEST_IMPL(fs_futime) { uv_fs_req_cleanup(&req); uv_fs_close(loop, &req, r, NULL); - atime = mtime = 400497753; /* 1982-09-10 11:22:33 */ - - /* - * Test sub-second timestamps only on Windows (assuming NTFS). Some other - * platforms support sub-second timestamps, but that support is filesystem- - * dependent. Notably OS X (HFS Plus) does NOT support sub-second timestamps. - */ -#ifdef _WIN32 - mtime += 0.444; /* 1982-09-10 11:22:33.444 */ -#endif + atime = mtime = 400497753.25; /* 1982-09-10 11:22:33.25 */ r = uv_fs_open(NULL, &req, path, O_RDWR, 0, NULL); ASSERT(r >= 0); @@ -2645,11 +2712,7 @@ TEST_IMPL(fs_futime) { #endif uv_fs_req_cleanup(&req); - r = uv_fs_stat(NULL, &req, path, NULL); - ASSERT(r == 0); - ASSERT(req.result == 0); check_utime(path, atime, mtime, /* test_lutime */ 0); - uv_fs_req_cleanup(&req); atime = mtime = 1291404900; /* 2010-12-03 20:35:00 - mees <3 */ @@ -2708,11 +2771,7 @@ TEST_IMPL(fs_lutime) { uv_fs_req_cleanup(&req); /* Test the synchronous version. */ - atime = mtime = 400497753; /* 1982-09-10 11:22:33 */ - -#ifdef _WIN32 - mtime += 0.444; /* 1982-09-10 11:22:33.444 */ -#endif + atime = mtime = 400497753.25; /* 1982-09-10 11:22:33.25 */ checkme.atime = atime; checkme.mtime = mtime; @@ -2837,6 +2896,9 @@ TEST_IMPL(fs_scandir_non_existent_dir) { } TEST_IMPL(fs_scandir_file) { +#if defined(__ASAN__) + RETURN_SKIP("Test does not currently work in ASAN"); +#endif const char* path; int r; @@ -3083,6 +3145,9 @@ static void fs_read_bufs(int add_flags) { uv_fs_req_cleanup(&close_req); } TEST_IMPL(fs_read_bufs) { +#if defined(__ASAN__) + RETURN_SKIP("Test does not currently work in ASAN"); +#endif fs_read_bufs(0); fs_read_bufs(UV_FS_O_FILEMAP); @@ -4372,6 +4437,7 @@ TEST_IMPL(fs_invalid_mkdir_name) { loop = uv_default_loop(); r = uv_fs_mkdir(loop, &req, "invalid>", 0, NULL); ASSERT(r == UV_EINVAL); + ASSERT_EQ(UV_EINVAL, uv_fs_mkdir(loop, &req, "test:lol", 0, NULL)); return 0; } diff --git a/deps/uv/test/test-getaddrinfo.c b/deps/uv/test/test-getaddrinfo.c index 628e4d13cc60db..b1fc312349f960 100644 --- a/deps/uv/test/test-getaddrinfo.c +++ b/deps/uv/test/test-getaddrinfo.c @@ -100,7 +100,7 @@ TEST_IMPL(getaddrinfo_fail) { ASSERT(0 == uv_getaddrinfo(uv_default_loop(), &req, getaddrinfo_fail_cb, - "xyzzy.xyzzy.xyzzy.", + "example.invalid.", NULL, NULL)); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); @@ -122,7 +122,7 @@ TEST_IMPL(getaddrinfo_fail_sync) { ASSERT(0 > uv_getaddrinfo(uv_default_loop(), &req, NULL, - "xyzzy.xyzzy.xyzzy.", + "example.invalid.", NULL, NULL)); uv_freeaddrinfo(req.addrinfo); diff --git a/deps/uv/test/test-ipc.c b/deps/uv/test/test-ipc.c index 39ef4f1175b0d4..ba3ba737481532 100644 --- a/deps/uv/test/test-ipc.c +++ b/deps/uv/test/test-ipc.c @@ -693,6 +693,11 @@ static void ipc_on_connection(uv_stream_t* server, int status) { } +static void close_and_free_cb(uv_handle_t* handle) { + close_cb_called++; + free(handle); +} + static void ipc_on_connection_tcp_conn(uv_stream_t* server, int status) { int r; uv_buf_t buf; @@ -721,7 +726,7 @@ static void ipc_on_connection_tcp_conn(uv_stream_t* server, int status) { on_tcp_child_process_read); ASSERT_EQ(r, 0); - uv_close((uv_handle_t*)conn, close_cb); + uv_close((uv_handle_t*)conn, close_and_free_cb); } diff --git a/deps/uv/test/test-list.h b/deps/uv/test/test-list.h index 52b17a69147aa0..d7c7b086f03a18 100644 --- a/deps/uv/test/test-list.h +++ b/deps/uv/test/test-list.h @@ -116,7 +116,8 @@ TEST_DECLARE (tcp_open_bound) TEST_DECLARE (tcp_open_connected) TEST_DECLARE (tcp_connect_error_after_write) TEST_DECLARE (tcp_shutdown_after_write) -TEST_DECLARE (tcp_bind_error_addrinuse) +TEST_DECLARE (tcp_bind_error_addrinuse_connect) +TEST_DECLARE (tcp_bind_error_addrinuse_listen) TEST_DECLARE (tcp_bind_error_addrnotavail_1) TEST_DECLARE (tcp_bind_error_addrnotavail_2) TEST_DECLARE (tcp_bind_error_fault) @@ -359,6 +360,7 @@ TEST_DECLARE (fs_open_flags) TEST_DECLARE (fs_fd_hash) #endif TEST_DECLARE (fs_utime) +TEST_DECLARE (fs_utime_round) TEST_DECLARE (fs_futime) TEST_DECLARE (fs_lutime) TEST_DECLARE (fs_file_open_append) @@ -451,12 +453,16 @@ TEST_DECLARE (poll_nested_epoll) #ifdef UV_HAVE_KQUEUE TEST_DECLARE (poll_nested_kqueue) #endif +TEST_DECLARE (poll_multiple_handles) TEST_DECLARE (ip4_addr) TEST_DECLARE (ip6_addr_link_local) TEST_DECLARE (poll_close_doesnt_corrupt_stack) TEST_DECLARE (poll_closesocket) +TEST_DECLARE (close_fd) +TEST_DECLARE (closed_fd_events) +TEST_DECLARE (spawn_fs_open) #ifdef _WIN32 TEST_DECLARE (spawn_detect_pipe_name_collisions_on_windows) #if !defined(USING_UV_SHARED) @@ -471,8 +477,6 @@ TEST_DECLARE (ipc_listen_after_bind_twice) TEST_DECLARE (win32_signum_number) #else TEST_DECLARE (emfile) -TEST_DECLARE (close_fd) -TEST_DECLARE (spawn_fs_open) TEST_DECLARE (spawn_setuid_setgid) TEST_DECLARE (we_get_signal) TEST_DECLARE (we_get_signals) @@ -481,7 +485,6 @@ TEST_DECLARE (we_get_signals_mixed) TEST_DECLARE (signal_multiple_loops) TEST_DECLARE (signal_pending_on_close) TEST_DECLARE (signal_close_loop_alive) -TEST_DECLARE (closed_fd_events) #endif #ifdef __APPLE__ TEST_DECLARE (osx_select) @@ -567,7 +570,8 @@ TASK_LIST_START #ifndef _WIN32 TEST_ENTRY (pipe_close_stdout_read_stdin) #endif - TEST_ENTRY (pipe_set_non_blocking) + /* Seems to be either about 0.5s or 5s, depending on the OS. */ + TEST_ENTRY_CUSTOM (pipe_set_non_blocking, 0, 0, 20000) TEST_ENTRY (pipe_set_chmod) TEST_ENTRY (tty) #ifdef _WIN32 @@ -671,7 +675,13 @@ TASK_LIST_START TEST_HELPER (tcp_shutdown_after_write, tcp4_echo_server) TEST_ENTRY (tcp_connect_error_after_write) - TEST_ENTRY (tcp_bind_error_addrinuse) + TEST_ENTRY (tcp_bind_error_addrinuse_connect) + /* tcp4_echo_server steals the port. It needs to be a separate process + * because libuv sets setsockopt(SO_REUSEADDR) that lets you steal an + * existing bind if it originates from the same process. + */ + TEST_HELPER (tcp_bind_error_addrinuse_connect, tcp4_echo_server) + TEST_ENTRY (tcp_bind_error_addrinuse_listen) TEST_ENTRY (tcp_bind_error_addrnotavail_1) TEST_ENTRY (tcp_bind_error_addrnotavail_2) TEST_ENTRY (tcp_bind_error_fault) @@ -894,6 +904,7 @@ TASK_LIST_START #ifdef UV_HAVE_KQUEUE TEST_ENTRY (poll_nested_kqueue) #endif + TEST_ENTRY (poll_multiple_handles) TEST_ENTRY (socket_buffer_size) @@ -935,6 +946,9 @@ TASK_LIST_START TEST_ENTRY (poll_close_doesnt_corrupt_stack) TEST_ENTRY (poll_closesocket) + TEST_ENTRY (close_fd) + TEST_ENTRY (closed_fd_events) + TEST_ENTRY (spawn_fs_open) #ifdef _WIN32 TEST_ENTRY (spawn_detect_pipe_name_collisions_on_windows) #if !defined(USING_UV_SHARED) @@ -949,8 +963,6 @@ TASK_LIST_START TEST_ENTRY (win32_signum_number) #else TEST_ENTRY (emfile) - TEST_ENTRY (close_fd) - TEST_ENTRY (spawn_fs_open) TEST_ENTRY (spawn_setuid_setgid) TEST_ENTRY (we_get_signal) TEST_ENTRY (we_get_signals) @@ -959,7 +971,6 @@ TASK_LIST_START TEST_ENTRY (signal_multiple_loops) TEST_ENTRY (signal_pending_on_close) TEST_ENTRY (signal_close_loop_alive) - TEST_ENTRY (closed_fd_events) #endif #ifdef __APPLE__ @@ -988,6 +999,7 @@ TASK_LIST_START #endif TEST_ENTRY (fs_chown) TEST_ENTRY (fs_utime) + TEST_ENTRY (fs_utime_round) TEST_ENTRY (fs_futime) TEST_ENTRY (fs_lutime) TEST_ENTRY (fs_readlink) diff --git a/deps/uv/test/test-ping-pong.c b/deps/uv/test/test-ping-pong.c index 7f7758b3b2ebea..4a26e4dee1b0f5 100644 --- a/deps/uv/test/test-ping-pong.c +++ b/deps/uv/test/test-ping-pong.c @@ -24,6 +24,7 @@ #include #include +#include /* strlen */ static int completed_pingers = 0; @@ -33,23 +34,21 @@ static int completed_pingers = 0; #define NUM_PINGS 1000 #endif -/* 64 bytes is enough for a pinger */ -#define BUFSIZE 10240 - static char PING[] = "PING\n"; +static char PONG[] = "PONG\n"; static int pinger_on_connect_count; typedef struct { int vectored_writes; - int pongs; - int state; + unsigned pongs; + unsigned state; union { uv_tcp_t tcp; uv_pipe_t pipe; } stream; uv_connect_t connect_req; - char read_buffer[BUFSIZE]; + char* pong; } pinger_t; @@ -59,28 +58,44 @@ static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { } +static void ponger_on_close(uv_handle_t* handle) { + if (handle->data) + free(handle->data); + else + free(handle); +} + + static void pinger_on_close(uv_handle_t* handle) { - pinger_t* pinger = (pinger_t*)handle->data; + pinger_t* pinger = (pinger_t*) handle->data; - ASSERT(NUM_PINGS == pinger->pongs); + ASSERT_EQ(NUM_PINGS, pinger->pongs); - free(pinger); + if (handle == (uv_handle_t*) &pinger->stream.tcp) { + free(pinger); /* also frees handle */ + } else { + uv_close((uv_handle_t*) &pinger->stream.tcp, ponger_on_close); + free(handle); + } completed_pingers++; } static void pinger_after_write(uv_write_t* req, int status) { - ASSERT(status == 0); + ASSERT_EQ(status, 0); free(req); } static void pinger_write_ping(pinger_t* pinger) { + uv_stream_t* stream; uv_write_t* req; uv_buf_t bufs[sizeof PING - 1]; int i, nbufs; + stream = (uv_stream_t*) &pinger->stream.tcp; + if (!pinger->vectored_writes) { /* Write a single buffer. */ nbufs = 1; @@ -94,13 +109,8 @@ static void pinger_write_ping(pinger_t* pinger) { } req = malloc(sizeof(*req)); - if (uv_write(req, - (uv_stream_t*) &pinger->stream.tcp, - bufs, - nbufs, - pinger_after_write)) { - FATAL("uv_write failed"); - } + ASSERT_NOT_NULL(req); + ASSERT_EQ(0, uv_write(req, stream, bufs, nbufs, pinger_after_write)); puts("PING"); } @@ -115,20 +125,20 @@ static void pinger_read_cb(uv_stream_t* stream, pinger = (pinger_t*) stream->data; if (nread < 0) { - ASSERT(nread == UV_EOF); + ASSERT_EQ(nread, UV_EOF); puts("got EOF"); free(buf->base); - uv_close((uv_handle_t*)(&pinger->stream.tcp), pinger_on_close); + uv_close((uv_handle_t*) stream, pinger_on_close); return; } - /* Now we count the pings */ + /* Now we count the pongs */ for (i = 0; i < nread; i++) { - ASSERT(buf->base[i] == PING[pinger->state]); - pinger->state = (pinger->state + 1) % (sizeof(PING) - 1); + ASSERT_EQ(buf->base[i], pinger->pong[pinger->state]); + pinger->state = (pinger->state + 1) % strlen(pinger->pong); if (pinger->state != 0) continue; @@ -139,7 +149,7 @@ static void pinger_read_cb(uv_stream_t* stream, if (pinger->pongs < NUM_PINGS) { pinger_write_ping(pinger); } else { - uv_close((uv_handle_t*)(&pinger->stream.tcp), pinger_on_close); + uv_close((uv_handle_t*) stream, pinger_on_close); break; } } @@ -148,20 +158,53 @@ static void pinger_read_cb(uv_stream_t* stream, } +static void ponger_read_cb(uv_stream_t* stream, + ssize_t nread, + const uv_buf_t* buf) { + uv_buf_t writebuf; + uv_write_t* req; + int i; + + if (nread < 0) { + ASSERT_EQ(nread, UV_EOF); + + puts("got EOF"); + free(buf->base); + + uv_close((uv_handle_t*) stream, ponger_on_close); + + return; + } + + /* Echo back */ + for (i = 0; i < nread; i++) { + if (buf->base[i] == 'I') + buf->base[i] = 'O'; + } + + writebuf = uv_buf_init(buf->base, nread); + req = malloc(sizeof(*req)); + ASSERT_NOT_NULL(req); + ASSERT_EQ(0, uv_write(req, stream, &writebuf, 1, pinger_after_write)); +} + + static void pinger_on_connect(uv_connect_t* req, int status) { - pinger_t* pinger = (pinger_t*)req->handle->data; + pinger_t* pinger = (pinger_t*) req->handle->data; pinger_on_connect_count++; - ASSERT(status == 0); + ASSERT_EQ(status, 0); - ASSERT(1 == uv_is_readable(req->handle)); - ASSERT(1 == uv_is_writable(req->handle)); - ASSERT(0 == uv_is_closing((uv_handle_t *) req->handle)); + ASSERT_EQ(1, uv_is_readable(req->handle)); + ASSERT_EQ(1, uv_is_writable(req->handle)); + ASSERT_EQ(0, uv_is_closing((uv_handle_t *) req->handle)); pinger_write_ping(pinger); - uv_read_start((uv_stream_t*)(req->handle), alloc_cb, pinger_read_cb); + ASSERT_EQ(0, uv_read_start((uv_stream_t*) req->handle, + alloc_cb, + pinger_read_cb)); } @@ -172,17 +215,18 @@ static void tcp_pinger_v6_new(int vectored_writes) { pinger_t* pinger; - ASSERT(0 == uv_ip6_addr("::1", TEST_PORT, &server_addr)); + ASSERT_EQ(0, uv_ip6_addr("::1", TEST_PORT, &server_addr)); pinger = malloc(sizeof(*pinger)); - ASSERT(pinger != NULL); + ASSERT_NOT_NULL(pinger); pinger->vectored_writes = vectored_writes; pinger->state = 0; pinger->pongs = 0; + pinger->pong = PING; /* Try to connect to the server and do NUM_PINGS ping-pongs. */ r = uv_tcp_init(uv_default_loop(), &pinger->stream.tcp); pinger->stream.tcp.data = pinger; - ASSERT(!r); + ASSERT_EQ(0, r); /* We are never doing multiple reads/connects at a time anyway, so these * handles can be pre-initialized. */ @@ -190,10 +234,10 @@ static void tcp_pinger_v6_new(int vectored_writes) { &pinger->stream.tcp, (const struct sockaddr*) &server_addr, pinger_on_connect); - ASSERT(!r); + ASSERT_EQ(0, r); /* Synchronous connect callbacks are not allowed. */ - ASSERT(pinger_on_connect_count == 0); + ASSERT_EQ(pinger_on_connect_count, 0); } @@ -202,17 +246,18 @@ static void tcp_pinger_new(int vectored_writes) { struct sockaddr_in server_addr; pinger_t* pinger; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); + ASSERT_EQ(0, uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); pinger = malloc(sizeof(*pinger)); - ASSERT(pinger != NULL); + ASSERT_NOT_NULL(pinger); pinger->vectored_writes = vectored_writes; pinger->state = 0; pinger->pongs = 0; + pinger->pong = PING; /* Try to connect to the server and do NUM_PINGS ping-pongs. */ r = uv_tcp_init(uv_default_loop(), &pinger->stream.tcp); pinger->stream.tcp.data = pinger; - ASSERT(!r); + ASSERT_EQ(0, r); /* We are never doing multiple reads/connects at a time anyway, so these * handles can be pre-initialized. */ @@ -220,10 +265,10 @@ static void tcp_pinger_new(int vectored_writes) { &pinger->stream.tcp, (const struct sockaddr*) &server_addr, pinger_on_connect); - ASSERT(!r); + ASSERT_EQ(0, r); /* Synchronous connect callbacks are not allowed. */ - ASSERT(pinger_on_connect_count == 0); + ASSERT_EQ(pinger_on_connect_count, 0); } @@ -232,15 +277,16 @@ static void pipe_pinger_new(int vectored_writes) { pinger_t* pinger; pinger = malloc(sizeof(*pinger)); - ASSERT(pinger != NULL); + ASSERT_NOT_NULL(pinger); pinger->vectored_writes = vectored_writes; pinger->state = 0; pinger->pongs = 0; + pinger->pong = PING; /* Try to connect to the server and do NUM_PINGS ping-pongs. */ r = uv_pipe_init(uv_default_loop(), &pinger->stream.pipe, 0); pinger->stream.pipe.data = pinger; - ASSERT(!r); + ASSERT_EQ(0, r); /* We are never doing multiple reads/connects at a time anyway, so these * handles can be pre-initialized. */ @@ -248,13 +294,86 @@ static void pipe_pinger_new(int vectored_writes) { pinger_on_connect); /* Synchronous connect callbacks are not allowed. */ - ASSERT(pinger_on_connect_count == 0); + ASSERT_EQ(pinger_on_connect_count, 0); } +static void socketpair_pinger_new(int vectored_writes) { + pinger_t* pinger; + uv_os_sock_t fds[2]; + uv_tcp_t* ponger; + + pinger = malloc(sizeof(*pinger)); + ASSERT_NOT_NULL(pinger); + pinger->vectored_writes = vectored_writes; + pinger->state = 0; + pinger->pongs = 0; + pinger->pong = PONG; + + /* Try to make a socketpair and do NUM_PINGS ping-pongs. */ + (void)uv_default_loop(); /* ensure WSAStartup has been performed */ + ASSERT_EQ(0, uv_socketpair(SOCK_STREAM, 0, fds, UV_NONBLOCK_PIPE, UV_NONBLOCK_PIPE)); +#ifndef _WIN32 + /* On Windows, this is actually a UV_TCP, but libuv doesn't detect that. */ + ASSERT_EQ(uv_guess_handle((uv_file) fds[0]), UV_NAMED_PIPE); + ASSERT_EQ(uv_guess_handle((uv_file) fds[1]), UV_NAMED_PIPE); +#endif + + ASSERT_EQ(0, uv_tcp_init(uv_default_loop(), &pinger->stream.tcp)); + pinger->stream.pipe.data = pinger; + ASSERT_EQ(0, uv_tcp_open(&pinger->stream.tcp, fds[1])); + + ponger = malloc(sizeof(*ponger)); + ASSERT_NOT_NULL(ponger); + ponger->data = NULL; + ASSERT_EQ(0, uv_tcp_init(uv_default_loop(), ponger)); + ASSERT_EQ(0, uv_tcp_open(ponger, fds[0])); + + pinger_write_ping(pinger); + + ASSERT_EQ(0, uv_read_start((uv_stream_t*) &pinger->stream.tcp, + alloc_cb, + pinger_read_cb)); + ASSERT_EQ(0, uv_read_start((uv_stream_t*) ponger, + alloc_cb, + ponger_read_cb)); +} + + +static void pipe2_pinger_new(int vectored_writes) { + uv_file fds[2]; + pinger_t* pinger; + uv_pipe_t* ponger; + + /* Try to make a pipe and do NUM_PINGS pings. */ + ASSERT_EQ(0, uv_pipe(fds, UV_NONBLOCK_PIPE, UV_NONBLOCK_PIPE)); + ASSERT_EQ(uv_guess_handle(fds[0]), UV_NAMED_PIPE); + ASSERT_EQ(uv_guess_handle(fds[1]), UV_NAMED_PIPE); + + ponger = malloc(sizeof(*ponger)); + ASSERT_NOT_NULL(ponger); + ASSERT_EQ(0, uv_pipe_init(uv_default_loop(), ponger, 0)); + ASSERT_EQ(0, uv_pipe_open(ponger, fds[0])); + + pinger = malloc(sizeof(*pinger)); + ASSERT_NOT_NULL(pinger); + pinger->vectored_writes = vectored_writes; + pinger->state = 0; + pinger->pongs = 0; + pinger->pong = PING; + ASSERT_EQ(0, uv_pipe_init(uv_default_loop(), &pinger->stream.pipe, 0)); + ASSERT_EQ(0, uv_pipe_open(&pinger->stream.pipe, fds[1])); + pinger->stream.pipe.data = pinger; /* record for close_cb */ + ponger->data = pinger; /* record for read_cb */ + + pinger_write_ping(pinger); + + ASSERT_EQ(0, uv_read_start((uv_stream_t*) ponger, alloc_cb, pinger_read_cb)); +} + static int run_ping_pong_test(void) { uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(completed_pingers == 1); + ASSERT_EQ(completed_pingers, 1); MAKE_VALGRIND_HAPPY(); return 0; @@ -263,12 +382,20 @@ static int run_ping_pong_test(void) { TEST_IMPL(tcp_ping_pong) { tcp_pinger_new(0); + run_ping_pong_test(); + + completed_pingers = 0; + socketpair_pinger_new(0); return run_ping_pong_test(); } TEST_IMPL(tcp_ping_pong_vec) { tcp_pinger_new(1); + run_ping_pong_test(); + + completed_pingers = 0; + socketpair_pinger_new(1); return run_ping_pong_test(); } @@ -291,11 +418,19 @@ TEST_IMPL(tcp6_ping_pong_vec) { TEST_IMPL(pipe_ping_pong) { pipe_pinger_new(0); + run_ping_pong_test(); + + completed_pingers = 0; + pipe2_pinger_new(0); return run_ping_pong_test(); } TEST_IMPL(pipe_ping_pong_vec) { pipe_pinger_new(1); + run_ping_pong_test(); + + completed_pingers = 0; + pipe2_pinger_new(1); return run_ping_pong_test(); } diff --git a/deps/uv/test/test-pipe-connect-error.c b/deps/uv/test/test-pipe-connect-error.c index ebb2a6ca826ce4..8bba328a344f17 100644 --- a/deps/uv/test/test-pipe-connect-error.c +++ b/deps/uv/test/test-pipe-connect-error.c @@ -76,6 +76,9 @@ TEST_IMPL(pipe_connect_bad_name) { TEST_IMPL(pipe_connect_to_file) { +#if defined(__ASAN__) + RETURN_SKIP("Test does not currently work in ASAN"); +#endif const char* path = "test/fixtures/empty_file"; uv_pipe_t client; uv_connect_t req; diff --git a/deps/uv/test/test-pipe-getsockname.c b/deps/uv/test/test-pipe-getsockname.c index 48ee400e74cf97..5f377dcbb60696 100644 --- a/deps/uv/test/test-pipe-getsockname.c +++ b/deps/uv/test/test-pipe-getsockname.c @@ -225,7 +225,9 @@ TEST_IMPL(pipe_getsockname_blocking) { ASSERT(r != -1); r = uv_pipe_open(&pipe_client, readfd); ASSERT(r == 0); - r = uv_read_start((uv_stream_t*)&pipe_client, NULL, NULL); + r = uv_read_start((uv_stream_t*) &pipe_client, + (uv_alloc_cb) abort, + (uv_read_cb) abort); ASSERT(r == 0); Sleep(100); r = uv_read_stop((uv_stream_t*)&pipe_client); @@ -236,7 +238,9 @@ TEST_IMPL(pipe_getsockname_blocking) { ASSERT(r == 0); ASSERT(len1 == 0); /* It's an annonymous pipe. */ - r = uv_read_start((uv_stream_t*)&pipe_client, NULL, NULL); + r = uv_read_start((uv_stream_t*)&pipe_client, + (uv_alloc_cb) abort, + (uv_read_cb) abort); ASSERT(r == 0); Sleep(100); diff --git a/deps/uv/test/test-pipe-set-non-blocking.c b/deps/uv/test/test-pipe-set-non-blocking.c index 626b53f09a2776..c45148f2bd028d 100644 --- a/deps/uv/test/test-pipe-set-non-blocking.c +++ b/deps/uv/test/test-pipe-set-non-blocking.c @@ -16,18 +16,10 @@ #include "uv.h" #include "task.h" -#ifdef _WIN32 - -TEST_IMPL(pipe_set_non_blocking) { - RETURN_SKIP("Test not implemented on Windows."); -} - -#else /* !_WIN32 */ - #include /* memset */ +#ifndef _WIN32 #include /* close */ -#include -#include +#endif struct thread_ctx { uv_barrier_t barrier; @@ -54,8 +46,27 @@ static void thread_main(void* arg) { uv_fs_req_cleanup(&req); } while (n > 0 || (n == -1 && uv_errno == UV_EINTR)); +#ifdef _WIN32 + ASSERT(n == UV_EOF); +#else ASSERT(n == 0); +#endif +} + + +#ifdef _WIN32 +static void write_cb(uv_write_t* req, int status) { + ASSERT(status == 0); + req->handle = NULL; /* signal completion of write_cb */ } +#endif + +#ifdef _WIN32 +#define NWRITES (10 << 16) +#else +#define NWRITES (10 << 20) +#endif + TEST_IMPL(pipe_set_non_blocking) { struct thread_ctx ctx; @@ -66,9 +77,12 @@ TEST_IMPL(pipe_set_non_blocking) { uv_buf_t buf; uv_file fd[2]; int n; +#ifdef _WIN32 + uv_write_t write_req; +#endif ASSERT(0 == uv_pipe_init(uv_default_loop(), &pipe_handle, 0)); - ASSERT(0 == socketpair(AF_UNIX, SOCK_STREAM, 0, fd)); + ASSERT(0 == uv_pipe(fd, 0, 0)); ASSERT(0 == uv_pipe_open(&pipe_handle, fd[1])); ASSERT(0 == uv_stream_set_blocking((uv_stream_t*) &pipe_handle, 1)); fd[1] = -1; /* fd[1] is owned by pipe_handle now. */ @@ -83,11 +97,20 @@ TEST_IMPL(pipe_set_non_blocking) { memset(data, '.', sizeof(data)); nwritten = 0; - while (nwritten < 10 << 20) { + while (nwritten < NWRITES) { /* The stream is in blocking mode so uv_try_write() should always succeed * with the exact number of bytes that we wanted written. */ n = uv_try_write((uv_stream_t*) &pipe_handle, &buf, 1); +#ifdef _WIN32 + ASSERT(n == UV_EAGAIN); /* E_NOTIMPL */ + ASSERT(0 == uv_write(&write_req, (uv_stream_t*) &pipe_handle, &buf, 1, write_cb)); + ASSERT(write_req.handle != NULL); + ASSERT(1 == uv_run(uv_default_loop(), UV_RUN_ONCE)); /* queue write_cb */ + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); /* process write_cb */ + ASSERT(write_req.handle == NULL); /* check for signaled completion of write_cb */ + n = buf.len; +#endif ASSERT(n == sizeof(data)); nwritten += n; } @@ -96,12 +119,14 @@ TEST_IMPL(pipe_set_non_blocking) { ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); ASSERT(0 == uv_thread_join(&thread)); +#ifdef _WIN32 + ASSERT(0 == _close(fd[0])); /* fd[1] is closed by uv_close(). */ +#else ASSERT(0 == close(fd[0])); /* fd[1] is closed by uv_close(). */ +#endif fd[0] = -1; uv_barrier_destroy(&ctx.barrier); MAKE_VALGRIND_HAPPY(); return 0; } - -#endif /* !_WIN32 */ diff --git a/deps/uv/test/test-platform-output.c b/deps/uv/test/test-platform-output.c index f547ddfd7696ff..341c7ae54ed28b 100644 --- a/deps/uv/test/test-platform-output.c +++ b/deps/uv/test/test-platform-output.c @@ -155,6 +155,7 @@ TEST_IMPL(platform_output) { printf(" username: %s\n", pwd.username); printf(" shell: %s\n", pwd.shell); printf(" home directory: %s\n", pwd.homedir); + uv_os_free_passwd(&pwd); pid = uv_os_getpid(); ASSERT(pid > 0); diff --git a/deps/uv/test/test-poll-multiple-handles.c b/deps/uv/test/test-poll-multiple-handles.c new file mode 100644 index 00000000000000..fc2205ddec74d5 --- /dev/null +++ b/deps/uv/test/test-poll-multiple-handles.c @@ -0,0 +1,99 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 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. + */ + +#include + +#ifndef _WIN32 +# include +# include +# include +#endif + +#include "uv.h" +#include "task.h" + + +static int close_cb_called = 0; + + +static void close_cb(uv_handle_t* handle) { + close_cb_called++; +} + +static void poll_cb(uv_poll_t* handle, int status, int events) { + /* Not a bound socket, linux immediately reports UV_READABLE, other OS do not */ + ASSERT(events == UV_READABLE); +} + +TEST_IMPL(poll_multiple_handles) { + uv_os_sock_t sock; + uv_poll_t first_poll_handle, second_poll_handle; + +#ifdef _WIN32 + { + struct WSAData wsa_data; + int r = WSAStartup(MAKEWORD(2, 2), &wsa_data); + ASSERT(r == 0); + } +#endif + + sock = socket(AF_INET, SOCK_STREAM, 0); +#ifdef _WIN32 + ASSERT(sock != INVALID_SOCKET); +#else + ASSERT(sock != -1); +#endif + ASSERT(0 == uv_poll_init_socket(uv_default_loop(), &first_poll_handle, sock)); + ASSERT(0 == uv_poll_init_socket(uv_default_loop(), &second_poll_handle, sock)); + + ASSERT(0 == uv_poll_start(&first_poll_handle, UV_READABLE, poll_cb)); + + /* We may not start polling while another polling handle is active + * on that fd. + */ +#ifndef _WIN32 + /* We do not track handles in an O(1) lookupable way on Windows, + * so not checking that here. + */ + ASSERT(uv_poll_start(&second_poll_handle, UV_READABLE, poll_cb) == UV_EEXIST); +#endif + + /* After stopping the other polling handle, we now should be able to poll */ + ASSERT(0 == uv_poll_stop(&first_poll_handle)); + ASSERT(0 == uv_poll_start(&second_poll_handle, UV_READABLE, poll_cb)); + + /* Closing an already stopped polling handle is safe in any case */ + uv_close((uv_handle_t*) &first_poll_handle, close_cb); + + uv_unref((uv_handle_t*) &second_poll_handle); + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT(close_cb_called == 1); + uv_ref((uv_handle_t*) &second_poll_handle); + + ASSERT(uv_is_active((uv_handle_t*) &second_poll_handle)); + uv_close((uv_handle_t*) &second_poll_handle, close_cb); + + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT(close_cb_called == 2); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/deps/uv/test/test-shutdown-eof.c b/deps/uv/test/test-shutdown-eof.c index 9f95e7561f26c7..0abab9175e9d26 100644 --- a/deps/uv/test/test-shutdown-eof.c +++ b/deps/uv/test/test-shutdown-eof.c @@ -89,7 +89,13 @@ static void connect_cb(uv_connect_t *req, int status) { ASSERT(req == &connect_req); /* Start reading from our connection so we can receive the EOF. */ - uv_read_start((uv_stream_t*)&tcp, alloc_cb, read_cb); + ASSERT_EQ(0, uv_read_start((uv_stream_t*)&tcp, alloc_cb, read_cb)); + + /* Check error handling. */ + ASSERT_EQ(UV_EALREADY, uv_read_start((uv_stream_t*)&tcp, alloc_cb, read_cb)); + ASSERT_EQ(UV_EINVAL, uv_read_start(NULL, alloc_cb, read_cb)); + ASSERT_EQ(UV_EINVAL, uv_read_start((uv_stream_t*)&tcp, NULL, read_cb)); + ASSERT_EQ(UV_EINVAL, uv_read_start((uv_stream_t*)&tcp, alloc_cb, NULL)); /* * Write the letter 'Q' to gracefully kill the echo-server. This will not diff --git a/deps/uv/test/test-spawn.c b/deps/uv/test/test-spawn.c index d1757337a6d468..886ddaf63b509d 100644 --- a/deps/uv/test/test-spawn.c +++ b/deps/uv/test/test-spawn.c @@ -29,11 +29,9 @@ #include #ifdef _WIN32 -# if defined(__MINGW32__) -# include -# endif # include # include + typedef BOOL (WINAPI *sCompareObjectHandles)(_In_ HANDLE, _In_ HANDLE); #else # include # include @@ -49,9 +47,7 @@ static char exepath[1024]; static size_t exepath_size = 1024; static char* args[5]; static int no_term_signal; -#ifndef _WIN32 static int timer_counter; -#endif static uv_tcp_t tcp_server; #define OUTPUT_SIZE 1024 @@ -140,12 +136,10 @@ static void on_read(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { } -#ifndef _WIN32 static void on_read_once(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { uv_read_stop(tcp); on_read(tcp, nread, buf); } -#endif static void write_cb(uv_write_t* req, int status) { @@ -154,6 +148,11 @@ static void write_cb(uv_write_t* req, int status) { } +static void write_null_cb(uv_write_t* req, int status) { + ASSERT(status == 0); +} + + static void init_process_options(char* test, uv_exit_cb exit_cb) { /* Note spawn_helper1 defined in test/run-tests.c */ int r = uv_exepath(exepath, &exepath_size); @@ -177,11 +176,9 @@ static void timer_cb(uv_timer_t* handle) { } -#ifndef _WIN32 static void timer_counter_cb(uv_timer_t* handle) { ++timer_counter; } -#endif TEST_IMPL(spawn_fails) { @@ -1328,9 +1325,8 @@ TEST_IMPL(environment_creation) { found = 1; } } - if (prev) { /* verify sort order -- requires Vista */ -#if _WIN32_WINNT >= 0x0600 && \ - (!defined(__MINGW32__) || defined(__MINGW64_VERSION_MAJOR)) + if (prev) { /* verify sort order */ +#if !defined(__MINGW32__) || defined(__MINGW64_VERSION_MAJOR) ASSERT(CompareStringOrdinal(prev, -1, str, -1, TRUE) == 1); #endif } @@ -1579,17 +1575,27 @@ TEST_IMPL(spawn_auto_unref) { } -#ifndef _WIN32 TEST_IMPL(spawn_fs_open) { - int fd; + int r; + uv_os_fd_t fd; + uv_os_fd_t dup_fd; uv_fs_t fs_req; uv_pipe_t in; uv_write_t write_req; + uv_write_t write_req2; uv_buf_t buf; uv_stdio_container_t stdio[1]; +#ifdef _WIN32 + const char dev_null[] = "NUL"; + HMODULE kernelbase_module; + sCompareObjectHandles pCompareObjectHandles; /* function introduced in Windows 10 */ +#else + const char dev_null[] = "/dev/null"; +#endif - fd = uv_fs_open(NULL, &fs_req, "/dev/null", O_RDWR, 0, NULL); - ASSERT(fd >= 0); + r = uv_fs_open(NULL, &fs_req, dev_null, O_RDWR, 0, NULL); + ASSERT(r != -1); + fd = uv_get_osfhandle((uv_file) fs_req.result); uv_fs_req_cleanup(&fs_req); init_process_options("spawn_helper8", exit_cb); @@ -1601,13 +1607,28 @@ TEST_IMPL(spawn_fs_open) { options.stdio[0].data.stream = (uv_stream_t*) ∈ options.stdio_count = 1; + /* make an inheritable copy */ +#ifdef _WIN32 + ASSERT(0 != DuplicateHandle(GetCurrentProcess(), fd, GetCurrentProcess(), &dup_fd, + 0, /* inherit */ TRUE, DUPLICATE_SAME_ACCESS)); + kernelbase_module = GetModuleHandleA("kernelbase.dll"); + pCompareObjectHandles = (sCompareObjectHandles) + GetProcAddress(kernelbase_module, "CompareObjectHandles"); + ASSERT(pCompareObjectHandles == NULL || pCompareObjectHandles(fd, dup_fd)); +#else + dup_fd = dup(fd); +#endif + ASSERT(0 == uv_spawn(uv_default_loop(), &process, &options)); buf = uv_buf_init((char*) &fd, sizeof(fd)); - ASSERT(0 == uv_write(&write_req, (uv_stream_t*) &in, &buf, 1, write_cb)); + ASSERT(0 == uv_write(&write_req, (uv_stream_t*) &in, &buf, 1, write_null_cb)); + + buf = uv_buf_init((char*) &dup_fd, sizeof(fd)); + ASSERT(0 == uv_write(&write_req2, (uv_stream_t*) &in, &buf, 1, write_cb)); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(0 == uv_fs_close(NULL, &fs_req, fd, NULL)); + ASSERT(0 == uv_fs_close(NULL, &fs_req, r, NULL)); ASSERT(exit_cb_called == 1); ASSERT(close_cb_called == 2); /* One for `in`, one for process */ @@ -1615,17 +1636,20 @@ TEST_IMPL(spawn_fs_open) { MAKE_VALGRIND_HAPPY(); return 0; } -#endif /* !_WIN32 */ -#ifndef _WIN32 TEST_IMPL(closed_fd_events) { uv_stdio_container_t stdio[3]; uv_pipe_t pipe_handle; - int fd[2]; + uv_fs_t req; + uv_buf_t bufs[1]; + uv_file fd[2]; + bufs[0] = uv_buf_init("", 1); /* create a pipe and share it with a child process */ - ASSERT(0 == pipe(fd)); + ASSERT(0 == uv_pipe(fd, 0, 0)); + ASSERT(fd[0] > 2); + ASSERT(fd[1] > 2); /* spawn_helper4 blocks indefinitely. */ init_process_options("spawn_helper4", exit_cb); @@ -1642,12 +1666,18 @@ TEST_IMPL(closed_fd_events) { /* read from the pipe with uv */ ASSERT(0 == uv_pipe_init(uv_default_loop(), &pipe_handle, 0)); ASSERT(0 == uv_pipe_open(&pipe_handle, fd[0])); + /* uv_pipe_open() takes ownership of the file descriptor. */ fd[0] = -1; ASSERT(0 == uv_read_start((uv_stream_t*) &pipe_handle, on_alloc, on_read_once)); - ASSERT(1 == write(fd[1], "", 1)); + ASSERT(1 == uv_fs_write(NULL, &req, fd[1], bufs, 1, -1, NULL)); + ASSERT(req.result == 1); + uv_fs_req_cleanup(&req); +#ifdef _WIN32 + ASSERT(1 == uv_run(uv_default_loop(), UV_RUN_ONCE)); +#endif ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); /* should have received just one byte */ @@ -1656,7 +1686,9 @@ TEST_IMPL(closed_fd_events) { /* close the pipe and see if we still get events */ uv_close((uv_handle_t*) &pipe_handle, close_cb); - ASSERT(1 == write(fd[1], "", 1)); + ASSERT(1 == uv_fs_write(NULL, &req, fd[1], bufs, 1, -1, NULL)); + ASSERT(req.result == 1); + uv_fs_req_cleanup(&req); ASSERT(0 == uv_timer_init(uv_default_loop(), &timer)); ASSERT(0 == uv_timer_start(&timer, timer_counter_cb, 10, 0)); @@ -1669,13 +1701,17 @@ TEST_IMPL(closed_fd_events) { ASSERT(timer_counter == 1); /* cleanup */ - ASSERT(0 == uv_process_kill(&process, /* SIGTERM */ 15)); + ASSERT(0 == uv_process_kill(&process, SIGTERM)); +#ifdef _WIN32 + ASSERT(0 == _close(fd[1])); +#else ASSERT(0 == close(fd[1])); +#endif MAKE_VALGRIND_HAPPY(); return 0; } -#endif /* !_WIN32 */ + TEST_IMPL(spawn_reads_child_path) { int r; @@ -1746,38 +1782,6 @@ TEST_IMPL(spawn_reads_child_path) { return 0; } -#ifndef _WIN32 -static int mpipe(int *fds) { - if (pipe(fds) == -1) - return -1; - if (fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 || - fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) { - close(fds[0]); - close(fds[1]); - return -1; - } - return 0; -} -#else -static int mpipe(int *fds) { - SECURITY_ATTRIBUTES attr; - HANDLE readh, writeh; - attr.nLength = sizeof(attr); - attr.lpSecurityDescriptor = NULL; - attr.bInheritHandle = FALSE; - if (!CreatePipe(&readh, &writeh, &attr, 0)) - return -1; - fds[0] = _open_osfhandle((intptr_t)readh, 0); - fds[1] = _open_osfhandle((intptr_t)writeh, 0); - if (fds[0] == -1 || fds[1] == -1) { - CloseHandle(readh); - CloseHandle(writeh); - return -1; - } - return 0; -} -#endif /* !_WIN32 */ - TEST_IMPL(spawn_inherit_streams) { uv_process_t child_req; uv_stdio_container_t child_stdio[2]; @@ -1803,8 +1807,8 @@ TEST_IMPL(spawn_inherit_streams) { ASSERT(uv_pipe_init(loop, &pipe_stdin_parent, 0) == 0); ASSERT(uv_pipe_init(loop, &pipe_stdout_parent, 0) == 0); - ASSERT(mpipe(fds_stdin) != -1); - ASSERT(mpipe(fds_stdout) != -1); + ASSERT(uv_pipe(fds_stdin, 0, 0) == 0); + ASSERT(uv_pipe(fds_stdout, 0, 0) == 0); ASSERT(uv_pipe_open(&pipe_stdin_child, fds_stdin[0]) == 0); ASSERT(uv_pipe_open(&pipe_stdout_child, fds_stdout[1]) == 0); diff --git a/deps/uv/test/test-tcp-bind-error.c b/deps/uv/test/test-tcp-bind-error.c index f95efd9f0c8900..7732267f44f54e 100644 --- a/deps/uv/test/test-tcp-bind-error.c +++ b/deps/uv/test/test-tcp-bind-error.c @@ -25,6 +25,7 @@ #include +static int connect_cb_called = 0; static int close_cb_called = 0; @@ -34,7 +35,49 @@ static void close_cb(uv_handle_t* handle) { } -TEST_IMPL(tcp_bind_error_addrinuse) { +static void connect_cb(uv_connect_t* req, int status) { + ASSERT(status == UV_EADDRINUSE); + uv_close((uv_handle_t*) req->handle, close_cb); + connect_cb_called++; +} + + +TEST_IMPL(tcp_bind_error_addrinuse_connect) { + struct sockaddr_in addr; + int addrlen; + uv_connect_t req; + uv_tcp_t conn; + + /* 127.0.0.1: is already taken by tcp4_echo_server running in + * another process. uv_tcp_bind() and uv_tcp_connect() should still succeed + * (greatest common denominator across platforms) but the connect callback + * should receive an UV_EADDRINUSE error. + */ + ASSERT(0 == uv_tcp_init(uv_default_loop(), &conn)); + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT(0 == uv_tcp_bind(&conn, (const struct sockaddr*) &addr, 0)); + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT + 1, &addr)); + ASSERT(0 == uv_tcp_connect(&req, + &conn, + (const struct sockaddr*) &addr, + connect_cb)); + + addrlen = sizeof(addr); + ASSERT(UV_EADDRINUSE == uv_tcp_getsockname(&conn, + (struct sockaddr*) &addr, + &addrlen)); + + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT(connect_cb_called == 1); + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(tcp_bind_error_addrinuse_listen) { struct sockaddr_in addr; uv_tcp_t server1, server2; int r; @@ -240,7 +283,9 @@ TEST_IMPL(tcp_bind_writable_flags) { ASSERT(r == UV_EPIPE); r = uv_shutdown(&shutdown_req, (uv_stream_t*) &server, NULL); ASSERT(r == UV_ENOTCONN); - r = uv_read_start((uv_stream_t*) &server, NULL, NULL); + r = uv_read_start((uv_stream_t*) &server, + (uv_alloc_cb) abort, + (uv_read_cb) abort); ASSERT(r == UV_ENOTCONN); uv_close((uv_handle_t*)&server, close_cb); diff --git a/deps/uv/test/test-tcp-connect-timeout.c b/deps/uv/test/test-tcp-connect-timeout.c index a67d325284da11..3c34e54ae7f85e 100644 --- a/deps/uv/test/test-tcp-connect-timeout.c +++ b/deps/uv/test/test-tcp-connect-timeout.c @@ -111,7 +111,7 @@ static int is_supported_system(void) { if (cnt != 3) { return 0; } - // relase >= 10.0.16299 + /* relase >= 10.0.16299 */ for (cnt = 0; cnt < 3; ++cnt) { if (semver[cnt] > min_semver[cnt]) return 1; diff --git a/deps/uv/test/test-tty.c b/deps/uv/test/test-tty.c index a9d38f22117afd..ff7d388d7c00f3 100644 --- a/deps/uv/test/test-tty.c +++ b/deps/uv/test/test-tty.c @@ -426,6 +426,9 @@ TEST_IMPL(tty_pty) { #if defined(__QEMU__) RETURN_SKIP("Test does not currently work in QEMU"); #endif +#if defined(__ASAN__) + RETURN_SKIP("Test does not currently work in ASAN"); +#endif #if defined(__APPLE__) || \ defined(__DragonFly__) || \ diff --git a/deps/uv/test/test-udp-connect.c b/deps/uv/test/test-udp-connect.c index 41ace117a1bba2..52856f700d210e 100644 --- a/deps/uv/test/test-udp-connect.c +++ b/deps/uv/test/test-udp-connect.c @@ -124,7 +124,7 @@ TEST_IMPL(udp_connect) { buf = uv_buf_init("EXIT", 4); - // connect() to INADDR_ANY fails on Windows wih WSAEADDRNOTAVAIL + /* connect() to INADDR_ANY fails on Windows wih WSAEADDRNOTAVAIL */ ASSERT_EQ(0, uv_ip4_addr("0.0.0.0", TEST_PORT, &tmp_addr)); r = uv_udp_connect(&client, (const struct sockaddr*) &tmp_addr); #ifdef _WIN32 diff --git a/deps/uv/tools/make_dist_html.py b/deps/uv/tools/make_dist_html.py index 7a19d3e1151fb8..4833b1b8e38180 100644 --- a/deps/uv/tools/make_dist_html.py +++ b/deps/uv/tools/make_dist_html.py @@ -1,6 +1,4 @@ -#!/usr/bin/python - -from __future__ import print_function +#!/usr/bin/python3 import itertools import os @@ -84,7 +82,7 @@ ''' def version(tag): - return map(int, re.match('^v(\d+)\.(\d+)\.(\d+)', tag).groups()) + return list(map(int, re.match('^v(\d+)\.(\d+)\.(\d+)', tag).groups())) def major_minor(tag): return version(tag)[:2] @@ -114,7 +112,7 @@ def groups_for(groups, n=4): if __name__ == '__main__': os.chdir(os.path.dirname(__file__)) - tags = subprocess.check_output(['git', 'tag']) + tags = subprocess.check_output(['git', 'tag'], text=True) tags = [tag for tag in tags.split('\n') if tag.startswith('v')] tags.sort(key=version, reverse=True) groups = [group_for(list(g)) for _, g in itertools.groupby(tags, major_minor)] diff --git a/deps/v8/AUTHORS b/deps/v8/AUTHORS index 47a83c5ff1c905..13a915c130b7a2 100644 --- a/deps/v8/AUTHORS +++ b/deps/v8/AUTHORS @@ -58,6 +58,7 @@ Andreas Anyuru Andrew Paprocki Andrei Kashcha Anna Henningsen +Antoine du Hamel Anton Bikineev Bangfu Tao Daniel Shelton @@ -66,6 +67,7 @@ Ben Newman Ben Noordhuis Benjamin Tan Bert Belder +Brendon Tiszka Burcu Dogan Caitlin Potter Craig Schlenter diff --git a/deps/v8/BUILD.gn b/deps/v8/BUILD.gn index 167e63503c5535..eaaeadfce3f6cd 100644 --- a/deps/v8/BUILD.gn +++ b/deps/v8/BUILD.gn @@ -3080,6 +3080,7 @@ v8_source_set("v8_base_without_compiler") { "src/wasm/baseline/liftoff-compiler.cc", "src/wasm/baseline/liftoff-compiler.h", "src/wasm/baseline/liftoff-register.h", + "src/wasm/code-space-access.h", "src/wasm/compilation-environment.h", "src/wasm/decoder.h", "src/wasm/function-body-decoder-impl.h", diff --git a/deps/v8/include/v8-platform.h b/deps/v8/include/v8-platform.h index bf474f26803c48..dfb9442f5798b4 100644 --- a/deps/v8/include/v8-platform.h +++ b/deps/v8/include/v8-platform.h @@ -338,7 +338,13 @@ class PageAllocator { kReadWrite, // TODO(hpayer): Remove this flag. Memory should never be rwx. kReadWriteExecute, - kReadExecute + kReadExecute, + // Set this when reserving memory that will later require kReadWriteExecute + // permissions. The resulting behavior is platform-specific, currently + // this is used to set the MAP_JIT flag on Apple Silicon. + // TODO(jkummerow): Remove this when Wasm has a platform-independent + // w^x implementation. + kNoAccessWillJitLater }; /** diff --git a/deps/v8/include/v8-version.h b/deps/v8/include/v8-version.h index cee7990e4bc193..d31f2a53076b7c 100644 --- a/deps/v8/include/v8-version.h +++ b/deps/v8/include/v8-version.h @@ -11,7 +11,7 @@ #define V8_MAJOR_VERSION 8 #define V8_MINOR_VERSION 4 #define V8_BUILD_NUMBER 371 -#define V8_PATCH_LEVEL 19 +#define V8_PATCH_LEVEL 23 // Use 1 for candidates and 0 otherwise. // (Boolean macro values are not supported by all preprocessors.) diff --git a/deps/v8/infra/testing/builders.pyl b/deps/v8/infra/testing/builders.pyl index 72f739487ccec3..113413bbe8c331 100644 --- a/deps/v8/infra/testing/builders.pyl +++ b/deps/v8/infra/testing/builders.pyl @@ -1078,7 +1078,7 @@ {'name': 'v8testing', 'variant': 'slow_path', 'shards': 1}, ], }, - 'V8 Linux64 TSAN - concurrent marking': { + 'V8 Linux64 TSAN - stress-incremental-marking': { 'swarming_dimensions' : { 'os': 'Ubuntu-16.04', }, @@ -1105,7 +1105,7 @@ { 'name': 'v8testing', 'test_args': ['--extra-flags=--stress-incremental-marking'], - 'shards': 4, + 'shards': 6, }, ], }, diff --git a/deps/v8/src/ast/ast-function-literal-id-reindexer.cc b/deps/v8/src/ast/ast-function-literal-id-reindexer.cc index b583b5e4214ad4..8c9318bfe7475d 100644 --- a/deps/v8/src/ast/ast-function-literal-id-reindexer.cc +++ b/deps/v8/src/ast/ast-function-literal-id-reindexer.cc @@ -54,10 +54,10 @@ void AstFunctionLiteralIdReindexer::VisitClassLiteral(ClassLiteral* expr) { // Private fields have their key and value present in // instance_members_initializer_function, so they will // already have been visited. - if (prop->value()->IsFunctionLiteral()) { - Visit(prop->value()); - } else { + if (prop->kind() == ClassLiteralProperty::Kind::FIELD) { CheckVisited(prop->value()); + } else { + Visit(prop->value()); } } ZonePtrList* props = expr->public_members(); @@ -67,7 +67,8 @@ void AstFunctionLiteralIdReindexer::VisitClassLiteral(ClassLiteral* expr) { // Public fields with computed names have their key // and value present in instance_members_initializer_function, so they will // already have been visited. - if (prop->is_computed_name() && !prop->value()->IsFunctionLiteral()) { + if (prop->is_computed_name() && + prop->kind() == ClassLiteralProperty::Kind::FIELD) { if (!prop->key()->IsLiteral()) { CheckVisited(prop->key()); } diff --git a/deps/v8/src/base/build_config.h b/deps/v8/src/base/build_config.h index 8d142c456c9cc1..327f9ab3060e3d 100644 --- a/deps/v8/src/base/build_config.h +++ b/deps/v8/src/base/build_config.h @@ -205,6 +205,10 @@ // PPC has large (64KB) physical pages. const int kPageSizeBits = 19; #else +// Arm64 supports up to 64k OS pages on Linux, however 4k pages are more common +// so we keep the V8 page size at 256k. Nonetheless, we need to make sure we +// don't decrease it further in the future due to reserving 3 OS pages for every +// executable V8 page. const int kPageSizeBits = 18; #endif diff --git a/deps/v8/src/base/page-allocator.cc b/deps/v8/src/base/page-allocator.cc index 76a0aff39953a4..eefd1d792b89bf 100644 --- a/deps/v8/src/base/page-allocator.cc +++ b/deps/v8/src/base/page-allocator.cc @@ -6,6 +6,10 @@ #include "src/base/platform/platform.h" +#if V8_OS_MACOSX +#include // For MAP_JIT. +#endif + namespace v8 { namespace base { @@ -21,6 +25,8 @@ STATIC_ASSERT_ENUM(PageAllocator::kReadWriteExecute, base::OS::MemoryPermission::kReadWriteExecute); STATIC_ASSERT_ENUM(PageAllocator::kReadExecute, base::OS::MemoryPermission::kReadExecute); +STATIC_ASSERT_ENUM(PageAllocator::kNoAccessWillJitLater, + base::OS::MemoryPermission::kNoAccessWillJitLater); #undef STATIC_ASSERT_ENUM @@ -38,6 +44,14 @@ void* PageAllocator::GetRandomMmapAddr() { void* PageAllocator::AllocatePages(void* hint, size_t size, size_t alignment, PageAllocator::Permission access) { +#if !(V8_OS_MACOSX && V8_HOST_ARCH_ARM64 && defined(MAP_JIT)) + // kNoAccessWillJitLater is only used on Apple Silicon. Map it to regular + // kNoAccess on other platforms, so code doesn't have to handle both enum + // values. + if (access == PageAllocator::kNoAccessWillJitLater) { + access = PageAllocator::kNoAccess; + } +#endif return base::OS::Allocate(hint, size, alignment, static_cast(access)); } diff --git a/deps/v8/src/base/platform/platform-cygwin.cc b/deps/v8/src/base/platform/platform-cygwin.cc index 92a5fbe490f4c3..b9da2f1cd592db 100644 --- a/deps/v8/src/base/platform/platform-cygwin.cc +++ b/deps/v8/src/base/platform/platform-cygwin.cc @@ -33,6 +33,7 @@ namespace { DWORD GetProtectionFromMemoryPermission(OS::MemoryPermission access) { switch (access) { case OS::MemoryPermission::kNoAccess: + case OS::MemoryPermission::kNoAccessWillJitLater: return PAGE_NOACCESS; case OS::MemoryPermission::kRead: return PAGE_READONLY; diff --git a/deps/v8/src/base/platform/platform-fuchsia.cc b/deps/v8/src/base/platform/platform-fuchsia.cc index fa175c39177aea..35a508a140ebd7 100644 --- a/deps/v8/src/base/platform/platform-fuchsia.cc +++ b/deps/v8/src/base/platform/platform-fuchsia.cc @@ -18,6 +18,7 @@ namespace { uint32_t GetProtectionFromMemoryPermission(OS::MemoryPermission access) { switch (access) { case OS::MemoryPermission::kNoAccess: + case OS::MemoryPermission::kNoAccessWillJitLater: return 0; // no permissions case OS::MemoryPermission::kRead: return ZX_VM_PERM_READ; diff --git a/deps/v8/src/base/platform/platform-macos.cc b/deps/v8/src/base/platform/platform-macos.cc index bee6b30f7cc71c..2d9d26558d93f8 100644 --- a/deps/v8/src/base/platform/platform-macos.cc +++ b/deps/v8/src/base/platform/platform-macos.cc @@ -49,14 +49,14 @@ std::vector OS::GetSharedLibraryAddresses() { for (unsigned int i = 0; i < images_count; ++i) { const mach_header* header = _dyld_get_image_header(i); if (header == nullptr) continue; -#if V8_HOST_ARCH_X64 +#if V8_HOST_ARCH_I32 + unsigned int size; + char* code_ptr = getsectdatafromheader(header, SEG_TEXT, SECT_TEXT, &size); +#else uint64_t size; char* code_ptr = getsectdatafromheader_64( reinterpret_cast(header), SEG_TEXT, SECT_TEXT, &size); -#else - unsigned int size; - char* code_ptr = getsectdatafromheader(header, SEG_TEXT, SECT_TEXT, &size); #endif if (code_ptr == nullptr) continue; const intptr_t slide = _dyld_get_image_vmaddr_slide(i); diff --git a/deps/v8/src/base/platform/platform-posix.cc b/deps/v8/src/base/platform/platform-posix.cc index 17fc5b508394c8..e5aa4de160c375 100644 --- a/deps/v8/src/base/platform/platform-posix.cc +++ b/deps/v8/src/base/platform/platform-posix.cc @@ -118,6 +118,7 @@ const int kMmapFdOffset = 0; int GetProtectionFromMemoryPermission(OS::MemoryPermission access) { switch (access) { case OS::MemoryPermission::kNoAccess: + case OS::MemoryPermission::kNoAccessWillJitLater: return PROT_NONE; case OS::MemoryPermission::kRead: return PROT_READ; @@ -141,6 +142,11 @@ int GetFlagsForMemoryPermission(OS::MemoryPermission access) { flags |= MAP_LAZY; #endif // V8_OS_QNX } +#if V8_OS_MACOSX && V8_HOST_ARCH_ARM64 && defined(MAP_JIT) + if (access == OS::MemoryPermission::kNoAccessWillJitLater) { + flags |= MAP_JIT; + } +#endif return flags; } @@ -384,6 +390,16 @@ bool OS::SetPermissions(void* address, size_t size, MemoryPermission access) { int prot = GetProtectionFromMemoryPermission(access); int ret = mprotect(address, size, prot); + + // MacOS 11.2 on Apple Silicon refuses to switch permissions from + // rwx to none. Just use madvise instead. +#if defined(V8_OS_MACOSX) + if (ret != 0 && access == OS::MemoryPermission::kNoAccess) { + ret = madvise(address, size, MADV_FREE_REUSABLE); + return ret == 0; + } +#endif + if (ret == 0 && access == OS::MemoryPermission::kNoAccess) { // This is advisory; ignore errors and continue execution. USE(DiscardSystemPages(address, size)); diff --git a/deps/v8/src/base/platform/platform-win32.cc b/deps/v8/src/base/platform/platform-win32.cc index 5db3e343103dd0..6be63dee137a81 100644 --- a/deps/v8/src/base/platform/platform-win32.cc +++ b/deps/v8/src/base/platform/platform-win32.cc @@ -753,6 +753,7 @@ namespace { DWORD GetProtectionFromMemoryPermission(OS::MemoryPermission access) { switch (access) { case OS::MemoryPermission::kNoAccess: + case OS::MemoryPermission::kNoAccessWillJitLater: return PAGE_NOACCESS; case OS::MemoryPermission::kRead: return PAGE_READONLY; diff --git a/deps/v8/src/base/platform/platform.h b/deps/v8/src/base/platform/platform.h index af55036a006aca..7a805d398cb4e8 100644 --- a/deps/v8/src/base/platform/platform.h +++ b/deps/v8/src/base/platform/platform.h @@ -167,7 +167,10 @@ class V8_BASE_EXPORT OS { kReadWrite, // TODO(hpayer): Remove this flag. Memory should never be rwx. kReadWriteExecute, - kReadExecute + kReadExecute, + // TODO(jkummerow): Remove this when Wasm has a platform-independent + // w^x implementation. + kNoAccessWillJitLater }; static bool HasLazyCommits(); diff --git a/deps/v8/src/builtins/builtins-array.cc b/deps/v8/src/builtins/builtins-array.cc index 3c2fe33c5b4b33..8055d8382d48f6 100644 --- a/deps/v8/src/builtins/builtins-array.cc +++ b/deps/v8/src/builtins/builtins-array.cc @@ -649,11 +649,14 @@ class ArrayConcatVisitor { index_offset_(0u), bit_field_(FastElementsField::encode(fast_elements) | ExceedsLimitField::encode(false) | - IsFixedArrayField::encode(storage->IsFixedArray()) | + IsFixedArrayField::encode(storage->IsFixedArray(isolate)) | HasSimpleElementsField::encode( - storage->IsFixedArray() || - !storage->map().IsCustomElementsReceiverMap())) { - DCHECK(!(this->fast_elements() && !is_fixed_array())); + storage->IsFixedArray(isolate) || + // Don't take fast path for storages that might have + // side effects when storing to them. + (!storage->map(isolate).IsCustomElementsReceiverMap() && + !storage->IsJSTypedArray(isolate)))) { + DCHECK_IMPLIES(this->fast_elements(), is_fixed_array()); } ~ArrayConcatVisitor() { clear_storage(); } @@ -1063,8 +1066,8 @@ bool IterateElements(Isolate* isolate, Handle receiver, return IterateElementsSlow(isolate, receiver, length, visitor); } - if (!HasOnlySimpleElements(isolate, *receiver) || - !visitor->has_simple_elements()) { + if (!visitor->has_simple_elements() || + !HasOnlySimpleElements(isolate, *receiver)) { return IterateElementsSlow(isolate, receiver, length, visitor); } Handle array = Handle::cast(receiver); @@ -1080,6 +1083,9 @@ bool IterateElements(Isolate* isolate, Handle receiver, case HOLEY_SEALED_ELEMENTS: case HOLEY_NONEXTENSIBLE_ELEMENTS: case HOLEY_ELEMENTS: { + // Disallow execution so the cached elements won't change mid execution. + DisallowJavascriptExecution no_js(isolate); + // Run through the elements FixedArray and use HasElement and GetElement // to check the prototype for missing elements. Handle elements(FixedArray::cast(array->elements()), isolate); @@ -1106,6 +1112,9 @@ bool IterateElements(Isolate* isolate, Handle receiver, } case HOLEY_DOUBLE_ELEMENTS: case PACKED_DOUBLE_ELEMENTS: { + // Disallow execution so the cached elements won't change mid execution. + DisallowJavascriptExecution no_js(isolate); + // Empty array is FixedArray but not FixedDoubleArray. if (length == 0) break; // Run through the elements FixedArray and use HasElement and GetElement @@ -1142,6 +1151,9 @@ bool IterateElements(Isolate* isolate, Handle receiver, } case DICTIONARY_ELEMENTS: { + // Disallow execution so the cached dictionary won't change mid execution. + DisallowJavascriptExecution no_js(isolate); + Handle dict(array->element_dictionary(), isolate); std::vector indices; indices.reserve(dict->Capacity() / 2); diff --git a/deps/v8/src/builtins/promise-all-element-closure.tq b/deps/v8/src/builtins/promise-all-element-closure.tq index 0b870ea3b185bc..4dfafec1c92555 100644 --- a/deps/v8/src/builtins/promise-all-element-closure.tq +++ b/deps/v8/src/builtins/promise-all-element-closure.tq @@ -82,7 +82,8 @@ const kPropertyArrayHashFieldMax: constexpr int31 transitioning macro PromiseAllResolveElementClosure( implicit context: Context)( - value: JSAny, function: JSFunction, wrapResultFunctor: F): JSAny { + value: JSAny, function: JSFunction, wrapResultFunctor: F, + hasResolveAndRejectClosures: constexpr bool): JSAny { // We use the {function}s context as the marker to remember whether this // resolve element closure was already called. It points to the resolve // element context (which is a FunctionContext) until it was called the @@ -98,10 +99,6 @@ transitioning macro PromiseAllResolveElementClosure( const nativeContext = LoadNativeContext(context); function.context = nativeContext; - // Update the value depending on whether Promise.all or - // Promise.allSettled is called. - const updatedValue = wrapResultFunctor.Call(nativeContext, value); - // Determine the index from the {function}. assert(kPropertyArrayNoHashSentinel == 0); const identityHash = @@ -116,13 +113,41 @@ transitioning macro PromiseAllResolveElementClosure( const elements = UnsafeCast(valuesArray.elements); const valuesLength = Convert(valuesArray.length); if (index < valuesLength) { - // The {index} is in bounds of the {values_array}, - // just store the {value} and continue. + // The {index} is in bounds of the {values_array}, check if this element has + // already been resolved, and store the {value} if not. + // + // Promise.allSettled, for each input element, has both a resolve and a + // reject closure that share an [[AlreadyCalled]] boolean. That is, the + // input element can only be settled once: after resolve is called, reject + // returns early, and vice versa. Using {function}'s context as the marker + // only tracks per-closure instead of per-element. When the second + // resolve/reject closure is called on the same index, values.object[index] + // will already exist and will not be the hole value. In that case, return + // early. Everything up to this point is not yet observable to user code. + // This is not a problem for Promise.all since Promise.all has a single + // resolve closure (no reject) per element. + if (hasResolveAndRejectClosures) { + if (elements.objects[index] != TheHole) deferred { + return Undefined; + } + } + + // Update the value depending on whether Promise.all or + // Promise.allSettled is called. + const updatedValue = wrapResultFunctor.Call(nativeContext, value); elements.objects[index] = updatedValue; } else { // Check if we need to grow the backing store. + // + // There's no need to check if this element has already been resolved for + // Promise.allSettled if {values_array} has not yet grown to the index. const newLength = index + 1; const elementsLength = elements.length_intptr; + + // Update the value depending on whether Promise.all or + // Promise.allSettled is called. + const updatedValue = wrapResultFunctor.Call(nativeContext, value); + if (index < elementsLength) { // The {index} is within bounds of the {elements} backing store, so // just store the {value} and update the "length" of the {values_array}. @@ -166,7 +191,7 @@ PromiseAllResolveElementClosure( js-implicit context: Context, receiver: JSAny, target: JSFunction)(value: JSAny): JSAny { return PromiseAllResolveElementClosure( - value, target, PromiseAllWrapResultAsFulfilledFunctor{}); + value, target, PromiseAllWrapResultAsFulfilledFunctor{}, false); } transitioning javascript builtin @@ -174,7 +199,7 @@ PromiseAllSettledResolveElementClosure( js-implicit context: Context, receiver: JSAny, target: JSFunction)(value: JSAny): JSAny { return PromiseAllResolveElementClosure( - value, target, PromiseAllSettledWrapResultAsFulfilledFunctor{}); + value, target, PromiseAllSettledWrapResultAsFulfilledFunctor{}, true); } transitioning javascript builtin @@ -182,6 +207,6 @@ PromiseAllSettledRejectElementClosure( js-implicit context: Context, receiver: JSAny, target: JSFunction)(value: JSAny): JSAny { return PromiseAllResolveElementClosure( - value, target, PromiseAllSettledWrapResultAsRejectedFunctor{}); + value, target, PromiseAllSettledWrapResultAsRejectedFunctor{}, true); } } diff --git a/deps/v8/src/codegen/arm/macro-assembler-arm.cc b/deps/v8/src/codegen/arm/macro-assembler-arm.cc index 7e5fa8cef1c1c2..fa9aba33876f04 100644 --- a/deps/v8/src/codegen/arm/macro-assembler-arm.cc +++ b/deps/v8/src/codegen/arm/macro-assembler-arm.cc @@ -722,23 +722,22 @@ void TurboAssembler::CallEphemeronKeyBarrier(Register object, Operand offset, void TurboAssembler::CallRecordWriteStub( Register object, Operand offset, RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode) { - CallRecordWriteStub( - object, offset, remembered_set_action, fp_mode, - isolate()->builtins()->builtin_handle(Builtins::kRecordWrite), - kNullAddress); + CallRecordWriteStub(object, offset, remembered_set_action, fp_mode, + Builtins::kRecordWrite, kNullAddress); } void TurboAssembler::CallRecordWriteStub( Register object, Operand offset, RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode, Address wasm_target) { CallRecordWriteStub(object, offset, remembered_set_action, fp_mode, - Handle::null(), wasm_target); + Builtins::kNoBuiltinId, wasm_target); } void TurboAssembler::CallRecordWriteStub( Register object, Operand offset, RememberedSetAction remembered_set_action, - SaveFPRegsMode fp_mode, Handle code_target, Address wasm_target) { - DCHECK_NE(code_target.is_null(), wasm_target == kNullAddress); + SaveFPRegsMode fp_mode, int builtin_index, Address wasm_target) { + DCHECK_NE(builtin_index == Builtins::kNoBuiltinId, + wasm_target == kNullAddress); // TODO(albertnetymk): For now we ignore remembered_set_action and fp_mode, // i.e. always emit remember set and save FP registers in RecordWriteStub. If // large performance regression is observed, we should use these values to @@ -762,9 +761,13 @@ void TurboAssembler::CallRecordWriteStub( Move(remembered_set_parameter, Smi::FromEnum(remembered_set_action)); Move(fp_mode_parameter, Smi::FromEnum(fp_mode)); - if (code_target.is_null()) { + if (builtin_index == Builtins::kNoBuiltinId) { Call(wasm_target, RelocInfo::WASM_STUB_CALL); + } else if (options().inline_offheap_trampolines) { + CallBuiltin(builtin_index); } else { + Handle code_target = + isolate()->builtins()->builtin_handle(Builtins::kRecordWrite); Call(code_target, RelocInfo::CODE_TARGET); } diff --git a/deps/v8/src/codegen/arm/macro-assembler-arm.h b/deps/v8/src/codegen/arm/macro-assembler-arm.h index a7dc5498b8b8ec..4821eb79c943fb 100644 --- a/deps/v8/src/codegen/arm/macro-assembler-arm.h +++ b/deps/v8/src/codegen/arm/macro-assembler-arm.h @@ -588,7 +588,7 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase { void CallRecordWriteStub(Register object, Operand offset, RememberedSetAction remembered_set_action, - SaveFPRegsMode fp_mode, Handle code_target, + SaveFPRegsMode fp_mode, int builtin_index, Address wasm_target); }; diff --git a/deps/v8/src/codegen/arm64/macro-assembler-arm64.cc b/deps/v8/src/codegen/arm64/macro-assembler-arm64.cc index c157df29966975..c93f5797257d6f 100644 --- a/deps/v8/src/codegen/arm64/macro-assembler-arm64.cc +++ b/deps/v8/src/codegen/arm64/macro-assembler-arm64.cc @@ -2738,23 +2738,22 @@ void TurboAssembler::CallEphemeronKeyBarrier(Register object, Operand offset, void TurboAssembler::CallRecordWriteStub( Register object, Operand offset, RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode) { - CallRecordWriteStub( - object, offset, remembered_set_action, fp_mode, - isolate()->builtins()->builtin_handle(Builtins::kRecordWrite), - kNullAddress); + CallRecordWriteStub(object, offset, remembered_set_action, fp_mode, + Builtins::kRecordWrite, kNullAddress); } void TurboAssembler::CallRecordWriteStub( Register object, Operand offset, RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode, Address wasm_target) { CallRecordWriteStub(object, offset, remembered_set_action, fp_mode, - Handle::null(), wasm_target); + Builtins::kNoBuiltinId, wasm_target); } void TurboAssembler::CallRecordWriteStub( Register object, Operand offset, RememberedSetAction remembered_set_action, - SaveFPRegsMode fp_mode, Handle code_target, Address wasm_target) { - DCHECK_NE(code_target.is_null(), wasm_target == kNullAddress); + SaveFPRegsMode fp_mode, int builtin_index, Address wasm_target) { + DCHECK_NE(builtin_index == Builtins::kNoBuiltinId, + wasm_target == kNullAddress); // TODO(albertnetymk): For now we ignore remembered_set_action and fp_mode, // i.e. always emit remember set and save FP registers in RecordWriteStub. If // large performance regression is observed, we should use these values to @@ -2778,9 +2777,13 @@ void TurboAssembler::CallRecordWriteStub( Mov(remembered_set_parameter, Smi::FromEnum(remembered_set_action)); Mov(fp_mode_parameter, Smi::FromEnum(fp_mode)); - if (code_target.is_null()) { + if (builtin_index == Builtins::kNoBuiltinId) { Call(wasm_target, RelocInfo::WASM_STUB_CALL); + } else if (options().inline_offheap_trampolines) { + CallBuiltin(builtin_index); } else { + Handle code_target = + isolate()->builtins()->builtin_handle(Builtins::kRecordWrite); Call(code_target, RelocInfo::CODE_TARGET); } diff --git a/deps/v8/src/codegen/arm64/macro-assembler-arm64.h b/deps/v8/src/codegen/arm64/macro-assembler-arm64.h index 109e73c3c229d4..e701a5b12a3168 100644 --- a/deps/v8/src/codegen/arm64/macro-assembler-arm64.h +++ b/deps/v8/src/codegen/arm64/macro-assembler-arm64.h @@ -1419,7 +1419,7 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase { void CallRecordWriteStub(Register object, Operand offset, RememberedSetAction remembered_set_action, - SaveFPRegsMode fp_mode, Handle code_target, + SaveFPRegsMode fp_mode, int builtin_index, Address wasm_target); }; diff --git a/deps/v8/src/codegen/code-stub-assembler.cc b/deps/v8/src/codegen/code-stub-assembler.cc index 901ce0c7b49410..50fb563244cee4 100644 --- a/deps/v8/src/codegen/code-stub-assembler.cc +++ b/deps/v8/src/codegen/code-stub-assembler.cc @@ -1801,12 +1801,13 @@ TNode CodeStubAssembler::LoadJSReceiverIdentityHash( return var_hash.value(); } -TNode CodeStubAssembler::LoadNameHashField(SloppyTNode name) { - CSA_ASSERT(this, IsName(name)); - return LoadObjectField(name, Name::kHashFieldOffset); +TNode CodeStubAssembler::LoadNameHashAssumeComputed(TNode name) { + TNode hash_field = LoadNameHashField(name); + CSA_ASSERT(this, IsClearWord32(hash_field, Name::kHashNotComputedMask)); + return Unsigned(Word32Shr(hash_field, Int32Constant(Name::kHashShift))); } -TNode CodeStubAssembler::LoadNameHash(SloppyTNode name, +TNode CodeStubAssembler::LoadNameHash(TNode name, Label* if_hash_not_computed) { TNode hash_field = LoadNameHashField(name); if (if_hash_not_computed != nullptr) { @@ -1994,13 +1995,13 @@ TNode CodeStubAssembler::LoadArrayElement(TNode array, } } -template TNode +template V8_EXPORT_PRIVATE TNode CodeStubAssembler::LoadArrayElement(TNode, int, Node*, int, ParameterMode, LoadSensitivity); -template TNode +template V8_EXPORT_PRIVATE TNode CodeStubAssembler::LoadArrayElement(TNode, int, Node*, int, ParameterMode, @@ -8063,7 +8064,7 @@ void CodeStubAssembler::LookupBinary(TNode unique_name, TNode limit = Unsigned(Int32Sub(NumberOfEntries(array), Int32Constant(1))); TVARIABLE(Uint32T, var_high, limit); - TNode hash = LoadNameHashField(unique_name); + TNode hash = LoadNameHashAssumeComputed(unique_name); CSA_ASSERT(this, Word32NotEqual(hash, Int32Constant(0))); // Assume non-empty array. @@ -8081,7 +8082,7 @@ void CodeStubAssembler::LookupBinary(TNode unique_name, TNode sorted_key_index = GetSortedKeyIndex(array, mid); TNode mid_name = GetKey(array, sorted_key_index); - TNode mid_hash = LoadNameHashField(mid_name); + TNode mid_hash = LoadNameHashAssumeComputed(mid_name); Label mid_greater(this), mid_less(this), merge(this); Branch(Uint32GreaterThanOrEqual(mid_hash, hash), &mid_greater, &mid_less); @@ -8108,7 +8109,7 @@ void CodeStubAssembler::LookupBinary(TNode unique_name, TNode sort_index = GetSortedKeyIndex(array, var_low.value()); TNode current_name = GetKey(array, sort_index); - TNode current_hash = LoadNameHashField(current_name); + TNode current_hash = LoadNameHashAssumeComputed(current_name); GotoIf(Word32NotEqual(current_hash, hash), if_not_found); Label next(this); GotoIf(TaggedNotEqual(current_name, unique_name), &next); diff --git a/deps/v8/src/codegen/code-stub-assembler.h b/deps/v8/src/codegen/code-stub-assembler.h index b01729c73db8d4..23b99377d8dccc 100644 --- a/deps/v8/src/codegen/code-stub-assembler.h +++ b/deps/v8/src/codegen/code-stub-assembler.h @@ -1353,13 +1353,12 @@ class V8_EXPORT_PRIVATE CodeStubAssembler // Check if the map is set for slow properties. TNode IsDictionaryMap(SloppyTNode map); - // Load the hash field of a name as an uint32 value. - TNode LoadNameHashField(SloppyTNode name); - // Load the hash value of a name as an uint32 value. + // Load the Name::hash() value of a name as an uint32 value. // If {if_hash_not_computed} label is specified then it also checks if // hash is actually computed. - TNode LoadNameHash(SloppyTNode name, + TNode LoadNameHash(TNode name, Label* if_hash_not_computed = nullptr); + TNode LoadNameHashAssumeComputed(TNode name); // Load length field of a String object as Smi value. TNode LoadStringLengthAsSmi(TNode string); diff --git a/deps/v8/src/codegen/ia32/assembler-ia32.h b/deps/v8/src/codegen/ia32/assembler-ia32.h index 60d978df5be26f..ded1e020e28627 100644 --- a/deps/v8/src/codegen/ia32/assembler-ia32.h +++ b/deps/v8/src/codegen/ia32/assembler-ia32.h @@ -959,6 +959,9 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase { void movapd(XMMRegister dst, Operand src) { sse2_instr(dst, src, 0x66, 0x0F, 0x28); } + void movupd(XMMRegister dst, Operand src) { + sse2_instr(dst, src, 0x66, 0x0F, 0x10); + } void movmskpd(Register dst, XMMRegister src); void movmskps(Register dst, XMMRegister src); @@ -1331,6 +1334,7 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase { void vmovapd(XMMRegister dst, Operand src) { vpd(0x28, dst, xmm0, src); } void vmovups(XMMRegister dst, XMMRegister src) { vmovups(dst, Operand(src)); } void vmovups(XMMRegister dst, Operand src) { vps(0x10, dst, xmm0, src); } + void vmovupd(XMMRegister dst, Operand src) { vpd(0x10, dst, xmm0, src); } void vshufps(XMMRegister dst, XMMRegister src1, XMMRegister src2, byte imm8) { vshufps(dst, src1, Operand(src2), imm8); } diff --git a/deps/v8/src/codegen/ia32/macro-assembler-ia32.cc b/deps/v8/src/codegen/ia32/macro-assembler-ia32.cc index b73050a680dc21..4b31481acdd85a 100644 --- a/deps/v8/src/codegen/ia32/macro-assembler-ia32.cc +++ b/deps/v8/src/codegen/ia32/macro-assembler-ia32.cc @@ -415,10 +415,8 @@ void TurboAssembler::CallEphemeronKeyBarrier(Register object, Register address, void TurboAssembler::CallRecordWriteStub( Register object, Register address, RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode) { - CallRecordWriteStub( - object, address, remembered_set_action, fp_mode, - isolate()->builtins()->builtin_handle(Builtins::kRecordWrite), - kNullAddress); + CallRecordWriteStub(object, address, remembered_set_action, fp_mode, + Builtins::kRecordWrite, kNullAddress); } void TurboAssembler::CallRecordWriteStub( @@ -426,14 +424,15 @@ void TurboAssembler::CallRecordWriteStub( RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode, Address wasm_target) { CallRecordWriteStub(object, address, remembered_set_action, fp_mode, - Handle::null(), wasm_target); + Builtins::kNoBuiltinId, wasm_target); } void TurboAssembler::CallRecordWriteStub( Register object, Register address, RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode, - Handle code_target, Address wasm_target) { - DCHECK_NE(code_target.is_null(), wasm_target == kNullAddress); + int builtin_index, Address wasm_target) { + DCHECK_NE(builtin_index == Builtins::kNoBuiltinId, + wasm_target == kNullAddress); // TODO(albertnetymk): For now we ignore remembered_set_action and fp_mode, // i.e. always emit remember set and save FP registers in RecordWriteStub. If // large performance regression is observed, we should use these values to @@ -461,10 +460,14 @@ void TurboAssembler::CallRecordWriteStub( Move(remembered_set_parameter, Smi::FromEnum(remembered_set_action)); Move(fp_mode_parameter, Smi::FromEnum(fp_mode)); - if (code_target.is_null()) { + if (builtin_index == Builtins::kNoBuiltinId) { // Use {wasm_call} for direct Wasm call within a module. wasm_call(wasm_target, RelocInfo::WASM_STUB_CALL); + } else if (options().inline_offheap_trampolines) { + CallBuiltin(builtin_index); } else { + Handle code_target = + isolate()->builtins()->builtin_handle(Builtins::kRecordWrite); Call(code_target, RelocInfo::CODE_TARGET); } diff --git a/deps/v8/src/codegen/ia32/macro-assembler-ia32.h b/deps/v8/src/codegen/ia32/macro-assembler-ia32.h index 94ddb2f784795a..0847599295a9b5 100644 --- a/deps/v8/src/codegen/ia32/macro-assembler-ia32.h +++ b/deps/v8/src/codegen/ia32/macro-assembler-ia32.h @@ -292,6 +292,7 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase { AVX_OP2_WITH_TYPE(Movaps, movaps, XMMRegister, XMMRegister) AVX_OP2_WITH_TYPE(Movapd, movapd, XMMRegister, XMMRegister) AVX_OP2_WITH_TYPE(Movapd, movapd, XMMRegister, const Operand&) + AVX_OP2_WITH_TYPE(Movupd, movupd, XMMRegister, const Operand&) AVX_OP2_WITH_TYPE(Pmovmskb, pmovmskb, Register, XMMRegister) AVX_OP2_WITH_TYPE(Movmskps, movmskps, Register, XMMRegister) @@ -566,7 +567,7 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase { void CallRecordWriteStub(Register object, Register address, RememberedSetAction remembered_set_action, - SaveFPRegsMode fp_mode, Handle code_target, + SaveFPRegsMode fp_mode, int builtin_index, Address wasm_target); }; diff --git a/deps/v8/src/codegen/x64/assembler-x64.h b/deps/v8/src/codegen/x64/assembler-x64.h index 24eb9765782f21..c1c4194f9c3745 100644 --- a/deps/v8/src/codegen/x64/assembler-x64.h +++ b/deps/v8/src/codegen/x64/assembler-x64.h @@ -1562,6 +1562,10 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase { vinstr(0x0F, dst, src1, src2, k66, k0F3A, kWIG); emit(imm8); } + void vpalignr(XMMRegister dst, XMMRegister src1, Operand src2, uint8_t imm8) { + vinstr(0x0F, dst, src1, src2, k66, k0F3A, kWIG); + emit(imm8); + } void vps(byte op, XMMRegister dst, XMMRegister src1, XMMRegister src2); void vps(byte op, XMMRegister dst, XMMRegister src1, Operand src2); diff --git a/deps/v8/src/codegen/x64/macro-assembler-x64.cc b/deps/v8/src/codegen/x64/macro-assembler-x64.cc index 7d6fdc5eb3d589..44e590843ebbc4 100644 --- a/deps/v8/src/codegen/x64/macro-assembler-x64.cc +++ b/deps/v8/src/codegen/x64/macro-assembler-x64.cc @@ -385,10 +385,8 @@ void TurboAssembler::CallEphemeronKeyBarrier(Register object, Register address, void TurboAssembler::CallRecordWriteStub( Register object, Register address, RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode) { - CallRecordWriteStub( - object, address, remembered_set_action, fp_mode, - isolate()->builtins()->builtin_handle(Builtins::kRecordWrite), - kNullAddress); + CallRecordWriteStub(object, address, remembered_set_action, fp_mode, + Builtins::kRecordWrite, kNullAddress); } void TurboAssembler::CallRecordWriteStub( @@ -396,14 +394,15 @@ void TurboAssembler::CallRecordWriteStub( RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode, Address wasm_target) { CallRecordWriteStub(object, address, remembered_set_action, fp_mode, - Handle::null(), wasm_target); + Builtins::kNoBuiltinId, wasm_target); } void TurboAssembler::CallRecordWriteStub( Register object, Register address, RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode, - Handle code_target, Address wasm_target) { - DCHECK_NE(code_target.is_null(), wasm_target == kNullAddress); + int builtin_index, Address wasm_target) { + DCHECK_NE(builtin_index == Builtins::kNoBuiltinId, + wasm_target == kNullAddress); RecordWriteDescriptor descriptor; RegList registers = descriptor.allocatable_registers(); @@ -432,10 +431,14 @@ void TurboAssembler::CallRecordWriteStub( } else { movq(fp_mode_parameter, remembered_set_parameter); } - if (code_target.is_null()) { + if (builtin_index == Builtins::kNoBuiltinId) { // Use {near_call} for direct Wasm call within a module. near_call(wasm_target, RelocInfo::WASM_STUB_CALL); + } else if (options().inline_offheap_trampolines) { + CallBuiltin(builtin_index); } else { + Handle code_target = + isolate()->builtins()->builtin_handle(Builtins::kRecordWrite); Call(code_target, RelocInfo::CODE_TARGET); } diff --git a/deps/v8/src/codegen/x64/macro-assembler-x64.h b/deps/v8/src/codegen/x64/macro-assembler-x64.h index 8382bf5a287bca..ea87002b08e862 100644 --- a/deps/v8/src/codegen/x64/macro-assembler-x64.h +++ b/deps/v8/src/codegen/x64/macro-assembler-x64.h @@ -693,7 +693,7 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase { void CallRecordWriteStub(Register object, Register address, RememberedSetAction remembered_set_action, - SaveFPRegsMode fp_mode, Handle code_target, + SaveFPRegsMode fp_mode, int builtin_index, Address wasm_target); }; diff --git a/deps/v8/src/common/checks.h b/deps/v8/src/common/checks.h index ef9eb27ca07e9f..eef59701d1d4a9 100644 --- a/deps/v8/src/common/checks.h +++ b/deps/v8/src/common/checks.h @@ -18,9 +18,11 @@ namespace internal { #ifdef ENABLE_SLOW_DCHECKS #define SLOW_DCHECK(condition) \ CHECK(!v8::internal::FLAG_enable_slow_asserts || (condition)) +#define SLOW_DCHECK_IMPLIES(lhs, rhs) SLOW_DCHECK(!(lhs) || (rhs)) V8_EXPORT_PRIVATE extern bool FLAG_enable_slow_asserts; #else #define SLOW_DCHECK(condition) ((void)0) +#define SLOW_DCHECK_IMPLIES(v1, v2) ((void)0) static const bool FLAG_enable_slow_asserts = false; #endif diff --git a/deps/v8/src/common/globals.h b/deps/v8/src/common/globals.h index 4309b7023476ca..70b17ab6495a71 100644 --- a/deps/v8/src/common/globals.h +++ b/deps/v8/src/common/globals.h @@ -334,6 +334,9 @@ constexpr int kUC16Size = sizeof(uc16); // NOLINT // 128 bit SIMD value size. constexpr int kSimd128Size = 16; +// Maximum ordinal used for tracking asynchronous module evaluation order. +constexpr unsigned kMaxModuleAsyncEvaluatingOrdinal = (1 << 30) - 1; + // FUNCTION_ADDR(f) gets the address of a C function f. #define FUNCTION_ADDR(f) (reinterpret_cast(f)) diff --git a/deps/v8/src/compiler/allocation-builder.h b/deps/v8/src/compiler/allocation-builder.h index 040dd014051270..3a8dbdfff71804 100644 --- a/deps/v8/src/compiler/allocation-builder.h +++ b/deps/v8/src/compiler/allocation-builder.h @@ -27,7 +27,7 @@ class AllocationBuilder final { // Primitive allocation of static size. void Allocate(int size, AllocationType allocation = AllocationType::kYoung, Type type = Type::Any()) { - DCHECK_LE(size, kMaxRegularHeapObjectSize); + DCHECK_LE(size, Heap::MaxRegularHeapObjectSize(allocation)); effect_ = graph()->NewNode( common()->BeginRegion(RegionObservability::kNotObservable), effect_); allocation_ = diff --git a/deps/v8/src/compiler/backend/code-generator.cc b/deps/v8/src/compiler/backend/code-generator.cc index 72c5750035a19c..67e2ad74702e53 100644 --- a/deps/v8/src/compiler/backend/code-generator.cc +++ b/deps/v8/src/compiler/backend/code-generator.cc @@ -607,8 +607,8 @@ void CodeGenerator::GetPushCompatibleMoves(Instruction* instr, // then the full gap resolver must be used since optimization with // pushes don't participate in the parallel move and might clobber // values needed for the gap resolve. - if (source.IsStackSlot() && LocationOperand::cast(source).index() >= - first_push_compatible_index) { + if (source.IsAnyStackSlot() && LocationOperand::cast(source).index() >= + first_push_compatible_index) { pushes->clear(); return; } diff --git a/deps/v8/src/compiler/backend/ia32/code-generator-ia32.cc b/deps/v8/src/compiler/backend/ia32/code-generator-ia32.cc index c673458c75371b..52371f9d1f9696 100644 --- a/deps/v8/src/compiler/backend/ia32/code-generator-ia32.cc +++ b/deps/v8/src/compiler/backend/ia32/code-generator-ia32.cc @@ -1966,7 +1966,7 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( tmp = i.TempSimd128Register(0); // The minpd instruction doesn't propagate NaNs and +0's in its first // operand. Perform minpd in both orders, merge the resuls, and adjust. - __ Movapd(tmp, src1); + __ Movupd(tmp, src1); __ Minpd(tmp, tmp, src); __ Minpd(dst, src, src1); // propagate -0's and NaNs, which may be non-canonical. @@ -1985,7 +1985,7 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( tmp = i.TempSimd128Register(0); // The maxpd instruction doesn't propagate NaNs and +0's in its first // operand. Perform maxpd in both orders, merge the resuls, and adjust. - __ Movapd(tmp, src1); + __ Movupd(tmp, src1); __ Maxpd(tmp, tmp, src); __ Maxpd(dst, src, src1); // Find discrepancies. @@ -2375,7 +2375,7 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( XMMRegister dst = i.OutputSimd128Register(); Operand src1 = i.InputOperand(1); // See comment above for correction of maxps. - __ movaps(kScratchDoubleReg, src1); + __ vmovups(kScratchDoubleReg, src1); __ vmaxps(kScratchDoubleReg, kScratchDoubleReg, dst); __ vmaxps(dst, dst, src1); __ vxorps(dst, dst, kScratchDoubleReg); diff --git a/deps/v8/src/compiler/backend/x64/code-generator-x64.cc b/deps/v8/src/compiler/backend/x64/code-generator-x64.cc index 4f99ad49ba8980..e32a98e78fc157 100644 --- a/deps/v8/src/compiler/backend/x64/code-generator-x64.cc +++ b/deps/v8/src/compiler/backend/x64/code-generator-x64.cc @@ -579,10 +579,14 @@ void EmitWordLoadPoisoningIfNeeded(CodeGenerator* codegen, ASSEMBLE_SIMD_INSTR(opcode, dst, input_index); \ } while (false) -#define ASSEMBLE_SIMD_IMM_SHUFFLE(opcode, imm) \ - do { \ - DCHECK_EQ(i.OutputSimd128Register(), i.InputSimd128Register(0)); \ - __ opcode(i.OutputSimd128Register(), i.InputSimd128Register(1), imm); \ +#define ASSEMBLE_SIMD_IMM_SHUFFLE(opcode, imm) \ + do { \ + DCHECK_EQ(i.OutputSimd128Register(), i.InputSimd128Register(0)); \ + if (instr->InputAt(1)->IsSimd128Register()) { \ + __ opcode(i.OutputSimd128Register(), i.InputSimd128Register(1), imm); \ + } else { \ + __ opcode(i.OutputSimd128Register(), i.InputOperand(1), imm); \ + } \ } while (false) #define ASSEMBLE_SIMD_ALL_TRUE(opcode) \ diff --git a/deps/v8/src/compiler/backend/x64/instruction-selector-x64.cc b/deps/v8/src/compiler/backend/x64/instruction-selector-x64.cc index dd3f556937d096..56dd17ac693e7e 100644 --- a/deps/v8/src/compiler/backend/x64/instruction-selector-x64.cc +++ b/deps/v8/src/compiler/backend/x64/instruction-selector-x64.cc @@ -1270,7 +1270,9 @@ void InstructionSelector::VisitChangeInt32ToInt64(Node* node) { opcode = load_rep.IsSigned() ? kX64Movsxwq : kX64Movzxwq; break; case MachineRepresentation::kWord32: - opcode = load_rep.IsSigned() ? kX64Movsxlq : kX64Movl; + // ChangeInt32ToInt64 must interpret its input as a _signed_ 32-bit + // integer, so here we must sign-extend the loaded value in any case. + opcode = kX64Movsxlq; break; default: UNREACHABLE(); diff --git a/deps/v8/src/compiler/js-inlining-heuristic.cc b/deps/v8/src/compiler/js-inlining-heuristic.cc index 1c2bf5bc0eafe6..aef85c5b48897d 100644 --- a/deps/v8/src/compiler/js-inlining-heuristic.cc +++ b/deps/v8/src/compiler/js-inlining-heuristic.cc @@ -259,10 +259,9 @@ void JSInliningHeuristic::Finalize() { Candidate candidate = *i; candidates_.erase(i); - // Make sure we don't try to inline dead candidate nodes. - if (candidate.node->IsDead()) { - continue; - } + // Ignore this candidate if it's no longer valid. + if (!IrOpcode::IsInlineeOpcode(candidate.node->opcode())) continue; + if (candidate.node->IsDead()) continue; // Make sure we have some extra budget left, so that any small functions // exposed by this function would be given a chance to inline. diff --git a/deps/v8/src/compiler/memory-lowering.cc b/deps/v8/src/compiler/memory-lowering.cc index 8c230b6efd2fdd..d9ab0b8174dd14 100644 --- a/deps/v8/src/compiler/memory-lowering.cc +++ b/deps/v8/src/compiler/memory-lowering.cc @@ -98,6 +98,10 @@ Reduction MemoryLowering::ReduceAllocateRaw( DCHECK_EQ(IrOpcode::kAllocateRaw, node->opcode()); DCHECK_IMPLIES(allocation_folding_ == AllocationFolding::kDoAllocationFolding, state_ptr != nullptr); + // Code objects may have a maximum size smaller than kMaxHeapObjectSize due to + // guard pages. If we need to support allocating code here we would need to + // call MemoryChunkLayout::MaxRegularCodeObjectSize() at runtime. + DCHECK_NE(allocation_type, AllocationType::kCode); Node* value; Node* size = node->InputAt(0); Node* effect = node->InputAt(1); diff --git a/deps/v8/src/compiler/representation-change.cc b/deps/v8/src/compiler/representation-change.cc index 7077f7d643f633..0ef026aa6eea51 100644 --- a/deps/v8/src/compiler/representation-change.cc +++ b/deps/v8/src/compiler/representation-change.cc @@ -919,10 +919,10 @@ Node* RepresentationChanger::GetWord32RepresentationFor( return node; } else if (output_rep == MachineRepresentation::kWord64) { if (output_type.Is(Type::Signed32()) || - output_type.Is(Type::Unsigned32())) { - op = machine()->TruncateInt64ToInt32(); - } else if (output_type.Is(cache_->kSafeInteger) && - use_info.truncation().IsUsedAsWord32()) { + (output_type.Is(Type::Unsigned32()) && + use_info.type_check() == TypeCheckKind::kNone) || + (output_type.Is(cache_->kSafeInteger) && + use_info.truncation().IsUsedAsWord32())) { op = machine()->TruncateInt64ToInt32(); } else if (use_info.type_check() == TypeCheckKind::kSignedSmall || use_info.type_check() == TypeCheckKind::kSigned32 || diff --git a/deps/v8/src/compiler/simplified-lowering.cc b/deps/v8/src/compiler/simplified-lowering.cc index d00acefc39c791..156d4fcef058ed 100644 --- a/deps/v8/src/compiler/simplified-lowering.cc +++ b/deps/v8/src/compiler/simplified-lowering.cc @@ -178,10 +178,16 @@ void ReplaceEffectControlUses(Node* node, Node* effect, Node* control) { } bool CanOverflowSigned32(const Operator* op, Type left, Type right, - Zone* type_zone) { - // We assume the inputs are checked Signed32 (or known statically - // to be Signed32). Technically, the inputs could also be minus zero, but - // that cannot cause overflow. + TypeCache const* type_cache, Zone* type_zone) { + // We assume the inputs are checked Signed32 (or known statically to be + // Signed32). Technically, the inputs could also be minus zero, which we treat + // as 0 for the purpose of this function. + if (left.Maybe(Type::MinusZero())) { + left = Type::Union(left, type_cache->kSingletonZero, type_zone); + } + if (right.Maybe(Type::MinusZero())) { + right = Type::Union(right, type_cache->kSingletonZero, type_zone); + } left = Type::Intersect(left, Type::Signed32(), type_zone); right = Type::Intersect(right, Type::Signed32(), type_zone); if (left.IsNone() || right.IsNone()) return false; @@ -1375,7 +1381,6 @@ class RepresentationSelector { IsSomePositiveOrderedNumber(input1_type) ? CheckForMinusZeroMode::kDontCheckForMinusZero : CheckForMinusZeroMode::kCheckForMinusZero; - NodeProperties::ChangeOp(node, simplified()->CheckedInt32Mul(mz_mode)); } @@ -1419,6 +1424,18 @@ class RepresentationSelector { Type left_feedback_type = TypeOf(node->InputAt(0)); Type right_feedback_type = TypeOf(node->InputAt(1)); + + // Using Signed32 as restriction type amounts to promising there won't be + // signed overflow. This is incompatible with relying on a Word32 truncation + // in order to skip the overflow check. Similarly, we must not drop -0 from + // the result type unless we deopt for -0 inputs. + Type const restriction = + truncation.IsUsedAsWord32() + ? Type::Any() + : (truncation.identify_zeros() == kIdentifyZeros) + ? Type::Signed32OrMinusZero() + : Type::Signed32(); + // Handle the case when no int32 checks on inputs are necessary (but // an overflow check is needed on the output). Note that we do not // have to do any check if at most one side can be minus zero. For @@ -1432,7 +1449,7 @@ class RepresentationSelector { right_upper.Is(Type::Signed32OrMinusZero()) && (left_upper.Is(Type::Signed32()) || right_upper.Is(Type::Signed32()))) { VisitBinop(node, UseInfo::TruncatingWord32(), - MachineRepresentation::kWord32, Type::Signed32()); + MachineRepresentation::kWord32, restriction); } else { // If the output's truncation is identify-zeros, we can pass it // along. Moreover, if the operation is addition and we know the @@ -1452,12 +1469,14 @@ class RepresentationSelector { UseInfo right_use = CheckedUseInfoAsWord32FromHint(hint, FeedbackSource(), kIdentifyZeros); VisitBinop(node, left_use, right_use, MachineRepresentation::kWord32, - Type::Signed32()); + restriction); } + if (lower()) { if (truncation.IsUsedAsWord32() || !CanOverflowSigned32(node->op(), left_feedback_type, - right_feedback_type, graph_zone())) { + right_feedback_type, type_cache_, + graph_zone())) { ChangeToPureOp(node, Int32Op(node)); } else { diff --git a/deps/v8/src/compiler/type-cache.h b/deps/v8/src/compiler/type-cache.h index b71ea8455d544d..54e725c64f7e35 100644 --- a/deps/v8/src/compiler/type-cache.h +++ b/deps/v8/src/compiler/type-cache.h @@ -80,7 +80,7 @@ class V8_EXPORT_PRIVATE TypeCache final { Type::Union(kPositiveIntegerOrMinusZero, Type::NaN(), zone()); Type const kAdditiveSafeInteger = - CreateRange(-4503599627370496.0, 4503599627370496.0); + CreateRange(-4503599627370495.0, 4503599627370495.0); Type const kSafeInteger = CreateRange(-kMaxSafeInteger, kMaxSafeInteger); Type const kAdditiveSafeIntegerOrMinusZero = Type::Union(kAdditiveSafeInteger, Type::MinusZero(), zone()); diff --git a/deps/v8/src/deoptimizer/deoptimizer.cc b/deps/v8/src/deoptimizer/deoptimizer.cc index 44c92f557046db..970723839ca0ad 100644 --- a/deps/v8/src/deoptimizer/deoptimizer.cc +++ b/deps/v8/src/deoptimizer/deoptimizer.cc @@ -249,6 +249,7 @@ class ActivationsFinder : public ThreadVisitor { SafepointEntry safepoint = code.GetSafepointEntry(it.frame()->pc()); int trampoline_pc = safepoint.trampoline_pc(); DCHECK_IMPLIES(code == topmost_, safe_to_deopt_); + CHECK_GE(trampoline_pc, 0); // Replace the current pc on the stack with the trampoline. // TODO(v8:10026): avoid replacing a signed pointer. Address* pc_addr = it.frame()->pc_address(); @@ -3265,7 +3266,8 @@ Address TranslatedState::DecompressIfNeeded(intptr_t value) { } } -TranslatedState::TranslatedState(const JavaScriptFrame* frame) { +TranslatedState::TranslatedState(const JavaScriptFrame* frame) + : purpose_(kFrameInspection) { int deopt_index = Safepoint::kNoDeoptimizationIndex; DeoptimizationData data = static_cast(frame)->GetDeoptimizationData( @@ -3640,25 +3642,63 @@ void TranslatedState::EnsureCapturedObjectAllocatedAt( } default: - CHECK(map->IsJSObjectMap()); EnsureJSObjectAllocated(slot, map); - TranslatedValue* properties_slot = &(frame->values_[value_index]); - value_index++; + int remaining_children_count = slot->GetChildrenCount() - 1; + + TranslatedValue* properties_slot = frame->ValueAt(value_index); + value_index++, remaining_children_count--; if (properties_slot->kind() == TranslatedValue::kCapturedObject) { - // If we are materializing the property array, make sure we put - // the mutable heap numbers at the right places. + // We are materializing the property array, so make sure we put the + // mutable heap numbers at the right places. EnsurePropertiesAllocatedAndMarked(properties_slot, map); EnsureChildrenAllocated(properties_slot->GetChildrenCount(), frame, &value_index, worklist); + } else { + CHECK_EQ(properties_slot->kind(), TranslatedValue::kTagged); } - // Make sure all the remaining children (after the map and properties) are - // allocated. - return EnsureChildrenAllocated(slot->GetChildrenCount() - 2, frame, + + TranslatedValue* elements_slot = frame->ValueAt(value_index); + value_index++, remaining_children_count--; + if (elements_slot->kind() == TranslatedValue::kCapturedObject || + !map->IsJSArrayMap()) { + // Handle this case with the other remaining children below. + value_index--, remaining_children_count++; + } else { + CHECK_EQ(elements_slot->kind(), TranslatedValue::kTagged); + elements_slot->GetValue(); + if (purpose_ == kFrameInspection) { + // We are materializing a JSArray for the purpose of frame inspection. + // If we were to construct it with the above elements value then an + // actual deopt later on might create another JSArray instance with + // the same elements store. That would violate the key assumption + // behind left-trimming. + elements_slot->ReplaceElementsArrayWithCopy(); + } + } + + // Make sure all the remaining children (after the map, properties store, + // and possibly elements store) are allocated. + return EnsureChildrenAllocated(remaining_children_count, frame, &value_index, worklist); } UNREACHABLE(); } +void TranslatedValue::ReplaceElementsArrayWithCopy() { + DCHECK_EQ(kind(), TranslatedValue::kTagged); + DCHECK_EQ(materialization_state(), TranslatedValue::kFinished); + auto elements = Handle::cast(GetValue()); + DCHECK(elements->IsFixedArray() || elements->IsFixedDoubleArray()); + if (elements->IsFixedDoubleArray()) { + DCHECK(!elements->IsCowArray()); + set_storage(isolate()->factory()->CopyFixedDoubleArray( + Handle::cast(elements))); + } else if (!elements->IsCowArray()) { + set_storage(isolate()->factory()->CopyFixedArray( + Handle::cast(elements))); + } +} + void TranslatedState::EnsureChildrenAllocated(int count, TranslatedFrame* frame, int* value_index, std::stack* worklist) { @@ -3723,6 +3763,7 @@ Handle TranslatedState::AllocateStorageFor(TranslatedValue* slot) { void TranslatedState::EnsureJSObjectAllocated(TranslatedValue* slot, Handle map) { + CHECK(map->IsJSObjectMap()); CHECK_EQ(map->instance_size(), slot->GetChildrenCount() * kTaggedSize); Handle object_storage = AllocateStorageFor(slot); diff --git a/deps/v8/src/deoptimizer/deoptimizer.h b/deps/v8/src/deoptimizer/deoptimizer.h index ee6978e6292b8b..eaf0578878da0d 100644 --- a/deps/v8/src/deoptimizer/deoptimizer.h +++ b/deps/v8/src/deoptimizer/deoptimizer.h @@ -117,6 +117,8 @@ class TranslatedValue { return storage_; } + void ReplaceElementsArrayWithCopy(); + Kind kind_; MaterializationState materialization_state_ = kUninitialized; TranslatedState* container_; // This is only needed for materialization of @@ -313,7 +315,15 @@ class TranslatedFrame { class TranslatedState { public: - TranslatedState() = default; + // There are two constructors, each for a different purpose: + + // The default constructor is for the purpose of deoptimizing an optimized + // frame (replacing it with one or several unoptimized frames). It is used by + // the Deoptimizer. + TranslatedState() : purpose_(kDeoptimization) {} + + // This constructor is for the purpose of merely inspecting an optimized + // frame. It is used by stack trace generation and various debugging features. explicit TranslatedState(const JavaScriptFrame* frame); void Prepare(Address stack_frame_pointer); @@ -347,6 +357,12 @@ class TranslatedState { private: friend TranslatedValue; + // See the description of the constructors for an explanation of the two + // purposes. The only actual difference is that in the kFrameInspection case + // extra work is needed to not violate assumptions made by left-trimming. For + // details, see the code around ReplaceElementsArrayWithCopy. + enum Purpose { kDeoptimization, kFrameInspection }; + TranslatedFrame CreateNextTranslatedFrame(TranslationIterator* iterator, FixedArray literal_array, Address fp, FILE* trace_file); @@ -404,6 +420,7 @@ class TranslatedState { static Float32 GetFloatSlot(Address fp, int slot_index); static Float64 GetDoubleSlot(Address fp, int slot_index); + Purpose const purpose_; std::vector frames_; Isolate* isolate_ = nullptr; Address stack_frame_pointer_ = kNullAddress; diff --git a/deps/v8/src/diagnostics/ia32/disasm-ia32.cc b/deps/v8/src/diagnostics/ia32/disasm-ia32.cc index 5e0c5c65e2342e..a489968e1bdf77 100644 --- a/deps/v8/src/diagnostics/ia32/disasm-ia32.cc +++ b/deps/v8/src/diagnostics/ia32/disasm-ia32.cc @@ -1161,6 +1161,10 @@ int DisassemblerIA32::AVXInstruction(byte* data) { int mod, regop, rm, vvvv = vex_vreg(); get_modrm(*current, &mod, ®op, &rm); switch (opcode) { + case 0x10: + AppendToBuffer("vmovupd %s,", NameOfXMMRegister(regop)); + current += PrintRightXMMOperand(current); + break; case 0x28: AppendToBuffer("vmovapd %s,", NameOfXMMRegister(regop)); current += PrintRightXMMOperand(current); @@ -2090,7 +2094,13 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector out_buffer, data += 2; } else if (*data == 0x0F) { data++; - if (*data == 0x28) { + if (*data == 0x10) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("movupd %s,", NameOfXMMRegister(regop)); + data += PrintRightXMMOperand(data); + } else if (*data == 0x28) { data++; int mod, regop, rm; get_modrm(*data, &mod, ®op, &rm); diff --git a/deps/v8/src/diagnostics/objects-debug.cc b/deps/v8/src/diagnostics/objects-debug.cc index 32caba2da849b0..4925a09a0047a5 100644 --- a/deps/v8/src/diagnostics/objects-debug.cc +++ b/deps/v8/src/diagnostics/objects-debug.cc @@ -955,7 +955,8 @@ void Code::CodeVerify(Isolate* isolate) { // everything is set up. // CHECK_EQ(ReadOnlyHeap::Contains(*this), !IsExecutable()); relocation_info().ObjectVerify(isolate); - CHECK(Code::SizeFor(body_size()) <= kMaxRegularHeapObjectSize || + CHECK(Code::SizeFor(body_size()) <= + MemoryChunkLayout::MaxRegularCodeObjectSize() || isolate->heap()->InSpace(*this, CODE_LO_SPACE)); Address last_gc_pc = kNullAddress; @@ -1350,7 +1351,8 @@ void SourceTextModule::SourceTextModuleVerify(Isolate* isolate) { (status() == kPreInstantiating && code().IsSharedFunctionInfo()) || (status() == kUninstantiated && code().IsSharedFunctionInfo())); CHECK(top_level_capability().IsUndefined() && !AsyncParentModuleCount() && - !pending_async_dependencies() && !async_evaluating()); + !pending_async_dependencies()); + CHECK(!IsAsyncEvaluating()); } CHECK_EQ(requested_modules().length(), info().module_requests().length()); @@ -1665,12 +1667,13 @@ bool DescriptorArray::IsSortedNoDuplicates(int valid_entries) { uint32_t current = 0; for (int i = 0; i < number_of_descriptors(); i++) { Name key = GetSortedKey(i); + CHECK(key.HasHashCode()); if (key == current_key) { Print(); return false; } current_key = key; - uint32_t hash = GetSortedKey(i).Hash(); + uint32_t hash = key.hash(); if (hash < current) { Print(); return false; @@ -1689,7 +1692,8 @@ bool TransitionArray::IsSortedNoDuplicates(int valid_entries) { for (int i = 0; i < number_of_transitions(); i++) { Name key = GetSortedKey(i); - uint32_t hash = key.Hash(); + CHECK(key.HasHashCode()); + uint32_t hash = key.hash(); PropertyKind kind = kData; PropertyAttributes attributes = NONE; if (!TransitionsAccessor::IsSpecialTransition(key.GetReadOnlyRoots(), diff --git a/deps/v8/src/diagnostics/objects-printer.cc b/deps/v8/src/diagnostics/objects-printer.cc index 00ef81f56a6bfc..e23c4674f84251 100644 --- a/deps/v8/src/diagnostics/objects-printer.cc +++ b/deps/v8/src/diagnostics/objects-printer.cc @@ -1621,6 +1621,8 @@ void SourceTextModule::SourceTextModulePrint(std::ostream& os) { // NOLINT os << "\n - requested_modules: " << Brief(requested_modules()); os << "\n - script: " << Brief(script()); os << "\n - import_meta: " << Brief(import_meta()); + os << "\n - cycle_root: " << Brief(cycle_root()); + os << "\n - async_evaluating_ordinal: " << async_evaluating_ordinal(); os << "\n"; } diff --git a/deps/v8/src/execution/isolate-inl.h b/deps/v8/src/execution/isolate-inl.h index 163ab4ea8cbfca..e39852f2a842bf 100644 --- a/deps/v8/src/execution/isolate-inl.h +++ b/deps/v8/src/execution/isolate-inl.h @@ -12,6 +12,7 @@ #include "src/objects/property-cell.h" #include "src/objects/regexp-match-info.h" #include "src/objects/shared-function-info.h" +#include "src/objects/source-text-module.h" namespace v8 { namespace internal { @@ -117,6 +118,36 @@ Isolate::ExceptionScope::~ExceptionScope() { isolate_->set_pending_exception(*pending_exception_); } +void Isolate::DidFinishModuleAsyncEvaluation(unsigned ordinal) { + // To address overflow, the ordinal is reset when the async module with the + // largest vended ordinal finishes evaluating. Modules are evaluated in + // ascending order of their async_evaluating_ordinal. + // + // While the specification imposes a global total ordering, the intention is + // that for each async module, all its parents are totally ordered by when + // they first had their [[AsyncEvaluating]] bit set. + // + // The module with largest vended ordinal finishes evaluating implies that the + // async dependency as well as all other modules in that module's graph + // depending on async dependencies are finished evaluating. + // + // If the async dependency participates in other module graphs (e.g. via + // dynamic import, or other diff --git a/lib/.eslintrc.yaml b/lib/.eslintrc.yaml index 20e01898695507..6bbdafab632fdd 100644 --- a/lib/.eslintrc.yaml +++ b/lib/.eslintrc.yaml @@ -95,7 +95,7 @@ rules: - selector: "ThrowStatement > CallExpression[callee.name=/Error$/]" message: "Use new keyword when throwing an Error." # Config specific to lib - - selector: "NewExpression[callee.name=/Error$/]:not([callee.name=/^(AssertionError|NghttpError)$/])" + - selector: "NewExpression[callee.name=/Error$/]:not([callee.name=/^(AssertionError|NghttpError|AbortError)$/])" message: "Use an error exported by the internal/errors module." - selector: "CallExpression[callee.object.name='Error'][callee.property.name='captureStackTrace']" message: "Please use `require('internal/errors').hideStackFrames()` instead." diff --git a/lib/_http_agent.js b/lib/_http_agent.js index aeca444ad158bf..237f7e104482be 100644 --- a/lib/_http_agent.js +++ b/lib/_http_agent.js @@ -94,7 +94,7 @@ function Agent(options) { this.keepAlive = this.options.keepAlive || false; this.maxSockets = this.options.maxSockets || Agent.defaultMaxSockets; this.maxFreeSockets = this.options.maxFreeSockets || 256; - this.scheduling = this.options.scheduling || 'fifo'; + this.scheduling = this.options.scheduling || 'lifo'; this.maxTotalSockets = this.options.maxTotalSockets; this.totalSocketCount = 0; diff --git a/lib/_http_client.js b/lib/_http_client.js index 7b1232f33f0a9b..c8edada6401cf9 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -36,6 +36,7 @@ const { const net = require('net'); const url = require('url'); const assert = require('internal/assert'); +const { once } = require('internal/util'); const { _checkIsHttpToken: checkIsHttpToken, debug, @@ -51,7 +52,7 @@ const { Buffer } = require('buffer'); const { defaultTriggerAsyncIdScope } = require('internal/async_hooks'); const { URL, urlToOptions, searchParamsSymbol } = require('internal/url'); const { kOutHeaders, kNeedDrain } = require('internal/http'); -const { connResetException, codes } = require('internal/errors'); +const { AbortError, connResetException, codes } = require('internal/errors'); const { ERR_HTTP_HEADERS_SENT, ERR_INVALID_ARG_TYPE, @@ -59,7 +60,10 @@ const { ERR_INVALID_PROTOCOL, ERR_UNESCAPED_CHARACTERS } = codes; -const { validateInteger } = require('internal/validators'); +const { + validateInteger, + validateAbortSignal, +} = require('internal/validators'); const { getTimerDuration } = require('internal/timers'); const { DTRACE_HTTP_CLIENT_REQUEST, @@ -169,6 +173,15 @@ function ClientRequest(input, options, cb) { if (options.timeout !== undefined) this.timeout = getTimerDuration(options.timeout, 'timeout'); + const signal = options.signal; + if (signal) { + validateAbortSignal(signal, 'options.signal'); + const listener = (e) => this.destroy(new AbortError()); + signal.addEventListener('abort', listener); + this.once('close', () => { + signal.removeEventListener('abort', listener); + }); + } let method = options.method; const methodIsString = (typeof method === 'string'); if (method !== null && method !== undefined && !methodIsString) { @@ -224,8 +237,6 @@ function ClientRequest(input, options, cb) { this.host = host; this.protocol = protocol; - let called = false; - if (this.agent) { // If there is an agent we should default to Connection:keep-alive, // but only if the Agent will actually reuse the connection! @@ -289,18 +300,6 @@ function ClientRequest(input, options, cb) { options.headers); } - const oncreate = (err, socket) => { - if (called) - return; - called = true; - if (err) { - process.nextTick(() => this.emit('error', err)); - return; - } - this.onSocket(socket); - this._deferToConnect(null, null, () => this._flush()); - }; - // initiate connection if (this.agent) { this.agent.addRequest(this, options); @@ -309,20 +308,27 @@ function ClientRequest(input, options, cb) { this._last = true; this.shouldKeepAlive = false; if (typeof options.createConnection === 'function') { - const newSocket = options.createConnection(options, oncreate); - if (newSocket && !called) { - called = true; - this.onSocket(newSocket); - } else { - return; + const oncreate = once((err, socket) => { + if (err) { + process.nextTick(() => this.emit('error', err)); + } else { + this.onSocket(socket); + } + }); + + try { + const newSocket = options.createConnection(options, oncreate); + if (newSocket) { + oncreate(null, newSocket); + } + } catch (err) { + oncreate(err); } } else { debug('CLIENT use net.createConnection', options); this.onSocket(net.createConnection(options)); } } - - this._deferToConnect(null, null, () => this._flush()); } ObjectSetPrototypeOf(ClientRequest.prototype, OutgoingMessage.prototype); ObjectSetPrototypeOf(ClientRequest, OutgoingMessage); @@ -815,6 +821,7 @@ function onSocketNT(req, socket, err) { _destroy(req, null, err); } else { tickOnSocket(req, socket); + req._flush(); } } diff --git a/lib/_http_incoming.js b/lib/_http_incoming.js index a33d75c3e7900e..628043aaf76131 100644 --- a/lib/_http_incoming.js +++ b/lib/_http_incoming.js @@ -100,7 +100,14 @@ IncomingMessage.prototype.setTimeout = function setTimeout(msecs, callback) { return this; }; - +// Argument n cannot be factored out due to the overhead of +// argument adaptor frame creation inside V8 in case that number of actual +// arguments is different from expected arguments. +// Ref: https://bugs.chromium.org/p/v8/issues/detail?id=10201 +// NOTE: Argument adapt frame issue might be solved in V8 engine v8.9. +// Refactoring `n` out might be possible when V8 is upgraded to that +// version. +// Ref: https://v8.dev/blog/v8-release-89 IncomingMessage.prototype._read = function _read(n) { if (!this._consuming) { this._readableState.readingMore = false; diff --git a/lib/_http_outgoing.js b/lib/_http_outgoing.js index c8a393ddcf31b4..7736c31c4d41a8 100644 --- a/lib/_http_outgoing.js +++ b/lib/_http_outgoing.js @@ -22,10 +22,12 @@ 'use strict'; const { + Array, ArrayIsArray, ObjectCreate, ObjectDefineProperty, ObjectKeys, + ObjectValues, ObjectPrototypeHasOwnProperty, ObjectSetPrototypeOf, MathFloor, @@ -445,7 +447,8 @@ function _storeHeader(firstLine, headers) { } if (!state.contLen && !state.te) { - if (!this._hasBody) { + if (!this._hasBody && (this.statusCode === 204 || + this.statusCode === 304)) { // Make sure we don't end the 0\r\n\r\n at the end of the message. this.chunkedEncoding = false; } else if (!this.useChunkedEncodingByDefault) { @@ -565,6 +568,7 @@ OutgoingMessage.prototype.setHeader = function setHeader(name, value) { this[kOutHeaders] = headers = ObjectCreate(null); headers[name.toLowerCase()] = [name, value]; + return this; }; @@ -586,6 +590,23 @@ OutgoingMessage.prototype.getHeaderNames = function getHeaderNames() { }; +// Returns an array of the names of the current outgoing raw headers. +OutgoingMessage.prototype.getRawHeaderNames = function getRawHeaderNames() { + const headersMap = this[kOutHeaders]; + if (headersMap === null) return []; + + const values = ObjectValues(headersMap); + const headers = Array(values.length); + // Retain for(;;) loop for performance reasons + // Refs: https://github.com/nodejs/node/pull/30958 + for (let i = 0, l = values.length; i < l; i++) { + headers[i] = values[i][0]; + } + + return headers; +}; + + // Returns a shallow copy of the current outgoing headers. OutgoingMessage.prototype.getHeaders = function getHeaders() { const headers = this[kOutHeaders]; @@ -655,6 +676,11 @@ ObjectDefineProperty(OutgoingMessage.prototype, 'writableEnded', { get: function() { return this.finished; } }); +ObjectDefineProperty(OutgoingMessage.prototype, 'writableNeedDrain', { + get: function() { + return !this.destroyed && !this.finished && this[kNeedDrain]; + } +}); const crlf_buf = Buffer.from('\r\n'); OutgoingMessage.prototype.write = function write(chunk, encoding, callback) { diff --git a/lib/_http_server.js b/lib/_http_server.js index dbcff03ca53e5d..1890e665cb9b55 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -79,6 +79,10 @@ const { observerCounts, constants } = internalBinding('performance'); const { setTimeout, clearTimeout } = require('timers'); const { NODE_PERFORMANCE_ENTRY_TYPE_HTTP } = constants; +const dc = require('diagnostics_channel'); +const onRequestStartChannel = dc.channel('http.server.request.start'); +const onResponseFinishChannel = dc.channel('http.server.response.finish'); + const kServerResponse = Symbol('ServerResponse'); const kServerResponseStatistics = Symbol('ServerResponseStatistics'); @@ -754,6 +758,15 @@ function clearRequestTimeout(req) { } function resOnFinish(req, res, socket, state, server) { + if (onResponseFinishChannel.hasSubscribers) { + onResponseFinishChannel.publish({ + request: req, + response: res, + socket, + server + }); + } + // Usually the first incoming element should be our request. it may // be that in the case abortIncoming() was called that the incoming // array will be empty. @@ -839,6 +852,15 @@ function parserOnIncoming(server, socket, state, req, keepAlive) { res.shouldKeepAlive = keepAlive; DTRACE_HTTP_SERVER_REQUEST(req, socket); + if (onRequestStartChannel.hasSubscribers) { + onRequestStartChannel.publish({ + request: req, + response: res, + socket, + server + }); + } + if (socket._httpMessage) { // There are already pending outgoing res, append. state.outgoing.push(res); diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index c5f30c01fa18e7..e63bcaaf6a7ce9 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -1320,6 +1320,9 @@ Server.prototype.setSecureContext = function(options) { if (options.ticketKeys) this.ticketKeys = options.ticketKeys; + this.privateKeyIdentifier = options.privateKeyIdentifier; + this.privateKeyEngine = options.privateKeyEngine; + this._sharedCreds = tls.createSecureContext({ pfx: this.pfx, key: this.key, @@ -1339,7 +1342,9 @@ Server.prototype.setSecureContext = function(options) { crl: this.crl, sessionIdContext: this.sessionIdContext, ticketKeys: this.ticketKeys, - sessionTimeout: this.sessionTimeout + sessionTimeout: this.sessionTimeout, + privateKeyIdentifier: this.privateKeyIdentifier, + privateKeyEngine: this.privateKeyEngine, }); }; @@ -1405,6 +1410,11 @@ Server.prototype.setOptions = deprecate(function(options) { } if (options.pskCallback) this[kPskCallback] = options.pskCallback; if (options.pskIdentityHint) this[kPskIdentityHint] = options.pskIdentityHint; + if (options.sigalgs) this.sigalgs = options.sigalgs; + if (options.privateKeyIdentifier !== undefined) + this.privateKeyIdentifier = options.privateKeyIdentifier; + if (options.privateKeyEngine !== undefined) + this.privateKeyEngine = options.privateKeyEngine; }, 'Server.prototype.setOptions() is deprecated', 'DEP0122'); // SNI Contexts High-Level API diff --git a/lib/assert.js b/lib/assert.js index af6b1fc59a953c..48daa56a29c4bd 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -105,6 +105,14 @@ function innerFail(obj) { throw new AssertionError(obj); } +/** + * @param {any} actual + * @param {any} expected + * @param {string | Error} [message] + * @param {string} [operator] + * @param {Function} [stackStartFn] + * @returns {never} + */ function fail(actual, expected, message, operator, stackStartFn) { const argsLen = arguments.length; @@ -384,14 +392,24 @@ function innerOk(fn, argLen, value, message) { } } -// Pure assertion tests whether a value is truthy, as determined -// by !!value. +/** + * Pure assertion tests whether a value is truthy, as determined + * by !!value. + * @param {...any} args + * @returns {void} + */ function ok(...args) { innerOk(ok, args.length, ...args); } assert.ok = ok; -// The equality assertion tests shallow, coercive equality with ==. +/** + * The equality assertion tests shallow, coercive equality with ==. + * @param {any} actual + * @param {any} expected + * @param {string | Error} [message] + * @returns {void} + */ /* eslint-disable no-restricted-properties */ assert.equal = function equal(actual, expected, message) { if (arguments.length < 2) { @@ -409,8 +427,14 @@ assert.equal = function equal(actual, expected, message) { } }; -// The non-equality assertion tests for whether two objects are not -// equal with !=. +/** + * The non-equality assertion tests for whether two objects are not + * equal with !=. + * @param {any} actual + * @param {any} expected + * @param {string | Error} [message] + * @returns {void} + */ assert.notEqual = function notEqual(actual, expected, message) { if (arguments.length < 2) { throw new ERR_MISSING_ARGS('actual', 'expected'); @@ -427,7 +451,13 @@ assert.notEqual = function notEqual(actual, expected, message) { } }; -// The equivalence assertion tests a deep equality relation. +/** + * The deep equivalence assertion tests a deep equality relation. + * @param {any} actual + * @param {any} expected + * @param {string | Error} [message] + * @returns {void} + */ assert.deepEqual = function deepEqual(actual, expected, message) { if (arguments.length < 2) { throw new ERR_MISSING_ARGS('actual', 'expected'); @@ -444,7 +474,13 @@ assert.deepEqual = function deepEqual(actual, expected, message) { } }; -// The non-equivalence assertion tests for any deep inequality. +/** + * The deep non-equivalence assertion tests for any deep inequality. + * @param {any} actual + * @param {any} expected + * @param {string | Error} [message] + * @returns {void} + */ assert.notDeepEqual = function notDeepEqual(actual, expected, message) { if (arguments.length < 2) { throw new ERR_MISSING_ARGS('actual', 'expected'); @@ -462,6 +498,14 @@ assert.notDeepEqual = function notDeepEqual(actual, expected, message) { }; /* eslint-enable */ +/** + * The deep strict equivalence assertion tests a deep strict equality + * relation. + * @param {any} actual + * @param {any} expected + * @param {string | Error} [message] + * @returns {void} + */ assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) { if (arguments.length < 2) { throw new ERR_MISSING_ARGS('actual', 'expected'); @@ -478,6 +522,14 @@ assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) { } }; +/** + * The deep strict non-equivalence assertion tests for any deep strict + * inequality. + * @param {any} actual + * @param {any} expected + * @param {string | Error} [message] + * @returns {void} + */ assert.notDeepStrictEqual = notDeepStrictEqual; function notDeepStrictEqual(actual, expected, message) { if (arguments.length < 2) { @@ -495,6 +547,13 @@ function notDeepStrictEqual(actual, expected, message) { } } +/** + * The strict equivalence assertion tests a strict equality relation. + * @param {any} actual + * @param {any} expected + * @param {string | Error} [message] + * @returns {void} + */ assert.strictEqual = function strictEqual(actual, expected, message) { if (arguments.length < 2) { throw new ERR_MISSING_ARGS('actual', 'expected'); @@ -510,6 +569,13 @@ assert.strictEqual = function strictEqual(actual, expected, message) { } }; +/** + * The strict non-equivalence assertion tests for any strict inequality. + * @param {any} actual + * @param {any} expected + * @param {string | Error} [message] + * @returns {void} + */ assert.notStrictEqual = function notStrictEqual(actual, expected, message) { if (arguments.length < 2) { throw new ERR_MISSING_ARGS('actual', 'expected'); @@ -820,22 +886,51 @@ function expectsNoError(stackStartFn, actual, error, message) { throw actual; } +/** + * Expects the function `promiseFn` to throw an error. + * @param {() => any} promiseFn + * @param {...any} [args] + * @returns {void} + */ assert.throws = function throws(promiseFn, ...args) { expectsError(throws, getActual(promiseFn), ...args); }; +/** + * Expects `promiseFn` function or its value to reject. + * @param {() => Promise} promiseFn + * @param {...any} [args] + * @returns {Promise} + */ assert.rejects = async function rejects(promiseFn, ...args) { expectsError(rejects, await waitForActual(promiseFn), ...args); }; +/** + * Asserts that the function `fn` does not throw an error. + * @param {() => any} fn + * @param {...any} [args] + * @returns {void} + */ assert.doesNotThrow = function doesNotThrow(fn, ...args) { expectsNoError(doesNotThrow, getActual(fn), ...args); }; +/** + * Expects `fn` or its value to not reject. + * @param {() => Promise} fn + * @param {...any} [args] + * @returns {Promise} + */ assert.doesNotReject = async function doesNotReject(fn, ...args) { expectsNoError(doesNotReject, await waitForActual(fn), ...args); }; +/** + * Throws `value` if the value is not `null` or `undefined`. + * @param {any} err + * @returns {void} + */ assert.ifError = function ifError(err) { if (err !== null && err !== undefined) { let message = 'ifError got unwanted exception: '; @@ -919,24 +1014,44 @@ function internalMatch(string, regexp, message, fn) { } } +/** + * Expects the `string` input to match the regular expression. + * @param {string} string + * @param {RegExp} regexp + * @param {string | Error} [message] + * @returns {void} + */ assert.match = function match(string, regexp, message) { internalMatch(string, regexp, message, match); }; +/** + * Expects the `string` input not to match the regular expression. + * @param {string} string + * @param {RegExp} regexp + * @param {string | Error} [message] + * @returns {void} + */ assert.doesNotMatch = function doesNotMatch(string, regexp, message) { internalMatch(string, regexp, message, doesNotMatch); }; assert.CallTracker = CallTracker; -// Expose a strict only variant of assert +/** + * Expose a strict only variant of assert. + * @param {...any} args + * @returns {void} + */ function strict(...args) { innerOk(strict, args.length, ...args); } + assert.strict = ObjectAssign(strict, assert, { equal: assert.strictEqual, deepEqual: assert.deepStrictEqual, notEqual: assert.notStrictEqual, notDeepEqual: assert.notDeepStrictEqual }); + assert.strict.strict = assert.strict; diff --git a/lib/buffer.js b/lib/buffer.js index 2c89a249f37dea..6726ab98fcdc35 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -1202,13 +1202,49 @@ if (internalBinding('config').hasIntl) { }; } +let DOMException; + +const lazyInvalidCharError = hideStackFrames((message, name) => { + if (DOMException === undefined) + DOMException = internalBinding('messaging').DOMException; + throw new DOMException('Invalid character', 'InvalidCharacterError'); +}); + +function btoa(input) { + // TODO(@jasnell): The implementation here has not been performance + // optimized in any way. + input = `${input}`; + for (let n = 0; n < input.length; n++) { + if (input[n].charCodeAt(0) > 0xff) + lazyInvalidCharError(); + } + const buf = Buffer.from(input, 'latin1'); + return buf.toString('base64'); +} + +const kBase64Digits = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; + +function atob(input) { + // TODO(@jasnell): The implementation here has not been performance + // optimized in any way. + input = `${input}`; + for (let n = 0; n < input.length; n++) { + if (!kBase64Digits.includes(input[n])) + lazyInvalidCharError(); + } + return Buffer.from(input, 'base64').toString('latin1'); +} + module.exports = { Buffer, SlowBuffer, transcode, // Legacy kMaxLength, - kStringMaxLength + kStringMaxLength, + btoa, + atob, }; ObjectDefineProperties(module.exports, { diff --git a/lib/child_process.js b/lib/child_process.js index 9e1c37af8f169f..ad5bce3db044d9 100644 --- a/lib/child_process.js +++ b/lib/child_process.js @@ -45,15 +45,24 @@ let debug = require('internal/util/debuglog').debuglog( ); const { Buffer } = require('buffer'); const { Pipe, constants: PipeConstants } = internalBinding('pipe_wrap'); + +const { + AbortError, + codes: errorCodes, +} = require('internal/errors'); const { ERR_INVALID_ARG_VALUE, ERR_CHILD_PROCESS_IPC_REQUIRED, ERR_CHILD_PROCESS_STDIO_MAXBUFFER, ERR_INVALID_ARG_TYPE, - ERR_OUT_OF_RANGE -} = require('internal/errors').codes; + ERR_OUT_OF_RANGE, +} = errorCodes; const { clearTimeout, setTimeout } = require('timers'); -const { validateString, isInt32 } = require('internal/validators'); +const { + validateString, + isInt32, + validateAbortSignal, +} = require('internal/validators'); const child_process = require('internal/child_process'); const { getValidStdio, @@ -76,8 +85,7 @@ function fork(modulePath /* , args, options */) { args = arguments[pos++]; } - if (pos < arguments.length && - (arguments[pos] === undefined || arguments[pos] === null)) { + if (pos < arguments.length && arguments[pos] == null) { pos++; } @@ -118,7 +126,7 @@ function fork(modulePath /* , args, options */) { options.execPath = options.execPath || process.execPath; options.shell = false; - return spawn(options.execPath, args, options); + return spawnWithSignal(options.execPath, args, options); } function _forkChild(fd, serializationMode) { @@ -232,6 +240,9 @@ function execFile(file /* , args, options, callback */) { // Validate maxBuffer, if present. validateMaxBuffer(options.maxBuffer); + // Validate signal, if present + validateAbortSignal(options.signal, 'options.signal'); + options.killSignal = sanitizeKillSignal(options.killSignal); const child = spawn(file, args, { @@ -349,6 +360,19 @@ function execFile(file /* , args, options, callback */) { timeoutId = null; }, options.timeout); } + if (options.signal) { + if (options.signal.aborted) { + process.nextTick(() => kill()); + } else { + const childController = new AbortController(); + options.signal.addEventListener('abort', () => { + if (!ex) + ex = new AbortError(); + kill(); + }, { signal: childController.signal }); + child.once('close', () => childController.abort()); + } + } if (child.stdout) { if (encoding) @@ -684,6 +708,30 @@ function sanitizeKillSignal(killSignal) { } } +// This level of indirection is here because the other child_process methods +// call spawn internally but should use different cancellation logic. +function spawnWithSignal(file, args, options) { + const child = spawn(file, args, options); + + if (options && options.signal) { + // Validate signal, if present + validateAbortSignal(options.signal, 'options.signal'); + function kill() { + if (child._handle) { + child.kill('SIGTERM'); + child.emit('error', new AbortError()); + } + } + if (options.signal.aborted) { + process.nextTick(kill); + } else { + options.signal.addEventListener('abort', kill); + const remove = () => options.signal.removeEventListener('abort', kill); + child.once('close', remove); + } + } + return child; +} module.exports = { _forkChild, ChildProcess, @@ -692,6 +740,6 @@ module.exports = { execFileSync, execSync, fork, - spawn, + spawn: spawnWithSignal, spawnSync }; diff --git a/lib/crypto.js b/lib/crypto.js index b2bcc4d0a44592..a41b02d97a70ad 100644 --- a/lib/crypto.js +++ b/lib/crypto.js @@ -53,7 +53,8 @@ const { randomBytes, randomFill, randomFillSync, - randomInt + randomInt, + randomUUID, } = require('internal/crypto/random'); const { pbkdf2, @@ -186,6 +187,7 @@ module.exports = { randomFill, randomFillSync, randomInt, + randomUUID, scrypt, scryptSync, sign: signOneShot, diff --git a/lib/dgram.js b/lib/dgram.js index 1c1b4781617695..4db4388c4f9fff 100644 --- a/lib/dgram.js +++ b/lib/dgram.js @@ -48,6 +48,7 @@ const { } = errors.codes; const { isInt32, + validateAbortSignal, validateString, validateNumber, validatePort, @@ -121,6 +122,20 @@ function Socket(type, listener) { recvBufferSize, sendBufferSize }; + + if (options?.signal !== undefined) { + const { signal } = options; + validateAbortSignal(signal, 'options.signal'); + const onAborted = () => { + this.close(); + }; + if (signal.aborted) { + onAborted(); + } else { + signal.addEventListener('abort', onAborted); + this.once('close', () => signal.removeEventListener('abort', onAborted)); + } + } } ObjectSetPrototypeOf(Socket.prototype, EventEmitter.prototype); ObjectSetPrototypeOf(Socket, EventEmitter); diff --git a/lib/diagnostics_channel.js b/lib/diagnostics_channel.js new file mode 100644 index 00000000000000..c29c9ff0052405 --- /dev/null +++ b/lib/diagnostics_channel.js @@ -0,0 +1,121 @@ +'use strict'; + +const { + ArrayPrototypeIndexOf, + ArrayPrototypePush, + ArrayPrototypeSplice, + ObjectCreate, + ObjectGetPrototypeOf, + ObjectSetPrototypeOf, + SymbolHasInstance, +} = primordials; + +const { + codes: { + ERR_INVALID_ARG_TYPE, + } +} = require('internal/errors'); + +const { triggerUncaughtException } = internalBinding('errors'); + +const { WeakReference } = internalBinding('util'); + +// TODO(qard): should there be a C++ channel interface? +class ActiveChannel { + subscribe(subscription) { + if (typeof subscription !== 'function') { + throw new ERR_INVALID_ARG_TYPE('subscription', ['function'], + subscription); + } + ArrayPrototypePush(this._subscribers, subscription); + } + + unsubscribe(subscription) { + const index = ArrayPrototypeIndexOf(this._subscribers, subscription); + if (index >= 0) { + ArrayPrototypeSplice(this._subscribers, index, 1); + + // When there are no more active subscribers, restore to fast prototype. + if (!this._subscribers.length) { + // eslint-disable-next-line no-use-before-define + ObjectSetPrototypeOf(this, Channel.prototype); + } + } + } + + get hasSubscribers() { + return true; + } + + publish(data) { + for (let i = 0; i < this._subscribers.length; i++) { + try { + const onMessage = this._subscribers[i]; + onMessage(data, this.name); + } catch (err) { + process.nextTick(() => { + triggerUncaughtException(err, false); + }); + } + } + } +} + +class Channel { + constructor(name) { + this._subscribers = undefined; + this.name = name; + } + + static [SymbolHasInstance](instance) { + const prototype = ObjectGetPrototypeOf(instance); + return prototype === Channel.prototype || + prototype === ActiveChannel.prototype; + } + + subscribe(subscription) { + ObjectSetPrototypeOf(this, ActiveChannel.prototype); + this._subscribers = []; + this.subscribe(subscription); + } + + get hasSubscribers() { + return false; + } + + publish() {} +} + +const channels = ObjectCreate(null); + +function channel(name) { + let channel; + const ref = channels[name]; + if (ref) channel = ref.get(); + if (channel) return channel; + + if (typeof name !== 'string' && typeof name !== 'symbol') { + throw new ERR_INVALID_ARG_TYPE('channel', ['string', 'symbol'], name); + } + + channel = new Channel(name); + channels[name] = new WeakReference(channel); + return channel; +} + +function hasSubscribers(name) { + let channel; + const ref = channels[name]; + if (ref) channel = ref.get(); + if (!channel) { + return false; + } + + return channel.hasSubscribers; +} + +module.exports = { + channel, + hasSubscribers, + Channel +}; diff --git a/lib/dns.js b/lib/dns.js index 0393069f811711..86b540cf08750a 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -236,6 +236,7 @@ const resolveMap = ObjectCreate(null); Resolver.prototype.resolveAny = resolveMap.ANY = resolver('queryAny'); Resolver.prototype.resolve4 = resolveMap.A = resolver('queryA'); Resolver.prototype.resolve6 = resolveMap.AAAA = resolver('queryAaaa'); +Resolver.prototype.resolveCaa = resolveMap.CAA = resolver('queryCaa'); Resolver.prototype.resolveCname = resolveMap.CNAME = resolver('queryCname'); Resolver.prototype.resolveMx = resolveMap.MX = resolver('queryMx'); Resolver.prototype.resolveNs = resolveMap.NS = resolver('queryNs'); diff --git a/lib/domain.js b/lib/domain.js index 1d6d75ee91527a..74cd59c54e705b 100644 --- a/lib/domain.js +++ b/lib/domain.js @@ -124,12 +124,15 @@ process.setUncaughtExceptionCaptureCallback = function(fn) { let sendMakeCallbackDeprecation = false; -function emitMakeCallbackDeprecation() { +function emitMakeCallbackDeprecation({ target, method }) { if (!sendMakeCallbackDeprecation) { process.emitWarning( 'Using a domain property in MakeCallback is deprecated. Use the ' + 'async_context variant of MakeCallback or the AsyncResource class ' + - 'instead.', 'DeprecationWarning', 'DEP0097'); + 'instead. ' + + `(Triggered by calling ${method?.name || ''} ` + + `on ${target?.constructor?.name}.)`, + 'DeprecationWarning', 'DEP0097'); sendMakeCallbackDeprecation = true; } } @@ -137,7 +140,7 @@ function emitMakeCallbackDeprecation() { function topLevelDomainCallback(cb, ...args) { const domain = this.domain; if (exports.active && domain) - emitMakeCallbackDeprecation(); + emitMakeCallbackDeprecation({ target: this, method: cb }); if (domain) domain.enter(); @@ -316,7 +319,7 @@ Domain.prototype.exit = function() { // Exit all domains until this one. ArrayPrototypeSplice(stack, index); - exports.active = stack[stack.length - 1]; + exports.active = stack.length === 0 ? undefined : stack[stack.length - 1]; process.domain = exports.active; updateExceptionCapture(); }; @@ -333,7 +336,7 @@ Domain.prototype.add = function(ee) { ee.domain.remove(ee); // Check for circular Domain->Domain links. - // This causes bad insanity! + // They cause big issues. // // For example: // var d = domain.create(); @@ -453,7 +456,7 @@ EventEmitter.init = function() { }; const eventEmit = EventEmitter.prototype.emit; -EventEmitter.prototype.emit = function(...args) { +EventEmitter.prototype.emit = function emit(...args) { const domain = this.domain; const type = args[0]; diff --git a/lib/events.js b/lib/events.js index 48da5e0ed1b72d..c80e6ae2eed3ac 100644 --- a/lib/events.js +++ b/lib/events.js @@ -22,6 +22,7 @@ 'use strict'; const { + ArrayPrototypePush, Boolean, Error, ErrorCaptureStackTrace, @@ -29,6 +30,7 @@ const { NumberIsNaN, ObjectCreate, ObjectDefineProperty, + ObjectDefineProperties, ObjectGetPrototypeOf, ObjectSetPrototypeOf, Promise, @@ -46,6 +48,7 @@ const kRejection = SymbolFor('nodejs.rejection'); let spliceOne; const { + hideStackFrames, kEnhanceStackBeforeInspector, codes } = require('internal/errors'); @@ -59,8 +62,23 @@ const { inspect } = require('internal/util/inspect'); +const { + validateAbortSignal +} = require('internal/validators'); + const kCapture = Symbol('kCapture'); const kErrorMonitor = Symbol('events.errorMonitor'); +const kMaxEventTargetListeners = Symbol('events.maxEventTargetListeners'); +const kMaxEventTargetListenersWarned = + Symbol('events.maxEventTargetListenersWarned'); + +let DOMException; +const lazyDOMException = hideStackFrames((message, name) => { + if (DOMException === undefined) + DOMException = internalBinding('messaging').DOMException; + return new DOMException(message, name); +}); + function EventEmitter(opts) { EventEmitter.init.call(this, opts); @@ -68,7 +86,7 @@ function EventEmitter(opts) { module.exports = EventEmitter; module.exports.once = once; module.exports.on = on; - +module.exports.getEventListeners = getEventListeners; // Backwards-compat with node 0.10.x EventEmitter.EventEmitter = EventEmitter; @@ -106,6 +124,7 @@ EventEmitter.prototype._maxListeners = undefined; // By default EventEmitters will print a warning if more than 10 listeners are // added to it. This is a useful default which helps finding memory leaks. let defaultMaxListeners = 10; +let isEventTarget; function checkListener(listener) { if (typeof listener !== 'function') { @@ -128,6 +147,48 @@ ObjectDefineProperty(EventEmitter, 'defaultMaxListeners', { } }); +ObjectDefineProperties(EventEmitter, { + kMaxEventTargetListeners: { + value: kMaxEventTargetListeners, + enumerable: false, + configurable: false, + writable: false, + }, + kMaxEventTargetListenersWarned: { + value: kMaxEventTargetListenersWarned, + enumerable: false, + configurable: false, + writable: false, + } +}); + +EventEmitter.setMaxListeners = + function(n = defaultMaxListeners, ...eventTargets) { + if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) + throw new ERR_OUT_OF_RANGE('n', 'a non-negative number', n); + if (eventTargets.length === 0) { + defaultMaxListeners = n; + } else { + if (isEventTarget === undefined) + isEventTarget = require('internal/event_target').isEventTarget; + + // Performance for forEach is now comparable with regular for-loop + eventTargets.forEach((target) => { + if (isEventTarget(target)) { + target[kMaxEventTargetListeners] = n; + target[kMaxEventTargetListenersWarned] = false; + } else if (typeof target.setMaxListeners === 'function') { + target.setMaxListeners(n); + } else { + throw new ERR_INVALID_ARG_TYPE( + 'eventTargets', + ['EventEmitter', 'EventTarget'], + target); + } + }); + } + }; + EventEmitter.init = function(opts) { if (this._events === undefined || @@ -622,22 +683,83 @@ function unwrapListeners(arr) { return ret; } -function once(emitter, name) { +function getEventListeners(emitterOrTarget, type) { + // First check if EventEmitter + if (typeof emitterOrTarget.listeners === 'function') { + return emitterOrTarget.listeners(type); + } + // Require event target lazily to avoid always loading it + const { isEventTarget, kEvents } = require('internal/event_target'); + if (isEventTarget(emitterOrTarget)) { + const root = emitterOrTarget[kEvents].get(type); + const listeners = []; + let handler = root?.next; + while (handler?.listener !== undefined) { + ArrayPrototypePush(listeners, handler.listener); + handler = handler.next; + } + return listeners; + } + throw new ERR_INVALID_ARG_TYPE('emitter', + ['EventEmitter', 'EventTarget'], + emitterOrTarget); +} + +async function once(emitter, name, options = {}) { + const signal = options ? options.signal : undefined; + validateAbortSignal(signal, 'options.signal'); + if (signal && signal.aborted) + throw lazyDOMException('The operation was aborted', 'AbortError'); return new Promise((resolve, reject) => { const errorListener = (err) => { emitter.removeListener(name, resolver); + if (signal != null) { + eventTargetAgnosticRemoveListener( + signal, + 'abort', + abortListener, + { once: true }); + } reject(err); }; const resolver = (...args) => { if (typeof emitter.removeListener === 'function') { emitter.removeListener('error', errorListener); } + if (signal != null) { + eventTargetAgnosticRemoveListener( + signal, + 'abort', + abortListener, + { once: true }); + } resolve(args); }; eventTargetAgnosticAddListener(emitter, name, resolver, { once: true }); if (name !== 'error') { addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true }); } + function abortListener() { + if (typeof emitter.removeListener === 'function') { + emitter.removeListener(name, resolver); + emitter.removeListener('error', errorListener); + } else { + eventTargetAgnosticRemoveListener( + emitter, + name, + resolver, + { once: true }); + eventTargetAgnosticRemoveListener( + emitter, + 'error', + errorListener, + { once: true }); + } + reject(lazyDOMException('The operation was aborted', 'AbortError')); + } + if (signal != null) { + signal.addEventListener('abort', abortListener, { once: true }); + } }); } @@ -680,7 +802,13 @@ function eventTargetAgnosticAddListener(emitter, name, listener, flags) { } } -function on(emitter, event) { +function on(emitter, event, options) { + const { signal } = { ...options }; + validateAbortSignal(signal, 'options.signal'); + if (signal && signal.aborted) { + throw lazyDOMException('The operation was aborted', 'AbortError'); + } + const unconsumedEvents = []; const unconsumedPromises = []; let error = null; @@ -718,6 +846,15 @@ function on(emitter, event) { return() { eventTargetAgnosticRemoveListener(emitter, event, eventHandler); eventTargetAgnosticRemoveListener(emitter, 'error', errorHandler); + + if (signal) { + eventTargetAgnosticRemoveListener( + signal, + 'abort', + abortListener, + { once: true }); + } + finished = true; for (const promise of unconsumedPromises) { @@ -747,9 +884,20 @@ function on(emitter, event) { addErrorHandlerIfEventEmitter(emitter, errorHandler); } + if (signal) { + eventTargetAgnosticAddListener( + signal, + 'abort', + abortListener, + { once: true }); + } return iterator; + function abortListener() { + errorHandler(lazyDOMException('The operation was aborted', 'AbortError')); + } + function eventHandler(...args) { const promise = unconsumedPromises.shift(); if (promise) { diff --git a/lib/fs.js b/lib/fs.js index 6186931971228c..a19f84b4f404bd 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -71,6 +71,8 @@ const { ERR_INVALID_CALLBACK, ERR_FEATURE_UNAVAILABLE_ON_PLATFORM }, + hideStackFrames, + uvErrmapGet, uvException } = require('internal/errors'); @@ -133,6 +135,13 @@ let ReadStream; let WriteStream; let rimraf; let rimrafSync; +let DOMException; + +const lazyDOMException = hideStackFrames((message, name) => { + if (DOMException === undefined) + DOMException = internalBinding('messaging').DOMException; + return new DOMException(message, name); +}); // These have to be separate because of how graceful-fs happens to do it's // monkeypatching. @@ -315,6 +324,9 @@ function readFile(path, options, callback) { const context = new ReadFileContext(callback, options.encoding); context.isUserFd = isFd(path); // File descriptor ownership + if (options.signal) { + context.signal = options.signal; + } if (context.isUserFd) { process.nextTick(function tick(context) { readFileAfterOpen.call({ context }, null, path); @@ -322,6 +334,11 @@ function readFile(path, options, callback) { return; } + if (options.signal?.aborted) { + callback(lazyDOMException('The operation was aborted', 'AbortError')); + return; + } + const flagsNumber = stringToFlags(options.flag); path = getValidatedPath(path); @@ -422,9 +439,14 @@ function readFileSync(path, options) { return buffer; } -function close(fd, callback) { +function defaultCloseCallback(err) { + if (err != null) throw err; +} + +function close(fd, callback = defaultCloseCallback) { validateInt32(fd, 'fd', 0); - callback = makeCallback(callback); + if (callback !== defaultCloseCallback) + callback = makeCallback(callback); const req = new FSReqCallback(); req.oncomplete = callback; @@ -1061,7 +1083,20 @@ function stat(path, options = { bigint: false }, callback) { binding.stat(pathModule.toNamespacedPath(path), options.bigint, req); } -function fstatSync(fd, options = { bigint: false }) { +function hasNoEntryError(ctx) { + if (ctx.errno) { + const uvErr = uvErrmapGet(ctx.errno); + return uvErr && uvErr[0] === 'ENOENT'; + } + + if (ctx.error) { + return ctx.error.code === 'ENOENT'; + } + + return false; +} + +function fstatSync(fd, options = { bigint: false, throwIfNoEntry: true }) { validateInt32(fd, 'fd', 0); const ctx = { fd }; const stats = binding.fstat(fd, options.bigint, undefined, ctx); @@ -1069,20 +1104,26 @@ function fstatSync(fd, options = { bigint: false }) { return getStatsFromBinding(stats); } -function lstatSync(path, options = { bigint: false }) { +function lstatSync(path, options = { bigint: false, throwIfNoEntry: true }) { path = getValidatedPath(path); const ctx = { path }; const stats = binding.lstat(pathModule.toNamespacedPath(path), options.bigint, undefined, ctx); + if (options.throwIfNoEntry === false && hasNoEntryError(ctx)) { + return undefined; + } handleErrorFromBinding(ctx); return getStatsFromBinding(stats); } -function statSync(path, options = { bigint: false }) { +function statSync(path, options = { bigint: false, throwIfNoEntry: true }) { path = getValidatedPath(path); const ctx = { path }; const stats = binding.stat(pathModule.toNamespacedPath(path), options.bigint, undefined, ctx); + if (options.throwIfNoEntry === false && hasNoEntryError(ctx)) { + return undefined; + } handleErrorFromBinding(ctx); return getStatsFromBinding(stats); } @@ -1402,7 +1443,17 @@ function lutimesSync(path, atime, mtime) { handleErrorFromBinding(ctx); } -function writeAll(fd, isUserFd, buffer, offset, length, callback) { +function writeAll(fd, isUserFd, buffer, offset, length, signal, callback) { + if (signal?.aborted) { + if (isUserFd) { + callback(lazyDOMException('The operation was aborted', 'AbortError')); + } else { + fs.close(fd, function() { + callback(lazyDOMException('The operation was aborted', 'AbortError')); + }); + } + return; + } // write(fd, buffer, offset, length, position, callback) fs.write(fd, buffer, offset, length, null, (writeErr, written) => { if (writeErr) { @@ -1422,7 +1473,7 @@ function writeAll(fd, isUserFd, buffer, offset, length, callback) { } else { offset += written; length -= written; - writeAll(fd, isUserFd, buffer, offset, length, callback); + writeAll(fd, isUserFd, buffer, offset, length, signal, callback); } }); } @@ -1439,16 +1490,22 @@ function writeFile(path, data, options, callback) { if (isFd(path)) { const isUserFd = true; - writeAll(path, isUserFd, data, 0, data.byteLength, callback); + const signal = options.signal; + writeAll(path, isUserFd, data, 0, data.byteLength, signal, callback); return; } + if (options.signal?.aborted) { + callback(lazyDOMException('The operation was aborted', 'AbortError')); + return; + } fs.open(path, flag, options.mode, (openErr, fd) => { if (openErr) { callback(openErr); } else { const isUserFd = false; - writeAll(fd, isUserFd, data, 0, data.byteLength, callback); + const signal = options.signal; + writeAll(fd, isUserFd, data, 0, data.byteLength, signal, callback); } }); } @@ -1530,6 +1587,17 @@ function watch(filename, options, listener) { if (listener) { watcher.addListener('change', listener); } + if (options.signal) { + if (options.signal.aborted) { + process.nextTick(() => watcher.close()); + } else { + const listener = () => watcher.close(); + options.signal.addEventListener('abort', listener); + watcher.once('close', () => { + options.signal.removeEventListener('abort', listener); + }); + } + } return watcher; } diff --git a/lib/http.js b/lib/http.js index 4bbe42e652c24e..07b65d76e1bd2f 100644 --- a/lib/http.js +++ b/lib/http.js @@ -42,14 +42,65 @@ const { } = require('_http_server'); let maxHeaderSize; +/** + * Returns a new instance of `http.Server`. + * @param {{ + * IncomingMessage?: IncomingMessage; + * ServerResponse?: ServerResponse; + * insecureHTTPParser?: boolean; + * maxHeaderSize?: number; + * }} [opts] + * @param {Function} [requestListener] + * @returns {Server} + */ function createServer(opts, requestListener) { return new Server(opts, requestListener); } +/** + * @typedef {Object} HTTPRequestOptions + * @property {httpAgent.Agent | boolean} [agent] + * @property {string} [auth] + * @property {Function} [createConnection] + * @property {number} [defaultPort] + * @property {number} [family] + * @property {Object} [headers] + * @property {number} [hints] + * @property {string} [host] + * @property {string} [hostname] + * @property {boolean} [insecureHTTPParser] + * @property {string} [localAddress] + * @property {number} [localPort] + * @property {Function} [lookup] + * @property {number} [maxHeaderSize] + * @property {string} [method] + * @property {string} [path] + * @property {number} [port] + * @property {string} [protocol] + * @property {boolean} [setHost] + * @property {string} [socketPath] + * @property {number} [timeout] + * @property {AbortSignal} [signal] + */ + +/** + * Makes an HTTP request. + * @param {string | URL} url + * @param {HTTPRequestOptions} [options] + * @param {Function} [cb] + * @returns {ClientRequest} + */ function request(url, options, cb) { return new ClientRequest(url, options, cb); } +/** + * Makes a `GET` HTTP request. + * @param {string | URL} url + * @param {HTTPRequestOptions} [options] + * @param {Function} [cb] + * @returns {ClientRequest} + */ function get(url, options, cb) { const req = request(url, options, cb); req.end(); diff --git a/lib/https.js b/lib/https.js index 6aafef6cb639e1..e1f0936b631ade 100644 --- a/lib/https.js +++ b/lib/https.js @@ -24,6 +24,7 @@ const { ObjectAssign, ObjectSetPrototypeOf, + JSONStringify, } = primordials; require('internal/util').assertCrypto(); @@ -236,6 +237,18 @@ Agent.prototype.getName = function getName(options) { if (options.sessionIdContext) name += options.sessionIdContext; + name += ':'; + if (options.sigalgs) + name += JSONStringify(options.sigalgs); + + name += ':'; + if (options.privateKeyIdentifier) + name += options.privateKeyIdentifier; + + name += ':'; + if (options.privateKeyEngine) + name += options.privateKeyEngine; + return name; }; diff --git a/lib/internal/abort_controller.js b/lib/internal/abort_controller.js new file mode 100644 index 00000000000000..95584feeeecb84 --- /dev/null +++ b/lib/internal/abort_controller.js @@ -0,0 +1,149 @@ +'use strict'; + +// Modeled very closely on the AbortController implementation +// in https://github.com/mysticatea/abort-controller (MIT license) + +const { + Object, + ObjectSetPrototypeOf, + ObjectDefineProperty, + Symbol, + SymbolToStringTag, + TypeError, +} = primordials; + +const { + defineEventHandler, + EventTarget, + Event, + kTrustEvent +} = require('internal/event_target'); +const { + customInspectSymbol, + emitExperimentalWarning +} = require('internal/util'); +const { inspect } = require('internal/util/inspect'); +const { + codes: { + ERR_INVALID_THIS, + } +} = require('internal/errors'); + +const kAborted = Symbol('kAborted'); + +function customInspect(self, obj, depth, options) { + if (depth < 0) + return self; + + const opts = Object.assign({}, options, { + depth: options.depth === null ? null : options.depth - 1 + }); + + return `${self.constructor.name} ${inspect(obj, opts)}`; +} + +function validateAbortSignal(obj) { + if (obj?.[kAborted] === undefined) + throw new ERR_INVALID_THIS('AbortSignal'); +} + +class AbortSignal extends EventTarget { + constructor() { + // eslint-disable-next-line no-restricted-syntax + throw new TypeError('Illegal constructor'); + } + + get aborted() { + validateAbortSignal(this); + return !!this[kAborted]; + } + + [customInspectSymbol](depth, options) { + return customInspect(this, { + aborted: this.aborted + }, depth, options); + } + + static abort() { + return createAbortSignal(true); + } +} + +Object.defineProperties(AbortSignal.prototype, { + aborted: { enumerable: true } +}); + +ObjectDefineProperty(AbortSignal.prototype, SymbolToStringTag, { + writable: false, + enumerable: false, + configurable: true, + value: 'AbortSignal', +}); + +defineEventHandler(AbortSignal.prototype, 'abort'); + +function createAbortSignal(aborted = false) { + const signal = new EventTarget(); + ObjectSetPrototypeOf(signal, AbortSignal.prototype); + signal[kAborted] = aborted; + return signal; +} + +function abortSignal(signal) { + if (signal[kAborted]) return; + signal[kAborted] = true; + const event = new Event('abort', { + [kTrustEvent]: true + }); + signal.dispatchEvent(event); +} + +// TODO(joyeecheung): V8 snapshot does not support instance member +// initializers for now: +// https://bugs.chromium.org/p/v8/issues/detail?id=10704 +const kSignal = Symbol('signal'); + +function validateAbortController(obj) { + if (obj?.[kSignal] === undefined) + throw new ERR_INVALID_THIS('AbortController'); +} + +class AbortController { + constructor() { + this[kSignal] = createAbortSignal(); + emitExperimentalWarning('AbortController'); + } + + get signal() { + validateAbortController(this); + return this[kSignal]; + } + + abort() { + validateAbortController(this); + abortSignal(this[kSignal]); + } + + [customInspectSymbol](depth, options) { + return customInspect(this, { + signal: this.signal + }, depth, options); + } +} + +Object.defineProperties(AbortController.prototype, { + signal: { enumerable: true }, + abort: { enumerable: true } +}); + +ObjectDefineProperty(AbortController.prototype, SymbolToStringTag, { + writable: false, + enumerable: false, + configurable: true, + value: 'AbortController', +}); + +module.exports = { + AbortController, + AbortSignal, +}; diff --git a/lib/internal/async_hooks.js b/lib/internal/async_hooks.js index 84e73280ccec48..28d0655d97c7f6 100644 --- a/lib/internal/async_hooks.js +++ b/lib/internal/async_hooks.js @@ -99,6 +99,9 @@ const { kInit, kBefore, kAfter, kDestroy, kTotals, kPromiseResolve, const { async_id_symbol, trigger_async_id_symbol } = internalBinding('symbols'); +// Lazy load of internal/util/inspect; +let inspect; + // Used in AsyncHook and AsyncResource. const init_symbol = Symbol('init'); const before_symbol = Symbol('before'); @@ -155,12 +158,17 @@ function executionAsyncResource() { return lookupPublicResource(resource); } +function inspectExceptionValue(e) { + inspect = inspect ?? require('internal/util/inspect').inspect; + return { message: inspect(e) }; +} + // Used to fatally abort the process if a callback throws. function fatalError(e) { - if (typeof e.stack === 'string') { + if (typeof e?.stack === 'string') { process._rawDebug(e.stack); } else { - const o = { message: e }; + const o = inspectExceptionValue(e); ErrorCaptureStackTrace(o, fatalError); process._rawDebug(o.stack); } diff --git a/lib/internal/bootstrap/loaders.js b/lib/internal/bootstrap/loaders.js index d939c2911d0df3..4a5e962092d790 100644 --- a/lib/internal/bootstrap/loaders.js +++ b/lib/internal/bootstrap/loaders.js @@ -4,7 +4,7 @@ // lib/internal/modules/esm/* (ES Modules). // // This file is compiled and run by node.cc before bootstrap/node.js -// was called, therefore the loaders are bootstraped before we start to +// was called, therefore the loaders are bootstrapped before we start to // actually bootstrap Node.js. It creates the following objects: // // C++ binding loaders: @@ -69,11 +69,11 @@ ObjectDefineProperty(process, 'moduleLoadList', { }); -// internalBindingWhitelist contains the name of internalBinding modules -// that are whitelisted for access via process.binding()... This is used +// internalBindingAllowlist contains the name of internalBinding modules +// that are allowed for access via process.binding()... This is used // to provide a transition path for modules that are being moved over to // internalBinding. -const internalBindingWhitelist = new SafeSet([ +const internalBindingAllowlist = new SafeSet([ 'async_wrap', 'buffer', 'cares_wrap', @@ -113,7 +113,7 @@ const internalBindingWhitelist = new SafeSet([ module = String(module); // Deprecated specific process.binding() modules, but not all, allow // selective fallback to internalBinding for the deprecated ones. - if (internalBindingWhitelist.has(module)) { + if (internalBindingAllowlist.has(module)) { return internalBinding(module); } // eslint-disable-next-line no-restricted-syntax diff --git a/lib/internal/bootstrap/pre_execution.js b/lib/internal/bootstrap/pre_execution.js index c76add4621b614..dfd7249e907ebc 100644 --- a/lib/internal/bootstrap/pre_execution.js +++ b/lib/internal/bootstrap/pre_execution.js @@ -3,6 +3,7 @@ const { NumberParseInt, ObjectDefineProperty, + ObjectDefineProperties, SafeMap, SafeWeakMap, StringPrototypeStartsWith, @@ -31,14 +32,6 @@ function prepareMainThreadExecution(expandArgv1 = false) { setupCoverageHooks(process.env.NODE_V8_COVERAGE); } - // If source-map support has been enabled, we substitute in a new - // prepareStackTrace method, replacing the default in errors.js. - if (getOptionValue('--enable-source-maps')) { - const { prepareStackTrace } = - require('internal/source_map/prepare_stack_trace'); - const { setPrepareStackTraceCallback } = internalBinding('errors'); - setPrepareStackTraceCallback(prepareStackTrace); - } setupDebugEnv(); @@ -66,6 +59,7 @@ function prepareMainThreadExecution(expandArgv1 = false) { // (including preload modules). initializeClusterIPC(); + initializeAbortController(); initializeDeprecations(); initializeWASI(); initializeCJSLoader(); @@ -230,9 +224,9 @@ function setupInspectorHooks() { } } -// In general deprecations are intialized wherever the APIs are implemented, +// In general deprecations are initialized wherever the APIs are implemented, // this is used to deprecate APIs implemented in C++ where the deprecation -// utitlities are not easily accessible. +// utilities are not easily accessible. function initializeDeprecations() { const { deprecate } = require('internal/util'); const pendingDeprecation = getOptionValue('--pending-deprecation'); @@ -320,6 +314,30 @@ function initializeDeprecations() { }); } +function initializeAbortController() { + const abortController = getOptionValue('--experimental-abortcontroller'); + if (abortController) { + const { + AbortController, + AbortSignal + } = require('internal/abort_controller'); + ObjectDefineProperties(global, { + AbortController: { + writable: true, + enumerable: false, + configurable: true, + value: AbortController + }, + AbortSignal: { + writable: true, + enumerable: false, + configurable: true, + value: AbortSignal + } + }); + } +} + function setupChildProcessIpcChannel() { if (process.env.NODE_CHANNEL_FD) { const assert = require('internal/assert'); @@ -456,6 +474,7 @@ module.exports = { setupWarningHandler, setupDebugEnv, prepareMainThreadExecution, + initializeAbortController, initializeDeprecations, initializeESMLoader, initializeFrozenIntrinsics, diff --git a/lib/internal/bootstrap/switches/does_own_process_state.js b/lib/internal/bootstrap/switches/does_own_process_state.js index 5ee7f079d10124..0d60fb1f4595d1 100644 --- a/lib/internal/bootstrap/switches/does_own_process_state.js +++ b/lib/internal/bootstrap/switches/does_own_process_state.js @@ -80,6 +80,7 @@ function wrapPosixCredentialSetters(credentials) { function wrapIdSetter(type, method) { return function(id) { validateId(id, 'id'); + if (typeof id === 'number') id |= 0; // Result is 0 on success, 1 if credential is unknown. const result = method(id); if (result === 1) { diff --git a/lib/internal/buffer.js b/lib/internal/buffer.js index 4a2c13f168b9a6..8a9e0fa3c6e8f8 100644 --- a/lib/internal/buffer.js +++ b/lib/internal/buffer.js @@ -60,8 +60,8 @@ function checkInt(value, min, max, buf, offset, byteLength) { if (min === 0 || min === 0n) { range = `>= 0${n} and < 2${n} ** ${(byteLength + 1) * 8}${n}`; } else { - range = `>= -(2${n} ** ${(byteLength + 1) * 8 - 1}${n}) and < 2 ** ` + - `${(byteLength + 1) * 8 - 1}${n}`; + range = `>= -(2${n} ** ${(byteLength + 1) * 8 - 1}${n}) and ` + + `< 2${n} ** ${(byteLength + 1) * 8 - 1}${n}`; } } else { range = `>= ${min}${n} and <= ${max}${n}`; diff --git a/lib/internal/child_process.js b/lib/internal/child_process.js index cb035c812a9b3a..0ec5bed4dbb0d1 100644 --- a/lib/internal/child_process.js +++ b/lib/internal/child_process.js @@ -401,6 +401,8 @@ ChildProcess.prototype.spawn = function(options) { this._handle.close(); this._handle = null; throw errnoException(err, 'spawn'); + } else { + process.nextTick(onSpawnNT, this); } this.pid = this._handle.pid; @@ -466,6 +468,11 @@ function onErrorNT(self, err) { } +function onSpawnNT(self) { + self.emit('spawn'); +} + + ChildProcess.prototype.kill = function(sig) { const signal = sig === 0 ? sig : @@ -720,7 +727,7 @@ function setupChannel(target, channel, serializationMode) { // Non-serializable messages should not reach the remote // end point; as any failure in the stringification there // will result in error message that is weakly consumable. - // So perform a sanity check on message prior to sending. + // So perform a final check on message prior to sending. if (typeof message !== 'string' && typeof message !== 'object' && typeof message !== 'number' && diff --git a/lib/internal/crypto/diffiehellman.js b/lib/internal/crypto/diffiehellman.js index 8f86911757fe1f..752aabb04c9277 100644 --- a/lib/internal/crypto/diffiehellman.js +++ b/lib/internal/crypto/diffiehellman.js @@ -75,12 +75,20 @@ function DiffieHellman(sizeOrKey, keyEncoding, generator, genEncoding) { if (typeof sizeOrKey !== 'number') sizeOrKey = toBuf(sizeOrKey, keyEncoding); - if (!generator) + if (!generator) { generator = DH_GENERATOR; - else if (typeof generator === 'number') + } else if (typeof generator === 'number') { validateInt32(generator, 'generator'); - else + } else if (generator !== true) { generator = toBuf(generator, genEncoding); + } else { + throw new ERR_INVALID_ARG_TYPE( + 'generator', + ['number', 'string', 'ArrayBuffer', 'Buffer', 'TypedArray', 'DataView'], + generator + ); + } + this[kHandle] = new _DiffieHellman(sizeOrKey, generator); ObjectDefineProperty(this, 'verifyError', { diff --git a/lib/internal/crypto/random.js b/lib/internal/crypto/random.js index 9e0500ea3a67bb..4425183537a4df 100644 --- a/lib/internal/crypto/random.js +++ b/lib/internal/crypto/random.js @@ -7,19 +7,32 @@ const { } = primordials; const { AsyncWrap, Providers } = internalBinding('async_wrap'); -const { kMaxLength } = require('buffer'); -const { randomBytes: _randomBytes } = internalBinding('crypto'); const { - ERR_INVALID_ARG_TYPE, - ERR_INVALID_CALLBACK, - ERR_OUT_OF_RANGE -} = require('internal/errors').codes; -const { validateNumber } = require('internal/validators'); + Buffer, + kMaxLength, +} = require('buffer'); +const { + randomBytes: _randomBytes, + secureBuffer, +} = internalBinding('crypto'); +const { + codes: { + ERR_INVALID_ARG_TYPE, + ERR_INVALID_CALLBACK, + ERR_OUT_OF_RANGE, + ERR_OPERATION_FAILED, + } +} = require('internal/errors'); +const { + validateBoolean, + validateNumber, + validateObject, +} = require('internal/validators'); const { isArrayBufferView } = require('internal/util/types'); const { FastBuffer } = require('internal/buffer'); -const kMaxUint32 = 2 ** 32 - 1; -const kMaxPossibleLength = MathMin(kMaxLength, kMaxUint32); +const kMaxInt32 = 2 ** 31 - 1; +const kMaxPossibleLength = MathMin(kMaxLength, kMaxInt32); function assertOffset(offset, elementSize, length) { validateNumber(offset, 'offset'); @@ -163,20 +176,20 @@ function randomInt(min, max, callback) { `<= ${RAND_MAX}`, range); } - const excess = RAND_MAX % range; - const randLimit = RAND_MAX - excess; + // For (x % range) to produce an unbiased value greater than or equal to 0 and + // less than range, x must be drawn randomly from the set of integers greater + // than or equal to 0 and less than randLimit. + const randLimit = RAND_MAX - (RAND_MAX % range); if (isSync) { // Sync API while (true) { const x = randomBytes(6).readUIntBE(0, 6); - // If x > (maxVal - (maxVal % range)), we will get "modulo bias" - if (x > randLimit) { - // Try again + if (x >= randLimit) { + // Try again. continue; } - const n = (x % range) + min; - return n; + return (x % range) + min; } } else { // Async API @@ -184,9 +197,8 @@ function randomInt(min, max, callback) { randomBytes(6, (err, bytes) => { if (err) return callback(err); const x = bytes.readUIntBE(0, 6); - // If x > (maxVal - (maxVal % range)), we will get "modulo bias" - if (x > randLimit) { - // Try again + if (x >= randLimit) { + // Try again. return pickAttempt(); } const n = (x % range) + min; @@ -203,9 +215,113 @@ function handleError(ex, buf) { return buf; } +// Implements an RFC 4122 version 4 random UUID. +// To improve performance, random data is generated in batches +// large enough to cover kBatchSize UUID's at a time. The uuidData +// and uuid buffers are reused. Each call to randomUUID() consumes +// 16 bytes from the buffer. + +const kHexDigits = [ + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 97, 98, 99, 100, 101, 102 +]; + +const kBatchSize = 128; +let uuidData; +let uuidNotBuffered; +let uuid; +let uuidBatch = 0; + +function getBufferedUUID() { + if (uuidData === undefined) { + uuidData = secureBuffer(16 * kBatchSize); + if (uuidData === undefined) + throw new ERR_OPERATION_FAILED('Out of memory'); + } + + if (uuidBatch === 0) randomFillSync(uuidData); + uuidBatch = (uuidBatch + 1) % kBatchSize; + return uuidData.slice(uuidBatch * 16, (uuidBatch * 16) + 16); +} + +function randomUUID(options) { + if (options !== undefined) + validateObject(options, 'options'); + const { + disableEntropyCache = false, + } = { ...options }; + + validateBoolean(disableEntropyCache, 'options.disableEntropyCache'); + + if (uuid === undefined) { + uuid = Buffer.alloc(36, '-'); + uuid[14] = 52; // '4', identifies the UUID version + } + + let uuidBuf; + if (!disableEntropyCache) { + uuidBuf = getBufferedUUID(); + } else { + uuidBuf = uuidNotBuffered; + if (uuidBuf === undefined) + uuidBuf = uuidNotBuffered = secureBuffer(16); + if (uuidBuf === undefined) + throw new ERR_OPERATION_FAILED('Out of memory'); + randomFillSync(uuidBuf); + } + + // Variant byte: 10xxxxxx (variant 1) + uuidBuf[8] = (uuidBuf[8] & 0x3f) | 0x80; + + // This function is structured the way it is for performance. + // The uuid buffer stores the serialization of the random + // bytes from uuidData. + // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + let n = 0; + uuid[0] = kHexDigits[uuidBuf[n] >> 4]; + uuid[1] = kHexDigits[uuidBuf[n++] & 0xf]; + uuid[2] = kHexDigits[uuidBuf[n] >> 4]; + uuid[3] = kHexDigits[uuidBuf[n++] & 0xf]; + uuid[4] = kHexDigits[uuidBuf[n] >> 4]; + uuid[5] = kHexDigits[uuidBuf[n++] & 0xf]; + uuid[6] = kHexDigits[uuidBuf[n] >> 4]; + uuid[7] = kHexDigits[uuidBuf[n++] & 0xf]; + // - + uuid[9] = kHexDigits[uuidBuf[n] >> 4]; + uuid[10] = kHexDigits[uuidBuf[n++] & 0xf]; + uuid[11] = kHexDigits[uuidBuf[n] >> 4]; + uuid[12] = kHexDigits[uuidBuf[n++] & 0xf]; + // - + // 4, uuid[14] is set already... + uuid[15] = kHexDigits[uuidBuf[n++] & 0xf]; + uuid[16] = kHexDigits[uuidBuf[n] >> 4]; + uuid[17] = kHexDigits[uuidBuf[n++] & 0xf]; + // - + uuid[19] = kHexDigits[uuidBuf[n] >> 4]; + uuid[20] = kHexDigits[uuidBuf[n++] & 0xf]; + uuid[21] = kHexDigits[uuidBuf[n] >> 4]; + uuid[22] = kHexDigits[uuidBuf[n++] & 0xf]; + // - + uuid[24] = kHexDigits[uuidBuf[n] >> 4]; + uuid[25] = kHexDigits[uuidBuf[n++] & 0xf]; + uuid[26] = kHexDigits[uuidBuf[n] >> 4]; + uuid[27] = kHexDigits[uuidBuf[n++] & 0xf]; + uuid[28] = kHexDigits[uuidBuf[n] >> 4]; + uuid[29] = kHexDigits[uuidBuf[n++] & 0xf]; + uuid[30] = kHexDigits[uuidBuf[n] >> 4]; + uuid[31] = kHexDigits[uuidBuf[n++] & 0xf]; + uuid[32] = kHexDigits[uuidBuf[n] >> 4]; + uuid[33] = kHexDigits[uuidBuf[n++] & 0xf]; + uuid[34] = kHexDigits[uuidBuf[n] >> 4]; + uuid[35] = kHexDigits[uuidBuf[n] & 0xf]; + + return uuid.latin1Slice(0, 36); +} + module.exports = { randomBytes, randomFill, randomFillSync, - randomInt + randomInt, + randomUUID, }; diff --git a/lib/internal/dns/promises.js b/lib/internal/dns/promises.js index 19ad6e5bfb3a6b..42ba76dda4f0ac 100644 --- a/lib/internal/dns/promises.js +++ b/lib/internal/dns/promises.js @@ -217,9 +217,12 @@ class Resolver { Resolver.prototype.getServers = CallbackResolver.prototype.getServers; Resolver.prototype.setServers = CallbackResolver.prototype.setServers; +Resolver.prototype.cancel = CallbackResolver.prototype.cancel; +Resolver.prototype.setLocalAddress = CallbackResolver.prototype.setLocalAddress; Resolver.prototype.resolveAny = resolveMap.ANY = resolver('queryAny'); Resolver.prototype.resolve4 = resolveMap.A = resolver('queryA'); Resolver.prototype.resolve6 = resolveMap.AAAA = resolver('queryAaaa'); +Resolver.prototype.resolveCaa = resolveMap.CAA = resolver('queryCaa'); Resolver.prototype.resolveCname = resolveMap.CNAME = resolver('queryCname'); Resolver.prototype.resolveMx = resolveMap.MX = resolver('queryMx'); Resolver.prototype.resolveNs = resolveMap.NS = resolver('queryNs'); diff --git a/lib/internal/dns/utils.js b/lib/internal/dns/utils.js index 3857bf21754e5b..afaad42ec4290e 100644 --- a/lib/internal/dns/utils.js +++ b/lib/internal/dns/utils.js @@ -114,6 +114,18 @@ class Resolver { throw new ERR_DNS_SET_SERVERS_FAILED(err, servers); } } + + setLocalAddress(ipv4, ipv6) { + if (typeof ipv4 !== 'string') { + throw new ERR_INVALID_ARG_TYPE('ipv4', 'String', ipv4); + } + + if (typeof ipv6 !== 'string' && ipv6 !== undefined) { + throw new ERR_INVALID_ARG_TYPE('ipv6', ['String', 'undefined'], ipv6); + } + + this._handle.setLocalAddress(ipv4, ipv6); + } } let defaultResolver = new Resolver(); @@ -123,6 +135,7 @@ const resolverKeys = [ 'resolve4', 'resolve6', 'resolveAny', + 'resolveCaa', 'resolveCname', 'resolveMx', 'resolveNaptr', diff --git a/lib/internal/encoding.js b/lib/internal/encoding.js index 5226bf518dac38..8cc9bc13fbb343 100644 --- a/lib/internal/encoding.js +++ b/lib/internal/encoding.js @@ -63,6 +63,7 @@ function validateDecoder(obj) { } function validateArgument(prop, expected, propName, expectedName) { + // eslint-disable-next-line valid-typeof if (typeof prop !== expected) throw new ERR_INVALID_ARG_TYPE(propName, expectedName, prop); } diff --git a/lib/internal/errors.js b/lib/internal/errors.js index ea3e0cc19eade7..447db182eb2ee3 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -414,7 +414,7 @@ function uvException(ctx) { message += ` -> '${dest}'`; } - // Reducing the limit improves the performance significantly. We do not loose + // Reducing the limit improves the performance significantly. We do not lose // the stack frames due to the `captureStackTrace()` function that is called // later. const tmpLimit = Error.stackTraceLimit; @@ -465,7 +465,7 @@ function uvExceptionWithHostPort(err, syscall, address, port) { details = ` ${address}`; } - // Reducing the limit improves the performance significantly. We do not loose + // Reducing the limit improves the performance significantly. We do not lose // the stack frames due to the `captureStackTrace()` function that is called // later. const tmpLimit = Error.stackTraceLimit; @@ -539,7 +539,7 @@ function exceptionWithHostPort(err, syscall, address, port, additional) { details += ` - Local (${additional})`; } - // Reducing the limit improves the performance significantly. We do not loose + // Reducing the limit improves the performance significantly. We do not lose // the stack frames due to the `captureStackTrace()` function that is called // later. const tmpLimit = Error.stackTraceLimit; @@ -584,7 +584,7 @@ function dnsException(code, syscall, hostname) { } } const message = `${syscall} ${code}${hostname ? ` ${hostname}` : ''}`; - // Reducing the limit improves the performance significantly. We do not loose + // Reducing the limit improves the performance significantly. We do not lose // the stack frames due to the `captureStackTrace()` function that is called // later. const tmpLimit = Error.stackTraceLimit; @@ -704,6 +704,16 @@ const fatalExceptionStackEnhancers = { } }; +// Node uses an AbortError that isn't exactly the same as the DOMException +// to make usage of the error in userland and readable-stream easier. +// It is a regular error with `.code` and `.name`. +class AbortError extends Error { + constructor() { + super('The operation was aborted'); + this.code = 'ABORT_ERR'; + this.name = 'AbortError'; + } +} module.exports = { addCodeToName, // Exported for NghttpError codes, @@ -718,6 +728,7 @@ module.exports = { uvException, uvExceptionWithHostPort, SystemError, + AbortError, // This is exported only to facilitate testing. E, kNoOverride, @@ -1259,6 +1270,7 @@ E('ERR_NO_CRYPTO', 'Node.js is not compiled with OpenSSL crypto support', Error); E('ERR_NO_ICU', '%s is not supported on Node.js compiled without ICU', TypeError); +E('ERR_OPERATION_FAILED', 'Operation failed: %s', Error); E('ERR_OUT_OF_RANGE', (str, range, input, replaceDefaultBoolean = false) => { assert(range, 'Missing "range" argument'); diff --git a/lib/internal/event_target.js b/lib/internal/event_target.js index 58ae809302461e..31e23feeb55f67 100644 --- a/lib/internal/event_target.js +++ b/lib/internal/event_target.js @@ -4,14 +4,17 @@ const { ArrayFrom, Boolean, Error, - Map, NumberIsInteger, Object, ObjectDefineProperty, + ObjectGetOwnPropertyDescriptor, + ReflectApply, + SafeMap, String, Symbol, SymbolFor, SymbolToStringTag, + SafeWeakSet, } = primordials; const { @@ -22,24 +25,30 @@ const { ERR_INVALID_THIS, } } = require('internal/errors'); -const { validateInteger, validateObject } = require('internal/validators'); +const { validateObject } = require('internal/validators'); const { customInspectSymbol } = require('internal/util'); const { inspect } = require('util'); const kIsEventTarget = SymbolFor('nodejs.event_target'); +const EventEmitter = require('events'); +const { + kMaxEventTargetListeners, + kMaxEventTargetListenersWarned, +} = EventEmitter; + const kEvents = Symbol('kEvents'); const kStop = Symbol('kStop'); const kTarget = Symbol('kTarget'); +const kHandlers = Symbol('khandlers'); const kHybridDispatch = Symbol.for('nodejs.internal.kHybridDispatch'); const kCreateEvent = Symbol('kCreateEvent'); const kNewListener = Symbol('kNewListener'); const kRemoveListener = Symbol('kRemoveListener'); const kIsNodeStyleListener = Symbol('kIsNodeStyleListener'); -const kMaxListeners = Symbol('kMaxListeners'); -const kMaxListenersWarned = Symbol('kMaxListenersWarned'); +const kTrustEvent = Symbol('kTrustEvent'); // Lazy load perf_hooks to avoid the additional overhead on startup let perf_hooks; @@ -60,13 +69,18 @@ const kBubbles = Symbol('bubbles'); const kComposed = Symbol('composed'); const kPropagationStopped = Symbol('propagationStopped'); -const isTrusted = () => false; +const isTrustedSet = new SafeWeakSet(); +const isTrusted = ObjectGetOwnPropertyDescriptor({ + get isTrusted() { + return isTrustedSet.has(this); + } +}, 'isTrusted').get; class Event { - constructor(type, options) { + constructor(type, options = null) { if (arguments.length === 0) throw new ERR_MISSING_ARGS('type'); - if (options != null) + if (options !== null) validateObject(options, 'options'); const { cancelable, bubbles, composed } = { ...options }; this[kCancelable] = !!cancelable; @@ -76,6 +90,10 @@ class Event { this[kDefaultPrevented] = false; this[kTimestamp] = lazyNow(); this[kPropagationStopped] = false; + if (options != null && options[kTrustEvent]) { + isTrustedSet.add(this); + } + // isTrusted is special (LegacyUnforgeable) ObjectDefineProperty(this, 'isTrusted', { get: isTrusted, @@ -159,6 +177,14 @@ Object.defineProperty(Event.prototype, SymbolToStringTag, { value: 'Event', }); +class NodeCustomEvent extends Event { + constructor(type, options) { + super(type, options); + if (options && options.detail) { + this.detail = options.detail; + } + } +} // The listeners for an EventTarget are maintained as a linked list. // Unfortunately, the way EventTarget is defined, listeners are accounted // using the tuple [handler,capture], and even if we don't actually make @@ -197,7 +223,9 @@ class Listener { } function initEventTarget(self) { - self[kEvents] = new Map(); + self[kEvents] = new SafeMap(); + self[kMaxEventTargetListeners] = EventEmitter.defaultMaxListeners; + self[kMaxEventTargetListenersWarned] = false; } class EventTarget { @@ -210,7 +238,24 @@ class EventTarget { initEventTarget(this); } - [kNewListener](size, type, listener, once, capture, passive) {} + [kNewListener](size, type, listener, once, capture, passive) { + if (this[kMaxEventTargetListeners] > 0 && + size > this[kMaxEventTargetListeners] && + !this[kMaxEventTargetListenersWarned]) { + this[kMaxEventTargetListenersWarned] = true; + // No error code for this since it is a Warning + // eslint-disable-next-line no-restricted-syntax + const w = new Error('Possible EventTarget memory leak detected. ' + + `${size} ${type} listeners ` + + `added to ${inspect(this, { depth: -1 })}. Use ` + + 'events.setMaxListeners() to increase limit'); + w.name = 'MaxListenersExceededWarning'; + w.target = this; + w.type = type; + w.count = size; + process.emitWarning(w); + } + } [kRemoveListener](size, type, listener, capture) {} addEventListener(type, listener, options = {}) { @@ -321,14 +366,13 @@ class EventTarget { } return event; }; + if (event !== undefined) + event[kTarget] = this; const root = this[kEvents].get(type); if (root === undefined || root.next === undefined) return true; - if (event !== undefined) - event[kTarget] = this; - let handler = root.next; let next; @@ -365,6 +409,9 @@ class EventTarget { event[kTarget] = undefined; } + [kCreateEvent](nodeValue, type) { + return new NodeCustomEvent(type, { detail: nodeValue }); + } [customInspectSymbol](depth, options) { const name = this.constructor.name; if (depth < 0) @@ -392,9 +439,6 @@ Object.defineProperty(EventTarget.prototype, SymbolToStringTag, { function initNodeEventTarget(self) { initEventTarget(self); - // eslint-disable-next-line no-use-before-define - self[kMaxListeners] = NodeEventTarget.defaultMaxListeners; - self[kMaxListenersWarned] = false; } class NodeEventTarget extends EventTarget { @@ -405,33 +449,12 @@ class NodeEventTarget extends EventTarget { initNodeEventTarget(this); } - [kNewListener](size, type, listener, once, capture, passive) { - if (this[kMaxListeners] > 0 && - size > this[kMaxListeners] && - !this[kMaxListenersWarned]) { - this[kMaxListenersWarned] = true; - // No error code for this since it is a Warning - // eslint-disable-next-line no-restricted-syntax - const w = new Error('Possible EventTarget memory leak detected. ' + - `${size} ${type} listeners ` + - `added to ${inspect(this, { depth: -1 })}. Use ` + - 'setMaxListeners() to increase limit'); - w.name = 'MaxListenersExceededWarning'; - w.target = this; - w.type = type; - w.count = size; - process.emitWarning(w); - } - } - setMaxListeners(n) { - validateInteger(n, 'n', 0); - this[kMaxListeners] = n; - return this; + EventEmitter.setMaxListeners(n, this); } getMaxListeners() { - return this[kMaxListeners]; + return this[kMaxEventTargetListeners]; } eventNames() { @@ -462,6 +485,14 @@ class NodeEventTarget extends EventTarget { this.addEventListener(type, listener, { [kIsNodeStyleListener]: true }); return this; } + emit(type, arg) { + if (typeof type !== 'string') { + throw new ERR_INVALID_ARG_TYPE('type', 'string', type); + } + const hadListeners = this.listenerCount(type) > 0; + this[kHybridDispatch](arg, type); + return hadListeners; + } once(type, listener) { this.addEventListener(type, listener, @@ -490,6 +521,7 @@ Object.defineProperties(NodeEventTarget.prototype, { on: { enumerable: true }, addListener: { enumerable: true }, once: { enumerable: true }, + emit: { enumerable: true }, removeAllListeners: { enumerable: true }, }); @@ -545,22 +577,50 @@ function emitUnhandledRejectionOrErr(that, err, event) { process.emit('error', err, event); } +function makeEventHandler(handler) { + // Event handlers are dispatched in the order they were first set + // See https://github.com/nodejs/node/pull/35949#issuecomment-722496598 + function eventHandler(...args) { + if (typeof eventHandler.handler !== 'function') { + return; + } + return ReflectApply(eventHandler.handler, this, args); + } + eventHandler.handler = handler; + return eventHandler; +} + function defineEventHandler(emitter, name) { // 8.1.5.1 Event handlers - basically `on[eventName]` attributes - let eventHandlerValue; - Object.defineProperty(emitter, `on${name}`, { + ObjectDefineProperty(emitter, `on${name}`, { get() { - return eventHandlerValue; + return this[kHandlers]?.get(name)?.handler; }, set(value) { - if (eventHandlerValue) { - emitter.removeEventListener(name, eventHandlerValue); + if (!this[kHandlers]) { + this[kHandlers] = new SafeMap(); } - if (typeof value === 'function') { - emitter.addEventListener(name, value); + let wrappedHandler = this[kHandlers]?.get(name); + if (wrappedHandler) { + if (typeof wrappedHandler.handler === 'function') { + this[kEvents].get(name).size--; + const size = this[kEvents].get(name).size; + this[kRemoveListener](size, name, wrappedHandler.handler, false); + } + wrappedHandler.handler = value; + if (typeof wrappedHandler.handler === 'function') { + this[kEvents].get(name).size++; + const size = this[kEvents].get(name).size; + this[kNewListener](size, name, value, false, false, false); + } + } else { + wrappedHandler = makeEventHandler(value); + this.addEventListener(name, wrappedHandler); } - eventHandlerValue = value; - } + this[kHandlers].set(name, wrappedHandler); + }, + configurable: true, + enumerable: true }); } module.exports = { @@ -572,5 +632,8 @@ module.exports = { initNodeEventTarget, kCreateEvent, kNewListener, + kTrustEvent, kRemoveListener, + kEvents, + isEventTarget, }; diff --git a/lib/internal/fs/dir.js b/lib/internal/fs/dir.js index b4cd72d3668e25..f794a62fdd358e 100644 --- a/lib/internal/fs/dir.js +++ b/lib/internal/fs/dir.js @@ -2,6 +2,7 @@ const { ObjectDefineProperty, + PromiseReject, Symbol, SymbolAsyncIterator, } = primordials; @@ -157,16 +158,24 @@ class Dir { } close(callback) { - if (this[kDirClosed] === true) { - throw new ERR_DIR_CLOSED(); - } - + // Promise if (callback === undefined) { + if (this[kDirClosed] === true) { + return PromiseReject(new ERR_DIR_CLOSED()); + } return this[kDirClosePromisified](); - } else if (typeof callback !== 'function') { + } + + // callback + if (typeof callback !== 'function') { throw new ERR_INVALID_CALLBACK(callback); } + if (this[kDirClosed] === true) { + process.nextTick(callback, new ERR_DIR_CLOSED()); + return; + } + if (this[kDirOperationQueue] !== null) { this[kDirOperationQueue].push(() => { this.close(callback); diff --git a/lib/internal/fs/promises.js b/lib/internal/fs/promises.js index 1f81a6f47b1d59..7b2c526b59850e 100644 --- a/lib/internal/fs/promises.js +++ b/lib/internal/fs/promises.js @@ -9,6 +9,9 @@ const kIoMaxLength = 2 ** 31 - 1; const kReadFileMaxChunkSize = 2 ** 14; const kWriteFileMaxChunkSize = 2 ** 14; +// 2 ** 32 - 1 +const kMaxUserId = 4294967295; + const { Error, MathMax, @@ -30,12 +33,14 @@ const { } = internalBinding('constants').fs; const binding = internalBinding('fs'); const { Buffer } = require('buffer'); + +const { codes, hideStackFrames } = require('internal/errors'); const { ERR_FS_FILE_TOO_LARGE, ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_VALUE, - ERR_METHOD_NOT_IMPLEMENTED -} = require('internal/errors').codes; + ERR_METHOD_NOT_IMPLEMENTED, +} = codes; const { isArrayBufferView } = require('internal/util/types'); const { rimrafPromises } = require('internal/fs/rimraf'); const { @@ -61,9 +66,9 @@ const { const { opendir } = require('internal/fs/dir'); const { parseFileMode, + validateAbortSignal, validateBuffer, validateInteger, - validateUint32 } = require('internal/validators'); const pathModule = require('path'); const { promisify } = require('internal/util'); @@ -83,6 +88,13 @@ const { const getDirectoryEntriesPromise = promisify(getDirents); const validateRmOptionsPromise = promisify(validateRmOptions); +let DOMException; +const lazyDOMException = hideStackFrames((message, name) => { + if (DOMException === undefined) + DOMException = internalBinding('messaging').DOMException; + return new DOMException(message, name); +}); + class FileHandle extends JSTransferable { constructor(filehandle) { super(); @@ -241,12 +253,15 @@ async function fsCall(fn, handle, ...args) { } } -async function writeFileHandle(filehandle, data) { +async function writeFileHandle(filehandle, data, signal) { // `data` could be any kind of typed array. data = new Uint8Array(data.buffer, data.byteOffset, data.byteLength); let remaining = data.length; if (remaining === 0) return; do { + if (signal?.aborted) { + throw new lazyDOMException('The operation was aborted', 'AbortError'); + } const { bytesWritten } = await write(filehandle, data, 0, MathMin(kWriteFileMaxChunkSize, data.length)); @@ -260,8 +275,17 @@ async function writeFileHandle(filehandle, data) { } async function readFileHandle(filehandle, options) { + const signal = options && options.signal; + + if (signal && signal.aborted) { + throw lazyDOMException('The operation was aborted', 'AbortError'); + } const statFields = await binding.fstat(filehandle.fd, false, kUsePromises); + if (signal && signal.aborted) { + throw lazyDOMException('The operation was aborted', 'AbortError'); + } + let size; if ((statFields[1/* mode */] & S_IFMT) === S_IFREG) { size = statFields[8/* size */]; @@ -278,6 +302,9 @@ async function readFileHandle(filehandle, options) { MathMin(size, kReadFileMaxChunkSize); let endOfFile = false; do { + if (signal && signal.aborted) { + throw lazyDOMException('The operation was aborted', 'AbortError'); + } const buf = Buffer.alloc(chunkSize); const { bytesRead, buffer } = await read(filehandle, buf, 0, chunkSize, -1); @@ -286,7 +313,7 @@ async function readFileHandle(filehandle, options) { chunks.push(buffer.slice(0, bytesRead)); } while (!endOfFile); - const result = Buffer.concat(chunks); + const result = chunks.length === 1 ? chunks[0] : Buffer.concat(chunks); return options.encoding ? result.toString(options.encoding) : result; } @@ -555,22 +582,22 @@ async function lchmod(path, mode) { async function lchown(path, uid, gid) { path = getValidatedPath(path); - validateUint32(uid, 'uid'); - validateUint32(gid, 'gid'); + validateInteger(uid, 'uid', -1, kMaxUserId); + validateInteger(gid, 'gid', -1, kMaxUserId); return binding.lchown(pathModule.toNamespacedPath(path), uid, gid, kUsePromises); } async function fchown(handle, uid, gid) { - validateUint32(uid, 'uid'); - validateUint32(gid, 'gid'); + validateInteger(uid, 'uid', -1, kMaxUserId); + validateInteger(gid, 'gid', -1, kMaxUserId); return binding.fchown(handle.fd, uid, gid, kUsePromises); } async function chown(path, uid, gid) { path = getValidatedPath(path); - validateUint32(uid, 'uid'); - validateUint32(gid, 'gid'); + validateInteger(uid, 'uid', -1, kMaxUserId); + validateInteger(gid, 'gid', -1, kMaxUserId); return binding.chown(pathModule.toNamespacedPath(path), uid, gid, kUsePromises); } @@ -622,11 +649,17 @@ async function writeFile(path, data, options) { data = Buffer.from(data, options.encoding || 'utf8'); } + validateAbortSignal(options.signal); if (path instanceof FileHandle) - return writeFileHandle(path, data); + return writeFileHandle(path, data, options.signal); + + if (options.signal?.aborted) { + throw new lazyDOMException('The operation was aborted', 'AbortError'); + } const fd = await open(path, flag, options.mode); - return PromisePrototypeFinally(writeFileHandle(fd, data), fd.close); + const { signal } = options; + return PromisePrototypeFinally(writeFileHandle(fd, data, signal), fd.close); } async function appendFile(path, data, options) { @@ -643,6 +676,10 @@ async function readFile(path, options) { if (path instanceof FileHandle) return readFileHandle(path, options); + if (options.signal?.aborted) { + throw lazyDOMException('The operation was aborted', 'AbortError'); + } + const fd = await open(path, flag, 0o666); return PromisePrototypeFinally(readFileHandle(fd, options), fd.close); } diff --git a/lib/internal/fs/read_file_context.js b/lib/internal/fs/read_file_context.js index 5091a34231665b..faca0e0c331e39 100644 --- a/lib/internal/fs/read_file_context.js +++ b/lib/internal/fs/read_file_context.js @@ -8,6 +8,16 @@ const { Buffer } = require('buffer'); const { FSReqCallback, close, read } = internalBinding('fs'); +const { hideStackFrames } = require('internal/errors'); + + +let DOMException; +const lazyDOMException = hideStackFrames((message, name) => { + if (DOMException === undefined) + DOMException = internalBinding('messaging').DOMException; + return new DOMException(message, name); +}); + // Use 64kb in case the file type is not a regular file and thus do not know the // actual file size. Increasing the value further results in more frequent over // allocation for small files and consumes CPU time and memory that should be @@ -74,6 +84,7 @@ class ReadFileContext { this.pos = 0; this.encoding = encoding; this.err = null; + this.signal = undefined; } read() { @@ -81,6 +92,11 @@ class ReadFileContext { let offset; let length; + if (this.signal && this.signal.aborted) { + return this.close( + lazyDOMException('The operation was aborted', 'AbortError') + ); + } if (this.size === 0) { buffer = Buffer.allocUnsafeSlow(kReadFileUnknownBufferLength); offset = 0; diff --git a/lib/internal/fs/streams.js b/lib/internal/fs/streams.js index 9ce21721faad80..decf5327c984ab 100644 --- a/lib/internal/fs/streams.js +++ b/lib/internal/fs/streams.js @@ -26,29 +26,8 @@ const { toPathIfFileURL } = require('internal/url'); const kIoDone = Symbol('kIoDone'); const kIsPerformingIO = Symbol('kIsPerformingIO'); -const kMinPoolSpace = 128; const kFs = Symbol('kFs'); -let pool; -// It can happen that we expect to read a large chunk of data, and reserve -// a large chunk of the pool accordingly, but the read() call only filled -// a portion of it. If a concurrently executing read() then uses the same pool, -// the "reserved" portion cannot be used, so we allow it to be re-used as a -// new pool later. -const poolFragments = []; - -function allocNewPool(poolSize) { - if (poolFragments.length > 0) - pool = poolFragments.pop(); - else - pool = Buffer.allocUnsafe(poolSize); - pool.used = 0; -} - -function roundUpToMultipleOf8(n) { - return (n + 7) & ~7; // Align to 8 byte boundary. -} - function ReadStream(path, options) { if (!(this instanceof ReadStream)) return new ReadStream(path, options); @@ -165,73 +144,54 @@ ReadStream.prototype._read = function(n) { if (this.destroyed) return; - if (!pool || pool.length - pool.used < kMinPoolSpace) { - // Discard the old pool. - allocNewPool(this.readableHighWaterMark); - } + n = this.pos !== undefined ? + MathMin(this.end - this.pos + 1, n) : + MathMin(this.end - this.bytesRead + 1, n); - // Grab another reference to the pool in the case that while we're - // in the thread pool another read() finishes up the pool, and - // allocates a new one. - const thisPool = pool; - let toRead = MathMin(pool.length - pool.used, n); - const start = pool.used; - - if (this.pos !== undefined) - toRead = MathMin(this.end - this.pos + 1, toRead); - else - toRead = MathMin(this.end - this.bytesRead + 1, toRead); + if (n <= 0) { + this.push(null); + return; + } - // Already read everything we were supposed to read! - // treat as EOF. - if (toRead <= 0) - return this.push(null); + const buf = Buffer.allocUnsafeSlow(n); - // the actual read. this[kIsPerformingIO] = true; - this[kFs].read( - this.fd, pool, pool.used, toRead, this.pos, (er, bytesRead) => { + this[kFs] + .read(this.fd, buf, 0, n, this.pos, (er, bytesRead, buf) => { this[kIsPerformingIO] = false; + // Tell ._destroy() that it's safe to close the fd now. - if (this.destroyed) return this.emit(kIoDone, er); + if (this.destroyed) { + this.emit(kIoDone, er); + return; + } if (er) { if (this.autoClose) { this.destroy(); } this.emit('error', er); - } else { - let b = null; - // Now that we know how much data we have actually read, re-wind the - // 'used' field if we can, and otherwise allow the remainder of our - // reservation to be used as a new pool later. - if (start + toRead === thisPool.used && thisPool === pool) { - const newUsed = thisPool.used + bytesRead - toRead; - thisPool.used = roundUpToMultipleOf8(newUsed); - } else { - // Round down to the next lowest multiple of 8 to ensure the new pool - // fragment start and end positions are aligned to an 8 byte boundary. - const alignedEnd = (start + toRead) & ~7; - const alignedStart = roundUpToMultipleOf8(start + bytesRead); - if (alignedEnd - alignedStart >= kMinPoolSpace) { - poolFragments.push(thisPool.slice(alignedStart, alignedEnd)); - } - } - - if (bytesRead > 0) { - this.bytesRead += bytesRead; - b = thisPool.slice(start, start + bytesRead); + } else if (bytesRead > 0) { + this.bytesRead += bytesRead; + + if (bytesRead !== buf.length) { + // Slow path. Shrink to fit. + // Copy instead of slice so that we don't retain + // large backing buffer for small reads. + const dst = Buffer.allocUnsafeSlow(bytesRead); + buf.copy(dst, 0, 0, bytesRead); + buf = dst; } - this.push(b); + this.push(buf); + } else { + this.push(null); } }); - // Move the pool positions, and internal position for reading. - if (this.pos !== undefined) - this.pos += toRead; - - pool.used = roundUpToMultipleOf8(pool.used + toRead); + if (this.pos !== undefined) { + this.pos += n; + } }; ReadStream.prototype._destroy = function(err, cb) { diff --git a/lib/internal/fs/utils.js b/lib/internal/fs/utils.js index e3255cdb2f2562..ee4a1a63b35adc 100644 --- a/lib/internal/fs/utils.js +++ b/lib/internal/fs/utils.js @@ -37,6 +37,7 @@ const { const { once } = require('internal/util'); const { toPathIfFileURL } = require('internal/url'); const { + validateAbortSignal, validateBoolean, validateInt32, validateUint32 @@ -297,6 +298,10 @@ function getOptions(options, defaultOptions) { if (options.encoding !== 'buffer') assertEncoding(options.encoding); + + if (options.signal !== undefined) { + validateAbortSignal(options.signal, 'options.signal'); + } return options; } @@ -344,6 +349,7 @@ function preprocessSymlinkDestination(path, type, linkPath) { // No preprocessing is needed on Unix. return path; } + path = '' + path; if (type === 'junction') { // Junctions paths need to be absolute and \\?\-prefixed. // A relative target is relative to the link's parent directory. @@ -355,7 +361,7 @@ function preprocessSymlinkDestination(path, type, linkPath) { return pathModule.toNamespacedPath(path); } // Windows symlinks don't tolerate forward slashes. - return ('' + path).replace(/\//g, '\\'); + return path.replace(/\//g, '\\'); } // Constructor for file stats. diff --git a/lib/internal/http.js b/lib/internal/http.js index aab4170a2f0e06..32ea2b5c1c09c5 100644 --- a/lib/internal/http.js +++ b/lib/internal/http.js @@ -7,14 +7,8 @@ const { const { setUnrefTimeout } = require('internal/timers'); const { PerformanceEntry, notify } = internalBinding('performance'); -let nowCache; let utcCache; -function nowDate() { - if (!nowCache) cache(); - return nowCache; -} - function utcDate() { if (!utcCache) cache(); return utcCache; @@ -22,13 +16,11 @@ function utcDate() { function cache() { const d = new Date(); - nowCache = d.valueOf(); utcCache = d.toUTCString(); setUnrefTimeout(resetCache, 1000 - d.getMilliseconds()); } function resetCache() { - nowCache = undefined; utcCache = undefined; } @@ -51,7 +43,6 @@ function emitStatistics(statistics) { module.exports = { kOutHeaders: Symbol('kOutHeaders'), kNeedDrain: Symbol('kNeedDrain'), - nowDate, utcDate, emitStatistics }; diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index f8f88957de36be..65a19199e03596 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -94,12 +94,15 @@ const { ERR_OUT_OF_RANGE, ERR_SOCKET_CLOSED }, - hideStackFrames + hideStackFrames, + AbortError } = require('internal/errors'); -const { validateNumber, - validateString, - validateUint32, - isUint32, +const { + isUint32, + validateNumber, + validateString, + validateUint32, + validateAbortSignal, } = require('internal/validators'); const fsPromisesInternal = require('internal/fs/promises'); const { utcDate } = require('internal/http'); @@ -1246,7 +1249,7 @@ class Http2Session extends EventEmitter { } // If ping is called while we are still connecting, or after close() has - // been called, the ping callback will be invoked immediately will a ping + // been called, the ping callback will be invoked immediately with a ping // cancelled error and a duration of 0.0. ping(payload, callback) { if (this.destroyed) @@ -1376,7 +1379,7 @@ class Http2Session extends EventEmitter { settingsFn(); } - // Sumits a GOAWAY frame to be sent to the remote peer. Note that this + // Submits a GOAWAY frame to be sent to the remote peer. Note that this // is only a notification, and does not affect the usable state of the // session with the notable exception that new incoming streams will // be rejected automatically. @@ -1666,6 +1669,20 @@ class ClientHttp2Session extends Http2Session { if (options.waitForTrailers) stream[kState].flags |= STREAM_FLAGS_HAS_TRAILERS; + const { signal } = options; + if (signal) { + validateAbortSignal(signal, 'options.signal'); + const aborter = () => stream.destroy(new AbortError()); + if (signal.aborted) { + aborter(); + } else { + signal.addEventListener('abort', aborter); + stream.once('close', () => { + signal.removeEventListener('abort', aborter); + }); + } + } + const onConnect = requestOnConnect.bind(stream, headersList, options); if (this.connecting) { if (this[kPendingRequestCalls] !== null) { @@ -2994,6 +3011,12 @@ class Http2SecureServer extends TLSServer { } return this; } + + updateSettings(settings) { + assertIsObject(settings, 'settings'); + validateSettings(settings); + this[kOptions].settings = { ...this[kOptions].settings, ...settings }; + } } class Http2Server extends NETServer { @@ -3016,6 +3039,12 @@ class Http2Server extends NETServer { } return this; } + + updateSettings(settings) { + assertIsObject(settings, 'settings'); + validateSettings(settings); + this[kOptions].settings = { ...this[kOptions].settings, ...settings }; + } } Http2Server.prototype[EventEmitter.captureRejectionSymbol] = function( diff --git a/lib/internal/main/print_help.js b/lib/internal/main/print_help.js index 34a8e19f08ec31..2ca405746c5a5d 100644 --- a/lib/internal/main/print_help.js +++ b/lib/internal/main/print_help.js @@ -24,7 +24,7 @@ const envVars = new Map([ ['NODE_DISABLE_COLORS', { helpText: 'set to 1 to disable colors in ' + 'the REPL' }], ['NODE_EXTRA_CA_CERTS', { helpText: 'path to additional CA certificates ' + - 'file' }], + 'file. Only read once during process startup.' }], ['NODE_NO_WARNINGS', { helpText: 'set to 1 to silence process warnings' }], ['NODE_PATH', { helpText: `'${require('path').delimiter}'-separated list ` + 'of directories prefixed to the module search path' }], diff --git a/lib/internal/main/worker_thread.js b/lib/internal/main/worker_thread.js index c2a4b67e6e0669..6f8cb6b942b5f2 100644 --- a/lib/internal/main/worker_thread.js +++ b/lib/internal/main/worker_thread.js @@ -13,6 +13,7 @@ const { setupInspectorHooks, setupWarningHandler, setupDebugEnv, + initializeAbortController, initializeDeprecations, initializeWASI, initializeCJSLoader, @@ -112,15 +113,12 @@ port.on('message', (message) => { if (manifestSrc) { require('internal/process/policy').setup(manifestSrc, manifestURL); } + initializeAbortController(); initializeDeprecations(); initializeWASI(); initializeCJSLoader(); initializeESMLoader(); - const CJSLoader = require('internal/modules/cjs/loader'); - assert(!CJSLoader.hasLoadedAnyUserCJSModule); - loadPreloadModules(); - initializeFrozenIntrinsics(); if (argv !== undefined) { process.argv = process.argv.concat(argv); } @@ -143,6 +141,11 @@ port.on('message', (message) => { }; workerIo.sharedCwdCounter = cwdCounter; + const CJSLoader = require('internal/modules/cjs/loader'); + assert(!CJSLoader.hasLoadedAnyUserCJSModule); + loadPreloadModules(); + initializeFrozenIntrinsics(); + if (!hasStdin) process.stdin.push(null); @@ -190,10 +193,12 @@ port.on('message', (message) => { function workerOnGlobalUncaughtException(error, fromPromise) { debug(`[${threadId}] gets uncaught exception`); let handled = false; + let handlerThrew = false; try { handled = onGlobalUncaughtException(error, fromPromise); } catch (e) { error = e; + handlerThrew = true; } debug(`[${threadId}] uncaught exception handled = ${handled}`); @@ -201,6 +206,16 @@ function workerOnGlobalUncaughtException(error, fromPromise) { return true; } + if (!process._exiting) { + try { + process._exiting = true; + process.exitCode = 1; + if (!handlerThrew) { + process.emit('exit', process.exitCode); + } + } catch {} + } + let serialized; try { const { serializeError } = require('internal/error_serdes'); diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index 223c6b21e39f48..ebe0c741c9e177 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -59,6 +59,7 @@ module.exports = { const { NativeModule } = require('internal/bootstrap/loaders'); const { + getSourceMapsEnabled, maybeCacheSourceMap, rekeySourceMap } = require('internal/source_map/source_map_cache'); @@ -81,7 +82,6 @@ const { loadNativeModule } = require('internal/modules/cjs/helpers'); const { getOptionValue } = require('internal/options'); -const enableSourceMaps = getOptionValue('--enable-source-maps'); const preserveSymlinks = getOptionValue('--preserve-symlinks'); const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main'); // Do not eagerly grab .manifest, it may be in TDZ @@ -128,6 +128,7 @@ const relativeResolveCache = ObjectCreate(null); let requireDepth = 0; let statCache = null; +let isPreloading = false; function stat(filename) { filename = path.toNamespacedPath(filename); @@ -219,6 +220,10 @@ ObjectDefineProperty(Module, 'wrapper', { } }); +ObjectDefineProperty(Module.prototype, 'isPreloading', { + get() { return isPreloading; } +}); + let debug = require('internal/util/debuglog').debuglog('module', (fn) => { debug = fn; }); @@ -758,7 +763,7 @@ Module._load = function(request, parent, isMain) { // Intercept exceptions that occur during the first tick and rekey them // on error instance rather than module instance (which will immediately be // garbage collected). - if (enableSourceMaps) { + if (getSourceMapsEnabled()) { try { module.load(filename); } catch (err) { @@ -1202,6 +1207,8 @@ Module._preloadModules = function(requests) { if (!ArrayIsArray(requests)) return; + isPreloading = true; + // Preloaded modules have a dummy parent module which is deemed to exist // in the current working directory. This seeds the search path for // preloaded modules. @@ -1210,11 +1217,13 @@ Module._preloadModules = function(requests) { parent.paths = Module._nodeModulePaths(process.cwd()); } catch (e) { if (e.code !== 'ENOENT') { + isPreloading = false; throw e; } } for (let n = 0; n < requests.length; n++) parent.require(requests[n]); + isPreloading = false; }; Module.syncBuiltinESMExports = function syncBuiltinESMExports() { diff --git a/lib/internal/modules/esm/loader.js b/lib/internal/modules/esm/loader.js index 232177698c73f6..14e66dcb9fdf62 100644 --- a/lib/internal/modules/esm/loader.js +++ b/lib/internal/modules/esm/loader.js @@ -6,12 +6,14 @@ require('internal/modules/cjs/loader'); const { FunctionPrototypeBind, ObjectSetPrototypeOf, + RegExpPrototypeExec, SafeWeakMap, StringPrototypeStartsWith, } = primordials; const { ERR_INVALID_ARG_VALUE, + ERR_INVALID_MODULE_SPECIFIER, ERR_INVALID_RETURN_PROPERTY, ERR_INVALID_RETURN_PROPERTY_VALUE, ERR_INVALID_RETURN_VALUE, @@ -107,6 +109,15 @@ class Loader { } const { format } = getFormatResponse; + if (format === null) { + const dataUrl = RegExpPrototypeExec( + /^data:([^/]+\/[^;,]+)(?:[^,]*?)(;base64)?,/, + url, + ); + throw new ERR_INVALID_MODULE_SPECIFIER( + url, + dataUrl ? `has an unsupported MIME type "${dataUrl[1]}"` : ''); + } if (typeof format !== 'string') { throw new ERR_INVALID_RETURN_PROPERTY_VALUE( 'string', 'loader getFormat', 'format', format); diff --git a/lib/internal/modules/esm/module_job.js b/lib/internal/modules/esm/module_job.js index 549d43cc20e119..8eeecf1c369964 100644 --- a/lib/internal/modules/esm/module_job.js +++ b/lib/internal/modules/esm/module_job.js @@ -118,7 +118,7 @@ class ModuleJob { const importStatement = splitStack[1]; // TODO(@ctavan): The original error stack only provides the single // line which causes the error. For multi-line import statements we - // cannot generate an equivalent object descructuring assignment by + // cannot generate an equivalent object destructuring assignment by // just parsing the error stack. const oneLineNamedImports = StringPrototypeMatch(importStatement, /{.*}/); const destructuringAssignment = oneLineNamedImports && diff --git a/lib/internal/per_context/primordials.js b/lib/internal/per_context/primordials.js index 8b1b8841c25379..a1ba7f52974fb2 100644 --- a/lib/internal/per_context/primordials.js +++ b/lib/internal/per_context/primordials.js @@ -101,9 +101,9 @@ primordials.SafeSet = makeSafe( Set, class SafeSet extends Set {} ); -primordials.SafePromise = makeSafe( - Promise, - class SafePromise extends Promise {} +primordials.SafeWeakSet = makeSafe( + WeakSet, + class SafeWeakSet extends WeakSet {} ); // Create copies of the namespace objects diff --git a/lib/internal/policy/manifest.js b/lib/internal/policy/manifest.js index 71dc3d78ab09e4..ba90de055c0949 100644 --- a/lib/internal/policy/manifest.js +++ b/lib/internal/policy/manifest.js @@ -41,8 +41,10 @@ const shouldAbortOnUncaughtException = getOptionValue('--abort-on-uncaught-exception'); const { abort, exit, _rawDebug } = process; +const kTerminate = () => null; + // From https://url.spec.whatwg.org/#special-scheme -const SPECIAL_SCHEMES = new SafeSet([ +const kSpecialSchemes = new SafeSet([ 'file:', 'ftp:', 'http:', @@ -76,7 +78,7 @@ function REACTION_LOG(error) { class Manifest { /** - * @type {Map} + * @type {Map} * * Used to compare a resource to the content body at the resource. * `true` is used to signify that all integrities are allowed, otherwise, @@ -139,6 +141,8 @@ class Manifest { */ constructor(obj, manifestURL) { const scopes = this.#scopeDependencies; + scopes.set(null, kTerminate); + scopes.set(undefined, kTerminate); const integrities = this.#resourceIntegrities; const dependencies = this.#resourceDependencies; let reaction = REACTION_THROW; @@ -205,18 +209,20 @@ class Manifest { return (toSpecifier, conditions) => { if (toSpecifier in dependencyMap !== true) { if (cascade === true) { - let scopeHREF; + /** @type {string | null} */ + let scopeHREF = resourceHREF; if (typeof parentDeps === 'undefined') { do { - scopeHREF = this.#findScopeHREF(resourceHREF); + scopeHREF = this.#findScopeHREF(scopeHREF); + if (scopeHREF === resourceHREF) { + scopeHREF = null; + } + if (scopes.has(scopeHREF)) { + break; + } } while ( - scopeHREF !== null && - scopes.has(scopeHREF) !== true + scopeHREF !== null ); - } - if (scopeHREF === null) { - parentDeps = () => null; - } else { parentDeps = scopes.get(scopeHREF); } return parentDeps(toSpecifier); @@ -416,7 +422,7 @@ class Manifest { protocol = currentURL.protocol; } // Only a few schemes are hierarchical - if (SPECIAL_SCHEMES.has(currentURL.protocol)) { + if (kSpecialSchemes.has(currentURL.protocol)) { // Make first '..' act like '.' if (currentURL.pathname.slice(-1) !== '/') { currentURL.pathname += '/'; diff --git a/lib/internal/process/task_queues.js b/lib/internal/process/task_queues.js index 3cf07601a23aa4..76147473b04f9b 100644 --- a/lib/internal/process/task_queues.js +++ b/lib/internal/process/task_queues.js @@ -39,6 +39,8 @@ const { } = require('internal/errors').codes; const FixedQueue = require('internal/fixed_queue'); +const { AsyncResource } = require('async_hooks'); + // *Must* match Environment::TickInfo::Fields in src/env.h. const kHasTickScheduled = 0; @@ -132,16 +134,6 @@ function nextTick(callback) { queue.push(tickObject); } -let AsyncResource; -const defaultMicrotaskResourceOpts = { requireManualDestroy: true }; -function createMicrotaskResource() { - // Lazy load the async_hooks module - if (AsyncResource === undefined) { - AsyncResource = require('async_hooks').AsyncResource; - } - return new AsyncResource('Microtask', defaultMicrotaskResourceOpts); -} - function runMicrotask() { this.runInAsyncScope(() => { const callback = this.callback; @@ -153,12 +145,17 @@ function runMicrotask() { }); } +const defaultMicrotaskResourceOpts = { requireManualDestroy: true }; + function queueMicrotask(callback) { if (typeof callback !== 'function') { throw new ERR_INVALID_ARG_TYPE('callback', 'function', callback); } - const asyncResource = createMicrotaskResource(); + const asyncResource = new AsyncResource( + 'Microtask', + defaultMicrotaskResourceOpts + ); asyncResource.callback = callback; enqueueMicrotask(FunctionPrototypeBind(runMicrotask, asyncResource)); diff --git a/lib/internal/querystring.js b/lib/internal/querystring.js index 5306c7f82bb14a..d6ecae0c0da5c8 100644 --- a/lib/internal/querystring.js +++ b/lib/internal/querystring.js @@ -30,6 +30,12 @@ const isHexTable = new Int8Array([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // ... 256 ]); +/** + * @param {string} str + * @param {Int8Array} noEscapeTable + * @param {string[]} hexTable + * @returns {string} + */ function encodeStr(str, noEscapeTable, hexTable) { const len = str.length; if (len === 0) diff --git a/lib/internal/repl/utils.js b/lib/internal/repl/utils.js index 058f262b5a6b47..ac81a08a11451d 100644 --- a/lib/internal/repl/utils.js +++ b/lib/internal/repl/utils.js @@ -144,7 +144,7 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) { let escaped = null; function getPreviewPos() { - const displayPos = repl._getDisplayPos(`${repl._prompt}${repl.line}`); + const displayPos = repl._getDisplayPos(`${repl.getPrompt()}${repl.line}`); const cursorPos = repl.line.length !== repl.cursor ? repl.getCursorPos() : displayPos; @@ -177,7 +177,7 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) { rows = pos.displayPos.rows - pos.cursorPos.rows; moveCursor(repl.output, 0, rows); } - const totalLine = `${repl._prompt}${repl.line}${completionPreview}`; + const totalLine = `${repl.getPrompt()}${repl.line}${completionPreview}`; const newPos = repl._getDisplayPos(totalLine); // Minimize work for the terminal. It is enough to clear the right part of // the current line in case the preview is visible on a single line. @@ -263,7 +263,7 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) { } repl.output.write(result); cursorTo(repl.output, cursorPos.cols); - const totalLine = `${repl._prompt}${repl.line}${suffix}`; + const totalLine = `${repl.getPrompt()}${repl.line}${suffix}`; const newPos = repl._getDisplayPos(totalLine); const rows = newPos.rows - cursorPos.rows - (newPos.cols === 0 ? 1 : 0); moveCursor(repl.output, 0, -rows); @@ -611,7 +611,7 @@ function setupReverseSearch(repl) { let rows = 0; if (lastMatch !== -1) { const line = repl.history[lastMatch].slice(0, lastCursor); - rows = repl._getDisplayPos(`${repl._prompt}${line}`).rows; + rows = repl._getDisplayPos(`${repl.getPrompt()}${line}`).rows; cursorTo(repl.output, promptPos.cols); } else if (isInReverseSearch && repl.line !== '') { rows = repl.getCursorPos().rows; @@ -631,7 +631,7 @@ function setupReverseSearch(repl) { // To know exactly how many rows we have to move the cursor back we need the // cursor rows, the output rows and the input rows. - const prompt = repl._prompt; + const prompt = repl.getPrompt(); const cursorLine = `${prompt}${outputLine.slice(0, cursor)}`; const cursorPos = repl._getDisplayPos(cursorLine); const outputPos = repl._getDisplayPos(`${prompt}${outputLine}`); @@ -682,7 +682,7 @@ function setupReverseSearch(repl) { if (!isInReverseSearch) { if (key.ctrl && checkAndSetDirectionKey(key.name)) { historyIndex = repl.historyIndex; - promptPos = repl._getDisplayPos(`${repl._prompt}`); + promptPos = repl._getDisplayPos(`${repl.getPrompt()}`); print(repl.line, `${labels[dir]}_`); isInReverseSearch = true; } diff --git a/lib/internal/source_map/prepare_stack_trace.js b/lib/internal/source_map/prepare_stack_trace.js index 8a10470d077fed..e9c3536c963504 100644 --- a/lib/internal/source_map/prepare_stack_trace.js +++ b/lib/internal/source_map/prepare_stack_trace.js @@ -1,18 +1,23 @@ 'use strict'; const { + ArrayPrototypeIndexOf, Error, + StringPrototypeStartsWith, } = primordials; let debug = require('internal/util/debuglog').debuglog('source_map', (fn) => { debug = fn; }); +const { getStringWidth } = require('internal/util/inspect'); +const { readFileSync } = require('fs'); const { findSourceMap } = require('internal/source_map/source_map_cache'); const { kNoOverride, overrideStackTrace, maybeOverridePrepareStackTrace } = require('internal/errors'); +const { fileURLToPath } = require('internal/url'); // Create a prettified stacktrace, inserting context from source maps // if possible. @@ -36,7 +41,15 @@ const prepareStackTrace = (globalThis, error, trace) => { if (trace.length === 0) { return errorString; } + + let errorSource = ''; + let firstLine; + let firstColumn; const preparedTrace = trace.map((t, i) => { + if (i === 0) { + firstLine = t.getLineNumber(); + firstColumn = t.getColumnNumber(); + } let str = i !== 0 ? '\n at ' : ''; str = `${str}${t}`; try { @@ -51,8 +64,24 @@ const prepareStackTrace = (globalThis, error, trace) => { } = sm.findEntry(t.getLineNumber() - 1, t.getColumnNumber() - 1); if (originalSource && originalLine !== undefined && originalColumn !== undefined) { - str += -`\n -> ${originalSource.replace('file://', '')}:${originalLine + 1}:${originalColumn + 1}`; + if (i === 0) { + firstLine = originalLine + 1; + firstColumn = originalColumn + 1; + + // Show error in original source context to help user pinpoint it: + errorSource = getErrorSource( + sm.payload, + originalSource, + firstLine, + firstColumn + ); + } + // Show both original and transpiled stack trace information: + const originalSourceNoScheme = + StringPrototypeStartsWith(originalSource, 'file://') ? + fileURLToPath(originalSource) : originalSource; + str += `\n -> ${originalSourceNoScheme}:${originalLine + 1}:` + + `${originalColumn + 1}`; } } } catch (err) { @@ -60,9 +89,53 @@ const prepareStackTrace = (globalThis, error, trace) => { } return str; }); - return `${errorString}\n at ${preparedTrace.join('')}`; + return `${errorSource}${errorString}\n at ${preparedTrace.join('')}`; }; +// Places a snippet of code from where the exception was originally thrown +// above the stack trace. This logic is modeled after GetErrorSource in +// node_errors.cc. +function getErrorSource(payload, originalSource, firstLine, firstColumn) { + let exceptionLine = ''; + const originalSourceNoScheme = + StringPrototypeStartsWith(originalSource, 'file://') ? + fileURLToPath(originalSource) : originalSource; + + let source; + const sourceContentIndex = + ArrayPrototypeIndexOf(payload.sources, originalSource); + if (payload.sourcesContent?.[sourceContentIndex]) { + // First we check if the original source content was provided in the + // source map itself: + source = payload.sourcesContent[sourceContentIndex]; + } else { + // If no sourcesContent was found, attempt to load the original source + // from disk: + try { + source = readFileSync(originalSourceNoScheme, 'utf8'); + } catch (err) { + debug(err); + } + } + + const lines = source.split(/\r?\n/, firstLine); + const line = lines[firstLine - 1]; + if (!line) return exceptionLine; + + // Display ^ in appropriate position, regardless of whether tabs or + // spaces are used: + let prefix = ''; + for (const character of line.slice(0, firstColumn)) { + prefix += (character === '\t') ? '\t' : + ' '.repeat(getStringWidth(character)); + } + prefix = prefix.slice(0, -1); // The last character is the '^'. + + exceptionLine = + `${originalSourceNoScheme}:${firstLine}\n${line}\n${prefix}^\n\n`; + return exceptionLine; +} + module.exports = { prepareStackTrace, }; diff --git a/lib/internal/source_map/source_map_cache.js b/lib/internal/source_map/source_map_cache.js index 8952940920b88f..f94ffa8387a451 100644 --- a/lib/internal/source_map/source_map_cache.js +++ b/lib/internal/source_map/source_map_cache.js @@ -25,7 +25,6 @@ const { Buffer } = require('buffer'); let debug = require('internal/util/debuglog').debuglog('source_map', (fn) => { debug = fn; }); -const { dirname, resolve } = require('path'); const fs = require('fs'); const { getOptionValue } = require('internal/options'); const { @@ -41,16 +40,30 @@ const { fileURLToPath, URL } = require('url'); let Module; let SourceMap; -let experimentalSourceMaps; -function maybeCacheSourceMap(filename, content, cjsModuleInstance) { - if (experimentalSourceMaps === undefined) { - experimentalSourceMaps = getOptionValue('--enable-source-maps'); +let sourceMapsEnabled; +function getSourceMapsEnabled() { + if (sourceMapsEnabled === undefined) { + sourceMapsEnabled = getOptionValue('--enable-source-maps'); + if (sourceMapsEnabled) { + const { + enableSourceMaps, + setPrepareStackTraceCallback + } = internalBinding('errors'); + const { + prepareStackTrace + } = require('internal/source_map/prepare_stack_trace'); + setPrepareStackTraceCallback(prepareStackTrace); + enableSourceMaps(); + } } - if (!(process.env.NODE_V8_COVERAGE || experimentalSourceMaps)) return; - let basePath; + return sourceMapsEnabled; +} + +function maybeCacheSourceMap(filename, content, cjsModuleInstance) { + const sourceMapsEnabled = getSourceMapsEnabled(); + if (!(process.env.NODE_V8_COVERAGE || sourceMapsEnabled)) return; try { filename = normalizeReferrerURL(filename); - basePath = dirname(fileURLToPath(filename)); } catch (err) { // This is most likely an [eval]-wrapper, which is currently not // supported. @@ -60,7 +73,7 @@ function maybeCacheSourceMap(filename, content, cjsModuleInstance) { const match = content.match(/\/[*/]#\s+sourceMappingURL=(?[^\s]+)/); if (match) { - const data = dataFromUrl(basePath, match.groups.sourceMappingURL); + const data = dataFromUrl(filename, match.groups.sourceMappingURL); const url = data ? null : match.groups.sourceMappingURL; if (cjsModuleInstance) { if (!Module) Module = require('internal/modules/cjs/loader').Module; @@ -82,12 +95,12 @@ function maybeCacheSourceMap(filename, content, cjsModuleInstance) { } } -function dataFromUrl(basePath, sourceMappingURL) { +function dataFromUrl(sourceURL, sourceMappingURL) { try { const url = new URL(sourceMappingURL); switch (url.protocol) { case 'data:': - return sourceMapFromDataUrl(basePath, url.pathname); + return sourceMapFromDataUrl(sourceURL, url.pathname); default: debug(`unknown protocol ${url.protocol}`); return null; @@ -95,8 +108,8 @@ function dataFromUrl(basePath, sourceMappingURL) { } catch (err) { debug(err.stack); // If no scheme is present, we assume we are dealing with a file path. - const sourceMapFile = resolve(basePath, sourceMappingURL); - return sourceMapFromFile(sourceMapFile); + const mapURL = new URL(sourceMappingURL, sourceURL).href; + return sourceMapFromFile(mapURL); } } @@ -112,11 +125,11 @@ function lineLengths(content) { }); } -function sourceMapFromFile(sourceMapFile) { +function sourceMapFromFile(mapURL) { try { - const content = fs.readFileSync(sourceMapFile, 'utf8'); + const content = fs.readFileSync(fileURLToPath(mapURL), 'utf8'); const data = JSONParse(content); - return sourcesToAbsolute(dirname(sourceMapFile), data); + return sourcesToAbsolute(mapURL, data); } catch (err) { debug(err.stack); return null; @@ -125,7 +138,7 @@ function sourceMapFromFile(sourceMapFile) { // data:[][;base64], see: // https://tools.ietf.org/html/rfc2397#section-2 -function sourceMapFromDataUrl(basePath, url) { +function sourceMapFromDataUrl(sourceURL, url) { const [format, data] = url.split(','); const splitFormat = format.split(';'); const contentType = splitFormat[0]; @@ -135,7 +148,7 @@ function sourceMapFromDataUrl(basePath, url) { Buffer.from(data, 'base64').toString('utf8') : data; try { const parsedData = JSONParse(decodedData); - return sourcesToAbsolute(basePath, parsedData); + return sourcesToAbsolute(sourceURL, parsedData); } catch (err) { debug(err.stack); return null; @@ -149,14 +162,10 @@ function sourceMapFromDataUrl(basePath, url) { // If the sources are not absolute URLs after prepending of the "sourceRoot", // the sources are resolved relative to the SourceMap (like resolving script // src in a html document). -function sourcesToAbsolute(base, data) { +function sourcesToAbsolute(baseURL, data) { data.sources = data.sources.map((source) => { source = (data.sourceRoot || '') + source; - if (!/^[\\/]/.test(source[0])) { - source = resolve(base, source); - } - if (!source.startsWith('file://')) source = `file://${source}`; - return source; + return new URL(source, baseURL).href; }); // The sources array is now resolved to absolute URLs, sourceRoot should // be updated to noop. @@ -250,6 +259,7 @@ function findSourceMap(uri, error) { module.exports = { findSourceMap, + getSourceMapsEnabled, maybeCacheSourceMap, rekeySourceMap, sourceMapCacheToObject, diff --git a/lib/internal/streams/duplex.js b/lib/internal/streams/duplex.js index 3bab26d9cef934..596d30a90a0418 100644 --- a/lib/internal/streams/duplex.js +++ b/lib/internal/streams/duplex.js @@ -87,6 +87,8 @@ ObjectDefineProperties(Duplex.prototype, { ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableCorked'), writableEnded: ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableEnded'), + writableNeedDrain: + ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableNeedDrain'), destroyed: { get() { diff --git a/lib/internal/streams/pipeline.js b/lib/internal/streams/pipeline.js index fe229bbd17ad57..134e9ea94fa797 100644 --- a/lib/internal/streams/pipeline.js +++ b/lib/internal/streams/pipeline.js @@ -5,8 +5,9 @@ const { ArrayIsArray, + ReflectApply, SymbolAsyncIterator, - SymbolIterator + SymbolIterator, } = primordials; let eos; @@ -77,10 +78,6 @@ function popCallback(streams) { return streams.pop(); } -function isPromise(obj) { - return !!(obj && typeof obj.then === 'function'); -} - function isReadable(obj) { return !!(obj && typeof obj.pipe === 'function'); } @@ -125,6 +122,10 @@ async function pump(iterable, writable, finish) { } let error; try { + if (writable.writableNeedDrain === true) { + await EE.once(writable, 'drain'); + } + for await (const chunk of iterable) { if (!writable.write(chunk)) { if (writable.destroyed) return; @@ -224,14 +225,19 @@ function pipeline(...streams) { const pt = new PassThrough({ objectMode: true }); - if (isPromise(ret)) { - ret - .then((val) => { + + // Handle Promises/A+ spec, `then` could be a getter that throws on + // second use. + const then = ret?.then; + if (typeof then === 'function') { + ReflectApply(then, ret, [ + (val) => { value = val; pt.end(val); }, (err) => { pt.destroy(err); - }); + } + ]); } else if (isIterable(ret, true)) { finishCount++; pump(ret, pt, finish); diff --git a/lib/internal/streams/readable.js b/lib/internal/streams/readable.js index 91b1877f43daa9..cb13210d2b87db 100644 --- a/lib/internal/streams/readable.js +++ b/lib/internal/streams/readable.js @@ -616,7 +616,7 @@ function maybeReadMore_(stream, state) { // conditions prevent the data from being read: // - The stream has ended (state.ended). // - There is already a pending 'read' operation (state.reading). This is a - // case where the the stream has called the implementation defined _read() + // case where the stream has called the implementation defined _read() // method, but they are processing the call asynchronously and have _not_ // called push() with new data. In this case we skip performing more // read()s. The execution ends in this method again after the _read() ends @@ -713,35 +713,39 @@ Readable.prototype.pipe = function(dest, pipeOpts) { ondrain(); } + function pause() { + // If the user unpiped during `dest.write()`, it is possible + // to get stuck in a permanently paused state if that write + // also returned false. + // => Check whether `dest` is still a piping destination. + if (!cleanedUp) { + if (state.pipes.length === 1 && state.pipes[0] === dest) { + debug('false write response, pause', 0); + state.awaitDrainWriters = dest; + state.multiAwaitDrain = false; + } else if (state.pipes.length > 1 && state.pipes.includes(dest)) { + debug('false write response, pause', state.awaitDrainWriters.size); + state.awaitDrainWriters.add(dest); + } + src.pause(); + } + if (!ondrain) { + // When the dest drains, it reduces the awaitDrain counter + // on the source. This would be more elegant with a .once() + // handler in flow(), but adding and removing repeatedly is + // too slow. + ondrain = pipeOnDrain(src, dest); + dest.on('drain', ondrain); + } + } + src.on('data', ondata); function ondata(chunk) { debug('ondata'); const ret = dest.write(chunk); debug('dest.write', ret); if (ret === false) { - // If the user unpiped during `dest.write()`, it is possible - // to get stuck in a permanently paused state if that write - // also returned false. - // => Check whether `dest` is still a piping destination. - if (!cleanedUp) { - if (state.pipes.length === 1 && state.pipes[0] === dest) { - debug('false write response, pause', 0); - state.awaitDrainWriters = dest; - state.multiAwaitDrain = false; - } else if (state.pipes.length > 1 && state.pipes.includes(dest)) { - debug('false write response, pause', state.awaitDrainWriters.size); - state.awaitDrainWriters.add(dest); - } - src.pause(); - } - if (!ondrain) { - // When the dest drains, it reduces the awaitDrain counter - // on the source. This would be more elegant with a .once() - // handler in flow(), but adding and removing repeatedly is - // too slow. - ondrain = pipeOnDrain(src, dest); - dest.on('drain', ondrain); - } + pause(); } } @@ -787,7 +791,12 @@ Readable.prototype.pipe = function(dest, pipeOpts) { dest.emit('pipe', src); // Start the flow if it hasn't been started already. - if (!state.flowing) { + + if (dest.writableNeedDrain === true) { + if (state.flowing) { + pause(); + } + } else if (!state.flowing) { debug('pipe resume'); src.resume(); } diff --git a/lib/internal/streams/writable.js b/lib/internal/streams/writable.js index fbc0532681b83d..67da8061295fe0 100644 --- a/lib/internal/streams/writable.js +++ b/lib/internal/streams/writable.js @@ -748,6 +748,14 @@ ObjectDefineProperties(Writable.prototype, { } }, + writableNeedDrain: { + get() { + const wState = this._writableState; + if (!wState) return false; + return !wState.destroyed && !wState.ending && wState.needDrain; + } + }, + writableHighWaterMark: { get() { return this._writableState && this._writableState.highWaterMark; diff --git a/lib/internal/timers.js b/lib/internal/timers.js index 045994a23d0210..af44fe6b91c922 100644 --- a/lib/internal/timers.js +++ b/lib/internal/timers.js @@ -84,7 +84,8 @@ const { scheduleTimer, toggleTimerRef, getLibuvNow, - immediateInfo + immediateInfo, + toggleImmediateRef } = internalBinding('timers'); const { @@ -274,11 +275,11 @@ ImmediateList.prototype.append = function(item) { // Removes an item from the linked list, adjusting the pointers of adjacent // items and the linked list's head or tail pointers as necessary ImmediateList.prototype.remove = function(item) { - if (item._idleNext !== null) { + if (item._idleNext) { item._idleNext._idlePrev = item._idlePrev; } - if (item._idlePrev !== null) { + if (item._idlePrev) { item._idlePrev._idleNext = item._idleNext; } @@ -593,12 +594,53 @@ function getTimerCallbacks(runNextTicks) { }; } +class Immediate { + constructor(callback, args) { + this._idleNext = null; + this._idlePrev = null; + this._onImmediate = callback; + this._argv = args; + this._destroyed = false; + this[kRefed] = false; + + initAsyncResource(this, 'Immediate'); + + this.ref(); + immediateInfo[kCount]++; + + immediateQueue.append(this); + } + + ref() { + if (this[kRefed] === false) { + this[kRefed] = true; + if (immediateInfo[kRefCount]++ === 0) + toggleImmediateRef(true); + } + return this; + } + + unref() { + if (this[kRefed] === true) { + this[kRefed] = false; + if (--immediateInfo[kRefCount] === 0) + toggleImmediateRef(false); + } + return this; + } + + hasRef() { + return !!this[kRefed]; + } +} + module.exports = { TIMEOUT_MAX, kTimeout: Symbol('timeout'), // For hiding Timeouts on other internals. async_id_symbol, trigger_async_id_symbol, Timeout, + Immediate, kRefed, kHasPrimitive, initAsyncResource, diff --git a/lib/internal/timers/promises.js b/lib/internal/timers/promises.js new file mode 100644 index 00000000000000..94c8f40a19db5e --- /dev/null +++ b/lib/internal/timers/promises.js @@ -0,0 +1,120 @@ +'use strict'; + +const { + Promise, + PromiseReject, +} = primordials; + +const { + Timeout, + Immediate, + insert +} = require('internal/timers'); + +const { + hideStackFrames, + codes: { ERR_INVALID_ARG_TYPE } +} = require('internal/errors'); + +const { validateAbortSignal } = require('internal/validators'); + +let DOMException; + +const lazyDOMException = hideStackFrames((message, name) => { + if (DOMException === undefined) + DOMException = internalBinding('messaging').DOMException; + return new DOMException(message, name); +}); + +function setTimeout(after, value, options = {}) { + const args = value !== undefined ? [value] : value; + if (options == null || typeof options !== 'object') { + return PromiseReject( + new ERR_INVALID_ARG_TYPE( + 'options', + 'Object', + options)); + } + const { signal, ref = true } = options; + try { + validateAbortSignal(signal, 'options.signal'); + } catch (err) { + return PromiseReject(err); + } + if (typeof ref !== 'boolean') { + return PromiseReject( + new ERR_INVALID_ARG_TYPE( + 'options.ref', + 'boolean', + ref)); + } + // TODO(@jasnell): If a decision is made that this cannot be backported + // to 12.x, then this can be converted to use optional chaining to + // simplify the check. + if (signal && signal.aborted) { + return PromiseReject( + lazyDOMException('The operation was aborted', 'AbortError')); + } + return new Promise((resolve, reject) => { + const timeout = new Timeout(resolve, after, args, false, true); + if (!ref) timeout.unref(); + insert(timeout, timeout._idleTimeout); + if (signal) { + signal.addEventListener('abort', () => { + if (!timeout._destroyed) { + // eslint-disable-next-line no-undef + clearTimeout(timeout); + reject(lazyDOMException('The operation was aborted', 'AbortError')); + } + }, { once: true }); + } + }); +} + +function setImmediate(value, options = {}) { + if (options == null || typeof options !== 'object') { + return PromiseReject( + new ERR_INVALID_ARG_TYPE( + 'options', + 'Object', + options)); + } + const { signal, ref = true } = options; + try { + validateAbortSignal(signal, 'options.signal'); + } catch (err) { + return PromiseReject(err); + } + if (typeof ref !== 'boolean') { + return PromiseReject( + new ERR_INVALID_ARG_TYPE( + 'options.ref', + 'boolean', + ref)); + } + // TODO(@jasnell): If a decision is made that this cannot be backported + // to 12.x, then this can be converted to use optional chaining to + // simplify the check. + if (signal && signal.aborted) { + return PromiseReject( + lazyDOMException('The operation was aborted', 'AbortError')); + } + return new Promise((resolve, reject) => { + const immediate = new Immediate(resolve, [value]); + if (!ref) immediate.unref(); + if (signal) { + signal.addEventListener('abort', () => { + if (!immediate._destroyed) { + // eslint-disable-next-line no-undef + clearImmediate(immediate); + reject(lazyDOMException('The operation was aborted', 'AbortError')); + } + }, { once: true }); + } + }); +} + +module.exports = { + setTimeout, + setImmediate, +}; diff --git a/lib/internal/url.js b/lib/internal/url.js index 22dc145b85e0c7..32e298b412c555 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -416,7 +416,7 @@ ObjectDefineProperties(URL.prototype, { ret += '@'; } ret += options.unicode ? - domainToUnicode(this.hostname) : this.hostname; + domainToUnicode(ctx.host) : ctx.host; if (ctx.port !== null) ret += `:${ctx.port}`; } else if (ctx.scheme === 'file:') { diff --git a/lib/internal/util.js b/lib/internal/util.js index f26ea970e8dce0..68aade2d03d991 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -44,6 +44,13 @@ const experimentalWarnings = new Set(); const colorRegExp = /\u001b\[\d\d?m/g; // eslint-disable-line no-control-regex +let uvBinding; + +function lazyUv() { + uvBinding = uvBinding ?? internalBinding('uv'); + return uvBinding; +} + function removeColors(str) { return str.replace(colorRegExp, ''); } @@ -271,6 +278,10 @@ function getSystemErrorName(err) { return entry ? entry[0] : `Unknown system error ${err}`; } +function getSystemErrorMap() { + return lazyUv().getErrorMap(); +} + const kCustomPromisifiedSymbol = SymbolFor('nodejs.util.promisify.custom'); const kCustomPromisifyArgsSymbol = Symbol('customPromisifyArgs'); @@ -414,6 +425,7 @@ module.exports = { emitExperimentalWarning, filterDuplicateStrings, getConstructorOf, + getSystemErrorMap, getSystemErrorName, isError, isInsideNodeModules, diff --git a/lib/internal/util/comparisons.js b/lib/internal/util/comparisons.js index 2c10f0c8929752..8d5692777c94e2 100644 --- a/lib/internal/util/comparisons.js +++ b/lib/internal/util/comparisons.js @@ -315,7 +315,7 @@ function keyCheck(val1, val2, strict, memos, iterationType, aKeys) { // Cheap key test let i = 0; for (; i < aKeys.length; i++) { - if (!ObjectPrototypeHasOwnProperty(val2, aKeys[i])) { + if (!ObjectPrototypePropertyIsEnumerable(val2, aKeys[i])) { return false; } } diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index cf4660b1f2d4d6..fd3bef5d63effc 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -1386,7 +1386,8 @@ function handleMaxCallStackSize(ctx, err, constructorName, indentationLvl) { 'special' ); } - throw err; + /* c8 ignore next */ + assert.fail(err.stack); } function formatNumber(fn, value) { @@ -1437,9 +1438,7 @@ function formatNamespaceObject(keys, ctx, value, recurseTimes) { output[i] = formatProperty(ctx, value, recurseTimes, keys[i], kObjectType); } catch (err) { - if (!(isNativeError(err) && err.name === 'ReferenceError')) { - throw err; - } + assert(isNativeError(err) && err.name === 'ReferenceError'); // Use the existing functionality. This makes sure the indentation and // line breaks are always correct. Otherwise it is very difficult to keep // this aligned, even though this is a hacky way of dealing with this. @@ -1738,6 +1737,8 @@ function formatProperty(ctx, value, recurseTimes, key, type, desc, if (typeof key === 'symbol') { const tmp = key.toString().replace(strEscapeSequencesReplacer, escapeFn); name = `[${ctx.stylize(tmp, 'symbol')}]`; + } else if (key === '__proto__') { + name = "['__proto__']"; } else if (desc.enumerable === false) { name = `[${key.replace(strEscapeSequencesReplacer, escapeFn)}]`; } else if (keyStrRegExp.test(key)) { @@ -1776,7 +1777,7 @@ function reduceToSingleString( ctx, output, base, braces, extrasType, recurseTimes, value) { if (ctx.compact !== true) { if (typeof ctx.compact === 'number' && ctx.compact >= 1) { - // Memorize the original output length. In case the the output is grouped, + // Memorize the original output length. In case the output is grouped, // prevent lining up the entries on a single line. const entries = output.length; // Group array elements together if the array contains at least six diff --git a/lib/internal/validators.js b/lib/internal/validators.js index c1655fb1a843fe..ac54c96bb2dec0 100644 --- a/lib/internal/validators.js +++ b/lib/internal/validators.js @@ -218,6 +218,15 @@ const validateCallback = hideStackFrames((callback) => { throw new ERR_INVALID_CALLBACK(callback); }); +const validateAbortSignal = hideStackFrames((signal, name) => { + if (signal !== undefined && + (signal === null || + typeof signal !== 'object' || + !('aborted' in signal))) { + throw new ERR_INVALID_ARG_TYPE(name, 'AbortSignal', signal); + } +}); + module.exports = { isInt32, isUint32, @@ -236,4 +245,5 @@ module.exports = { validateString, validateUint32, validateCallback, + validateAbortSignal, }; diff --git a/lib/internal/vm/module.js b/lib/internal/vm/module.js index 72f53727d86138..a8ea29a4138531 100644 --- a/lib/internal/vm/module.js +++ b/lib/internal/vm/module.js @@ -324,6 +324,8 @@ class SourceTextModule extends Module { throw new ERR_VM_MODULE_DIFFERENT_CONTEXT(); } if (module.status === 'errored') { + // TODO(devsnek): replace with ERR_VM_MODULE_LINK_FAILURE + // and error cause proposal. throw new ERR_VM_MODULE_LINKING_ERRORED(); } if (module.status === 'unlinked') { diff --git a/lib/internal/worker.js b/lib/internal/worker.js index 2b943d1ec072d6..d38649c7fb1583 100644 --- a/lib/internal/worker.js +++ b/lib/internal/worker.js @@ -20,6 +20,7 @@ const { const EventEmitter = require('events'); const assert = require('internal/assert'); const path = require('path'); +const { timeOrigin } = internalBinding('performance'); const errorCodes = require('internal/errors').codes; const { @@ -70,6 +71,8 @@ const kOnMessage = Symbol('kOnMessage'); const kOnCouldNotSerializeErr = Symbol('kOnCouldNotSerializeErr'); const kOnErrorMessage = Symbol('kOnErrorMessage'); const kParentSideStdio = Symbol('kParentSideStdio'); +const kLoopStartTime = Symbol('kLoopStartTime'); +const kIsOnline = Symbol('kIsOnline'); const SHARE_ENV = SymbolFor('nodejs.worker_threads.SHARE_ENV'); let debug = require('internal/util/debuglog').debuglog('worker', (fn) => { @@ -223,6 +226,12 @@ class Worker extends EventEmitter { null, hasStdin: !!options.stdin }, transferList); + // Use this to cache the Worker's loopStart value once available. + this[kLoopStartTime] = -1; + this[kIsOnline] = false; + this.performance = { + eventLoopUtilization: eventLoopUtilization.bind(this), + }; // Actually start the new thread now that everything is in place. this[kHandle].startThread(); } @@ -254,6 +263,7 @@ class Worker extends EventEmitter { [kOnMessage](message) { switch (message.type) { case messageTypes.UP_AND_RUNNING: + this[kIsOnline] = true; return this.emit('online'); case messageTypes.COULD_NOT_SERIALIZE_ERROR: return this[kOnCouldNotSerializeErr](); @@ -415,6 +425,52 @@ function makeResourceLimits(float64arr) { }; } +function eventLoopUtilization(util1, util2) { + // TODO(trevnorris): Works to solve the thread-safe read/write issue of + // loopTime, but has the drawback that it can't be set until the event loop + // has had a chance to turn. So it will be impossible to read the ELU of + // a worker thread immediately after it's been created. + if (!this[kIsOnline] || !this[kHandle]) { + return { idle: 0, active: 0, utilization: 0 }; + } + + // Cache loopStart, since it's only written to once. + if (this[kLoopStartTime] === -1) { + this[kLoopStartTime] = this[kHandle].loopStartTime(); + if (this[kLoopStartTime] === -1) + return { idle: 0, active: 0, utilization: 0 }; + } + + if (util2) { + const idle = util1.idle - util2.idle; + const active = util1.active - util2.active; + return { idle, active, utilization: active / (idle + active) }; + } + + const idle = this[kHandle].loopIdleTime(); + + // Using performance.now() here is fine since it's always the time from + // the beginning of the process, and is why it needs to be offset by the + // loopStart time (which is also calculated from the beginning of the + // process). + const active = now() - this[kLoopStartTime] - idle; + + if (!util1) { + return { idle, active, utilization: active / (idle + active) }; + } + + const idle_delta = idle - util1.idle; + const active_delta = active - util1.active; + const utilization = active_delta / (idle_delta + active_delta); + return { idle: idle_delta, active: active_delta, utilization }; +} + +// Duplicate code from performance.now() so don't need to require perf_hooks. +function now() { + const hr = process.hrtime(); + return (hr[0] * 1000 + hr[1] / 1e6) - timeOrigin; +} + module.exports = { ownsProcessState, isMainThread, diff --git a/lib/internal/worker/io.js b/lib/internal/worker/io.js index 5b5118a1d70b21..406c8e77fd3375 100644 --- a/lib/internal/worker/io.js +++ b/lib/internal/worker/io.js @@ -30,6 +30,7 @@ const { const { Readable, Writable } = require('stream'); const { Event, + EventTarget, NodeEventTarget, defineEventHandler, initNodeEventTarget, @@ -79,11 +80,15 @@ class MessageEvent extends Event { } } +const originalCreateEvent = EventTarget.prototype[kCreateEvent]; ObjectDefineProperty( MessagePort.prototype, kCreateEvent, { value: function(data, type) { + if (type !== 'message' && type !== 'messageerror') { + return originalCreateEvent.call(this, data, type); + } return new MessageEvent(data, this, type); }, configurable: false, @@ -94,13 +99,12 @@ ObjectDefineProperty( // This is called from inside the `MessagePort` constructor. function oninit() { initNodeEventTarget(this); - // TODO(addaleax): This should be on MessagePort.prototype, but - // defineEventHandler() does not support that. - defineEventHandler(this, 'message'); - defineEventHandler(this, 'messageerror'); setupPortReferencing(this, this, 'message'); } +defineEventHandler(MessagePort.prototype, 'message'); +defineEventHandler(MessagePort.prototype, 'messageerror'); + ObjectDefineProperty(MessagePort.prototype, onInitSymbol, { enumerable: true, writable: false, diff --git a/lib/os.js b/lib/os.js index d03be051d09144..d02a4ada5de5d7 100644 --- a/lib/os.js +++ b/lib/os.js @@ -76,8 +76,17 @@ const [ const getHomeDirectory = getCheckedFunction(_getHomeDirectory); const getHostname = getCheckedFunction(_getHostname); const getInterfaceAddresses = getCheckedFunction(_getInterfaceAddresses); +/** + * @returns {string} + */ const getOSRelease = () => release; +/** + * @returns {string} + */ const getOSType = () => type; +/** + * @returns {string} + */ const getOSVersion = () => version; getFreeMem[SymbolToPrimitive] = () => getFreeMem(); @@ -93,11 +102,30 @@ const kEndianness = isBigEndian ? 'BE' : 'LE'; const avgValues = new Float64Array(3); +/** + * @returns {[number, number, number]} + */ function loadavg() { getLoadAvg(avgValues); return [avgValues[0], avgValues[1], avgValues[2]]; } +/** + * Returns an array of objects containing information about each + * logical CPU core. + * + * @returns {Array<{ + * model: string + * speed: number + * times: { + * user: number + * nice: number + * sys: number + * idle: number + * irq: number + * } + * }>} + */ function cpus() { // [] is a bugfix for a regression introduced in 51cea61 const data = getCPUs() || []; @@ -119,16 +147,25 @@ function cpus() { return result; } +/** + * @returns {string} + */ function arch() { return process.arch; } arch[SymbolToPrimitive] = () => process.arch; +/** + * @returns {string} + */ function platform() { return process.platform; } platform[SymbolToPrimitive] = () => process.platform; +/** + * @returns {string} + */ function tmpdir() { var path; if (isWindows) { @@ -150,6 +187,9 @@ function tmpdir() { } tmpdir[SymbolToPrimitive] = () => tmpdir(); +/** + * @returns {'BE' | 'LE'} + */ function endianness() { return kEndianness; } @@ -199,6 +239,17 @@ function getCIDR(address, netmask, family) { return `${address}/${ones}`; } +/** + * @returns {Record>} + */ function networkInterfaces() { const data = getInterfaceAddresses(); const result = {}; @@ -229,6 +280,11 @@ function networkInterfaces() { return result; } +/** + * @param {number} pid + * @param {number} priority + * @returns {void} + */ function setPriority(pid, priority) { if (priority === undefined) { priority = pid; @@ -244,6 +300,10 @@ function setPriority(pid, priority) { throw new ERR_SYSTEM_ERROR(ctx); } +/** + * @param {number} pid + * @returns {number} + */ function getPriority(pid) { if (pid === undefined) pid = 0; @@ -259,6 +319,18 @@ function getPriority(pid) { return priority; } +/** + * @param {{ encoding?: string }} options If `encoding` is set to `'buffer'`, + * the `username`, `shell`, and `homedir` values will be `Buffer` instances. + * Default: `'utf8'` + * @returns {{ + * uid: number + * gid: number + * username: string + * homedir: string + * shell: string | null + * }} + */ function userInfo(options) { if (typeof options !== 'object') options = null; diff --git a/lib/path.js b/lib/path.js index 7532b795bf63f7..71788e53d3efd8 100644 --- a/lib/path.js +++ b/lib/path.js @@ -112,6 +112,17 @@ function normalizeString(path, allowAboveRoot, separator, isPathSeparator) { return res; } +/** + * @param {string} sep + * @param {{ + * dir?: string; + * root?: string; + * base?: string; + * name?: string; + * ext?: string; + * }} pathObject + * @returns {string} + */ function _format(sep, pathObject) { if (pathObject === null || typeof pathObject !== 'object') { throw new ERR_INVALID_ARG_TYPE('pathObject', 'Object', pathObject); @@ -126,7 +137,11 @@ function _format(sep, pathObject) { } const win32 = { - // path.resolve([from ...], to) + /** + * path.resolve([from ...], to) + * @param {...string} args + * @returns {string} + */ resolve(...args) { let resolvedDevice = ''; let resolvedTail = ''; @@ -262,6 +277,10 @@ const win32 = { `${resolvedDevice}${resolvedTail}` || '.'; }, + /** + * @param {string} path + * @returns {string} + */ normalize(path) { validateString(path, 'path'); const len = path.length; @@ -349,6 +368,10 @@ const win32 = { return isAbsolute ? `${device}\\${tail}` : `${device}${tail}`; }, + /** + * @param {string} path + * @returns {boolean} + */ isAbsolute(path) { validateString(path, 'path'); const len = path.length; @@ -364,6 +387,10 @@ const win32 = { isPathSeparator(path.charCodeAt(2))); }, + /** + * @param {...string} args + * @returns {string} + */ join(...args) { if (args.length === 0) return '.'; @@ -429,10 +456,15 @@ const win32 = { return win32.normalize(joined); }, - // It will solve the relative path from `from` to `to`, for instance: - // from = 'C:\\orandea\\test\\aaa' - // to = 'C:\\orandea\\impl\\bbb' - // The output of the function should be: '..\\..\\impl\\bbb' + /** + * It will solve the relative path from `from` to `to`, for instancee + * from = 'C:\\orandea\\test\\aaa' + * to = 'C:\\orandea\\impl\\bbb' + * The output of the function should be: '..\\..\\impl\\bbb' + * @param {string} from + * @param {string} to + * @returns {string} + */ relative(from, to) { validateString(from, 'from'); validateString(to, 'to'); @@ -579,6 +611,10 @@ const win32 = { return path; }, + /** + * @param {string} path + * @returns {string} + */ dirname(path) { validateString(path, 'path'); const len = path.length; @@ -665,6 +701,11 @@ const win32 = { return path.slice(0, end); }, + /** + * @param {string} path + * @param {string} [ext] + * @returns {string} + */ basename(path, ext) { if (ext !== undefined) validateString(ext, 'ext'); @@ -748,6 +789,10 @@ const win32 = { return path.slice(start, end); }, + /** + * @param {string} path + * @returns {string} + */ extname(path) { validateString(path, 'path'); let start = 0; @@ -814,6 +859,16 @@ const win32 = { format: _format.bind(null, '\\'), + /** + * @param {string} path + * @returns {{ + * dir: string; + * root: string; + * base: string; + * name: string; + * ext: string; + * }} + */ parse(path) { validateString(path, 'path'); @@ -969,7 +1024,11 @@ const win32 = { }; const posix = { - // path.resolve([from ...], to) + /** + * path.resolve([from ...], to) + * @param {...string} args + * @returns {string} + */ resolve(...args) { let resolvedPath = ''; let resolvedAbsolute = false; @@ -1001,6 +1060,10 @@ const posix = { return resolvedPath.length > 0 ? resolvedPath : '.'; }, + /** + * @param {string} path + * @returns {string} + */ normalize(path) { validateString(path, 'path'); @@ -1025,11 +1088,19 @@ const posix = { return isAbsolute ? `/${path}` : path; }, + /** + * @param {string} path + * @returns {boolean} + */ isAbsolute(path) { validateString(path, 'path'); return path.length > 0 && path.charCodeAt(0) === CHAR_FORWARD_SLASH; }, + /** + * @param {...string} args + * @returns {string} + */ join(...args) { if (args.length === 0) return '.'; @@ -1049,6 +1120,11 @@ const posix = { return posix.normalize(joined); }, + /** + * @param {string} from + * @param {string} to + * @returns {string} + */ relative(from, to) { validateString(from, 'from'); validateString(to, 'to'); @@ -1124,6 +1200,10 @@ const posix = { return path; }, + /** + * @param {string} path + * @returns {string} + */ dirname(path) { validateString(path, 'path'); if (path.length === 0) @@ -1150,6 +1230,11 @@ const posix = { return path.slice(0, end); }, + /** + * @param {string} path + * @param {string} [ext] + * @returns {string} + */ basename(path, ext) { if (ext !== undefined) validateString(ext, 'ext'); @@ -1225,6 +1310,10 @@ const posix = { return path.slice(start, end); }, + /** + * @param {string} path + * @returns {string} + */ extname(path) { validateString(path, 'path'); let startDot = -1; @@ -1279,6 +1368,16 @@ const posix = { format: _format.bind(null, '/'), + /** + * @param {string} path + * @returns {{ + * dir: string; + * root: string; + * base: string; + * name: string; + * ext: string; + * }} + */ parse(path) { validateString(path, 'path'); diff --git a/lib/perf_hooks.js b/lib/perf_hooks.js index 683dec3a90a61d..3df0ea16cda1e5 100644 --- a/lib/perf_hooks.js +++ b/lib/perf_hooks.js @@ -75,7 +75,6 @@ const kInsertEntry = Symbol('insert-entry'); const kGetEntries = Symbol('get-entries'); const kIndex = Symbol('index'); const kMarks = Symbol('marks'); -const kCount = Symbol('count'); const observers = {}; const observerableTypes = [ @@ -166,50 +165,94 @@ function getMilestoneTimestamp(milestoneIdx) { } class PerformanceNodeTiming extends PerformanceEntry { - get name() { - return 'node'; - } + constructor() { + super(); - get entryType() { - return 'node'; - } + ObjectDefineProperties(this, { + name: { + enumerable: true, + configurable: true, + value: 'node' + }, - get startTime() { - return 0; - } + entryType: { + enumerable: true, + configurable: true, + value: 'node' + }, - get duration() { - return now() - timeOrigin; - } + startTime: { + enumerable: true, + configurable: true, + value: 0 + }, - get nodeStart() { - return getMilestoneTimestamp(NODE_PERFORMANCE_MILESTONE_NODE_START); - } + duration: { + enumerable: true, + configurable: true, + get() { + return now() - timeOrigin; + } + }, - get v8Start() { - return getMilestoneTimestamp(NODE_PERFORMANCE_MILESTONE_V8_START); - } + nodeStart: { + enumerable: true, + configurable: true, + get() { + return getMilestoneTimestamp(NODE_PERFORMANCE_MILESTONE_NODE_START); + } + }, - get environment() { - return getMilestoneTimestamp(NODE_PERFORMANCE_MILESTONE_ENVIRONMENT); - } + v8Start: { + enumerable: true, + configurable: true, + get() { + return getMilestoneTimestamp(NODE_PERFORMANCE_MILESTONE_V8_START); + } + }, - get loopStart() { - return getMilestoneTimestamp(NODE_PERFORMANCE_MILESTONE_LOOP_START); - } + environment: { + enumerable: true, + configurable: true, + get() { + return getMilestoneTimestamp(NODE_PERFORMANCE_MILESTONE_ENVIRONMENT); + } + }, - get loopExit() { - return getMilestoneTimestamp(NODE_PERFORMANCE_MILESTONE_LOOP_EXIT); - } + loopStart: { + enumerable: true, + configurable: true, + get() { + return getMilestoneTimestamp(NODE_PERFORMANCE_MILESTONE_LOOP_START); + } + }, - get bootstrapComplete() { - return getMilestoneTimestamp(NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE); - } + loopExit: { + enumerable: true, + configurable: true, + get() { + return getMilestoneTimestamp(NODE_PERFORMANCE_MILESTONE_LOOP_EXIT); + } + }, - get idleTime() { - return loopIdleTime(); - } + bootstrapComplete: { + enumerable: true, + configurable: true, + get() { + return getMilestoneTimestamp( + NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE); + } + }, + idleTime: { + enumerable: true, + configurable: true, + get() { + return loopIdleTime(); + } + } + }); + } [kInspect]() { return { name: 'node', @@ -236,11 +279,6 @@ class PerformanceObserverEntryList { writable: true, enumerable: false, value: {} - }, - [kCount]: { - writable: true, - enumerable: false, - value: 0 } }); L.init(this[kEntries]); @@ -249,11 +287,6 @@ class PerformanceObserverEntryList { [kInsertEntry](entry) { const item = { entry }; L.append(this[kEntries], item); - this[kCount]++; - } - - get length() { - return this[kCount]; } [kGetEntries](name, type) { diff --git a/lib/querystring.js b/lib/querystring.js index df954cfbd85211..afc150a89cb1d2 100644 --- a/lib/querystring.js +++ b/lib/querystring.js @@ -72,7 +72,12 @@ const unhexTable = new Int8Array([ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // ... 255 ]); -// A safe fast alternative to decodeURIComponent +/** + * A safe fast alternative to decodeURIComponent + * @param {string} s + * @param {boolean} decodeSpaces + * @returns {string} + */ function unescapeBuffer(s, decodeSpaces) { const out = Buffer.allocUnsafe(s.length); let index = 0; @@ -115,7 +120,11 @@ function unescapeBuffer(s, decodeSpaces) { return hasHex ? out.slice(0, outIndex) : out; } - +/** + * @param {string} s + * @param {boolean} decodeSpaces + * @returns {string} + */ function qsUnescape(s, decodeSpaces) { try { return decodeURIComponent(s); @@ -141,8 +150,13 @@ const noEscape = new Int8Array([ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 111 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0 // 112 - 127 ]); -// QueryString.escape() replaces encodeURIComponent() -// https://www.ecma-international.org/ecma-262/5.1/#sec-15.1.3.4 + +/** + * QueryString.escape() replaces encodeURIComponent() + * @see https://www.ecma-international.org/ecma-262/5.1/#sec-15.1.3.4 + * @param {any} str + * @returns {string} + */ function qsEscape(str) { if (typeof str !== 'string') { if (typeof str === 'object') @@ -154,17 +168,27 @@ function qsEscape(str) { return encodeStr(str, noEscape, hexTable); } +/** + * @param {string | number | bigint | boolean | symbol | undefined | null} v + * @returns {string} + */ function stringifyPrimitive(v) { if (typeof v === 'string') return v; if (typeof v === 'number' && isFinite(v)) return '' + v; + if (typeof v === 'bigint') + return '' + v; if (typeof v === 'boolean') return v ? 'true' : 'false'; return ''; } - +/** + * @param {string | number | bigint | boolean} v + * @param {(v: string) => string} encode + * @returns + */ function encodeStringified(v, encode) { if (typeof v === 'string') return (v.length ? encode(v) : ''); @@ -173,17 +197,30 @@ function encodeStringified(v, encode) { // escaping due to the inclusion of a '+' in the output return (MathAbs(v) < 1e21 ? '' + v : encode('' + v)); } + if (typeof v === 'bigint') + return '' + v; if (typeof v === 'boolean') return v ? 'true' : 'false'; return ''; } - +/** + * @param {string | number | boolean | null} v + * @param {(v: string) => string} encode + * @returns {string} + */ function encodeStringifiedCustom(v, encode) { return encode(stringifyPrimitive(v)); } - +/** + * @param {Record | null>} obj + * @param {string} [sep] + * @param {string} [eq] + * @param {{ encodeURIComponent?: (v: string) => string }} [options] + * @returns {string} + */ function stringify(obj, sep, eq, options) { sep = sep || '&'; eq = eq || '='; @@ -228,6 +265,10 @@ function stringify(obj, sep, eq, options) { return ''; } +/** + * @param {string} str + * @returns {number[]} + */ function charCodes(str) { if (str.length === 0) return []; if (str.length === 1) return [str.charCodeAt(0)]; @@ -259,7 +300,17 @@ function addKeyVal(obj, key, value, keyEncoded, valEncoded, decode) { } } -// Parse a key/val string. +/** + * Parse a key/val string. + * @param {string} qs + * @param {string} sep + * @param {string} eq + * @param {{ + * maxKeys?: number; + * decodeURIComponent?(v: string): string; + * }} [options] + * @returns {Record} + */ function parse(qs, sep, eq, options) { const obj = ObjectCreate(null); @@ -413,9 +464,14 @@ function parse(qs, sep, eq, options) { } -// v8 does not optimize functions with try-catch blocks, so we isolate them here -// to minimize the damage (Note: no longer true as of V8 5.4 -- but still will -// not be inlined). +/** + * V8 does not optimize functions with try-catch blocks, so we isolate them here + * to minimize the damage (Note: no longer true as of V8 5.4 -- but still will + * not be inlined). + * @param {string} s + * @param {(v: string) => string} decoder + * @returns {string} + */ function decodeStr(s, decoder) { try { return decoder(s); diff --git a/lib/readline.js b/lib/readline.js index a8f60ac9111e26..6fe456ec313493 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -252,9 +252,6 @@ function Interface(input, output, completer, terminal) { input.on('keypress', onkeypress); input.on('end', ontermend); - // Current line - this.line = ''; - this._setRawMode(true); this.terminal = true; @@ -270,6 +267,9 @@ function Interface(input, output, completer, terminal) { self.once('close', onSelfCloseWithTerminal); } + // Current line + this.line = ''; + input.resume(); } @@ -291,6 +291,11 @@ Interface.prototype.setPrompt = function(prompt) { }; +Interface.prototype.getPrompt = function() { + return this._prompt; +}; + + Interface.prototype._setRawMode = function(mode) { const wasInRawMode = this.input.isRaw; diff --git a/lib/repl.js b/lib/repl.js index adb20c653199ea..40d585af526ffd 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -58,6 +58,7 @@ const { Promise, PromiseRace, RegExp, + RegExpPrototypeTest, Set, StringPrototypeCharAt, StringPrototypeIncludes, @@ -267,6 +268,7 @@ function REPLServer(prompt, configurable: true }); + this.allowBlockingCompletions = !!options.allowBlockingCompletions; this.useColors = !!options.useColors; this._domain = options.domain || domain.create(); this.useGlobal = !!useGlobal; @@ -608,7 +610,7 @@ function REPLServer(prompt, errStack = self.writer(e); // Remove one line error braces to keep the old style in place. - if (errStack[errStack.length - 1] === ']') { + if (errStack[0] === '[' && errStack[errStack.length - 1] === ']') { errStack = errStack.slice(1, -1); } } @@ -1186,7 +1188,8 @@ function complete(line, callback) { if (completeOn.length) { filter = completeOn; } - } else if (requireRE.test(line)) { + } else if (RegExpPrototypeTest(requireRE, line) && + this.allowBlockingCompletions) { // require('...') const extensions = ObjectKeys(this.context.require.extensions); const indexes = extensions.map((extension) => `index${extension}`); @@ -1244,7 +1247,8 @@ function complete(line, callback) { if (!subdir) { completionGroups.push(_builtinLibs); } - } else if (fsAutoCompleteRE.test(line)) { + } else if (RegExpPrototypeTest(fsAutoCompleteRE, line) && + this.allowBlockingCompletions) { [completionGroups, completeOn] = completeFSFunctions(line); // Handle variable member lookup. // We support simple chained expressions like the following (no function diff --git a/lib/timers.js b/lib/timers.js index de50e56a11909a..6732ae06bc0b2e 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -24,8 +24,8 @@ const { ObjectCreate, MathTrunc, - Promise, - SymbolToPrimitive + Object, + SymbolToPrimitive, } = primordials; const { @@ -36,6 +36,7 @@ const L = require('internal/linkedlist'); const { async_id_symbol, Timeout, + Immediate, decRefCount, immediateInfoFields: { kCount, @@ -43,7 +44,6 @@ const { }, kRefed, kHasPrimitive, - initAsyncResource, getTimerDuration, timerListMap, timerListQueue, @@ -61,6 +61,8 @@ let debug = require('internal/util/debuglog').debuglog('timer', (fn) => { }); const { validateCallback } = require('internal/validators'); +let timersPromises; + const { destroyHooksExist, // The needed emit*() functions. @@ -129,7 +131,6 @@ function enroll(item, msecs) { * DOM-style timers */ - function setTimeout(callback, after, arg1, arg2, arg3) { validateCallback(callback); @@ -160,13 +161,14 @@ function setTimeout(callback, after, arg1, arg2, arg3) { return timeout; } -setTimeout[customPromisify] = function(after, value) { - const args = value !== undefined ? [value] : value; - return new Promise((resolve) => { - const timeout = new Timeout(resolve, after, args, false, true); - insert(timeout, timeout._idleTimeout); - }); -}; +Object.defineProperty(setTimeout, customPromisify, { + enumerable: true, + get() { + if (!timersPromises) + timersPromises = require('internal/timers/promises'); + return timersPromises.setTimeout; + } +}); function clearTimeout(timer) { if (timer && timer._onTimeout) { @@ -234,46 +236,6 @@ Timeout.prototype[SymbolToPrimitive] = function() { return id; }; -const Immediate = class Immediate { - constructor(callback, args) { - this._idleNext = null; - this._idlePrev = null; - this._onImmediate = callback; - this._argv = args; - this._destroyed = false; - this[kRefed] = false; - - initAsyncResource(this, 'Immediate'); - - this.ref(); - immediateInfo[kCount]++; - - immediateQueue.append(this); - } - - ref() { - if (this[kRefed] === false) { - this[kRefed] = true; - if (immediateInfo[kRefCount]++ === 0) - toggleImmediateRef(true); - } - return this; - } - - unref() { - if (this[kRefed] === true) { - this[kRefed] = false; - if (--immediateInfo[kRefCount] === 0) - toggleImmediateRef(false); - } - return this; - } - - hasRef() { - return !!this[kRefed]; - } -}; - function setImmediate(callback, arg1, arg2, arg3) { validateCallback(callback); @@ -300,9 +262,15 @@ function setImmediate(callback, arg1, arg2, arg3) { return new Immediate(callback, args); } -setImmediate[customPromisify] = function(value) { - return new Promise((resolve) => new Immediate(resolve, [value])); -}; +Object.defineProperty(setImmediate, customPromisify, { + enumerable: true, + get() { + if (!timersPromises) + timersPromises = require('internal/timers/promises'); + return timersPromises.setImmediate; + } +}); + function clearImmediate(immediate) { if (!immediate || immediate._destroyed) @@ -315,7 +283,7 @@ function clearImmediate(immediate) { toggleImmediateRef(false); immediate[kRefed] = null; - if (destroyHooksExist()) { + if (destroyHooksExist() && immediate[async_id_symbol] !== undefined) { emitDestroy(immediate[async_id_symbol]); } diff --git a/lib/tty.js b/lib/tty.js index 583cc1329830c9..1828077c0adc0c 100644 --- a/lib/tty.js +++ b/lib/tty.js @@ -40,7 +40,8 @@ const { let readline; function isatty(fd) { - return NumberIsInteger(fd) && fd >= 0 && isTTY(fd); + return NumberIsInteger(fd) && fd >= 0 && fd <= 2147483647 && + isTTY(fd); } function ReadStream(fd, options) { diff --git a/lib/url.js b/lib/url.js index cc36216f9eb916..49f99a8b5fa621 100644 --- a/lib/url.js +++ b/lib/url.js @@ -26,6 +26,7 @@ const { ObjectCreate, ObjectKeys, SafeSet, + StringPrototypeCharCodeAt, } = primordials; const { toASCII } = require('internal/idna'); @@ -156,6 +157,14 @@ function urlParse(url, parseQueryString, slashesDenoteHost) { return urlObject; } +function isIpv6Hostname(hostname) { + return ( + StringPrototypeCharCodeAt(hostname, 0) === CHAR_LEFT_SQUARE_BRACKET && + StringPrototypeCharCodeAt(hostname, hostname.length - 1) === + CHAR_RIGHT_SQUARE_BRACKET + ); +} + Url.prototype.parse = function parse(url, parseQueryString, slashesDenoteHost) { validateString(url, 'url'); @@ -364,8 +373,7 @@ Url.prototype.parse = function parse(url, parseQueryString, slashesDenoteHost) { // If hostname begins with [ and ends with ] // assume that it's an IPv6 address. - const ipv6Hostname = hostname.charCodeAt(0) === CHAR_LEFT_SQUARE_BRACKET && - hostname.charCodeAt(hostname.length - 1) === CHAR_RIGHT_SQUARE_BRACKET; + const ipv6Hostname = isIpv6Hostname(hostname); // validate a little. if (!ipv6Hostname) { @@ -590,7 +598,7 @@ Url.prototype.format = function format() { host = auth + this.host; } else if (this.hostname) { host = auth + ( - this.hostname.includes(':') ? + this.hostname.includes(':') && !isIpv6Hostname(this.hostname) ? '[' + this.hostname + ']' : this.hostname ); diff --git a/lib/util.js b/lib/util.js index 1414c0d11d58a8..63a20d581cdd5a 100644 --- a/lib/util.js +++ b/lib/util.js @@ -57,6 +57,7 @@ const types = require('internal/util/types'); const { deprecate, + getSystemErrorMap, getSystemErrorName: internalErrorName, promisify } = require('internal/util'); @@ -244,6 +245,7 @@ module.exports = { deprecate, format, formatWithOptions, + getSystemErrorMap, getSystemErrorName, inherits, inspect, diff --git a/lib/v8.js b/lib/v8.js index 56e3e6056214ca..ef84a8b406771d 100644 --- a/lib/v8.js +++ b/lib/v8.js @@ -34,8 +34,8 @@ const { const { Buffer } = require('buffer'); const { validateString } = require('internal/validators'); const { - Serializer: _Serializer, - Deserializer: _Deserializer + Serializer, + Deserializer } = internalBinding('serdes'); const assert = require('internal/assert'); const { copy } = internalBinding('buffer'); @@ -63,13 +63,6 @@ function getHeapSnapshot() { return new HeapSnapshotStream(handle); } -// Calling exposed c++ functions directly throws exception as it expected to be -// called with new operator and caused an assert to fire. -// Creating JS wrapper so that it gets caught at JS layer. -class Serializer extends _Serializer { } - -class Deserializer extends _Deserializer { } - const { cachedDataVersionTag, setFlagsFromString: _setFlagsFromString, diff --git a/node.gyp b/node.gyp index f18a0d58a84c79..61b789ec74ab55 100644 --- a/node.gyp +++ b/node.gyp @@ -47,6 +47,7 @@ 'lib/constants.js', 'lib/crypto.js', 'lib/cluster.js', + 'lib/diagnostics_channel.js', 'lib/dgram.js', 'lib/dns.js', 'lib/domain.js', @@ -95,6 +96,7 @@ 'lib/wasi.js', 'lib/worker_threads.js', 'lib/zlib.js', + 'lib/internal/abort_controller.js', 'lib/internal/assert.js', 'lib/internal/assert/assertion_error.js', 'lib/internal/assert/calltracker.js', @@ -198,6 +200,7 @@ 'lib/internal/source_map/source_map.js', 'lib/internal/source_map/source_map_cache.js', 'lib/internal/test/binding.js', + 'lib/internal/timers/promises.js', 'lib/internal/timers.js', 'lib/internal/tls.js', 'lib/internal/trace_events_async_hooks.js', @@ -636,6 +639,7 @@ 'src/string_decoder.cc', 'src/tcp_wrap.cc', 'src/timers.cc', + 'src/timer_wrap.cc', 'src/tracing/agent.cc', 'src/tracing/node_trace_buffer.cc', 'src/tracing/node_trace_writer.cc', @@ -717,6 +721,7 @@ 'src/node_union_bytes.h', 'src/node_url.h', 'src/node_version.h', + 'src/node_v8.h', 'src/node_v8_platform-inl.h', 'src/node_wasi.h', 'src/node_watchdog.h', @@ -740,6 +745,7 @@ 'src/tracing/trace_event.h', 'src/tracing/trace_event_common.h', 'src/tracing/traced_value.h', + 'src/timer_wrap.h', 'src/tty_wrap.h', 'src/udp_wrap.h', 'src/util.h', diff --git a/onboarding.md b/onboarding.md index 8036e459f5122a..a70f2157eaefed 100644 --- a/onboarding.md +++ b/onboarding.md @@ -114,7 +114,7 @@ The project has two venues for real-time discussion: organization (not just Collaborators on Node.js core) have access. Its contents should not be shared externally. * You can find the full moderation policy - [here](https://github.com/nodejs/admin/blob/master/Moderation-Policy.md). + [here](https://github.com/nodejs/admin/blob/HEAD/Moderation-Policy.md). ## Reviewing PRs @@ -204,11 +204,14 @@ needs to be pointed out separately during the onboarding. ## Exercise: Make a PR adding yourself to the README * Example: - - * For raw commit message: `git log ce986de829457c39257cd205067602e765768fb0 - -1` + + * For raw commit message: + `git show --format=%Bb58fe52692659c0bc25ddbe6afa7f4ae2c7f14a8` * Collaborators are in alphabetical order by GitHub username. * Optionally, include your personal pronouns. +* Add the `Fixes: ` to the commit message + so that when the commit lands, the nomination issue url will be + automatically closed. * Label your pull request with the `doc`, `notable-change`, and `fast-track` labels. * Run CI on the PR. Use the `node-test-pull-request` CI task. @@ -244,12 +247,12 @@ needs to be pointed out separately during the onboarding. including accommodations, transportation, visa fees, etc. if needed. Check out the [summit](https://github.com/nodejs/summit) repository for details. -[Code of Conduct]: https://github.com/nodejs/admin/blob/master/CODE_OF_CONDUCT.md +[Code of Conduct]: https://github.com/nodejs/admin/blob/HEAD/CODE_OF_CONDUCT.md [Landing Pull Requests]: doc/guides/collaborator-guide.md#landing-pull-requests [Publicizing or hiding organization membership]: https://help.github.com/articles/publicizing-or-hiding-organization-membership/ [`author-ready`]: doc/guides/collaborator-guide.md#author-ready-pull-requests [`core-validate-commit`]: https://github.com/nodejs/core-validate-commit -[`git-node`]: https://github.com/nodejs/node-core-utils/blob/master/docs/git-node.md +[`git-node`]: https://github.com/nodejs/node-core-utils/blob/HEAD/docs/git-node.md [`node-core-utils`]: https://github.com/nodejs/node-core-utils [set up the credentials]: https://github.com/nodejs/node-core-utils#setting-up-credentials [two-factor authentication]: https://help.github.com/articles/securing-your-account-with-two-factor-authentication-2fa/ diff --git a/src/README.md b/src/README.md index 2be97e28b8fbea..7d82b5a3e7530f 100644 --- a/src/README.md +++ b/src/README.md @@ -426,7 +426,7 @@ that state is through the use of `Environment::AddBindingData`, which gives binding functions access to an object for storing such state. That object is always a [`BaseObject`][]. -Its class needs to have a static `binding_data_name` field based on a +Its class needs to have a static `type_name` field based on a constant string, in order to disambiguate it from other classes of this type, and which could e.g. match the binding’s name (in the example above, that would be `cares_wrap`). @@ -437,7 +437,7 @@ class BindingData : public BaseObject { public: BindingData(Environment* env, Local obj) : BaseObject(env, obj) {} - static constexpr FastStringKey binding_data_name { "http_parser" }; + static constexpr FastStringKey type_name { "http_parser" }; std::vector parser_buffer; bool parser_buffer_in_use = false; @@ -761,7 +761,7 @@ reference to its associated JavaScript object. This can be useful when one `BaseObject` refers to another `BaseObject` and wants to make sure it stays alive during the lifetime of that reference. -A `BaseObject` can be “detached” throught the `BaseObject::Detach()` method. +A `BaseObject` can be “detached” through the `BaseObject::Detach()` method. In this case, it will be deleted once the last `BaseObjectPtr` referring to it is destroyed. There must be at least one such pointer when `Detach()` is called. This can be useful when one `BaseObject` fully owns another diff --git a/src/api/callback.cc b/src/api/callback.cc index 3d4f91a866ea39..2a1c6301414176 100644 --- a/src/api/callback.cc +++ b/src/api/callback.cc @@ -61,6 +61,8 @@ InternalCallbackScope::InternalCallbackScope(Environment* env, // If you hit this assertion, you forgot to enter the v8::Context first. CHECK_EQ(Environment::GetCurrent(env->isolate()), env); + env->isolate()->SetIdle(false); + env->async_hooks()->push_async_context( async_context_.async_id, async_context_.trigger_async_id, object); @@ -82,6 +84,8 @@ void InternalCallbackScope::Close() { if (closed_) return; closed_ = true; + auto idle = OnScopeLeave([&]() { env_->isolate()->SetIdle(true); }); + if (!env_->can_call_into_js()) return; auto perform_stopping_check = [&]() { if (env_->is_stopping()) { diff --git a/src/base64-inl.h b/src/base64-inl.h index 9132faa614ea2b..57c11bde2d7af2 100644 --- a/src/base64-inl.h +++ b/src/base64-inl.h @@ -3,6 +3,7 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#include "base64.h" #include "util.h" namespace node { @@ -91,6 +92,90 @@ size_t base64_decode_fast(char* const dst, const size_t dstlen, return k; } + +template +size_t base64_decoded_size(const TypeName* src, size_t size) { + // 1-byte input cannot be decoded + if (size < 2) + return 0; + + if (src[size - 1] == '=') { + size--; + if (src[size - 1] == '=') + size--; + } + return base64_decoded_size_fast(size); +} + + +template +size_t base64_decode(char* const dst, const size_t dstlen, + const TypeName* const src, const size_t srclen) { + const size_t decoded_size = base64_decoded_size(src, srclen); + return base64_decode_fast(dst, dstlen, src, srclen, decoded_size); +} + + +inline size_t base64_encode(const char* src, + size_t slen, + char* dst, + size_t dlen) { + // We know how much we'll write, just make sure that there's space. + CHECK(dlen >= base64_encoded_size(slen) && + "not enough space provided for base64 encode"); + + dlen = base64_encoded_size(slen); + + unsigned a; + unsigned b; + unsigned c; + unsigned i; + unsigned k; + unsigned n; + + static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + i = 0; + k = 0; + n = slen / 3 * 3; + + while (i < n) { + a = src[i + 0] & 0xff; + b = src[i + 1] & 0xff; + c = src[i + 2] & 0xff; + + dst[k + 0] = table[a >> 2]; + dst[k + 1] = table[((a & 3) << 4) | (b >> 4)]; + dst[k + 2] = table[((b & 0x0f) << 2) | (c >> 6)]; + dst[k + 3] = table[c & 0x3f]; + + i += 3; + k += 4; + } + + switch (slen - n) { + case 1: + a = src[i + 0] & 0xff; + dst[k + 0] = table[a >> 2]; + dst[k + 1] = table[(a & 3) << 4]; + dst[k + 2] = '='; + dst[k + 3] = '='; + break; + case 2: + a = src[i + 0] & 0xff; + b = src[i + 1] & 0xff; + dst[k + 0] = table[a >> 2]; + dst[k + 1] = table[((a & 3) << 4) | (b >> 4)]; + dst[k + 2] = table[(b & 0x0f) << 2]; + dst[k + 3] = '='; + break; + } + + return dlen; +} + } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/src/base64.h b/src/base64.h index 24fc57e1d60385..e58baa5f6e4692 100644 --- a/src/base64.h +++ b/src/base64.h @@ -4,7 +4,6 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #include "util.h" -#include "base64-inl.h" #include #include @@ -24,85 +23,16 @@ static inline constexpr size_t base64_decoded_size_fast(size_t size) { inline uint32_t ReadUint32BE(const unsigned char* p); template -size_t base64_decoded_size(const TypeName* src, size_t size) { - // 1-byte input cannot be decoded - if (size < 2) - return 0; - - if (src[size - 1] == '=') { - size--; - if (src[size - 1] == '=') - size--; - } - return base64_decoded_size_fast(size); -} +size_t base64_decoded_size(const TypeName* src, size_t size); template size_t base64_decode(char* const dst, const size_t dstlen, - const TypeName* const src, const size_t srclen) { - const size_t decoded_size = base64_decoded_size(src, srclen); - return base64_decode_fast(dst, dstlen, src, srclen, decoded_size); -} + const TypeName* const src, const size_t srclen); -static size_t base64_encode(const char* src, +inline size_t base64_encode(const char* src, size_t slen, char* dst, - size_t dlen) { - // We know how much we'll write, just make sure that there's space. - CHECK(dlen >= base64_encoded_size(slen) && - "not enough space provided for base64 encode"); - - dlen = base64_encoded_size(slen); - - unsigned a; - unsigned b; - unsigned c; - unsigned i; - unsigned k; - unsigned n; - - static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; - - i = 0; - k = 0; - n = slen / 3 * 3; - - while (i < n) { - a = src[i + 0] & 0xff; - b = src[i + 1] & 0xff; - c = src[i + 2] & 0xff; - - dst[k + 0] = table[a >> 2]; - dst[k + 1] = table[((a & 3) << 4) | (b >> 4)]; - dst[k + 2] = table[((b & 0x0f) << 2) | (c >> 6)]; - dst[k + 3] = table[c & 0x3f]; - - i += 3; - k += 4; - } - - switch (slen - n) { - case 1: - a = src[i + 0] & 0xff; - dst[k + 0] = table[a >> 2]; - dst[k + 1] = table[(a & 3) << 4]; - dst[k + 2] = '='; - dst[k + 3] = '='; - break; - case 2: - a = src[i + 0] & 0xff; - b = src[i + 1] & 0xff; - dst[k + 0] = table[a >> 2]; - dst[k + 1] = table[((a & 3) << 4) | (b >> 4)]; - dst[k + 2] = table[(b & 0x0f) << 2]; - dst[k + 3] = '='; - break; - } - - return dlen; -} + size_t dlen); } // namespace node diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc index cc7ad316d2b0ff..11206b16dd8e41 100644 --- a/src/cares_wrap.cc +++ b/src/cares_wrap.cc @@ -19,15 +19,17 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -#define CARES_STATICLIB -#include "ares.h" #include "async_wrap-inl.h" +#include "base_object-inl.h" #include "base64-inl.h" +#include "cares_wrap.h" #include "env-inl.h" #include "memory_tracker-inl.h" #include "node.h" +#include "node_errors.h" #include "req_wrap-inl.h" #include "util-inl.h" +#include "v8.h" #include "uv.h" #include @@ -36,24 +38,6 @@ #include #include -#ifdef __POSIX__ -# include -#endif // __POSIX__ - -#if defined(__ANDROID__) || \ - defined(__MINGW32__) || \ - defined(__OpenBSD__) || \ - defined(_MSC_VER) - -# include -#else -# include -#endif - -#if defined(__OpenBSD__) -# define AI_V4MAPPED 0 -#endif - namespace node { namespace cares_wrap { @@ -80,190 +64,8 @@ inline uint16_t cares_get_16bit(const unsigned char* p) { return static_cast(p[0] << 8U) | (static_cast(p[1])); } -const int ns_t_cname_or_a = -1; - -#define DNS_ESETSRVPENDING -1000 -inline const char* ToErrorCodeString(int status) { - switch (status) { -#define V(code) case ARES_##code: return #code; - V(EADDRGETNETWORKPARAMS) - V(EBADFAMILY) - V(EBADFLAGS) - V(EBADHINTS) - V(EBADNAME) - V(EBADQUERY) - V(EBADRESP) - V(EBADSTR) - V(ECANCELLED) - V(ECONNREFUSED) - V(EDESTRUCTION) - V(EFILE) - V(EFORMERR) - V(ELOADIPHLPAPI) - V(ENODATA) - V(ENOMEM) - V(ENONAME) - V(ENOTFOUND) - V(ENOTIMP) - V(ENOTINITIALIZED) - V(EOF) - V(EREFUSED) - V(ESERVFAIL) - V(ETIMEOUT) -#undef V - } - - return "UNKNOWN_ARES_ERROR"; -} - -class ChannelWrap; - -struct node_ares_task : public MemoryRetainer { - ChannelWrap* channel; - ares_socket_t sock; - uv_poll_t poll_watcher; - - inline void MemoryInfo(MemoryTracker* tracker) const override; - SET_MEMORY_INFO_NAME(node_ares_task) - SET_SELF_SIZE(node_ares_task) -}; - -struct TaskHash { - size_t operator()(node_ares_task* a) const { - return std::hash()(a->sock); - } -}; - -struct TaskEqual { - inline bool operator()(node_ares_task* a, node_ares_task* b) const { - return a->sock == b->sock; - } -}; - -using node_ares_task_list = - std::unordered_set; - -class ChannelWrap : public AsyncWrap { - public: - ChannelWrap(Environment* env, Local object, int timeout); - ~ChannelWrap() override; - - static void New(const FunctionCallbackInfo& args); - - void Setup(); - void EnsureServers(); - void StartTimer(); - void CloseTimer(); - - void ModifyActivityQueryCount(int count); - - inline uv_timer_t* timer_handle() { return timer_handle_; } - inline ares_channel cares_channel() { return channel_; } - inline void set_query_last_ok(bool ok) { query_last_ok_ = ok; } - inline void set_is_servers_default(bool is_default) { - is_servers_default_ = is_default; - } - inline int active_query_count() { return active_query_count_; } - inline node_ares_task_list* task_list() { return &task_list_; } - - void MemoryInfo(MemoryTracker* tracker) const override { - if (timer_handle_ != nullptr) - tracker->TrackField("timer_handle", *timer_handle_); - tracker->TrackField("task_list", task_list_, "node_ares_task_list"); - } - - SET_MEMORY_INFO_NAME(ChannelWrap) - SET_SELF_SIZE(ChannelWrap) - - static void AresTimeout(uv_timer_t* handle); - - private: - uv_timer_t* timer_handle_; - ares_channel channel_; - bool query_last_ok_; - bool is_servers_default_; - bool library_inited_; - int timeout_; - int active_query_count_; - node_ares_task_list task_list_; -}; - -ChannelWrap::ChannelWrap(Environment* env, - Local object, - int timeout) - : AsyncWrap(env, object, PROVIDER_DNSCHANNEL), - timer_handle_(nullptr), - channel_(nullptr), - query_last_ok_(true), - is_servers_default_(true), - library_inited_(false), - timeout_(timeout), - active_query_count_(0) { - MakeWeak(); - - Setup(); -} - -void ChannelWrap::New(const FunctionCallbackInfo& args) { - CHECK(args.IsConstructCall()); - CHECK_EQ(args.Length(), 1); - CHECK(args[0]->IsInt32()); - const int timeout = args[0].As()->Value(); - Environment* env = Environment::GetCurrent(args); - new ChannelWrap(env, args.This(), timeout); -} - -class GetAddrInfoReqWrap : public ReqWrap { - public: - GetAddrInfoReqWrap(Environment* env, - Local req_wrap_obj, - bool verbatim); - - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(GetAddrInfoReqWrap) - SET_SELF_SIZE(GetAddrInfoReqWrap) - - bool verbatim() const { return verbatim_; } - - private: - const bool verbatim_; -}; - -GetAddrInfoReqWrap::GetAddrInfoReqWrap(Environment* env, - Local req_wrap_obj, - bool verbatim) - : ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_GETADDRINFOREQWRAP) - , verbatim_(verbatim) { -} - - -class GetNameInfoReqWrap : public ReqWrap { - public: - GetNameInfoReqWrap(Environment* env, Local req_wrap_obj); - - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(GetNameInfoReqWrap) - SET_SELF_SIZE(GetNameInfoReqWrap) -}; - -GetNameInfoReqWrap::GetNameInfoReqWrap(Environment* env, - Local req_wrap_obj) - : ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_GETNAMEINFOREQWRAP) { -} - - -/* This is called once per second by loop->timer. It is used to constantly */ -/* call back into c-ares for possibly processing timeouts. */ -void ChannelWrap::AresTimeout(uv_timer_t* handle) { - ChannelWrap* channel = static_cast(handle->data); - CHECK_EQ(channel->timer_handle(), handle); - CHECK_EQ(false, channel->task_list()->empty()); - ares_process_fd(channel->cares_channel(), ARES_SOCKET_BAD, ARES_SOCKET_BAD); -} - - void ares_poll_cb(uv_poll_t* watcher, int status, int events) { - node_ares_task* task = ContainerOf(&node_ares_task::poll_watcher, watcher); + NodeAresTask* task = ContainerOf(&NodeAresTask::poll_watcher, watcher); ChannelWrap* channel = task->channel; /* Reset the idle timer */ @@ -284,41 +86,17 @@ void ares_poll_cb(uv_poll_t* watcher, int status, int events) { void ares_poll_close_cb(uv_poll_t* watcher) { - node_ares_task* task = ContainerOf(&node_ares_task::poll_watcher, watcher); - delete task; -} - -void node_ares_task::MemoryInfo(MemoryTracker* tracker) const { - tracker->TrackField("channel", channel); -} - -/* Allocates and returns a new node_ares_task */ -node_ares_task* ares_task_create(ChannelWrap* channel, ares_socket_t sock) { - auto task = new node_ares_task(); - - task->channel = channel; - task->sock = sock; - - if (uv_poll_init_socket(channel->env()->event_loop(), - &task->poll_watcher, sock) < 0) { - /* This should never happen. */ - delete task; - return nullptr; - } - - return task; + std::unique_ptr free_me( + ContainerOf(&NodeAresTask::poll_watcher, watcher)); } /* Callback from ares when socket operation is started */ -void ares_sockstate_cb(void* data, - ares_socket_t sock, - int read, - int write) { +void ares_sockstate_cb(void* data, ares_socket_t sock, int read, int write) { ChannelWrap* channel = static_cast(data); - node_ares_task* task; + NodeAresTask* task; - node_ares_task lookup_task; + NodeAresTask lookup_task; lookup_task.sock = sock; auto it = channel->task_list()->find(&lookup_task); @@ -329,7 +107,7 @@ void ares_sockstate_cb(void* data, /* New socket */ channel->StartTimer(); - task = ares_task_create(channel, sock); + task = NodeAresTask::Create(channel, sock); if (task == nullptr) { /* This should never happen unless we're out of memory or something */ /* is seriously wrong. The socket won't be polled, but the query will */ @@ -362,414 +140,55 @@ void ares_sockstate_cb(void* data, } } - -Local HostentToNames(Environment* env, - struct hostent* host, - Local append_to = Local()) { +Local HostentToNames(Environment* env, struct hostent* host) { EscapableHandleScope scope(env->isolate()); - auto context = env->context(); - bool append = !append_to.IsEmpty(); - Local names = append ? append_to : Array::New(env->isolate()); - size_t offset = names->Length(); - - for (uint32_t i = 0; host->h_aliases[i] != nullptr; ++i) { - Local address = OneByteString(env->isolate(), host->h_aliases[i]); - names->Set(context, i + offset, address).Check(); - } - - return append ? names : scope.Escape(names); -} - -void safe_free_hostent(struct hostent* host) { - int idx; - - if (host->h_addr_list != nullptr) { - idx = 0; - while (host->h_addr_list[idx]) { - free(host->h_addr_list[idx++]); - } - free(host->h_addr_list); - host->h_addr_list = nullptr; - } - - if (host->h_aliases != nullptr) { - idx = 0; - while (host->h_aliases[idx]) { - free(host->h_aliases[idx++]); - } - free(host->h_aliases); - host->h_aliases = nullptr; - } - free(host->h_name); - free(host); -} + std::vector> names; -void cares_wrap_hostent_cpy(struct hostent* dest, const struct hostent* src) { - dest->h_addr_list = nullptr; - dest->h_addrtype = 0; - dest->h_aliases = nullptr; - dest->h_length = 0; - dest->h_name = nullptr; + for (uint32_t i = 0; host->h_aliases[i] != nullptr; ++i) + names.emplace_back(OneByteString(env->isolate(), host->h_aliases[i])); - /* copy `h_name` */ - size_t name_size = strlen(src->h_name) + 1; - dest->h_name = node::Malloc(name_size); - memcpy(dest->h_name, src->h_name, name_size); + Local ret = Array::New(env->isolate(), names.data(), names.size()); - /* copy `h_aliases` */ - size_t alias_count; - for (alias_count = 0; - src->h_aliases[alias_count] != nullptr; - alias_count++) { - } - - dest->h_aliases = node::Malloc(alias_count + 1); - for (size_t i = 0; i < alias_count; i++) { - const size_t cur_alias_size = strlen(src->h_aliases[i]) + 1; - dest->h_aliases[i] = node::Malloc(cur_alias_size); - memcpy(dest->h_aliases[i], src->h_aliases[i], cur_alias_size); - } - dest->h_aliases[alias_count] = nullptr; - - /* copy `h_addr_list` */ - size_t list_count; - for (list_count = 0; - src->h_addr_list[list_count] != nullptr; - list_count++) { - } - - dest->h_addr_list = node::Malloc(list_count + 1); - for (size_t i = 0; i < list_count; i++) { - dest->h_addr_list[i] = node::Malloc(src->h_length); - memcpy(dest->h_addr_list[i], src->h_addr_list[i], src->h_length); - } - dest->h_addr_list[list_count] = nullptr; - - /* work after work */ - dest->h_length = src->h_length; - dest->h_addrtype = src->h_addrtype; + return scope.Escape(ret); } -class QueryWrap; - -void ChannelWrap::Setup() { - struct ares_options options; - memset(&options, 0, sizeof(options)); - options.flags = ARES_FLAG_NOCHECKRESP; - options.sock_state_cb = ares_sockstate_cb; - options.sock_state_cb_data = this; - options.timeout = timeout_; - - int r; - if (!library_inited_) { - Mutex::ScopedLock lock(ares_library_mutex); - // Multiple calls to ares_library_init() increase a reference counter, - // so this is a no-op except for the first call to it. - r = ares_library_init(ARES_LIB_INIT_ALL); - if (r != ARES_SUCCESS) - return env()->ThrowError(ToErrorCodeString(r)); - } - - /* We do the call to ares_init_option for caller. */ - const int optmask = - ARES_OPT_FLAGS | ARES_OPT_TIMEOUTMS | ARES_OPT_SOCK_STATE_CB; - r = ares_init_options(&channel_, &options, optmask); - - if (r != ARES_SUCCESS) { - Mutex::ScopedLock lock(ares_library_mutex); - ares_library_cleanup(); - return env()->ThrowError(ToErrorCodeString(r)); - } - - library_inited_ = true; -} - -void ChannelWrap::StartTimer() { - if (timer_handle_ == nullptr) { - timer_handle_ = new uv_timer_t(); - timer_handle_->data = static_cast(this); - uv_timer_init(env()->event_loop(), timer_handle_); - } else if (uv_is_active(reinterpret_cast(timer_handle_))) { - return; - } - int timeout = timeout_; - if (timeout == 0) timeout = 1; - if (timeout < 0 || timeout > 1000) timeout = 1000; - uv_timer_start(timer_handle_, AresTimeout, timeout, timeout); -} - -void ChannelWrap::CloseTimer() { - if (timer_handle_ == nullptr) - return; - - env()->CloseHandle(timer_handle_, [](uv_timer_t* handle) { delete handle; }); - timer_handle_ = nullptr; -} - -ChannelWrap::~ChannelWrap() { - ares_destroy(channel_); - - if (library_inited_) { - Mutex::ScopedLock lock(ares_library_mutex); - // This decreases the reference counter increased by ares_library_init(). - ares_library_cleanup(); - } - - CloseTimer(); -} - - -void ChannelWrap::ModifyActivityQueryCount(int count) { - active_query_count_ += count; - CHECK_GE(active_query_count_, 0); -} - - -/** - * This function is to check whether current servers are fallback servers - * when cares initialized. - * - * The fallback servers of cares is [ "127.0.0.1" ] with no user additional - * setting. - */ -void ChannelWrap::EnsureServers() { - /* if last query is OK or servers are set by user self, do not check */ - if (query_last_ok_ || !is_servers_default_) { - return; - } - - ares_addr_port_node* servers = nullptr; - - ares_get_servers_ports(channel_, &servers); - - /* if no server or multi-servers, ignore */ - if (servers == nullptr) return; - if (servers->next != nullptr) { - ares_free_data(servers); - is_servers_default_ = false; - return; - } +Local HostentToNames(Environment* env, + struct hostent* host, + Local names) { + size_t offset = names->Length(); - /* if the only server is not 127.0.0.1, ignore */ - if (servers[0].family != AF_INET || - servers[0].addr.addr4.s_addr != htonl(INADDR_LOOPBACK) || - servers[0].tcp_port != 0 || - servers[0].udp_port != 0) { - ares_free_data(servers); - is_servers_default_ = false; - return; + for (uint32_t i = 0; host->h_aliases[i] != nullptr; ++i) { + names->Set( + env->context(), + i + offset, + OneByteString(env->isolate(), host->h_aliases[i])).Check(); } - ares_free_data(servers); - servers = nullptr; - - /* destroy channel and reset channel */ - ares_destroy(channel_); - - CloseTimer(); - Setup(); + return names; } - -class QueryWrap : public AsyncWrap { - public: - QueryWrap(ChannelWrap* channel, Local req_wrap_obj, const char* name) - : AsyncWrap(channel->env(), req_wrap_obj, AsyncWrap::PROVIDER_QUERYWRAP), - channel_(channel), - trace_name_(name) { - } - - ~QueryWrap() override { - CHECK_EQ(false, persistent().IsEmpty()); - - // Let Callback() know that this object no longer exists. - if (callback_ptr_ != nullptr) - *callback_ptr_ = nullptr; - } - - // Subclasses should implement the appropriate Send method. - virtual int Send(const char* name) { - UNREACHABLE(); - return 0; - } - - virtual int Send(const char* name, int family) { - UNREACHABLE(); - return 0; - } - - protected: - void AresQuery(const char* name, - int dnsclass, - int type) { - channel_->EnsureServers(); - TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( - TRACING_CATEGORY_NODE2(dns, native), trace_name_, this, - "name", TRACE_STR_COPY(name)); - ares_query(channel_->cares_channel(), name, dnsclass, type, Callback, - MakeCallbackPointer()); - } - - struct ResponseData { - int status; - bool is_host; - DeleteFnPtr host; - MallocedBuffer buf; - }; - - void AfterResponse() { - CHECK(response_data_); - - const int status = response_data_->status; - - if (status != ARES_SUCCESS) { - ParseError(status); - } else if (!response_data_->is_host) { - Parse(response_data_->buf.data, response_data_->buf.size); - } else { - Parse(response_data_->host.get()); - } - } - - void* MakeCallbackPointer() { - CHECK_NULL(callback_ptr_); - callback_ptr_ = new QueryWrap*(this); - return callback_ptr_; - } - - static QueryWrap* FromCallbackPointer(void* arg) { - std::unique_ptr wrap_ptr { static_cast(arg) }; - QueryWrap* wrap = *wrap_ptr.get(); - if (wrap == nullptr) return nullptr; - wrap->callback_ptr_ = nullptr; - return wrap; - } - - static void Callback(void* arg, int status, int timeouts, - unsigned char* answer_buf, int answer_len) { - QueryWrap* wrap = FromCallbackPointer(arg); - if (wrap == nullptr) return; - - unsigned char* buf_copy = nullptr; - if (status == ARES_SUCCESS) { - buf_copy = node::Malloc(answer_len); - memcpy(buf_copy, answer_buf, answer_len); - } - - wrap->response_data_ = std::make_unique(); - ResponseData* data = wrap->response_data_.get(); - data->status = status; - data->is_host = false; - data->buf = MallocedBuffer(buf_copy, answer_len); - - wrap->QueueResponseCallback(status); - } - - static void Callback(void* arg, int status, int timeouts, - struct hostent* host) { - QueryWrap* wrap = FromCallbackPointer(arg); - if (wrap == nullptr) return; - - struct hostent* host_copy = nullptr; - if (status == ARES_SUCCESS) { - host_copy = node::Malloc(1); - cares_wrap_hostent_cpy(host_copy, host); - } - - wrap->response_data_ = std::make_unique(); - ResponseData* data = wrap->response_data_.get(); - data->status = status; - data->host.reset(host_copy); - data->is_host = true; - - wrap->QueueResponseCallback(status); - } - - void QueueResponseCallback(int status) { - BaseObjectPtr strong_ref{this}; - env()->SetImmediate([this, strong_ref](Environment*) { - AfterResponse(); - - // Delete once strong_ref goes out of scope. - Detach(); - }); - - channel_->set_query_last_ok(status != ARES_ECONNREFUSED); - channel_->ModifyActivityQueryCount(-1); - } - - void CallOnComplete(Local answer, - Local extra = Local()) { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); - Local argv[] = { - Integer::New(env()->isolate(), 0), - answer, - extra - }; - const int argc = arraysize(argv) - extra.IsEmpty(); - TRACE_EVENT_NESTABLE_ASYNC_END0( - TRACING_CATEGORY_NODE2(dns, native), trace_name_, this); - - MakeCallback(env()->oncomplete_string(), argc, argv); - } - - void ParseError(int status) { - CHECK_NE(status, ARES_SUCCESS); - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); - const char* code = ToErrorCodeString(status); - Local arg = OneByteString(env()->isolate(), code); - TRACE_EVENT_NESTABLE_ASYNC_END1( - TRACING_CATEGORY_NODE2(dns, native), trace_name_, this, - "error", status); - MakeCallback(env()->oncomplete_string(), 1, &arg); - } - - // Subclasses should implement the appropriate Parse method. - virtual void Parse(unsigned char* buf, int len) { - UNREACHABLE(); - } - - virtual void Parse(struct hostent* host) { - UNREACHABLE(); - } - - BaseObjectPtr channel_; - - private: - std::unique_ptr response_data_; - const char* trace_name_; - // Pointer to pointer to 'this' that can be reset from the destructor, - // in order to let Callback() know that 'this' no longer exists. - QueryWrap** callback_ptr_ = nullptr; -}; - - template -Local AddrTTLToArray(Environment* env, - const T* addrttls, - size_t naddrttls) { - auto isolate = env->isolate(); - +Local AddrTTLToArray( + Environment* env, + const T* addrttls, + size_t naddrttls) { MaybeStackBuffer, 8> ttls(naddrttls); for (size_t i = 0; i < naddrttls; i++) - ttls[i] = Integer::NewFromUnsigned(isolate, addrttls[i].ttl); + ttls[i] = Integer::NewFromUnsigned(env->isolate(), addrttls[i].ttl); - return Array::New(isolate, ttls.out(), naddrttls); + return Array::New(env->isolate(), ttls.out(), naddrttls); } - -int ParseGeneralReply(Environment* env, - const unsigned char* buf, - int len, - int* type, - Local ret, - void* addrttls = nullptr, - int* naddrttls = nullptr) { +int ParseGeneralReply( + Environment* env, + const unsigned char* buf, + int len, + int* type, + Local ret, + void* addrttls = nullptr, + int* naddrttls = nullptr) { HandleScope handle_scope(env->isolate()); - auto context = env->context(); hostent* host; int status; @@ -804,18 +223,20 @@ int ParseGeneralReply(Environment* env, if (status != ARES_SUCCESS) return status; + CHECK_NOT_NULL(host); + HostEntPointer ptr(host); + /* If it's `CNAME`, return the CNAME value; * And if it's `CNAME_OR_A` and it has value in `h_name` and `h_aliases[0]`, * we consider it's a CNAME record, otherwise we consider it's an A record. */ - if ((*type == ns_t_cname_or_a && host->h_name && host->h_aliases[0]) || + if ((*type == ns_t_cname_or_a && ptr->h_name && ptr->h_aliases[0]) || *type == ns_t_cname) { // A cname lookup always returns a single record but we follow the // common API here. *type = ns_t_cname; - ret->Set(context, + ret->Set(env->context(), ret->Length(), - OneByteString(env->isolate(), host->h_name)).Check(); - ares_free_hostent(host); + OneByteString(env->isolate(), ptr->h_name)).Check(); return ARES_SUCCESS; } @@ -823,79 +244,110 @@ int ParseGeneralReply(Environment* env, *type = ns_t_a; if (*type == ns_t_ns) { - HostentToNames(env, host, ret); + HostentToNames(env, ptr.get(), ret); } else if (*type == ns_t_ptr) { uint32_t offset = ret->Length(); - for (uint32_t i = 0; host->h_aliases[i] != nullptr; i++) { - auto alias = OneByteString(env->isolate(), host->h_aliases[i]); - ret->Set(context, i + offset, alias).Check(); + for (uint32_t i = 0; ptr->h_aliases[i] != nullptr; i++) { + auto alias = OneByteString(env->isolate(), ptr->h_aliases[i]); + ret->Set(env->context(), i + offset, alias).Check(); } } else { uint32_t offset = ret->Length(); char ip[INET6_ADDRSTRLEN]; - for (uint32_t i = 0; host->h_addr_list[i] != nullptr; ++i) { - uv_inet_ntop(host->h_addrtype, host->h_addr_list[i], ip, sizeof(ip)); + for (uint32_t i = 0; ptr->h_addr_list[i] != nullptr; ++i) { + uv_inet_ntop(ptr->h_addrtype, ptr->h_addr_list[i], ip, sizeof(ip)); auto address = OneByteString(env->isolate(), ip); - ret->Set(context, i + offset, address).Check(); + ret->Set(env->context(), i + offset, address).Check(); } } - ares_free_hostent(host); - return ARES_SUCCESS; } - -int ParseMxReply(Environment* env, - const unsigned char* buf, - int len, - Local ret, - bool need_type = false) { +int ParseMxReply( + Environment* env, + const unsigned char* buf, + int len, + Local ret, + bool need_type = false) { HandleScope handle_scope(env->isolate()); - auto context = env->context(); struct ares_mx_reply* mx_start; int status = ares_parse_mx_reply(buf, len, &mx_start); - if (status != ARES_SUCCESS) { + if (status != ARES_SUCCESS) return status; - } uint32_t offset = ret->Length(); ares_mx_reply* current = mx_start; for (uint32_t i = 0; current != nullptr; ++i, current = current->next) { Local mx_record = Object::New(env->isolate()); - mx_record->Set(context, + mx_record->Set(env->context(), env->exchange_string(), OneByteString(env->isolate(), current->host)).Check(); - mx_record->Set(context, + mx_record->Set(env->context(), env->priority_string(), Integer::New(env->isolate(), current->priority)).Check(); if (need_type) - mx_record->Set(context, + mx_record->Set(env->context(), env->type_string(), env->dns_mx_string()).Check(); - ret->Set(context, i + offset, mx_record).Check(); + ret->Set(env->context(), i + offset, mx_record).Check(); } ares_free_data(mx_start); return ARES_SUCCESS; } -int ParseTxtReply(Environment* env, - const unsigned char* buf, - int len, - Local ret, - bool need_type = false) { +int ParseCaaReply( + Environment* env, + const unsigned char* buf, + int len, + Local ret, + bool need_type = false) { + HandleScope handle_scope(env->isolate()); + + struct ares_caa_reply* caa_start; + int status = ares_parse_caa_reply(buf, len, &caa_start); + if (status != ARES_SUCCESS) + return status; + + uint32_t offset = ret->Length(); + ares_caa_reply* current = caa_start; + for (uint32_t i = 0; current != nullptr; ++i, current = current->next) { + Local caa_record = Object::New(env->isolate()); + + caa_record->Set(env->context(), + env->dns_critical_string(), + Integer::New(env->isolate(), current->critical)).Check(); + caa_record->Set(env->context(), + OneByteString(env->isolate(), current->property), + OneByteString(env->isolate(), current->value)).Check(); + if (need_type) + caa_record->Set(env->context(), + env->type_string(), + env->dns_caa_string()).Check(); + + ret->Set(env->context(), i + offset, caa_record).Check(); + } + + ares_free_data(caa_start); + return ARES_SUCCESS; +} + +int ParseTxtReply( + Environment* env, + const unsigned char* buf, + int len, + Local ret, + bool need_type = false) { HandleScope handle_scope(env->isolate()); - auto context = env->context(); struct ares_txt_ext* txt_out; int status = ares_parse_txt_reply_ext(buf, len, &txt_out); - if (status != ARES_SUCCESS) { + if (status != ARES_SUCCESS) return status; - } Local txt_chunk; @@ -911,13 +363,13 @@ int ParseTxtReply(Environment* env, if (!txt_chunk.IsEmpty()) { if (need_type) { Local elem = Object::New(env->isolate()); - elem->Set(context, env->entries_string(), txt_chunk).Check(); - elem->Set(context, + elem->Set(env->context(), env->entries_string(), txt_chunk).Check(); + elem->Set(env->context(), env->type_string(), env->dns_txt_string()).Check(); - ret->Set(context, offset + i++, elem).Check(); + ret->Set(env->context(), offset + i++, elem).Check(); } else { - ret->Set(context, offset + i++, txt_chunk).Check(); + ret->Set(env->context(), offset + i++, txt_chunk).Check(); } } @@ -925,20 +377,20 @@ int ParseTxtReply(Environment* env, j = 0; } - txt_chunk->Set(context, j++, txt).Check(); + txt_chunk->Set(env->context(), j++, txt).Check(); } // Push last chunk if it isn't empty if (!txt_chunk.IsEmpty()) { if (need_type) { Local elem = Object::New(env->isolate()); - elem->Set(context, env->entries_string(), txt_chunk).Check(); - elem->Set(context, + elem->Set(env->context(), env->entries_string(), txt_chunk).Check(); + elem->Set(env->context(), env->type_string(), env->dns_txt_string()).Check(); - ret->Set(context, offset + i, elem).Check(); + ret->Set(env->context(), offset + i, elem).Check(); } else { - ret->Set(context, offset + i, txt_chunk).Check(); + ret->Set(env->context(), offset + i, txt_chunk).Check(); } } @@ -947,42 +399,41 @@ int ParseTxtReply(Environment* env, } -int ParseSrvReply(Environment* env, - const unsigned char* buf, - int len, - Local ret, - bool need_type = false) { +int ParseSrvReply( + Environment* env, + const unsigned char* buf, + int len, + Local ret, + bool need_type = false) { HandleScope handle_scope(env->isolate()); - auto context = env->context(); struct ares_srv_reply* srv_start; int status = ares_parse_srv_reply(buf, len, &srv_start); - if (status != ARES_SUCCESS) { + if (status != ARES_SUCCESS) return status; - } ares_srv_reply* current = srv_start; int offset = ret->Length(); for (uint32_t i = 0; current != nullptr; ++i, current = current->next) { Local srv_record = Object::New(env->isolate()); - srv_record->Set(context, + srv_record->Set(env->context(), env->name_string(), OneByteString(env->isolate(), current->host)).Check(); - srv_record->Set(context, + srv_record->Set(env->context(), env->port_string(), Integer::New(env->isolate(), current->port)).Check(); - srv_record->Set(context, + srv_record->Set(env->context(), env->priority_string(), Integer::New(env->isolate(), current->priority)).Check(); - srv_record->Set(context, + srv_record->Set(env->context(), env->weight_string(), Integer::New(env->isolate(), current->weight)).Check(); if (need_type) - srv_record->Set(context, + srv_record->Set(env->context(), env->type_string(), env->dns_srv_string()).Check(); - ret->Set(context, i + offset, srv_record).Check(); + ret->Set(env->context(), i + offset, srv_record).Check(); } ares_free_data(srv_start); @@ -990,53 +441,52 @@ int ParseSrvReply(Environment* env, } -int ParseNaptrReply(Environment* env, - const unsigned char* buf, - int len, - Local ret, - bool need_type = false) { +int ParseNaptrReply( + Environment* env, + const unsigned char* buf, + int len, + Local ret, + bool need_type = false) { HandleScope handle_scope(env->isolate()); - auto context = env->context(); ares_naptr_reply* naptr_start; int status = ares_parse_naptr_reply(buf, len, &naptr_start); - if (status != ARES_SUCCESS) { + if (status != ARES_SUCCESS) return status; - } ares_naptr_reply* current = naptr_start; int offset = ret->Length(); for (uint32_t i = 0; current != nullptr; ++i, current = current->next) { Local naptr_record = Object::New(env->isolate()); - naptr_record->Set(context, + naptr_record->Set(env->context(), env->flags_string(), OneByteString(env->isolate(), current->flags)).Check(); - naptr_record->Set(context, + naptr_record->Set(env->context(), env->service_string(), OneByteString(env->isolate(), current->service)).Check(); - naptr_record->Set(context, + naptr_record->Set(env->context(), env->regexp_string(), OneByteString(env->isolate(), current->regexp)).Check(); - naptr_record->Set(context, + naptr_record->Set(env->context(), env->replacement_string(), OneByteString(env->isolate(), current->replacement)).Check(); - naptr_record->Set(context, + naptr_record->Set(env->context(), env->order_string(), Integer::New(env->isolate(), current->order)).Check(); - naptr_record->Set(context, + naptr_record->Set(env->context(), env->preference_string(), Integer::New(env->isolate(), current->preference)).Check(); if (need_type) - naptr_record->Set(context, + naptr_record->Set(env->context(), env->type_string(), env->dns_naptr_string()).Check(); - ret->Set(context, i + offset, naptr_record).Check(); + ret->Set(env->context(), i + offset, naptr_record).Check(); } ares_free_data(naptr_start); @@ -1044,12 +494,12 @@ int ParseNaptrReply(Environment* env, } -int ParseSoaReply(Environment* env, - unsigned char* buf, - int len, - Local* ret) { +int ParseSoaReply( + Environment* env, + unsigned char* buf, + int len, + Local* ret) { EscapableHandleScope handle_scope(env->isolate()); - auto context = env->context(); // Manage memory using standardard smart pointer std::unique_tr struct AresDeleter { @@ -1130,29 +580,29 @@ int ParseSoaReply(Environment* env, const unsigned int minttl = ReadUint32BE(ptr + 4 * 4); Local soa_record = Object::New(env->isolate()); - soa_record->Set(context, + soa_record->Set(env->context(), env->nsname_string(), OneByteString(env->isolate(), nsname.get())).Check(); - soa_record->Set(context, + soa_record->Set(env->context(), env->hostmaster_string(), OneByteString(env->isolate(), hostmaster.get())).Check(); - soa_record->Set(context, + soa_record->Set(env->context(), env->serial_string(), Integer::NewFromUnsigned(env->isolate(), serial)).Check(); - soa_record->Set(context, + soa_record->Set(env->context(), env->refresh_string(), Integer::New(env->isolate(), refresh)).Check(); - soa_record->Set(context, + soa_record->Set(env->context(), env->retry_string(), Integer::New(env->isolate(), retry)).Check(); - soa_record->Set(context, + soa_record->Set(env->context(), env->expire_string(), Integer::New(env->isolate(), expire)).Check(); - soa_record->Set(context, + soa_record->Set(env->context(), env->minttl_string(), Integer::NewFromUnsigned(env->isolate(), minttl)).Check(); - soa_record->Set(context, + soa_record->Set(env->context(), env->type_string(), env->dns_soa_string()).Check(); @@ -1164,623 +614,766 @@ int ParseSoaReply(Environment* env, ptr += rr_len; } - return ARES_SUCCESS; + return ARES_SUCCESS; +} +} // anonymous namespace + +ChannelWrap::ChannelWrap(Environment* env, Local object, int timeout) + : AsyncWrap(env, object, PROVIDER_DNSCHANNEL), + timeout_(timeout) { + MakeWeak(); + + Setup(); +} + +void ChannelWrap::MemoryInfo(MemoryTracker* tracker) const { + if (timer_handle_ != nullptr) + tracker->TrackField("timer_handle", *timer_handle_); + tracker->TrackField("task_list", task_list_, "NodeAresTask::List"); +} + +void ChannelWrap::New(const FunctionCallbackInfo& args) { + CHECK(args.IsConstructCall()); + CHECK_EQ(args.Length(), 1); + CHECK(args[0]->IsInt32()); + const int timeout = args[0].As()->Value(); + Environment* env = Environment::GetCurrent(args); + new ChannelWrap(env, args.This(), timeout); +} + +GetAddrInfoReqWrap::GetAddrInfoReqWrap( + Environment* env, + Local req_wrap_obj, + bool verbatim) + : ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_GETADDRINFOREQWRAP), + verbatim_(verbatim) {} + +GetNameInfoReqWrap::GetNameInfoReqWrap( + Environment* env, + Local req_wrap_obj) + : ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_GETNAMEINFOREQWRAP) {} + +/* This is called once per second by loop->timer. It is used to constantly */ +/* call back into c-ares for possibly processing timeouts. */ +void ChannelWrap::AresTimeout(uv_timer_t* handle) { + ChannelWrap* channel = static_cast(handle->data); + CHECK_EQ(channel->timer_handle(), handle); + CHECK_EQ(false, channel->task_list()->empty()); + ares_process_fd(channel->cares_channel(), ARES_SOCKET_BAD, ARES_SOCKET_BAD); +} + + +void NodeAresTask::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("channel", channel); +} + +/* Allocates and returns a new NodeAresTask */ +NodeAresTask* NodeAresTask::Create(ChannelWrap* channel, ares_socket_t sock) { + auto task = new NodeAresTask(); + + task->channel = channel; + task->sock = sock; + + if (uv_poll_init_socket(channel->env()->event_loop(), + &task->poll_watcher, sock) < 0) { + /* This should never happen. */ + delete task; + return nullptr; + } + + return task; +} + +void ChannelWrap::Setup() { + struct ares_options options; + memset(&options, 0, sizeof(options)); + options.flags = ARES_FLAG_NOCHECKRESP; + options.sock_state_cb = ares_sockstate_cb; + options.sock_state_cb_data = this; + options.timeout = timeout_; + + int r; + if (!library_inited_) { + Mutex::ScopedLock lock(ares_library_mutex); + // Multiple calls to ares_library_init() increase a reference counter, + // so this is a no-op except for the first call to it. + r = ares_library_init(ARES_LIB_INIT_ALL); + if (r != ARES_SUCCESS) + return env()->ThrowError(ToErrorCodeString(r)); + } + + /* We do the call to ares_init_option for caller. */ + const int optmask = + ARES_OPT_FLAGS | ARES_OPT_TIMEOUTMS | ARES_OPT_SOCK_STATE_CB; + r = ares_init_options(&channel_, &options, optmask); + + if (r != ARES_SUCCESS) { + Mutex::ScopedLock lock(ares_library_mutex); + ares_library_cleanup(); + return env()->ThrowError(ToErrorCodeString(r)); + } + + library_inited_ = true; +} + +void ChannelWrap::StartTimer() { + if (timer_handle_ == nullptr) { + timer_handle_ = new uv_timer_t(); + timer_handle_->data = static_cast(this); + uv_timer_init(env()->event_loop(), timer_handle_); + } else if (uv_is_active(reinterpret_cast(timer_handle_))) { + return; + } + int timeout = timeout_; + if (timeout == 0) timeout = 1; + if (timeout < 0 || timeout > 1000) timeout = 1000; + uv_timer_start(timer_handle_, AresTimeout, timeout, timeout); +} + +void ChannelWrap::CloseTimer() { + if (timer_handle_ == nullptr) + return; + + env()->CloseHandle(timer_handle_, [](uv_timer_t* handle) { delete handle; }); + timer_handle_ = nullptr; +} + +ChannelWrap::~ChannelWrap() { + ares_destroy(channel_); + + if (library_inited_) { + Mutex::ScopedLock lock(ares_library_mutex); + // This decreases the reference counter increased by ares_library_init(). + ares_library_cleanup(); + } + + CloseTimer(); +} + + +void ChannelWrap::ModifyActivityQueryCount(int count) { + active_query_count_ += count; + CHECK_GE(active_query_count_, 0); +} + + +/** + * This function is to check whether current servers are fallback servers + * when cares initialized. + * + * The fallback servers of cares is [ "127.0.0.1" ] with no user additional + * setting. + */ +void ChannelWrap::EnsureServers() { + /* if last query is OK or servers are set by user self, do not check */ + if (query_last_ok_ || !is_servers_default_) { + return; + } + + ares_addr_port_node* servers = nullptr; + + ares_get_servers_ports(channel_, &servers); + + /* if no server or multi-servers, ignore */ + if (servers == nullptr) return; + if (servers->next != nullptr) { + ares_free_data(servers); + is_servers_default_ = false; + return; + } + + /* if the only server is not 127.0.0.1, ignore */ + if (servers[0].family != AF_INET || + servers[0].addr.addr4.s_addr != htonl(INADDR_LOOPBACK) || + servers[0].tcp_port != 0 || + servers[0].udp_port != 0) { + ares_free_data(servers); + is_servers_default_ = false; + return; + } + + ares_free_data(servers); + servers = nullptr; + + /* destroy channel and reset channel */ + ares_destroy(channel_); + + CloseTimer(); + Setup(); +} + +int AnyTraits::Send(QueryWrap* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_any); + return 0; +} + +int ATraits::Send(QueryWrap* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_a); + return 0; +} + +int AaaaTraits::Send(QueryWrap* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_aaaa); + return 0; +} + +int CaaTraits::Send(QueryWrap* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, T_CAA); + return 0; } +int CnameTraits::Send(QueryWrap* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_cname); + return 0; +} -class QueryAnyWrap: public QueryWrap { - public: - QueryAnyWrap(ChannelWrap* channel, Local req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolveAny") { - } +int MxTraits::Send(QueryWrap* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_mx); + return 0; +} - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_any); - return 0; - } +int NsTraits::Send(QueryWrap* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_ns); + return 0; +} - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QueryAnyWrap) - SET_SELF_SIZE(QueryAnyWrap) - - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - auto context = env()->context(); - Context::Scope context_scope(context); - - Local ret = Array::New(env()->isolate()); - int type, status, old_count; - - /* Parse A records or CNAME records */ - ares_addrttl addrttls[256]; - int naddrttls = arraysize(addrttls); - - type = ns_t_cname_or_a; - status = ParseGeneralReply(env(), - buf, - len, - &type, - ret, - addrttls, - &naddrttls); - uint32_t a_count = ret->Length(); - if (status != ARES_SUCCESS && status != ARES_ENODATA) { - ParseError(status); - return; - } +int TxtTraits::Send(QueryWrap* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_txt); + return 0; +} - if (type == ns_t_a) { - CHECK_EQ(static_cast(naddrttls), a_count); - for (uint32_t i = 0; i < a_count; i++) { - Local obj = Object::New(env()->isolate()); - obj->Set(context, - env()->address_string(), - ret->Get(context, i).ToLocalChecked()).Check(); - obj->Set(context, - env()->ttl_string(), - Integer::NewFromUnsigned( - env()->isolate(), addrttls[i].ttl)).Check(); - obj->Set(context, - env()->type_string(), - env()->dns_a_string()).Check(); - ret->Set(context, i, obj).Check(); - } - } else { - for (uint32_t i = 0; i < a_count; i++) { - Local obj = Object::New(env()->isolate()); - obj->Set(context, - env()->value_string(), - ret->Get(context, i).ToLocalChecked()).Check(); - obj->Set(context, - env()->type_string(), - env()->dns_cname_string()).Check(); - ret->Set(context, i, obj).Check(); - } - } +int SrvTraits::Send(QueryWrap* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_srv); + return 0; +} - /* Parse AAAA records */ - ares_addr6ttl addr6ttls[256]; - int naddr6ttls = arraysize(addr6ttls); - - type = ns_t_aaaa; - status = ParseGeneralReply(env(), - buf, - len, - &type, - ret, - addr6ttls, - &naddr6ttls); - uint32_t aaaa_count = ret->Length() - a_count; - if (status != ARES_SUCCESS && status != ARES_ENODATA) { - ParseError(status); - return; - } +int PtrTraits::Send(QueryWrap* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_ptr); + return 0; +} - CHECK_EQ(aaaa_count, static_cast(naddr6ttls)); - CHECK_EQ(ret->Length(), a_count + aaaa_count); - for (uint32_t i = a_count; i < ret->Length(); i++) { - Local obj = Object::New(env()->isolate()); - obj->Set(context, - env()->address_string(), - ret->Get(context, i).ToLocalChecked()).Check(); - obj->Set(context, - env()->ttl_string(), - Integer::NewFromUnsigned( - env()->isolate(), addr6ttls[i - a_count].ttl)).Check(); - obj->Set(context, - env()->type_string(), - env()->dns_aaaa_string()).Check(); - ret->Set(context, i, obj).Check(); - } +int NaptrTraits::Send(QueryWrap* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_naptr); + return 0; +} - /* Parse MX records */ - status = ParseMxReply(env(), buf, len, ret, true); - if (status != ARES_SUCCESS && status != ARES_ENODATA) { - ParseError(status); - return; - } +int SoaTraits::Send(QueryWrap* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_soa); + return 0; +} - /* Parse NS records */ - type = ns_t_ns; - old_count = ret->Length(); - status = ParseGeneralReply(env(), buf, len, &type, ret); - if (status != ARES_SUCCESS && status != ARES_ENODATA) { - ParseError(status); - return; - } - for (uint32_t i = old_count; i < ret->Length(); i++) { - Local obj = Object::New(env()->isolate()); - obj->Set(context, - env()->value_string(), - ret->Get(context, i).ToLocalChecked()).Check(); - obj->Set(context, - env()->type_string(), - env()->dns_ns_string()).Check(); - ret->Set(context, i, obj).Check(); - } +int AnyTraits::Parse( + QueryAnyWrap* wrap, + const std::unique_ptr& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; - /* Parse TXT records */ - status = ParseTxtReply(env(), buf, len, ret, true); - if (status != ARES_SUCCESS && status != ARES_ENODATA) { - ParseError(status); - return; - } + unsigned char* buf = response->buf.data; + int len = response->buf.size; - /* Parse SRV records */ - status = ParseSrvReply(env(), buf, len, ret, true); - if (status != ARES_SUCCESS && status != ARES_ENODATA) { - return; - } + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); - /* Parse PTR records */ - type = ns_t_ptr; - old_count = ret->Length(); - status = ParseGeneralReply(env(), buf, len, &type, ret); - for (uint32_t i = old_count; i < ret->Length(); i++) { - Local obj = Object::New(env()->isolate()); - obj->Set(context, - env()->value_string(), - ret->Get(context, i).ToLocalChecked()).Check(); - obj->Set(context, - env()->type_string(), - env()->dns_ptr_string()).Check(); - ret->Set(context, i, obj).Check(); - } + Local ret = Array::New(env->isolate()); + int type, status, old_count; + + /* Parse A records or CNAME records */ + ares_addrttl addrttls[256]; + int naddrttls = arraysize(addrttls); + + type = ns_t_cname_or_a; + status = ParseGeneralReply(env, + buf, + len, + &type, + ret, + addrttls, + &naddrttls); + uint32_t a_count = ret->Length(); + if (status != ARES_SUCCESS && status != ARES_ENODATA) + return status; - /* Parse NAPTR records */ - status = ParseNaptrReply(env(), buf, len, ret, true); - if (status != ARES_SUCCESS && status != ARES_ENODATA) { - ParseError(status); - return; + if (type == ns_t_a) { + CHECK_EQ(static_cast(naddrttls), a_count); + for (uint32_t i = 0; i < a_count; i++) { + Local obj = Object::New(env->isolate()); + obj->Set(env->context(), + env->address_string(), + ret->Get(env->context(), i).ToLocalChecked()).Check(); + obj->Set(env->context(), + env->ttl_string(), + Integer::NewFromUnsigned( + env->isolate(), addrttls[i].ttl)).Check(); + obj->Set(env->context(), + env->type_string(), + env->dns_a_string()).Check(); + ret->Set(env->context(), i, obj).Check(); } - - /* Parse SOA records */ - Local soa_record = Local(); - status = ParseSoaReply(env(), buf, len, &soa_record); - if (status != ARES_SUCCESS && status != ARES_ENODATA) { - ParseError(status); - return; + } else { + for (uint32_t i = 0; i < a_count; i++) { + Local obj = Object::New(env->isolate()); + obj->Set(env->context(), + env->value_string(), + ret->Get(env->context(), i).ToLocalChecked()).Check(); + obj->Set(env->context(), + env->type_string(), + env->dns_cname_string()).Check(); + ret->Set(env->context(), i, obj).Check(); } - if (!soa_record.IsEmpty()) - ret->Set(context, ret->Length(), soa_record).Check(); - - CallOnComplete(ret); - } -}; - - -class QueryAWrap: public QueryWrap { - public: - QueryAWrap(ChannelWrap* channel, Local req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolve4") { } - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_a); - return 0; - } + /* Parse AAAA records */ + ares_addr6ttl addr6ttls[256]; + int naddr6ttls = arraysize(addr6ttls); + + type = ns_t_aaaa; + status = ParseGeneralReply(env, + buf, + len, + &type, + ret, + addr6ttls, + &naddr6ttls); + uint32_t aaaa_count = ret->Length() - a_count; + if (status != ARES_SUCCESS && status != ARES_ENODATA) + return status; - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QueryAWrap) - SET_SELF_SIZE(QueryAWrap) - - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); - - ares_addrttl addrttls[256]; - int naddrttls = arraysize(addrttls), status; - Local ret = Array::New(env()->isolate()); - - int type = ns_t_a; - status = ParseGeneralReply(env(), - buf, - len, - &type, - ret, - addrttls, - &naddrttls); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + CHECK_EQ(aaaa_count, static_cast(naddr6ttls)); + CHECK_EQ(ret->Length(), a_count + aaaa_count); + for (uint32_t i = a_count; i < ret->Length(); i++) { + Local obj = Object::New(env->isolate()); + obj->Set(env->context(), + env->address_string(), + ret->Get(env->context(), i).ToLocalChecked()).Check(); + obj->Set(env->context(), + env->ttl_string(), + Integer::NewFromUnsigned( + env->isolate(), addr6ttls[i - a_count].ttl)).Check(); + obj->Set(env->context(), + env->type_string(), + env->dns_aaaa_string()).Check(); + ret->Set(env->context(), i, obj).Check(); + } + + /* Parse MX records */ + status = ParseMxReply(env, buf, len, ret, true); + if (status != ARES_SUCCESS && status != ARES_ENODATA) + return status; - Local ttls = AddrTTLToArray(env(), - addrttls, - naddrttls); + /* Parse NS records */ + type = ns_t_ns; + old_count = ret->Length(); + status = ParseGeneralReply(env, buf, len, &type, ret); + if (status != ARES_SUCCESS && status != ARES_ENODATA) + return status; - CallOnComplete(ret, ttls); + for (uint32_t i = old_count; i < ret->Length(); i++) { + Local obj = Object::New(env->isolate()); + obj->Set(env->context(), + env->value_string(), + ret->Get(env->context(), i).ToLocalChecked()).Check(); + obj->Set(env->context(), + env->type_string(), + env->dns_ns_string()).Check(); + ret->Set(env->context(), i, obj).Check(); } -}; + /* Parse TXT records */ + status = ParseTxtReply(env, buf, len, ret, true); + if (status != ARES_SUCCESS && status != ARES_ENODATA) + return status; -class QueryAaaaWrap: public QueryWrap { - public: - QueryAaaaWrap(ChannelWrap* channel, Local req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolve6") { - } + /* Parse SRV records */ + status = ParseSrvReply(env, buf, len, ret, true); + if (status != ARES_SUCCESS && status != ARES_ENODATA) + return status; - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_aaaa); - return 0; - } + /* Parse PTR records */ + type = ns_t_ptr; + old_count = ret->Length(); + status = ParseGeneralReply(env, buf, len, &type, ret); + if (status != ARES_SUCCESS && status != ARES_ENODATA) + return status; + for (uint32_t i = old_count; i < ret->Length(); i++) { + Local obj = Object::New(env->isolate()); + obj->Set(env->context(), + env->value_string(), + ret->Get(env->context(), i).ToLocalChecked()).Check(); + obj->Set(env->context(), + env->type_string(), + env->dns_ptr_string()).Check(); + ret->Set(env->context(), i, obj).Check(); + } + + /* Parse NAPTR records */ + status = ParseNaptrReply(env, buf, len, ret, true); + if (status != ARES_SUCCESS && status != ARES_ENODATA) + return status; - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QueryAaaaWrap) - SET_SELF_SIZE(QueryAaaaWrap) - - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); - - ares_addr6ttl addrttls[256]; - int naddrttls = arraysize(addrttls), status; - Local ret = Array::New(env()->isolate()); - - int type = ns_t_aaaa; - status = ParseGeneralReply(env(), - buf, - len, - &type, - ret, - addrttls, - &naddrttls); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + /* Parse SOA records */ + Local soa_record = Local(); + status = ParseSoaReply(env, buf, len, &soa_record); + if (status != ARES_SUCCESS && status != ARES_ENODATA) + return status; - Local ttls = AddrTTLToArray(env(), - addrttls, - naddrttls); + if (!soa_record.IsEmpty()) + ret->Set(env->context(), ret->Length(), soa_record).Check(); - CallOnComplete(ret, ttls); - } -}; + /* Parse CAA records */ + status = ParseCaaReply(env, buf, len, ret, true); + if (status != ARES_SUCCESS && status != ARES_ENODATA) + return status; + wrap->CallOnComplete(ret); + return 0; +} -class QueryCnameWrap: public QueryWrap { - public: - QueryCnameWrap(ChannelWrap* channel, Local req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolveCname") { - } +int ATraits::Parse( + QueryAWrap* wrap, + const std::unique_ptr& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_cname); - return 0; - } + unsigned char* buf = response->buf.data; + int len = response->buf.size; - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QueryCnameWrap) - SET_SELF_SIZE(QueryCnameWrap) + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); + ares_addrttl addrttls[256]; + int naddrttls = arraysize(addrttls), status; + Local ret = Array::New(env->isolate()); + + int type = ns_t_a; + status = ParseGeneralReply(env, + buf, + len, + &type, + ret, + addrttls, + &naddrttls); + if (status != ARES_SUCCESS) + return status; - Local ret = Array::New(env()->isolate()); - int type = ns_t_cname; - int status = ParseGeneralReply(env(), buf, len, &type, ret); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + Local ttls = AddrTTLToArray(env, addrttls, naddrttls); - this->CallOnComplete(ret); - } -}; + wrap->CallOnComplete(ret, ttls); + return 0; +} +int AaaaTraits::Parse( + QueryAaaaWrap* wrap, + const std::unique_ptr& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; -class QueryMxWrap: public QueryWrap { - public: - QueryMxWrap(ChannelWrap* channel, Local req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolveMx") { - } + unsigned char* buf = response->buf.data; + int len = response->buf.size; - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_mx); - return 0; - } + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QueryMxWrap) - SET_SELF_SIZE(QueryMxWrap) + ares_addr6ttl addrttls[256]; + int naddrttls = arraysize(addrttls), status; + Local ret = Array::New(env->isolate()); + + int type = ns_t_aaaa; + status = ParseGeneralReply(env, + buf, + len, + &type, + ret, + addrttls, + &naddrttls); + if (status != ARES_SUCCESS) + return status; - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); + Local ttls = AddrTTLToArray(env, addrttls, naddrttls); - Local mx_records = Array::New(env()->isolate()); - int status = ParseMxReply(env(), buf, len, mx_records); + wrap->CallOnComplete(ret, ttls); + return 0; +} - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } +int CaaTraits::Parse( + QueryCaaWrap* wrap, + const std::unique_ptr& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; - this->CallOnComplete(mx_records); - } -}; + unsigned char* buf = response->buf.data; + int len = response->buf.size; + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); -class QueryNsWrap: public QueryWrap { - public: - QueryNsWrap(ChannelWrap* channel, Local req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolveNs") { - } + Local ret = Array::New(env->isolate()); + int status = ParseCaaReply(env, buf, len, ret); + if (status != ARES_SUCCESS) + return status; - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_ns); - return 0; - } + wrap->CallOnComplete(ret); + return 0; +} - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QueryNsWrap) - SET_SELF_SIZE(QueryNsWrap) +int CnameTraits::Parse( + QueryCnameWrap* wrap, + const std::unique_ptr& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); + unsigned char* buf = response->buf.data; + int len = response->buf.size; - int type = ns_t_ns; - Local names = Array::New(env()->isolate()); - int status = ParseGeneralReply(env(), buf, len, &type, names); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); - this->CallOnComplete(names); - } -}; + Local ret = Array::New(env->isolate()); + int type = ns_t_cname; + int status = ParseGeneralReply(env, buf, len, &type, ret); + if (status != ARES_SUCCESS) + return status; + wrap->CallOnComplete(ret); + return 0; +} -class QueryTxtWrap: public QueryWrap { - public: - QueryTxtWrap(ChannelWrap* channel, Local req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolveTxt") { - } +int MxTraits::Parse( + QueryMxWrap* wrap, + const std::unique_ptr& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_txt); - return 0; - } + unsigned char* buf = response->buf.data; + int len = response->buf.size; + + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QueryTxtWrap) - SET_SELF_SIZE(QueryTxtWrap) + Local mx_records = Array::New(env->isolate()); + int status = ParseMxReply(env, buf, len, mx_records); - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); + if (status != ARES_SUCCESS) + return status; - Local txt_records = Array::New(env()->isolate()); - int status = ParseTxtReply(env(), buf, len, txt_records); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + wrap->CallOnComplete(mx_records); + return 0; +} - this->CallOnComplete(txt_records); - } -}; +int NsTraits::Parse( + QueryNsWrap* wrap, + const std::unique_ptr& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; + unsigned char* buf = response->buf.data; + int len = response->buf.size; -class QuerySrvWrap: public QueryWrap { - public: - explicit QuerySrvWrap(ChannelWrap* channel, Local req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolveSrv") { - } + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_srv); - return 0; - } + int type = ns_t_ns; + Local names = Array::New(env->isolate()); + int status = ParseGeneralReply(env, buf, len, &type, names); + if (status != ARES_SUCCESS) + return status; - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QuerySrvWrap) - SET_SELF_SIZE(QuerySrvWrap) + wrap->CallOnComplete(names); + return 0; +} - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); +int TxtTraits::Parse( + QueryTxtWrap* wrap, + const std::unique_ptr& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; - Local srv_records = Array::New(env()->isolate()); - int status = ParseSrvReply(env(), buf, len, srv_records); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + unsigned char* buf = response->buf.data; + int len = response->buf.size; - this->CallOnComplete(srv_records); - } -}; + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); -class QueryPtrWrap: public QueryWrap { - public: - explicit QueryPtrWrap(ChannelWrap* channel, Local req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolvePtr") { - } + Local txt_records = Array::New(env->isolate()); + int status = ParseTxtReply(env, buf, len, txt_records); + if (status != ARES_SUCCESS) + return status; - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_ptr); - return 0; - } + wrap->CallOnComplete(txt_records); + return 0; +} - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QueryPtrWrap) - SET_SELF_SIZE(QueryPtrWrap) +int SrvTraits::Parse( + QuerySrvWrap* wrap, + const std::unique_ptr& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); + unsigned char* buf = response->buf.data; + int len = response->buf.size; - int type = ns_t_ptr; - Local aliases = Array::New(env()->isolate()); + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); - int status = ParseGeneralReply(env(), buf, len, &type, aliases); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + Local srv_records = Array::New(env->isolate()); + int status = ParseSrvReply(env, buf, len, srv_records); + if (status != ARES_SUCCESS) + return status; - this->CallOnComplete(aliases); - } -}; + wrap->CallOnComplete(srv_records); + return 0; +} -class QueryNaptrWrap: public QueryWrap { - public: - explicit QueryNaptrWrap(ChannelWrap* channel, Local req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolveNaptr") { - } +int PtrTraits::Parse( + QueryPtrWrap* wrap, + const std::unique_ptr& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_naptr); - return 0; - } + unsigned char* buf = response->buf.data; + int len = response->buf.size; - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QueryNaptrWrap) - SET_SELF_SIZE(QueryNaptrWrap) + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); + int type = ns_t_ptr; + Local aliases = Array::New(env->isolate()); - Local naptr_records = Array::New(env()->isolate()); - int status = ParseNaptrReply(env(), buf, len, naptr_records); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + int status = ParseGeneralReply(env, buf, len, &type, aliases); + if (status != ARES_SUCCESS) + return status; - this->CallOnComplete(naptr_records); - } -}; + wrap->CallOnComplete(aliases); + return 0; +} +int NaptrTraits::Parse( + QueryNaptrWrap* wrap, + const std::unique_ptr& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; -class QuerySoaWrap: public QueryWrap { - public: - QuerySoaWrap(ChannelWrap* channel, Local req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolveSoa") { - } + unsigned char* buf = response->buf.data; + int len = response->buf.size; - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_soa); - return 0; - } + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QuerySoaWrap) - SET_SELF_SIZE(QuerySoaWrap) + Local naptr_records = Array::New(env->isolate()); + int status = ParseNaptrReply(env, buf, len, naptr_records); + if (status != ARES_SUCCESS) + return status; - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - auto context = env()->context(); - Context::Scope context_scope(context); + wrap->CallOnComplete(naptr_records); + return 0; +} - ares_soa_reply* soa_out; - int status = ares_parse_soa_reply(buf, len, &soa_out); +int SoaTraits::Parse( + QuerySoaWrap* wrap, + const std::unique_ptr& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + unsigned char* buf = response->buf.data; + int len = response->buf.size; - Local soa_record = Object::New(env()->isolate()); - - soa_record->Set(context, - env()->nsname_string(), - OneByteString(env()->isolate(), - soa_out->nsname)).Check(); - soa_record->Set(context, - env()->hostmaster_string(), - OneByteString(env()->isolate(), - soa_out->hostmaster)).Check(); - soa_record->Set(context, - env()->serial_string(), - Integer::NewFromUnsigned( - env()->isolate(), soa_out->serial)).Check(); - soa_record->Set(context, - env()->refresh_string(), - Integer::New(env()->isolate(), - soa_out->refresh)).Check(); - soa_record->Set(context, - env()->retry_string(), - Integer::New(env()->isolate(), soa_out->retry)).Check(); - soa_record->Set(context, - env()->expire_string(), - Integer::New(env()->isolate(), soa_out->expire)).Check(); - soa_record->Set(context, - env()->minttl_string(), - Integer::NewFromUnsigned( - env()->isolate(), soa_out->minttl)).Check(); - - ares_free_data(soa_out); - - this->CallOnComplete(soa_record); - } -}; + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + ares_soa_reply* soa_out; + int status = ares_parse_soa_reply(buf, len, &soa_out); -class GetHostByAddrWrap: public QueryWrap { - public: - explicit GetHostByAddrWrap(ChannelWrap* channel, Local req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "reverse") { - } + if (status != ARES_SUCCESS) + return status; - int Send(const char* name) override { - int length, family; - char address_buffer[sizeof(struct in6_addr)]; + Local soa_record = Object::New(env->isolate()); + + soa_record->Set(env->context(), + env->nsname_string(), + OneByteString(env->isolate(), soa_out->nsname)).Check(); + soa_record->Set(env->context(), + env->hostmaster_string(), + OneByteString(env->isolate(), soa_out->hostmaster)).Check(); + soa_record->Set(env->context(), + env->serial_string(), + Integer::NewFromUnsigned( + env->isolate(), soa_out->serial)).Check(); + soa_record->Set(env->context(), + env->refresh_string(), + Integer::New(env->isolate(), soa_out->refresh)).Check(); + soa_record->Set(env->context(), + env->retry_string(), + Integer::New(env->isolate(), soa_out->retry)).Check(); + soa_record->Set(env->context(), + env->expire_string(), + Integer::New(env->isolate(), soa_out->expire)).Check(); + soa_record->Set(env->context(), + env->minttl_string(), + Integer::NewFromUnsigned( + env->isolate(), soa_out->minttl)).Check(); + + ares_free_data(soa_out); + + wrap->CallOnComplete(soa_record); + return 0; +} - if (uv_inet_pton(AF_INET, name, &address_buffer) == 0) { - length = sizeof(struct in_addr); - family = AF_INET; - } else if (uv_inet_pton(AF_INET6, name, &address_buffer) == 0) { - length = sizeof(struct in6_addr); - family = AF_INET6; - } else { - return UV_EINVAL; // So errnoException() reports a proper error. - } +int ReverseTraits::Send(GetHostByAddrWrap* wrap, const char* name) { + int length, family; + char address_buffer[sizeof(struct in6_addr)]; - TRACE_EVENT_NESTABLE_ASYNC_BEGIN2( - TRACING_CATEGORY_NODE2(dns, native), "reverse", this, - "name", TRACE_STR_COPY(name), - "family", family == AF_INET ? "ipv4" : "ipv6"); - - ares_gethostbyaddr(channel_->cares_channel(), - address_buffer, - length, - family, - Callback, - MakeCallbackPointer()); - return 0; + if (uv_inet_pton(AF_INET, name, &address_buffer) == 0) { + length = sizeof(struct in_addr); + family = AF_INET; + } else if (uv_inet_pton(AF_INET6, name, &address_buffer) == 0) { + length = sizeof(struct in6_addr); + family = AF_INET6; + } else { + return UV_EINVAL; // So errnoException() reports a proper error. } - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(GetHostByAddrWrap) - SET_SELF_SIZE(GetHostByAddrWrap) + TRACE_EVENT_NESTABLE_ASYNC_BEGIN2( + TRACING_CATEGORY_NODE2(dns, native), "reverse", wrap, + "name", TRACE_STR_COPY(name), + "family", family == AF_INET ? "ipv4" : "ipv6"); + + ares_gethostbyaddr( + wrap->channel()->cares_channel(), + address_buffer, + length, + family, + GetHostByAddrWrap::Callback, + wrap->MakeCallbackPointer()); + return 0; +} - protected: - void Parse(struct hostent* host) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); - this->CallOnComplete(HostentToNames(env(), host)); - } -}; +int ReverseTraits::Parse( + GetHostByAddrWrap* wrap, + const std::unique_ptr& response) { + if (UNLIKELY(!response->is_host)) + return ARES_EBADRESP; + + struct hostent* host = response->host.get(); + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + wrap->CallOnComplete(HostentToNames(env, host)); + return 0; +} +namespace { template static void Query(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); @@ -2149,6 +1742,70 @@ void SetServers(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(err); } +void SetLocalAddress(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + ChannelWrap* channel; + ASSIGN_OR_RETURN_UNWRAP(&channel, args.Holder()); + + CHECK_EQ(args.Length(), 2); + CHECK(args[0]->IsString()); + + Isolate* isolate = args.GetIsolate(); + node::Utf8Value ip0(isolate, args[0]); + + unsigned char addr0[sizeof(struct in6_addr)]; + unsigned char addr1[sizeof(struct in6_addr)]; + int type0 = 0; + + // This function accepts 2 arguments. The first may be either an IPv4 + // address or an IPv6 address. If present, the second argument must be the + // other type of address. Otherwise, the unspecified type of IP is set + // to 0 (any). + + if (uv_inet_pton(AF_INET, *ip0, &addr0) == 0) { + ares_set_local_ip4(channel->cares_channel(), ReadUint32BE(addr0)); + type0 = 4; + } else if (uv_inet_pton(AF_INET6, *ip0, &addr0) == 0) { + ares_set_local_ip6(channel->cares_channel(), addr0); + type0 = 6; + } else { + THROW_ERR_INVALID_ARG_VALUE(env, "Invalid IP address."); + return; + } + + if (!args[1]->IsUndefined()) { + CHECK(args[1]->IsString()); + node::Utf8Value ip1(isolate, args[1]); + + if (uv_inet_pton(AF_INET, *ip1, &addr1) == 0) { + if (type0 == 4) { + THROW_ERR_INVALID_ARG_VALUE(env, "Cannot specify two IPv4 addresses."); + return; + } else { + ares_set_local_ip4(channel->cares_channel(), ReadUint32BE(addr1)); + } + } else if (uv_inet_pton(AF_INET6, *ip1, &addr1) == 0) { + if (type0 == 6) { + THROW_ERR_INVALID_ARG_VALUE(env, "Cannot specify two IPv6 addresses."); + return; + } else { + ares_set_local_ip6(channel->cares_channel(), addr1); + } + } else { + THROW_ERR_INVALID_ARG_VALUE(env, "Invalid IP address."); + return; + } + } else { + // No second arg specifed + if (type0 == 4) { + memset(&addr1, 0, sizeof(addr1)); + ares_set_local_ip6(channel->cares_channel(), addr1); + } else { + ares_set_local_ip4(channel->cares_channel(), 0); + } + } +} + void Cancel(const FunctionCallbackInfo& args) { ChannelWrap* channel; ASSIGN_OR_RETURN_UNWRAP(&channel, args.Holder()); @@ -2169,6 +1826,32 @@ void StrError(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(OneByteString(env->isolate(), errmsg)); } +} // namespace + +inline void safe_free_hostent(struct hostent* host) { + int idx; + + if (host->h_addr_list != nullptr) { + idx = 0; + while (host->h_addr_list[idx]) { + free(host->h_addr_list[idx++]); + } + free(host->h_addr_list); + host->h_addr_list = nullptr; + } + + if (host->h_aliases != nullptr) { + idx = 0; + while (host->h_aliases[idx]) { + free(host->h_aliases[idx++]); + } + free(host->h_aliases); + host->h_aliases = nullptr; + } + + free(host->h_name); + free(host); +} void Initialize(Local target, Local unused, @@ -2238,6 +1921,7 @@ void Initialize(Local target, env->SetProtoMethod(channel_wrap, "queryAny", Query); env->SetProtoMethod(channel_wrap, "queryA", Query); env->SetProtoMethod(channel_wrap, "queryAaaa", Query); + env->SetProtoMethod(channel_wrap, "queryCaa", Query); env->SetProtoMethod(channel_wrap, "queryCname", Query); env->SetProtoMethod(channel_wrap, "queryMx", Query); env->SetProtoMethod(channel_wrap, "queryNs", Query); @@ -2250,6 +1934,7 @@ void Initialize(Local target, env->SetProtoMethodNoSideEffect(channel_wrap, "getServers", GetServers); env->SetProtoMethod(channel_wrap, "setServers", SetServers); + env->SetProtoMethod(channel_wrap, "setLocalAddress", SetLocalAddress); env->SetProtoMethod(channel_wrap, "cancel", Cancel); Local channelWrapString = @@ -2259,7 +1944,6 @@ void Initialize(Local target, channel_wrap->GetFunction(context).ToLocalChecked()).Check(); } -} // anonymous namespace } // namespace cares_wrap } // namespace node diff --git a/src/cares_wrap.h b/src/cares_wrap.h new file mode 100644 index 00000000000000..bceafcb8b7f46b --- /dev/null +++ b/src/cares_wrap.h @@ -0,0 +1,534 @@ +#ifndef SRC_CARES_WRAP_H_ +#define SRC_CARES_WRAP_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#define CARES_STATICLIB + +#include "async_wrap.h" +#include "base_object.h" +#include "env.h" +#include "memory_tracker.h" +#include "util.h" +#include "node.h" + +#include "ares.h" +#include "v8.h" +#include "uv.h" + +#include + +#ifdef __POSIX__ +# include +#endif // __POSIX__ + +#if defined(__ANDROID__) || \ + defined(__MINGW32__) || \ + defined(__OpenBSD__) || \ + defined(_MSC_VER) + +# include +#else +# include +#endif + +#ifndef T_CAA +# define T_CAA 257 /* Certification Authority Authorization */ +#endif + +#if defined(__OpenBSD__) +# define AI_V4MAPPED 0 +#endif + +namespace node { +namespace cares_wrap { + +constexpr int ns_t_cname_or_a = -1; +constexpr int DNS_ESETSRVPENDING = -1000; + +class ChannelWrap; + +inline void safe_free_hostent(struct hostent* host); + +using HostEntPointer = DeleteFnPtr; +using SafeHostEntPointer = DeleteFnPtr; + +inline const char* ToErrorCodeString(int status) { + switch (status) { +#define V(code) case ARES_##code: return #code; + V(EADDRGETNETWORKPARAMS) + V(EBADFAMILY) + V(EBADFLAGS) + V(EBADHINTS) + V(EBADNAME) + V(EBADQUERY) + V(EBADRESP) + V(EBADSTR) + V(ECANCELLED) + V(ECONNREFUSED) + V(EDESTRUCTION) + V(EFILE) + V(EFORMERR) + V(ELOADIPHLPAPI) + V(ENODATA) + V(ENOMEM) + V(ENONAME) + V(ENOTFOUND) + V(ENOTIMP) + V(ENOTINITIALIZED) + V(EOF) + V(EREFUSED) + V(ESERVFAIL) + V(ETIMEOUT) +#undef V + } + + return "UNKNOWN_ARES_ERROR"; +} + +inline void cares_wrap_hostent_cpy( + struct hostent* dest, + const struct hostent* src) { + dest->h_addr_list = nullptr; + dest->h_addrtype = 0; + dest->h_aliases = nullptr; + dest->h_length = 0; + dest->h_name = nullptr; + + /* copy `h_name` */ + size_t name_size = strlen(src->h_name) + 1; + dest->h_name = node::Malloc(name_size); + memcpy(dest->h_name, src->h_name, name_size); + + /* copy `h_aliases` */ + size_t alias_count; + for (alias_count = 0; + src->h_aliases[alias_count] != nullptr; + alias_count++) { + } + + dest->h_aliases = node::Malloc(alias_count + 1); + for (size_t i = 0; i < alias_count; i++) { + const size_t cur_alias_size = strlen(src->h_aliases[i]) + 1; + dest->h_aliases[i] = node::Malloc(cur_alias_size); + memcpy(dest->h_aliases[i], src->h_aliases[i], cur_alias_size); + } + dest->h_aliases[alias_count] = nullptr; + + /* copy `h_addr_list` */ + size_t list_count; + for (list_count = 0; + src->h_addr_list[list_count] != nullptr; + list_count++) { + } + + dest->h_addr_list = node::Malloc(list_count + 1); + for (size_t i = 0; i < list_count; i++) { + dest->h_addr_list[i] = node::Malloc(src->h_length); + memcpy(dest->h_addr_list[i], src->h_addr_list[i], src->h_length); + } + dest->h_addr_list[list_count] = nullptr; + + /* work after work */ + dest->h_length = src->h_length; + dest->h_addrtype = src->h_addrtype; +} + + +struct NodeAresTask final : public MemoryRetainer { + ChannelWrap* channel; + ares_socket_t sock; + uv_poll_t poll_watcher; + + inline void MemoryInfo(MemoryTracker* trakcer) const override; + SET_MEMORY_INFO_NAME(NodeAresTask); + SET_SELF_SIZE(NodeAresTask); + + struct Hash { + inline size_t operator()(NodeAresTask* a) const { + return std::hash()(a->sock); + } + }; + + struct Equal { + inline bool operator()(NodeAresTask* a, NodeAresTask* b) const { + return a->sock == b->sock; + } + }; + + static NodeAresTask* Create(ChannelWrap* channel, ares_socket_t sock); + + using List = std::unordered_set; +}; + +class ChannelWrap final : public AsyncWrap { + public: + ChannelWrap(Environment* env, v8::Local object, int timeout); + ~ChannelWrap() override; + + static void New(const v8::FunctionCallbackInfo& args); + + void Setup(); + void EnsureServers(); + void StartTimer(); + void CloseTimer(); + + void ModifyActivityQueryCount(int count); + + inline uv_timer_t* timer_handle() { return timer_handle_; } + inline ares_channel cares_channel() { return channel_; } + inline void set_query_last_ok(bool ok) { query_last_ok_ = ok; } + inline void set_is_servers_default(bool is_default) { + is_servers_default_ = is_default; + } + inline int active_query_count() { return active_query_count_; } + inline NodeAresTask::List* task_list() { return &task_list_; } + + void MemoryInfo(MemoryTracker* tracker) const override; + SET_MEMORY_INFO_NAME(ChannelWrap) + SET_SELF_SIZE(ChannelWrap) + + static void AresTimeout(uv_timer_t* handle); + + private: + uv_timer_t* timer_handle_ = nullptr; + ares_channel channel_ = nullptr; + bool query_last_ok_ = true; + bool is_servers_default_ = true; + bool library_inited_ = false; + int timeout_; + int active_query_count_ = 0; + NodeAresTask::List task_list_; +}; + +class GetAddrInfoReqWrap final : public ReqWrap { + public: + GetAddrInfoReqWrap(Environment* env, + v8::Local req_wrap_obj, + bool verbatim); + + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(GetAddrInfoReqWrap) + SET_SELF_SIZE(GetAddrInfoReqWrap) + + bool verbatim() const { return verbatim_; } + + private: + const bool verbatim_; +}; + +class GetNameInfoReqWrap final : public ReqWrap { + public: + GetNameInfoReqWrap(Environment* env, v8::Local req_wrap_obj); + + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(GetNameInfoReqWrap) + SET_SELF_SIZE(GetNameInfoReqWrap) +}; + +struct ResponseData final { + int status; + bool is_host; + SafeHostEntPointer host; + MallocedBuffer buf; +}; + +template +class QueryWrap final : public AsyncWrap { + public: + QueryWrap(ChannelWrap* channel, v8::Local req_wrap_obj) + : AsyncWrap(channel->env(), req_wrap_obj, AsyncWrap::PROVIDER_QUERYWRAP), + channel_(channel), + trace_name_(Traits::name) {} + + ~QueryWrap() { + CHECK_EQ(false, persistent().IsEmpty()); + + // Let Callback() know that this object no longer exists. + if (callback_ptr_ != nullptr) + *callback_ptr_ = nullptr; + } + + int Send(const char* name) { + return Traits::Send(this, name); + } + + void AresQuery(const char* name, int dnsclass, int type) { + channel_->EnsureServers(); + TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( + TRACING_CATEGORY_NODE2(dns, native), trace_name_, this, + "name", TRACE_STR_COPY(name)); + ares_query( + channel_->cares_channel(), + name, + dnsclass, + type, + Callback, + MakeCallbackPointer()); + } + + void ParseError(int status) { + CHECK_NE(status, ARES_SUCCESS); + v8::HandleScope handle_scope(env()->isolate()); + v8::Context::Scope context_scope(env()->context()); + const char* code = ToErrorCodeString(status); + v8::Local arg = OneByteString(env()->isolate(), code); + TRACE_EVENT_NESTABLE_ASYNC_END1( + TRACING_CATEGORY_NODE2(dns, native), trace_name_, this, + "error", status); + MakeCallback(env()->oncomplete_string(), 1, &arg); + } + + const BaseObjectPtr& channel() const { return channel_; } + + void AfterResponse() { + CHECK(response_data_); + + int status = response_data_->status; + + if (status != ARES_SUCCESS) + return ParseError(status); + + status = Traits::Parse(this, response_data_); + + if (status != ARES_SUCCESS) + ParseError(status); + } + + void* MakeCallbackPointer() { + CHECK_NULL(callback_ptr_); + callback_ptr_ = new QueryWrap*(this); + return callback_ptr_; + } + + static QueryWrap* FromCallbackPointer(void* arg) { + std::unique_ptr*> wrap_ptr { + static_cast**>(arg) + }; + QueryWrap* wrap = *wrap_ptr.get(); + if (wrap == nullptr) return nullptr; + wrap->callback_ptr_ = nullptr; + return wrap; + } + + static void Callback( + void* arg, + int status, + int timeouts, + unsigned char* answer_buf, + int answer_len) { + QueryWrap* wrap = FromCallbackPointer(arg); + if (wrap == nullptr) return; + + unsigned char* buf_copy = nullptr; + if (status == ARES_SUCCESS) { + buf_copy = node::Malloc(answer_len); + memcpy(buf_copy, answer_buf, answer_len); + } + + wrap->response_data_ = std::make_unique(); + ResponseData* data = wrap->response_data_.get(); + data->status = status; + data->is_host = false; + data->buf = MallocedBuffer(buf_copy, answer_len); + + wrap->QueueResponseCallback(status); + } + + static void Callback( + void* arg, + int status, + int timeouts, + struct hostent* host) { + QueryWrap* wrap = FromCallbackPointer(arg); + if (wrap == nullptr) return; + + struct hostent* host_copy = nullptr; + if (status == ARES_SUCCESS) { + host_copy = node::Malloc(1); + cares_wrap_hostent_cpy(host_copy, host); + } + + wrap->response_data_ = std::make_unique(); + ResponseData* data = wrap->response_data_.get(); + data->status = status; + data->host.reset(host_copy); + data->is_host = true; + + wrap->QueueResponseCallback(status); + } + + void QueueResponseCallback(int status) { + BaseObjectPtr> strong_ref{this}; + env()->SetImmediate([this, strong_ref](Environment*) { + AfterResponse(); + + // Delete once strong_ref goes out of scope. + Detach(); + }); + + channel_->set_query_last_ok(status != ARES_ECONNREFUSED); + channel_->ModifyActivityQueryCount(-1); + } + + void CallOnComplete( + v8::Local answer, + v8::Local extra = v8::Local()) { + v8::HandleScope handle_scope(env()->isolate()); + v8::Context::Scope context_scope(env()->context()); + v8::Local argv[] = { + v8::Integer::New(env()->isolate(), 0), + answer, + extra + }; + const int argc = arraysize(argv) - extra.IsEmpty(); + TRACE_EVENT_NESTABLE_ASYNC_END0( + TRACING_CATEGORY_NODE2(dns, native), trace_name_, this); + + MakeCallback(env()->oncomplete_string(), argc, argv); + } + + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackField("channel", channel_); + if (response_data_) { + tracker->TrackFieldWithSize("response", response_data_->buf.size); + } + } + + SET_MEMORY_INFO_NAME(QueryWrap) + SET_SELF_SIZE(QueryWrap) + + private: + BaseObjectPtr channel_; + + std::unique_ptr response_data_; + const char* trace_name_; + // Pointer to pointer to 'this' that can be reset from the destructor, + // in order to let Callback() know that 'this' no longer exists. + QueryWrap** callback_ptr_ = nullptr; +}; + +struct AnyTraits final { + static constexpr const char* name = "resolveAny"; + static int Send(QueryWrap* wrap, const char* name); + static int Parse( + QueryWrap* wrap, + const std::unique_ptr& response); +}; + +struct ATraits final { + static constexpr const char* name = "resolve4"; + static int Send(QueryWrap* wrap, const char* name); + static int Parse( + QueryWrap* wrap, + const std::unique_ptr& response); +}; + +struct AaaaTraits final { + static constexpr const char* name = "resolve6"; + static int Send(QueryWrap* wrap, const char* name); + static int Parse( + QueryWrap* wrap, + const std::unique_ptr& response); +}; + +struct CaaTraits final { + static constexpr const char* name = "resolveCaa"; + static int Send(QueryWrap* wrap, const char* name); + static int Parse( + QueryWrap* wrap, + const std::unique_ptr& response); +}; + +struct CnameTraits final { + static constexpr const char* name = "resolveCname"; + static int Send(QueryWrap* wrap, const char* name); + static int Parse( + QueryWrap* wrap, + const std::unique_ptr& response); +}; + +struct MxTraits final { + static constexpr const char* name = "resolveMx"; + static int Send(QueryWrap* wrap, const char* name); + static int Parse( + QueryWrap* wrap, + const std::unique_ptr& response); +}; + +struct NsTraits final { + static constexpr const char* name = "resolveNs"; + static int Send(QueryWrap* wrap, const char* name); + static int Parse( + QueryWrap* wrap, + const std::unique_ptr& response); +}; + +struct TxtTraits final { + static constexpr const char* name = "resolveTxt"; + static int Send(QueryWrap* wrap, const char* name); + static int Parse( + QueryWrap* wrap, + const std::unique_ptr& response); +}; + +struct SrvTraits final { + static constexpr const char* name = "resolveSrv"; + static int Send(QueryWrap* wrap, const char* name); + static int Parse( + QueryWrap* wrap, + const std::unique_ptr& response); +}; + +struct PtrTraits final { + static constexpr const char* name = "resolvePtr"; + static int Send(QueryWrap* wrap, const char* name); + static int Parse( + QueryWrap* wrap, + const std::unique_ptr& response); +}; + +struct NaptrTraits final { + static constexpr const char* name = "resolveNaptr"; + static int Send(QueryWrap* wrap, const char* name); + static int Parse( + QueryWrap* wrap, + const std::unique_ptr& response); +}; + +struct SoaTraits final { + static constexpr const char* name = "resolveSoa"; + static int Send(QueryWrap* wrap, const char* name); + static int Parse( + QueryWrap* wrap, + const std::unique_ptr& response); +}; + +struct ReverseTraits final { + static constexpr const char* name = "reverse"; + static int Send(QueryWrap* wrap, const char* name); + static int Parse( + QueryWrap* wrap, + const std::unique_ptr& response); +}; + +using QueryAnyWrap = QueryWrap; +using QueryAWrap = QueryWrap; +using QueryAaaaWrap = QueryWrap; +using QueryCaaWrap = QueryWrap; +using QueryCnameWrap = QueryWrap; +using QueryMxWrap = QueryWrap; +using QueryNsWrap = QueryWrap; +using QueryTxtWrap = QueryWrap; +using QuerySrvWrap = QueryWrap; +using QueryPtrWrap = QueryWrap; +using QueryNaptrWrap = QueryWrap; +using QuerySoaWrap = QueryWrap; +using GetHostByAddrWrap = QueryWrap; + +} // namespace cares_wrap +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_CARES_WRAP_H_ diff --git a/src/debug_utils.cc b/src/debug_utils.cc index a601c5ecf40ea9..aa97bfbe943bab 100644 --- a/src/debug_utils.cc +++ b/src/debug_utils.cc @@ -377,7 +377,7 @@ std::vector NativeSymbolDebuggingContext::GetLoadedLibraries() { [](struct dl_phdr_info* info, size_t size, void* data) { auto list = static_cast*>(data); if (*info->dlpi_name != '\0') { - list->push_back(info->dlpi_name); + list->emplace_back(info->dlpi_name); } return 0; }, @@ -386,7 +386,7 @@ std::vector NativeSymbolDebuggingContext::GetLoadedLibraries() { uint32_t i = 0; for (const char* name = _dyld_get_image_name(i); name != nullptr; name = _dyld_get_image_name(++i)) { - list.push_back(name); + list.emplace_back(name); } #elif _AIX @@ -411,10 +411,10 @@ std::vector NativeSymbolDebuggingContext::GetLoadedLibraries() { strlen(cur_info->ldinfo_filename) + 1; if (*member_name != '\0') { str << cur_info->ldinfo_filename << "(" << member_name << ")"; - list.push_back(str.str()); + list.emplace_back(str.str()); str.str(""); } else { - list.push_back(cur_info->ldinfo_filename); + list.emplace_back(cur_info->ldinfo_filename); } buf += cur_info->ldinfo_next; } while (cur_info->ldinfo_next != 0); @@ -424,7 +424,7 @@ std::vector NativeSymbolDebuggingContext::GetLoadedLibraries() { if (dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &p) != -1) { for (Link_map* l = p; l != nullptr; l = l->l_next) { - list.push_back(l->l_name); + list.emplace_back(l->l_name); } } @@ -459,7 +459,7 @@ std::vector NativeSymbolDebuggingContext::GetLoadedLibraries() { char* str = new char[size]; WideCharToMultiByte( CP_UTF8, 0, module_name, -1, str, size, nullptr, nullptr); - list.push_back(str); + list.emplace_back(str); } } } diff --git a/src/env-inl.h b/src/env-inl.h index c1853f81b68bd2..9b2f82cc00e661 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -387,7 +387,7 @@ inline T* Environment::GetBindingData(v8::Local context) { context->GetAlignedPointerFromEmbedderData( ContextEmbedderIndex::kBindingListIndex)); DCHECK_NOT_NULL(map); - auto it = map->find(T::binding_data_name); + auto it = map->find(T::type_name); if (UNLIKELY(it == map->end())) return nullptr; T* result = static_cast(it->second.get()); DCHECK_NOT_NULL(result); @@ -406,7 +406,7 @@ inline T* Environment::AddBindingData( context->GetAlignedPointerFromEmbedderData( ContextEmbedderIndex::kBindingListIndex)); DCHECK_NOT_NULL(map); - auto result = map->emplace(T::binding_data_name, item); + auto result = map->emplace(T::type_name, item); CHECK(result.second); DCHECK_EQ(GetBindingData(context), item.get()); return item.get(); @@ -837,6 +837,14 @@ void Environment::set_filehandle_close_warning(bool on) { emit_filehandle_warning_ = on; } +void Environment::set_source_maps_enabled(bool on) { + source_maps_enabled_ = on; +} + +bool Environment::source_maps_enabled() const { + return source_maps_enabled_; +} + inline uint64_t Environment::thread_id() const { return thread_id_; } @@ -1033,7 +1041,7 @@ inline void Environment::SetInstanceMethod(v8::Local that, t->SetClassName(name_string); } -void Environment::AddCleanupHook(void (*fn)(void*), void* arg) { +void Environment::AddCleanupHook(CleanupCallback fn, void* arg) { auto insertion_info = cleanup_hooks_.emplace(CleanupHookCallback { fn, arg, cleanup_hook_counter_++ }); @@ -1041,7 +1049,7 @@ void Environment::AddCleanupHook(void (*fn)(void*), void* arg) { CHECK_EQ(insertion_info.second, true); } -void Environment::RemoveCleanupHook(void (*fn)(void*), void* arg) { +void Environment::RemoveCleanupHook(CleanupCallback fn, void* arg) { CleanupHookCallback search { fn, arg, 0 }; cleanup_hooks_.erase(search); } diff --git a/src/env.cc b/src/env.cc index 3ad13dd94a0954..7155ae67ac625b 100644 --- a/src/env.cc +++ b/src/env.cc @@ -92,12 +92,13 @@ void IsolateData::DeserializeProperties(const std::vector* indexes) { #define VS(PropertyName, StringValue) V(String, PropertyName) #define V(TypeName, PropertyName) \ do { \ - MaybeLocal field = \ + MaybeLocal maybe_field = \ isolate_->GetDataFromSnapshotOnce((*indexes)[i++]); \ - if (field.IsEmpty()) { \ + Local field; \ + if (!maybe_field.ToLocal(&field)) { \ fprintf(stderr, "Failed to deserialize " #PropertyName "\n"); \ } \ - PropertyName##_.Set(isolate_, field.ToLocalChecked()); \ + PropertyName##_.Set(isolate_, field); \ } while (0); PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP) PER_ISOLATE_SYMBOL_PROPERTIES(VY) @@ -108,12 +109,13 @@ void IsolateData::DeserializeProperties(const std::vector* indexes) { #undef VP for (size_t j = 0; j < AsyncWrap::PROVIDERS_LENGTH; j++) { - MaybeLocal field = + MaybeLocal maybe_field = isolate_->GetDataFromSnapshotOnce((*indexes)[i++]); - if (field.IsEmpty()) { + Local field; + if (!maybe_field.ToLocal(&field)) { fprintf(stderr, "Failed to deserialize AsyncWrap provider %zu\n", j); } - async_wrap_providers_[j].Set(isolate_, field.ToLocalChecked()); + async_wrap_providers_[j].Set(isolate_, field); } } @@ -205,10 +207,7 @@ void IsolateData::MemoryInfo(MemoryTracker* tracker) const { #define V(PropertyName, StringValue) \ tracker->TrackField(#PropertyName, PropertyName()); PER_ISOLATE_SYMBOL_PROPERTIES(V) -#undef V -#define V(PropertyName, StringValue) \ - tracker->TrackField(#PropertyName, PropertyName()); PER_ISOLATE_STRING_PROPERTIES(V) #undef V @@ -344,9 +343,10 @@ Environment::Environment(IsolateData* isolate_data, // easier to modify them after Environment creation. The defaults are // part of the per-Isolate option set, for which in turn the defaults are // part of the per-process option set. - options_.reset(new EnvironmentOptions(*isolate_data->options()->per_env)); - inspector_host_port_.reset( - new ExclusiveAccess(options_->debug_options().host_port)); + options_ = std::make_shared( + *isolate_data->options()->per_env); + inspector_host_port_ = std::make_shared>( + options_->debug_options().host_port); if (!(flags_ & EnvironmentFlags::kOwnsProcessState)) { set_abort_on_uncaught_exception(false); @@ -495,14 +495,25 @@ void Environment::InitializeLibuv() { uv_check_start(immediate_check_handle(), CheckImmediate); + // Inform V8's CPU profiler when we're idle. The profiler is sampling-based + // but not all samples are created equal; mark the wall clock time spent in + // epoll_wait() and friends so profiling tools can filter it out. The samples + // still end up in v8.log but with state=IDLE rather than state=EXTERNAL. + uv_prepare_init(event_loop(), &idle_prepare_handle_); + uv_check_init(event_loop(), &idle_check_handle_); + uv_async_init( event_loop(), &task_queues_async_, [](uv_async_t* async) { Environment* env = ContainerOf( &Environment::task_queues_async_, async); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); env->RunAndClearNativeImmediates(); }); + uv_unref(reinterpret_cast(&idle_prepare_handle_)); + uv_unref(reinterpret_cast(&idle_check_handle_)); uv_unref(reinterpret_cast(&task_queues_async_)); { @@ -519,6 +530,8 @@ void Environment::InitializeLibuv() { // the one environment per process setup, but will be called in // FreeEnvironment. RegisterHandleCleanups(); + + StartProfilerIdleNotifier(); } void Environment::ExitEnv() { @@ -546,6 +559,8 @@ void Environment::RegisterHandleCleanups() { register_handle(reinterpret_cast(timer_handle())); register_handle(reinterpret_cast(immediate_check_handle())); register_handle(reinterpret_cast(immediate_idle_handle())); + register_handle(reinterpret_cast(&idle_prepare_handle_)); + register_handle(reinterpret_cast(&idle_check_handle_)); register_handle(reinterpret_cast(&task_queues_async_)); } @@ -577,6 +592,17 @@ void Environment::CleanupHandles() { } } +void Environment::StartProfilerIdleNotifier() { + uv_prepare_start(&idle_prepare_handle_, [](uv_prepare_t* handle) { + Environment* env = ContainerOf(&Environment::idle_prepare_handle_, handle); + env->isolate()->SetIdle(true); + }); + uv_check_start(&idle_check_handle_, [](uv_check_t* handle) { + Environment* env = ContainerOf(&Environment::idle_check_handle_, handle); + env->isolate()->SetIdle(false); + }); +} + void Environment::PrintSyncTrace() const { if (!trace_sync_io_) return; diff --git a/src/env.h b/src/env.h index f89365a1aa7ffa..9c435f12db0e2d 100644 --- a/src/env.h +++ b/src/env.h @@ -213,6 +213,8 @@ constexpr size_t kFsStatsBufferLength = V(dh_string, "DH") \ V(dns_a_string, "A") \ V(dns_aaaa_string, "AAAA") \ + V(dns_caa_string, "CAA") \ + V(dns_critical_string, "critical") \ V(dns_cname_string, "CNAME") \ V(dns_mx_string, "MX") \ V(dns_naptr_string, "NAPTR") \ @@ -787,7 +789,9 @@ class ShouldNotAbortOnUncaughtScope { class CleanupHookCallback { public: - CleanupHookCallback(void (*fn)(void*), + typedef void (*Callback)(void*); + + CleanupHookCallback(Callback fn, void* arg, uint64_t insertion_order_counter) : fn_(fn), arg_(arg), insertion_order_counter_(insertion_order_counter) {} @@ -807,7 +811,7 @@ class CleanupHookCallback { private: friend class Environment; - void (*fn_)(void*); + Callback fn_; void* arg_; // We keep track of the insertion order for these objects, so that we can @@ -915,6 +919,8 @@ class Environment : public MemoryRetainer { inline void AssignToContext(v8::Local context, const ContextInfo& info); + void StartProfilerIdleNotifier(); + inline v8::Isolate* isolate() const; inline uv_loop_t* event_loop() const; inline void TryLoadAddon( @@ -1037,6 +1043,9 @@ class Environment : public MemoryRetainer { inline bool filehandle_close_warning() const; inline void set_filehandle_close_warning(bool on); + inline void set_source_maps_enabled(bool on); + inline bool source_maps_enabled() const; + inline void ThrowError(const char* errmsg); inline void ThrowTypeError(const char* errmsg); inline void ThrowRangeError(const char* errmsg); @@ -1166,8 +1175,9 @@ class Environment : public MemoryRetainer { void ScheduleTimer(int64_t duration); void ToggleTimerRef(bool ref); - inline void AddCleanupHook(void (*fn)(void*), void* arg); - inline void RemoveCleanupHook(void (*fn)(void*), void* arg); + using CleanupCallback = CleanupHookCallback::Callback; + inline void AddCleanupHook(CleanupCallback cb, void* arg); + inline void RemoveCleanupHook(CleanupCallback cb, void* arg); void RunCleanup(); static void BuildEmbedderGraph(v8::Isolate* isolate, @@ -1244,6 +1254,8 @@ class Environment : public MemoryRetainer { uv_timer_t timer_handle_; uv_check_t immediate_check_handle_; uv_idle_t immediate_idle_handle_; + uv_prepare_t idle_prepare_handle_; + uv_check_t idle_check_handle_; uv_async_t task_queues_async_; int64_t task_queues_async_refs_ = 0; @@ -1257,6 +1269,8 @@ class Environment : public MemoryRetainer { bool emit_env_nonstring_warning_ = true; bool emit_err_name_warning_ = true; bool emit_filehandle_warning_ = true; + bool source_maps_enabled_ = false; + size_t async_callback_scope_depth_ = 0; std::vector destroy_async_id_list_; diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index e6ab76abf56168..4b7997a02fdc30 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -15,6 +15,7 @@ #include "node_process.h" #include "node_url.h" #include "util-inl.h" +#include "timer_wrap.h" #include "v8-inspector.h" #include "v8-platform.h" @@ -326,86 +327,6 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel, bool retaining_context_; }; -class InspectorTimer { - public: - InspectorTimer(Environment* env, - double interval_s, - V8InspectorClient::TimerCallback callback, - void* data) : env_(env), - callback_(callback), - data_(data) { - uv_timer_init(env->event_loop(), &timer_); - int64_t interval_ms = 1000 * interval_s; - uv_timer_start(&timer_, OnTimer, interval_ms, interval_ms); - timer_.data = this; - } - - InspectorTimer(const InspectorTimer&) = delete; - - void Stop() { - if (timer_.data == nullptr) return; - - timer_.data = nullptr; - uv_timer_stop(&timer_); - env_->CloseHandle(reinterpret_cast(&timer_), TimerClosedCb); - } - - inline Environment* env() const { return env_; } - - private: - static void OnTimer(uv_timer_t* uvtimer) { - InspectorTimer* timer = node::ContainerOf(&InspectorTimer::timer_, uvtimer); - timer->callback_(timer->data_); - } - - static void TimerClosedCb(uv_handle_t* uvtimer) { - std::unique_ptr timer( - node::ContainerOf(&InspectorTimer::timer_, - reinterpret_cast(uvtimer))); - // Unique_ptr goes out of scope here and pointer is deleted. - } - - ~InspectorTimer() = default; - - Environment* env_; - uv_timer_t timer_; - V8InspectorClient::TimerCallback callback_; - void* data_; - - friend std::unique_ptr::deleter_type; -}; - -class InspectorTimerHandle { - public: - InspectorTimerHandle(Environment* env, double interval_s, - V8InspectorClient::TimerCallback callback, void* data) { - timer_ = new InspectorTimer(env, interval_s, callback, data); - - env->AddCleanupHook(CleanupHook, this); - } - - InspectorTimerHandle(const InspectorTimerHandle&) = delete; - - ~InspectorTimerHandle() { - Stop(); - } - - private: - void Stop() { - if (timer_ != nullptr) { - timer_->env()->RemoveCleanupHook(CleanupHook, this); - timer_->Stop(); - } - timer_ = nullptr; - } - - static void CleanupHook(void* data) { - static_cast(data)->Stop(); - } - - InspectorTimer* timer_; -}; - class SameThreadInspectorSession : public InspectorSession { public: SameThreadInspectorSession( @@ -602,9 +523,12 @@ class NodeInspectorClient : public V8InspectorClient { void startRepeatingTimer(double interval_s, TimerCallback callback, void* data) override { - timers_.emplace(std::piecewise_construct, std::make_tuple(data), - std::make_tuple(env_, interval_s, callback, - data)); + auto result = + timers_.emplace(std::piecewise_construct, std::make_tuple(data), + std::make_tuple(env_, [=]() { callback(data); })); + CHECK(result.second); + uint64_t interval = 1000 * interval_s; + result.first->second.Update(interval, interval); } void cancelTimer(void* data) override { @@ -724,7 +648,7 @@ class NodeInspectorClient : public V8InspectorClient { bool running_nested_loop_ = false; std::unique_ptr client_; // Note: ~ChannelImpl may access timers_ so timers_ has to come first. - std::unordered_map timers_; + std::unordered_map timers_; std::unordered_map> channels_; int next_session_id_ = 1; bool waiting_for_resume_ = false; diff --git a/src/inspector_socket.cc b/src/inspector_socket.cc index a48300c93bc077..cacff747d08bc3 100644 --- a/src/inspector_socket.cc +++ b/src/inspector_socket.cc @@ -1,7 +1,7 @@ #include "inspector_socket.h" #include "llhttp.h" -#include "base64.h" +#include "base64-inl.h" #include "util-inl.h" #include "openssl/sha.h" // Sha-1 hash @@ -318,7 +318,7 @@ class WsHandler : public ProtocolHandler { WsHandler(InspectorSocket* inspector, TcpHolder::Pointer tcp) : ProtocolHandler(inspector, std::move(tcp)), OnCloseSent(&WsHandler::WaitForCloseReply), - OnCloseRecieved(&WsHandler::CloseFrameReceived), + OnCloseReceived(&WsHandler::CloseFrameReceived), dispose_(false) { } void AcceptUpgrade(const std::string& accept_key) override { } @@ -369,7 +369,7 @@ class WsHandler : public ProtocolHandler { } void WaitForCloseReply() { - OnCloseRecieved = &WsHandler::OnEof; + OnCloseReceived = &WsHandler::OnEof; } void SendClose() { @@ -396,7 +396,7 @@ class WsHandler : public ProtocolHandler { OnEof(); bytes_consumed = 0; } else if (r == FRAME_CLOSE) { - (this->*OnCloseRecieved)(); + (this->*OnCloseReceived)(); bytes_consumed = 0; } else if (r == FRAME_OK) { delegate()->OnWsFrame(output); @@ -406,7 +406,7 @@ class WsHandler : public ProtocolHandler { Callback OnCloseSent; - Callback OnCloseRecieved; + Callback OnCloseReceived; bool dispose_; }; diff --git a/src/js_native_api.h b/src/js_native_api.h index 5daa20f9040096..e804d1d45d2365 100644 --- a/src/js_native_api.h +++ b/src/js_native_api.h @@ -17,7 +17,7 @@ // functions available in a new version of N-API that is not yet ported in all // LTS versions, they can set NAPI_VERSION knowing that they have specifically // depended on that version. -#define NAPI_VERSION 7 +#define NAPI_VERSION 8 #endif #endif @@ -539,7 +539,7 @@ NAPI_EXTERN napi_status napi_is_detached_arraybuffer(napi_env env, bool* result); #endif // NAPI_VERSION >= 7 -#ifdef NAPI_EXPERIMENTAL +#if NAPI_VERSION >= 8 // Type tagging NAPI_EXTERN napi_status napi_type_tag_object(napi_env env, napi_value value, @@ -554,7 +554,7 @@ NAPI_EXTERN napi_status napi_object_freeze(napi_env env, napi_value object); NAPI_EXTERN napi_status napi_object_seal(napi_env env, napi_value object); -#endif // NAPI_EXPERIMENTAL +#endif // NAPI_VERSION >= 8 EXTERN_C_END diff --git a/src/js_native_api_types.h b/src/js_native_api_types.h index 7011c80e671a15..6aba06629b3154 100644 --- a/src/js_native_api_types.h +++ b/src/js_native_api_types.h @@ -31,7 +31,7 @@ typedef enum { // from instance properties. Ignored by napi_define_properties. napi_static = 1 << 10, -#ifdef NAPI_EXPERIMENTAL +#if NAPI_VERSION >= 8 // Default for class methods. napi_default_method = napi_writable | napi_configurable, @@ -39,7 +39,7 @@ typedef enum { napi_default_jsproperty = napi_writable | napi_enumerable | napi_configurable, -#endif // NAPI_EXPERIMENTAL +#endif // NAPI_VERSION >= 8 } napi_property_attributes; typedef enum { @@ -150,11 +150,11 @@ typedef enum { } napi_key_conversion; #endif // NAPI_VERSION >= 6 -#ifdef NAPI_EXPERIMENTAL +#if NAPI_VERSION >= 8 typedef struct { uint64_t lower; uint64_t upper; } napi_type_tag; -#endif // NAPI_EXPERIMENTAL +#endif // NAPI_VERSION >= 8 #endif // SRC_JS_NATIVE_API_TYPES_H_ diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc index e037c4297de0c5..54bcf2e9715a0a 100644 --- a/src/js_native_api_v8.cc +++ b/src/js_native_api_v8.cc @@ -270,8 +270,32 @@ class RefBase : protected Finalizer, RefTracker { protected: inline void Finalize(bool is_env_teardown = false) override { + // In addition to being called during environment teardown, this method is + // also the entry point for the garbage collector. During environment + // teardown we have to remove the garbage collector's reference to this + // method so that, if, as part of the user's callback, JS gets executed, + // resulting in a garbage collection pass, this method is not re-entered as + // part of that pass, because that'll cause a double free (as seen in + // https://github.com/nodejs/node/issues/37236). + // + // Since this class does not have access to the V8 persistent reference, + // this method is overridden in the `Reference` class below. Therein the + // weak callback is removed, ensuring that the garbage collector does not + // re-enter this method, and the method chains up to continue the process of + // environment-teardown-induced finalization. + + // During environment teardown we have to convert a strong reference to + // a weak reference to force the deferring behavior if the user's finalizer + // happens to delete this reference so that the code in this function that + // follows the call to the user's finalizer may safely access variables from + // this instance. + if (is_env_teardown && RefCount() > 0) _refcount = 0; + if (_finalize_callback != nullptr) { - _env->CallFinalizer(_finalize_callback, _finalize_data, _finalize_hint); + // This ensures that we never call the finalizer twice. + napi_finalize fini = _finalize_callback; + _finalize_callback = nullptr; + _env->CallFinalizer(fini, _finalize_data, _finalize_hint); } // this is safe because if a request to delete the reference @@ -346,6 +370,23 @@ class Reference : public RefBase { } } + protected: + inline void Finalize(bool is_env_teardown = false) override { + if (is_env_teardown) env_teardown_finalize_started_ = true; + if (!is_env_teardown && env_teardown_finalize_started_) return; + + // During env teardown, `~napi_env()` alone is responsible for finalizing. + // Thus, we don't want any stray gc passes to trigger a second call to + // `Finalize()`, so let's reset the persistent here if nothing is + // keeping it alive. + if (is_env_teardown && _persistent.IsWeak()) { + _persistent.ClearWeak(); + } + + // Chain up to perform the rest of the finalization. + RefBase::Finalize(is_env_teardown); + } + private: // The N-API finalizer callback may make calls into the engine. V8's heap is // not in a consistent state during the weak callback, and therefore it does @@ -367,6 +408,7 @@ class Reference : public RefBase { data.GetParameter()->Finalize(); } + bool env_teardown_finalize_started_ = false; v8impl::Persistent _persistent; }; diff --git a/src/large_pages/node_large_page.cc b/src/large_pages/node_large_page.cc index a4b109d1b0ef69..eb546c581a1652 100644 --- a/src/large_pages/node_large_page.cc +++ b/src/large_pages/node_large_page.cc @@ -258,21 +258,21 @@ struct text_region FindNodeTextRegion() { #if defined(__linux__) bool IsTransparentHugePagesEnabled() { - std::ifstream ifs; - - ifs.open("/sys/kernel/mm/transparent_hugepage/enabled"); - if (!ifs) { + // File format reference: + // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/mm/huge_memory.c?id=13391c60da3308ed9980de0168f74cce6c62ac1d#n163 + const char* filename = "/sys/kernel/mm/transparent_hugepage/enabled"; + std::ifstream config_stream(filename, std::ios::in); + if (!config_stream.good()) { PrintWarning("could not open /sys/kernel/mm/transparent_hugepage/enabled"); return false; } - std::string always, madvise; - if (ifs.is_open()) { - while (ifs >> always >> madvise) {} - } - ifs.close(); - - return always == "[always]" || madvise == "[madvise]"; + std::string token; + config_stream >> token; + if ("[always]" == token) return true; + config_stream >> token; + if ("[madvise]" == token) return true; + return false; } #elif defined(__FreeBSD__) bool IsSuperPagesEnabled() { diff --git a/src/large_pages/node_text_start.S b/src/large_pages/node_text_start.S index 1609b254f0495a..3227b62464932c 100644 --- a/src/large_pages/node_text_start.S +++ b/src/large_pages/node_text_start.S @@ -1,3 +1,6 @@ +#if defined(__ELF__) +.section .note.GNU-stack,"",@progbits +#endif .text .align 0x2000 .global __node_text_start diff --git a/src/memory_tracker-inl.h b/src/memory_tracker-inl.h index 4167064847253e..0ba44f1f16efbb 100644 --- a/src/memory_tracker-inl.h +++ b/src/memory_tracker-inl.h @@ -22,7 +22,7 @@ inline const char* GetNodeName(const char* node_name, const char* edge_name) { class MemoryRetainerNode : public v8::EmbedderGraph::Node { public: inline MemoryRetainerNode(MemoryTracker* tracker, - const MemoryRetainer* retainer) + const MemoryRetainer* retainer) : retainer_(retainer) { CHECK_NOT_NULL(retainer_); v8::HandleScope handle_scope(tracker->isolate()); @@ -34,9 +34,9 @@ class MemoryRetainerNode : public v8::EmbedderGraph::Node { } inline MemoryRetainerNode(MemoryTracker* tracker, - const char* name, - size_t size, - bool is_root_node = false) + const char* name, + size_t size, + bool is_root_node = false) : retainer_(nullptr) { name_ = name; size_ = size; diff --git a/src/module_wrap.cc b/src/module_wrap.cc index f778b089dc4009..90484774eb29ab 100644 --- a/src/module_wrap.cc +++ b/src/module_wrap.cc @@ -291,7 +291,9 @@ void ModuleWrap::Link(const FunctionCallbackInfo& args) { Local resolve_return_value = maybe_resolve_return_value.ToLocalChecked(); if (!resolve_return_value->IsPromise()) { - env->ThrowError("linking error, expected resolver to return a promise"); + THROW_ERR_VM_MODULE_LINK_FAILURE( + env, "request for '%s' did not return promise", specifier_std); + return; } Local resolve_promise = resolve_return_value.As(); obj->resolve_cache_[specifier_std].Reset(env->isolate(), resolve_promise); @@ -350,6 +352,7 @@ void ModuleWrap::Evaluate(const FunctionCallbackInfo& args) { ShouldNotAbortOnUncaughtScope no_abort_scope(env); TryCatchScope try_catch(env); + Isolate::SafeForTerminationScope safe_for_termination(env->isolate()); bool timed_out = false; bool received_signal = false; @@ -484,17 +487,19 @@ MaybeLocal ModuleWrap::ResolveCallback(Local context, Isolate* isolate = env->isolate(); + Utf8Value specifier_utf8(isolate, specifier); + std::string specifier_std(*specifier_utf8, specifier_utf8.length()); + ModuleWrap* dependent = GetFromModule(env, referrer); if (dependent == nullptr) { - env->ThrowError("linking error, null dep"); + THROW_ERR_VM_MODULE_LINK_FAILURE( + env, "request for '%s' is from invalid module", specifier_std); return MaybeLocal(); } - Utf8Value specifier_utf8(isolate, specifier); - std::string specifier_std(*specifier_utf8, specifier_utf8.length()); - if (dependent->resolve_cache_.count(specifier_std) != 1) { - env->ThrowError("linking error, not in local cache"); + THROW_ERR_VM_MODULE_LINK_FAILURE( + env, "request for '%s' is not in cache", specifier_std); return MaybeLocal(); } @@ -502,15 +507,15 @@ MaybeLocal ModuleWrap::ResolveCallback(Local context, dependent->resolve_cache_[specifier_std].Get(isolate); if (resolve_promise->State() != Promise::kFulfilled) { - env->ThrowError("linking error, dependency promises must be resolved on " - "instantiate"); + THROW_ERR_VM_MODULE_LINK_FAILURE( + env, "request for '%s' is not yet fulfilled", specifier_std); return MaybeLocal(); } Local module_object = resolve_promise->Result().As(); if (module_object.IsEmpty() || !module_object->IsObject()) { - env->ThrowError("linking error, expected a valid module object from " - "resolver"); + THROW_ERR_VM_MODULE_LINK_FAILURE( + env, "request for '%s' did not return an object", specifier_std); return MaybeLocal(); } @@ -664,7 +669,14 @@ MaybeLocal ModuleWrap::SyntheticModuleEvaluationStepsCallback( try_catch.ReThrow(); return MaybeLocal(); } - return Undefined(isolate); + + Local resolver; + if (!Promise::Resolver::New(context).ToLocal(&resolver)) { + return MaybeLocal(); + } + + resolver->Resolve(context, Undefined(isolate)).ToChecked(); + return resolver->GetPromise(); } void ModuleWrap::SetSyntheticExport(const FunctionCallbackInfo& args) { diff --git a/src/node_api.h b/src/node_api.h index 786988e296b8b2..7c2c84398cc5fe 100644 --- a/src/node_api.h +++ b/src/node_api.h @@ -250,7 +250,7 @@ napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func); #endif // NAPI_VERSION >= 4 -#ifdef NAPI_EXPERIMENTAL +#if NAPI_VERSION >= 8 NAPI_EXTERN napi_status napi_add_async_cleanup_hook( napi_env env, @@ -261,7 +261,7 @@ NAPI_EXTERN napi_status napi_add_async_cleanup_hook( NAPI_EXTERN napi_status napi_remove_async_cleanup_hook( napi_async_cleanup_hook_handle remove_handle); -#endif // NAPI_EXPERIMENTAL +#endif // NAPI_VERSION >= 8 EXTERN_C_END diff --git a/src/node_api_types.h b/src/node_api_types.h index 0e400e9676df5b..58ffc61b3a5f51 100644 --- a/src/node_api_types.h +++ b/src/node_api_types.h @@ -41,10 +41,10 @@ typedef struct { const char* release; } napi_node_version; -#ifdef NAPI_EXPERIMENTAL +#if NAPI_VERSION >= 8 typedef struct napi_async_cleanup_hook_handle__* napi_async_cleanup_hook_handle; typedef void (*napi_async_cleanup_hook)(napi_async_cleanup_hook_handle handle, void* data); -#endif // NAPI_EXPERIMENTAL +#endif // NAPI_VERSION >= 8 #endif // SRC_NODE_API_TYPES_H_ diff --git a/src/node_buffer.cc b/src/node_buffer.cc index 217854255d2806..3a943cc6369deb 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -495,18 +495,19 @@ void StringSlice(const FunctionCallbackInfo& args) { size_t length = end - start; Local error; - MaybeLocal ret = + MaybeLocal maybe_ret = StringBytes::Encode(isolate, buffer.data() + start, length, encoding, &error); - if (ret.IsEmpty()) { + Local ret; + if (!maybe_ret.ToLocal(&ret)) { CHECK(!error.IsEmpty()); isolate->ThrowException(error); return; } - args.GetReturnValue().Set(ret.ToLocalChecked()); + args.GetReturnValue().Set(ret); } diff --git a/src/node_contextify.cc b/src/node_contextify.cc index 848e883a829a97..ddfe043cc41e69 100644 --- a/src/node_contextify.cc +++ b/src/node_contextify.cc @@ -209,7 +209,8 @@ MaybeLocal ContextifyContext::CreateV8Context( return MaybeLocal(); } - ctx->SetSecurityToken(env->context()->GetSecurityToken()); + Local context = env->context(); + ctx->SetSecurityToken(context->GetSecurityToken()); // We need to tie the lifetime of the sandbox object with the lifetime of // newly created context. We do this by making them hold references to each @@ -218,7 +219,7 @@ MaybeLocal ContextifyContext::CreateV8Context( // directly in an Object, we instead hold onto the new context's global // object instead (which then has a reference to the context). ctx->SetEmbedderData(ContextEmbedderIndex::kSandboxObject, sandbox_obj); - sandbox_obj->SetPrivate(env->context(), + sandbox_obj->SetPrivate(context, env->contextify_global_private_symbol(), ctx->Global()); @@ -393,16 +394,17 @@ void ContextifyContext::PropertySetterCallback( if (ctx->context_.IsEmpty()) return; + Local context = ctx->context(); auto attributes = PropertyAttribute::None; bool is_declared_on_global_proxy = ctx->global_proxy() - ->GetRealNamedPropertyAttributes(ctx->context(), property) + ->GetRealNamedPropertyAttributes(context, property) .To(&attributes); bool read_only = static_cast(attributes) & static_cast(PropertyAttribute::ReadOnly); bool is_declared_on_sandbox = ctx->sandbox() - ->GetRealNamedPropertyAttributes(ctx->context(), property) + ->GetRealNamedPropertyAttributes(context, property) .To(&attributes); read_only = read_only || (static_cast(attributes) & @@ -440,7 +442,7 @@ void ContextifyContext::PropertySetterCallback( args.GetReturnValue().Set(false); } - USE(ctx->sandbox()->Set(ctx->context(), property, value)); + USE(ctx->sandbox()->Set(context, property, value)); } // static @@ -481,7 +483,7 @@ void ContextifyContext::PropertyDefinerCallback( auto attributes = PropertyAttribute::None; bool is_declared = - ctx->global_proxy()->GetRealNamedPropertyAttributes(ctx->context(), + ctx->global_proxy()->GetRealNamedPropertyAttributes(context, property) .To(&attributes); bool read_only = @@ -655,8 +657,10 @@ void ContextifyScript::Init(Environment* env, Local target) { env->SetProtoMethod(script_tmpl, "runInContext", RunInContext); env->SetProtoMethod(script_tmpl, "runInThisContext", RunInThisContext); - target->Set(env->context(), class_name, - script_tmpl->GetFunction(env->context()).ToLocalChecked()).Check(); + Local context = env->context(); + + target->Set(context, class_name, + script_tmpl->GetFunction(context).ToLocalChecked()).Check(); env->set_script_context_constructor_template(script_tmpl); } @@ -776,9 +780,10 @@ void ContextifyScript::New(const FunctionCallbackInfo& args) { } contextify_script->script_.Reset(isolate, v8_script.ToLocalChecked()); + Local env_context = env->context(); if (compile_options == ScriptCompiler::kConsumeCodeCache) { args.This()->Set( - env->context(), + env_context, env->cached_data_rejected_string(), Boolean::New(isolate, source.GetCachedData()->rejected)).Check(); } else if (produce_cached_data) { @@ -790,12 +795,12 @@ void ContextifyScript::New(const FunctionCallbackInfo& args) { env, reinterpret_cast(cached_data->data), cached_data->length); - args.This()->Set(env->context(), + args.This()->Set(env_context, env->cached_data_string(), buf.ToLocalChecked()).Check(); } args.This()->Set( - env->context(), + env_context, env->cached_data_produced_string(), Boolean::New(isolate, cached_data_produced)).Check(); } @@ -885,7 +890,8 @@ void ContextifyScript::RunInContext(const FunctionCallbackInfo& args) { ContextifyContext::ContextFromContextifiedSandbox(env, sandbox); CHECK_NOT_NULL(contextify_context); - if (contextify_context->context().IsEmpty()) + Local context = contextify_context->context(); + if (context.IsEmpty()) return; TRACE_EVENT_NESTABLE_ASYNC_BEGIN0( @@ -904,7 +910,7 @@ void ContextifyScript::RunInContext(const FunctionCallbackInfo& args) { bool break_on_first_line = args[4]->IsTrue(); // Do the eval within the context - Context::Scope context_scope(contextify_context->context()); + Context::Scope context_scope(context); EvalMachine(contextify_context->env(), timeout, display_errors, @@ -933,6 +939,7 @@ bool ContextifyScript::EvalMachine(Environment* env, return false; } TryCatchScope try_catch(env); + Isolate::SafeForTerminationScope safe_for_termination(env->isolate()); ContextifyScript* wrapped_script; ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder(), false); Local unbound_script = diff --git a/src/node_crypto.cc b/src/node_crypto.cc index ed886abd749661..bd40705e6b1ae0 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -67,7 +67,9 @@ namespace crypto { using node::THROW_ERR_TLS_INVALID_PROTOCOL_METHOD; using v8::Array; +using v8::ArrayBuffer; using v8::ArrayBufferView; +using v8::BackingStore; using v8::Boolean; using v8::ConstructorBehavior; using v8::Context; @@ -97,6 +99,7 @@ using v8::SideEffectType; using v8::Signature; using v8::String; using v8::Uint32; +using v8::Uint8Array; using v8::Undefined; using v8::Value; @@ -6944,6 +6947,35 @@ void SetFipsCrypto(const FunctionCallbackInfo& args) { } #endif /* NODE_FIPS_MODE */ +namespace { +// SecureBuffer uses openssl to allocate a Uint8Array using +// OPENSSL_secure_malloc. Because we do not yet actually +// make use of secure heap, this has the same semantics as +// using OPENSSL_malloc. However, if the secure heap is +// initialized, SecureBuffer will automatically use it. +void SecureBuffer(const FunctionCallbackInfo& args) { + CHECK(args[0]->IsUint32()); + Environment* env = Environment::GetCurrent(args); + uint32_t len = args[0].As()->Value(); + char* data = static_cast(OPENSSL_secure_malloc(len)); + if (data == nullptr) { + // There's no memory available for the allocation. + // Return nothing. + return; + } + memset(data, 0, len); + std::shared_ptr store = + ArrayBuffer::NewBackingStore( + data, + len, + [](void* data, size_t len, void* deleter_data) { + OPENSSL_secure_clear_free(data, len); + }, + data); + Local buffer = ArrayBuffer::New(env->isolate(), store); + args.GetReturnValue().Set(Uint8Array::New(buffer, 0, len)); +} +} // namespace void Initialize(Local target, Local unused, @@ -7038,6 +7070,8 @@ void Initialize(Local target, #ifndef OPENSSL_NO_SCRYPT env->SetMethod(target, "scrypt", Scrypt); #endif // OPENSSL_NO_SCRYPT + + env->SetMethod(target, "secureBuffer", SecureBuffer); } } // namespace crypto diff --git a/src/node_env_var.cc b/src/node_env_var.cc index 817e2d924079b8..2b118326e5c4f6 100644 --- a/src/node_env_var.cc +++ b/src/node_env_var.cc @@ -178,9 +178,7 @@ Local RealEnvStore::Enumerate(Isolate* isolate) const { for (int i = 0; i < count; i++) { #ifdef _WIN32 // If the key starts with '=' it is a hidden environment variable. - // The '\0' check is a workaround for the bug behind - // https://github.com/libuv/libuv/pull/2473 and can be removed later. - if (items[i].name[0] == '=' || items[i].name[0] == '\0') continue; + if (items[i].name[0] == '=') continue; #endif MaybeLocal str = String::NewFromUtf8(isolate, items[i].name); if (str.IsEmpty()) { diff --git a/src/node_errors.cc b/src/node_errors.cc index 6961e748c06ad9..c69ced947a4e33 100644 --- a/src/node_errors.cc +++ b/src/node_errors.cc @@ -55,9 +55,18 @@ static std::string GetErrorSource(Isolate* isolate, MaybeLocal source_line_maybe = message->GetSourceLine(context); node::Utf8Value encoded_source(isolate, source_line_maybe.ToLocalChecked()); std::string sourceline(*encoded_source, encoded_source.length()); + *added_exception_line = false; + + // If source maps have been enabled, the exception line will instead be + // added in the JavaScript context: + Environment* env = Environment::GetCurrent(isolate); + const bool has_source_map_url = + !message->GetScriptOrigin().SourceMapUrl().IsEmpty(); + if (has_source_map_url && env != nullptr && env->source_maps_enabled()) { + return sourceline; + } if (sourceline.find("node-do-not-add-exception-line") != std::string::npos) { - *added_exception_line = false; return sourceline; } @@ -104,6 +113,13 @@ static std::string GetErrorSource(Isolate* isolate, linenum, sourceline.c_str()); CHECK_GT(buf.size(), 0); + *added_exception_line = true; + + if (start > end || + start < 0 || + static_cast(end) > sourceline.size()) { + return buf; + } constexpr int kUnderlineBufsize = 1020; char underline_buf[kUnderlineBufsize + 4]; @@ -126,7 +142,6 @@ static std::string GetErrorSource(Isolate* isolate, CHECK_LE(off, kUnderlineBufsize); underline_buf[off++] = '\n'; - *added_exception_line = true; return buf + std::string(underline_buf, off); } @@ -801,6 +816,11 @@ void SetPrepareStackTraceCallback(const FunctionCallbackInfo& args) { env->set_prepare_stack_trace_callback(args[0].As()); } +static void EnableSourceMaps(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + env->set_source_maps_enabled(true); +} + static void SetEnhanceStackForFatalException( const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); @@ -839,6 +859,7 @@ void Initialize(Local target, Environment* env = Environment::GetCurrent(context); env->SetMethod( target, "setPrepareStackTraceCallback", SetPrepareStackTraceCallback); + env->SetMethod(target, "enableSourceMaps", EnableSourceMaps); env->SetMethod(target, "setEnhanceStackForFatalException", SetEnhanceStackForFatalException); @@ -924,7 +945,7 @@ void TriggerUncaughtException(Isolate* isolate, return; } - MaybeLocal handled; + MaybeLocal maybe_handled; if (env->can_call_into_js()) { // We do not expect the global uncaught exception itself to throw any more // exceptions. If it does, exit the current Node.js instance. @@ -938,7 +959,7 @@ void TriggerUncaughtException(Isolate* isolate, Local argv[2] = { error, Boolean::New(env->isolate(), from_promise) }; - handled = fatal_exception_function.As()->Call( + maybe_handled = fatal_exception_function.As()->Call( env->context(), process_object, arraysize(argv), argv); } @@ -946,7 +967,8 @@ void TriggerUncaughtException(Isolate* isolate, // instance so return to continue the exit routine. // TODO(joyeecheung): return a Maybe here to prevent the caller from // stepping on the exit. - if (handled.IsEmpty()) { + Local handled; + if (!maybe_handled.ToLocal(&handled)) { return; } @@ -956,7 +978,7 @@ void TriggerUncaughtException(Isolate* isolate, // TODO(joyeecheung): This has been only checking that the return value is // exactly false. Investigate whether this can be turned to an "if true" // similar to how the worker global uncaught exception handler handles it. - if (!handled.ToLocalChecked()->IsFalse()) { + if (!handled->IsFalse()) { return; } diff --git a/src/node_errors.h b/src/node_errors.h index a4eff2569e5e3d..cec94023e19b56 100644 --- a/src/node_errors.h +++ b/src/node_errors.h @@ -3,6 +3,7 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#include "debug_utils-inl.h" #include "env.h" #include "v8.h" @@ -56,29 +57,40 @@ void OnFatalError(const char* location, const char* message); V(ERR_TLS_INVALID_PROTOCOL_METHOD, TypeError) \ V(ERR_TLS_PSK_SET_IDENTIY_HINT_FAILED, Error) \ V(ERR_VM_MODULE_CACHED_DATA_REJECTED, Error) \ + V(ERR_VM_MODULE_LINK_FAILURE, Error) \ V(ERR_WASI_NOT_STARTED, Error) \ V(ERR_WORKER_INIT_FAILED, Error) \ V(ERR_PROTO_ACCESS, Error) -#define V(code, type) \ - inline v8::Local code(v8::Isolate* isolate, \ - const char* message) { \ - v8::Local js_code = OneByteString(isolate, #code); \ - v8::Local js_msg = OneByteString(isolate, message); \ - v8::Local e = \ - v8::Exception::type(js_msg)->ToObject( \ - isolate->GetCurrentContext()).ToLocalChecked(); \ - e->Set(isolate->GetCurrentContext(), OneByteString(isolate, "code"), \ - js_code).Check(); \ - return e; \ - } \ - inline void THROW_ ## code(v8::Isolate* isolate, const char* message) { \ - isolate->ThrowException(code(isolate, message)); \ - } \ - inline void THROW_ ## code(Environment* env, const char* message) { \ - THROW_ ## code(env->isolate(), message); \ +#define V(code, type) \ + template \ + inline v8::Local code( \ + v8::Isolate* isolate, const char* format, Args&&... args) { \ + std::string message = SPrintF(format, std::forward(args)...); \ + v8::Local js_code = OneByteString(isolate, #code); \ + v8::Local js_msg = \ + OneByteString(isolate, message.c_str(), message.length()); \ + v8::Local e = v8::Exception::type(js_msg) \ + ->ToObject(isolate->GetCurrentContext()) \ + .ToLocalChecked(); \ + e->Set(isolate->GetCurrentContext(), \ + OneByteString(isolate, "code"), \ + js_code) \ + .Check(); \ + return e; \ + } \ + template \ + inline void THROW_##code( \ + v8::Isolate* isolate, const char* format, Args&&... args) { \ + isolate->ThrowException( \ + code(isolate, format, std::forward(args)...)); \ + } \ + template \ + inline void THROW_##code( \ + Environment* env, const char* format, Args&&... args) { \ + THROW_##code(env->isolate(), format, std::forward(args)...); \ } - ERRORS_WITH_CODE(V) +ERRORS_WITH_CODE(V) #undef V // Errors with predefined static messages diff --git a/src/node_file.cc b/src/node_file.cc index de5c455c7a2a85..cce3540a49027f 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -69,7 +69,6 @@ using v8::ObjectTemplate; using v8::Promise; using v8::String; using v8::Symbol; -using v8::Uint32; using v8::Undefined; using v8::Value; @@ -288,6 +287,7 @@ inline void FileHandle::Close() { void FileHandle::CloseReq::Resolve() { Isolate* isolate = env()->isolate(); HandleScope scope(isolate); + Context::Scope context_scope(env()->context()); InternalCallbackScope callback_scope(this); Local promise = promise_.Get(isolate); Local resolver = promise.As(); @@ -297,6 +297,7 @@ void FileHandle::CloseReq::Resolve() { void FileHandle::CloseReq::Reject(Local reason) { Isolate* isolate = env()->isolate(); HandleScope scope(isolate); + Context::Scope context_scope(env()->context()); InternalCallbackScope callback_scope(this); Local promise = promise_.Get(isolate); Local resolver = promise.As(); @@ -1830,8 +1831,8 @@ static void WriteBuffer(const FunctionCallbackInfo& args) { CHECK_LE(static_cast(off_64), buffer_length); const size_t off = static_cast(off_64); - CHECK(args[3]->IsInt32()); - const size_t len = static_cast(args[3].As()->Value()); + CHECK(IsSafeJsInt(args[3])); + const size_t len = static_cast(args[3].As()->Value()); CHECK(Buffer::IsWithinBounds(off, len, buffer_length)); CHECK_LE(len, buffer_length); CHECK_GE(off + len, off); @@ -2176,11 +2177,11 @@ static void Chown(const FunctionCallbackInfo& args) { BufferValue path(env->isolate(), args[0]); CHECK_NOT_NULL(*path); - CHECK(args[1]->IsUint32()); - const uv_uid_t uid = static_cast(args[1].As()->Value()); + CHECK(IsSafeJsInt(args[1])); + const uv_uid_t uid = static_cast(args[1].As()->Value()); - CHECK(args[2]->IsUint32()); - const uv_gid_t gid = static_cast(args[2].As()->Value()); + CHECK(IsSafeJsInt(args[2])); + const uv_gid_t gid = static_cast(args[2].As()->Value()); FSReqBase* req_wrap_async = GetReqWrap(args, 3); if (req_wrap_async != nullptr) { // chown(path, uid, gid, req) @@ -2209,11 +2210,11 @@ static void FChown(const FunctionCallbackInfo& args) { CHECK(args[0]->IsInt32()); const int fd = args[0].As()->Value(); - CHECK(args[1]->IsUint32()); - const uv_uid_t uid = static_cast(args[1].As()->Value()); + CHECK(IsSafeJsInt(args[1])); + const uv_uid_t uid = static_cast(args[1].As()->Value()); - CHECK(args[2]->IsUint32()); - const uv_gid_t gid = static_cast(args[2].As()->Value()); + CHECK(IsSafeJsInt(args[2])); + const uv_gid_t gid = static_cast(args[2].As()->Value()); FSReqBase* req_wrap_async = GetReqWrap(args, 3); if (req_wrap_async != nullptr) { // fchown(fd, uid, gid, req) @@ -2239,11 +2240,11 @@ static void LChown(const FunctionCallbackInfo& args) { BufferValue path(env->isolate(), args[0]); CHECK_NOT_NULL(*path); - CHECK(args[1]->IsUint32()); - const uv_uid_t uid = static_cast(args[1].As()->Value()); + CHECK(IsSafeJsInt(args[1])); + const uv_uid_t uid = static_cast(args[1].As()->Value()); - CHECK(args[2]->IsUint32()); - const uv_gid_t gid = static_cast(args[2].As()->Value()); + CHECK(IsSafeJsInt(args[2])); + const uv_gid_t gid = static_cast(args[2].As()->Value()); FSReqBase* req_wrap_async = GetReqWrap(args, 3); if (req_wrap_async != nullptr) { // lchown(path, uid, gid, req) @@ -2392,7 +2393,7 @@ void BindingData::MemoryInfo(MemoryTracker* tracker) const { } // TODO(addaleax): Remove once we're on C++17. -constexpr FastStringKey BindingData::binding_data_name; +constexpr FastStringKey BindingData::type_name; void Initialize(Local target, Local unused, @@ -2534,6 +2535,9 @@ void Initialize(Local target, use_promises_symbol).Check(); } +BindingData* FSReqBase::binding_data() { + return binding_data_.get(); +} } // namespace fs } // end namespace node diff --git a/src/node_file.h b/src/node_file.h index 2b157de5eb485d..9e652dc7a4afa4 100644 --- a/src/node_file.h +++ b/src/node_file.h @@ -27,7 +27,7 @@ class BindingData : public BaseObject { std::vector> file_handle_read_wrap_freelist; - static constexpr FastStringKey binding_data_name { "fs" }; + static constexpr FastStringKey type_name { "fs" }; void MemoryInfo(MemoryTracker* tracker) const override; SET_SELF_SIZE(BindingData) @@ -109,7 +109,7 @@ class FSReqBase : public ReqWrap { void MemoryInfo(MemoryTracker* tracker) const override; - BindingData* binding_data() { return binding_data_.get(); } + BindingData* binding_data(); private: std::unique_ptr continuation_data_; diff --git a/src/node_http2.cc b/src/node_http2.cc index d3db319def4ad4..fb23cb0f51073c 100644 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -2975,7 +2975,7 @@ void Http2State::MemoryInfo(MemoryTracker* tracker) const { } // TODO(addaleax): Remove once we're on C++17. -constexpr FastStringKey Http2State::binding_data_name; +constexpr FastStringKey Http2State::type_name; // Set up the process.binding('http2') binding. void Initialize(Local target, diff --git a/src/node_http2_state.h b/src/node_http2_state.h index cd29b207498dc0..7cf40ff1017ca3 100644 --- a/src/node_http2_state.h +++ b/src/node_http2_state.h @@ -127,7 +127,7 @@ class Http2State : public BaseObject { SET_SELF_SIZE(Http2State) SET_MEMORY_INFO_NAME(Http2State) - static constexpr FastStringKey binding_data_name { "http2" }; + static constexpr FastStringKey type_name { "http2" }; private: struct http2_state_internal { diff --git a/src/node_http_parser.cc b/src/node_http_parser.cc index eada685f2d6427..ed92cc459a5a66 100644 --- a/src/node_http_parser.cc +++ b/src/node_http_parser.cc @@ -88,7 +88,7 @@ class BindingData : public BaseObject { BindingData(Environment* env, Local obj) : BaseObject(env, obj) {} - static constexpr FastStringKey binding_data_name { "http_parser" }; + static constexpr FastStringKey type_name { "http_parser" }; std::vector parser_buffer; bool parser_buffer_in_use = false; @@ -101,7 +101,7 @@ class BindingData : public BaseObject { }; // TODO(addaleax): Remove once we're on C++17. -constexpr FastStringKey BindingData::binding_data_name; +constexpr FastStringKey BindingData::type_name; // helper class for the Parser struct StringPtr { diff --git a/src/node_mutex.h b/src/node_mutex.h index b82505cc639b7a..40c44c6d4fadb1 100644 --- a/src/node_mutex.h +++ b/src/node_mutex.h @@ -32,7 +32,7 @@ class ExclusiveAccess { class Scoped { public: - // ExclusiveAccess will commonly be used in conjuction with std::shared_ptr + // ExclusiveAccess will commonly be used in conjunction with std::shared_ptr // and without this constructor it's too easy to forget to keep a reference // around to the shared_ptr while operating on the ExclusiveAccess object. explicit Scoped(const std::shared_ptr& shared) diff --git a/src/node_options.cc b/src/node_options.cc index e7dc220f5c3fca..0ed8dac7217753 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -281,6 +281,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { "experimental Source Map V3 support", &EnvironmentOptions::enable_source_maps, kAllowedInEnvironment); + AddOption("--experimental-abortcontroller", + "experimental AbortController support", + &EnvironmentOptions::experimental_abortcontroller, + kAllowedInEnvironment); AddOption("--experimental-json-modules", "experimental JSON interop support for the ES Module loader", &EnvironmentOptions::experimental_json_modules, @@ -472,8 +476,11 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { &EnvironmentOptions::trace_warnings, kAllowedInEnvironment); AddOption("--unhandled-rejections", - "define unhandled rejections behavior. Options are 'strict' (raise " - "an error), 'warn' (enforce warnings) or 'none' (silence warnings)", + "define unhandled rejections behavior. Options are 'strict' " + "(always raise an error), 'throw' (raise an error unless " + "'unhandledRejection' hook is set), 'warn' (log a warning), 'none' " + "(silence warnings), 'warn-with-error-code' (log a warning and set " + "exit code 1 unless 'unhandledRejection' hook is set).", &EnvironmentOptions::unhandled_rejections, kAllowedInEnvironment); diff --git a/src/node_options.h b/src/node_options.h index bd63fcb0485c77..c9624c04a6eff1 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -102,6 +102,7 @@ class EnvironmentOptions : public Options { bool abort_on_uncaught_exception = false; std::vector conditions; bool enable_source_maps = false; + bool experimental_abortcontroller = false; bool experimental_json_modules = false; bool experimental_modules = false; std::string experimental_specifier_resolution; @@ -225,7 +226,7 @@ class PerProcessOptions : public Options { #endif // Per-process because they affect singleton OpenSSL shared library state, - // or are used once during process intialization. + // or are used once during process initialization. #if HAVE_OPENSSL std::string openssl_config; std::string tls_cipher_list = DEFAULT_CIPHER_LIST_CORE; diff --git a/src/node_os.cc b/src/node_os.cc index 2e151ac4f89213..3cde80996f095f 100644 --- a/src/node_os.cc +++ b/src/node_os.cc @@ -112,16 +112,17 @@ static void GetCPUInfo(const FunctionCallbackInfo& args) { // assemble them into objects in JS than to call Object::Set() repeatedly // The array is in the format // [model, speed, (5 entries of cpu_times), model2, speed2, ...] - std::vector> result(count * 7); - for (int i = 0, j = 0; i < count; i++) { + std::vector> result; + result.reserve(count * 7); + for (int i = 0; i < count; i++) { uv_cpu_info_t* ci = cpu_infos + i; - result[j++] = OneByteString(isolate, ci->model); - result[j++] = Number::New(isolate, ci->speed); - result[j++] = Number::New(isolate, ci->cpu_times.user); - result[j++] = Number::New(isolate, ci->cpu_times.nice); - result[j++] = Number::New(isolate, ci->cpu_times.sys); - result[j++] = Number::New(isolate, ci->cpu_times.idle); - result[j++] = Number::New(isolate, ci->cpu_times.irq); + result.emplace_back(OneByteString(isolate, ci->model)); + result.emplace_back(Number::New(isolate, ci->speed)); + result.emplace_back(Number::New(isolate, ci->cpu_times.user)); + result.emplace_back(Number::New(isolate, ci->cpu_times.nice)); + result.emplace_back(Number::New(isolate, ci->cpu_times.sys)); + result.emplace_back(Number::New(isolate, ci->cpu_times.idle)); + result.emplace_back(Number::New(isolate, ci->cpu_times.irq)); } uv_free_cpu_info(cpu_infos, count); @@ -182,7 +183,8 @@ static void GetInterfaceAddresses(const FunctionCallbackInfo& args) { } Local no_scope_id = Integer::New(isolate, -1); - std::vector> result(count * 7); + std::vector> result; + result.reserve(count * 7); for (i = 0; i < count; i++) { const char* const raw_name = interfaces[i].name; @@ -216,18 +218,18 @@ static void GetInterfaceAddresses(const FunctionCallbackInfo& args) { family = env->unknown_string(); } - result[i * 7] = name; - result[i * 7 + 1] = OneByteString(isolate, ip); - result[i * 7 + 2] = OneByteString(isolate, netmask); - result[i * 7 + 3] = family; - result[i * 7 + 4] = FIXED_ONE_BYTE_STRING(isolate, mac); - result[i * 7 + 5] = - interfaces[i].is_internal ? True(isolate) : False(isolate); + result.emplace_back(name); + result.emplace_back(OneByteString(isolate, ip)); + result.emplace_back(OneByteString(isolate, netmask)); + result.emplace_back(family); + result.emplace_back(FIXED_ONE_BYTE_STRING(isolate, mac)); + result.emplace_back( + interfaces[i].is_internal ? True(isolate) : False(isolate)); if (interfaces[i].address.address4.sin_family == AF_INET6) { uint32_t scopeid = interfaces[i].address.address6.sin6_scope_id; - result[i * 7 + 6] = Integer::NewFromUnsigned(isolate, scopeid); + result.emplace_back(Integer::NewFromUnsigned(isolate, scopeid)); } else { - result[i * 7 + 6] = no_scope_id; + result.emplace_back(no_scope_id); } } diff --git a/src/node_postmortem_metadata.cc b/src/node_postmortem_metadata.cc index ccb347e95175c1..2dc534f59bf80a 100644 --- a/src/node_postmortem_metadata.cc +++ b/src/node_postmortem_metadata.cc @@ -36,6 +36,7 @@ extern "C" { int nodedbg_const_ContextEmbedderIndex__kEnvironment__int; +int nodedbg_const_BaseObject__kInternalFieldCount__int; uintptr_t nodedbg_offset_ExternalString__data__uintptr_t; uintptr_t nodedbg_offset_ReqWrap__req_wrap_queue___ListNode_ReqWrapQueue; @@ -50,6 +51,8 @@ namespace node { int GenDebugSymbols() { nodedbg_const_ContextEmbedderIndex__kEnvironment__int = ContextEmbedderIndex::kEnvironment; + nodedbg_const_BaseObject__kInternalFieldCount__int = + BaseObject::kInternalFieldCount; nodedbg_offset_ExternalString__data__uintptr_t = NODE_OFF_EXTSTR_DATA; nodedbg_offset_ReqWrap__req_wrap_queue___ListNode_ReqWrapQueue = diff --git a/src/node_report.cc b/src/node_report.cc index 3b97bb705b6985..13f87b1e52e5d6 100644 --- a/src/node_report.cc +++ b/src/node_report.cc @@ -294,6 +294,10 @@ static void WriteNodeReport(Isolate* isolate, static_cast(uv_loop_alive(env->event_loop()))); writer.json_keyvalue("address", ValueToHexString(reinterpret_cast(env->event_loop()))); + + // Report Event loop idle time + uint64_t idle_time = uv_metrics_idle_time(env->event_loop()); + writer.json_keyvalue("loopIdleTimeSeconds", 1.0 * idle_time / 1e9); writer.json_end(); } diff --git a/src/node_serdes.cc b/src/node_serdes.cc index 28844f1858ff3d..b51d315989ce71 100644 --- a/src/node_serdes.cc +++ b/src/node_serdes.cc @@ -169,6 +169,10 @@ Maybe SerializerContext::WriteHostObject(Isolate* isolate, void SerializerContext::New(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); + if (!args.IsConstructCall()) { + return THROW_ERR_CONSTRUCT_CALL_REQUIRED( + env, "Class constructor Serializer cannot be invoked without 'new'"); + } new SerializerContext(env, args.This()); } @@ -319,6 +323,10 @@ MaybeLocal DeserializerContext::ReadHostObject(Isolate* isolate) { void DeserializerContext::New(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); + if (!args.IsConstructCall()) { + return THROW_ERR_CONSTRUCT_CALL_REQUIRED( + env, "Class constructor Deserializer cannot be invoked without 'new'"); + } if (!args[0]->IsArrayBufferView()) { return node::THROW_ERR_INVALID_ARG_TYPE( @@ -470,6 +478,7 @@ void Initialize(Local target, Local serializerString = FIXED_ONE_BYTE_STRING(env->isolate(), "Serializer"); ser->SetClassName(serializerString); + ser->ReadOnlyPrototype(); target->Set(env->context(), serializerString, ser->GetFunction(env->context()).ToLocalChecked()).Check(); @@ -496,6 +505,8 @@ void Initialize(Local target, Local deserializerString = FIXED_ONE_BYTE_STRING(env->isolate(), "Deserializer"); + des->SetLength(1); + des->ReadOnlyPrototype(); des->SetClassName(deserializerString); target->Set(env->context(), deserializerString, diff --git a/src/node_url.cc b/src/node_url.cc index b157c2ded1b5ae..e0a07326825e11 100644 --- a/src/node_url.cc +++ b/src/node_url.cc @@ -1426,7 +1426,7 @@ void URL::Parse(const char* input, const char ch = p < end ? p[0] : kEOL; bool special = (url->flags & URL_FLAGS_SPECIAL); bool cannot_be_base; - const bool special_back_slash = (special && ch == '\\'); + bool special_back_slash = (special && ch == '\\'); switch (state) { case kSchemeStart: @@ -1476,6 +1476,7 @@ void URL::Parse(const char* input, url->flags &= ~URL_FLAGS_SPECIAL; special = false; } + special_back_slash = (special && ch == '\\'); buffer.clear(); if (has_state_override) return; @@ -1520,6 +1521,7 @@ void URL::Parse(const char* input, url->flags &= ~URL_FLAGS_SPECIAL; special = false; } + special_back_slash = (special && ch == '\\'); if (base->flags & URL_FLAGS_HAS_PATH) { url->flags |= URL_FLAGS_HAS_PATH; url->path = base->path; @@ -1543,6 +1545,7 @@ void URL::Parse(const char* input, url->flags |= URL_FLAGS_SPECIAL; special = true; state = kFile; + special_back_slash = (special && ch == '\\'); continue; } break; @@ -1572,6 +1575,7 @@ void URL::Parse(const char* input, url->flags &= ~URL_FLAGS_SPECIAL; special = false; } + special_back_slash = (special && ch == '\\'); switch (ch) { case kEOL: if (base->flags & URL_FLAGS_HAS_USERNAME) { diff --git a/src/node_v8.cc b/src/node_v8.cc index f34125d5f49e16..4354e1e1772d82 100644 --- a/src/node_v8.cc +++ b/src/node_v8.cc @@ -19,15 +19,16 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -#include "node.h" +#include "node_v8.h" #include "base_object-inl.h" #include "env-inl.h" #include "memory_tracker-inl.h" +#include "node.h" #include "util-inl.h" #include "v8.h" namespace node { - +namespace v8_utils { using v8::Array; using v8::Context; using v8::FunctionCallbackInfo; @@ -44,7 +45,6 @@ using v8::Uint32; using v8::V8; using v8::Value; - #define HEAP_STATISTICS_PROPERTIES(V) \ V(0, total_heap_size, kTotalHeapSizeIndex) \ V(1, total_heap_size_executable, kTotalHeapSizeExecutableIndex) \ @@ -63,7 +63,6 @@ static constexpr size_t kHeapStatisticsPropertiesCount = HEAP_STATISTICS_PROPERTIES(V); #undef V - #define HEAP_SPACE_STATISTICS_PROPERTIES(V) \ V(0, space_size, kSpaceSizeIndex) \ V(1, space_used_size, kSpaceUsedSizeIndex) \ @@ -85,35 +84,37 @@ static const size_t kHeapCodeStatisticsPropertiesCount = HEAP_CODE_STATISTICS_PROPERTIES(V); #undef V -class BindingData : public BaseObject { - public: - BindingData(Environment* env, Local obj) - : BaseObject(env, obj), - heap_statistics_buffer(env->isolate(), kHeapStatisticsPropertiesCount), - heap_space_statistics_buffer(env->isolate(), - kHeapSpaceStatisticsPropertiesCount), - heap_code_statistics_buffer(env->isolate(), - kHeapCodeStatisticsPropertiesCount) {} - - static constexpr FastStringKey binding_data_name { "v8" }; - - AliasedFloat64Array heap_statistics_buffer; - AliasedFloat64Array heap_space_statistics_buffer; - AliasedFloat64Array heap_code_statistics_buffer; - - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackField("heap_statistics_buffer", heap_statistics_buffer); - tracker->TrackField("heap_space_statistics_buffer", - heap_space_statistics_buffer); - tracker->TrackField("heap_code_statistics_buffer", - heap_code_statistics_buffer); - } - SET_SELF_SIZE(BindingData) - SET_MEMORY_INFO_NAME(BindingData) -}; +BindingData::BindingData(Environment* env, Local obj) + : BaseObject(env, obj), + heap_statistics_buffer(env->isolate(), kHeapStatisticsPropertiesCount), + heap_space_statistics_buffer(env->isolate(), + kHeapSpaceStatisticsPropertiesCount), + heap_code_statistics_buffer(env->isolate(), + kHeapCodeStatisticsPropertiesCount) { + obj->Set(env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "heapStatisticsBuffer"), + heap_statistics_buffer.GetJSArray()) + .Check(); + obj->Set(env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "heapCodeStatisticsBuffer"), + heap_code_statistics_buffer.GetJSArray()) + .Check(); + obj->Set(env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "heapSpaceStatisticsBuffer"), + heap_space_statistics_buffer.GetJSArray()) + .Check(); +} + +void BindingData::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("heap_statistics_buffer", heap_statistics_buffer); + tracker->TrackField("heap_space_statistics_buffer", + heap_space_statistics_buffer); + tracker->TrackField("heap_code_statistics_buffer", + heap_code_statistics_buffer); +} // TODO(addaleax): Remove once we're on C++17. -constexpr FastStringKey BindingData::binding_data_name; +constexpr FastStringKey BindingData::type_name; void CachedDataVersionTag(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); @@ -179,43 +180,12 @@ void Initialize(Local target, env->SetMethodNoSideEffect(target, "cachedDataVersionTag", CachedDataVersionTag); - - // Export symbols used by v8.getHeapStatistics() env->SetMethod( target, "updateHeapStatisticsBuffer", UpdateHeapStatisticsBuffer); - target - ->Set(env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "heapStatisticsBuffer"), - binding_data->heap_statistics_buffer.GetJSArray()) - .Check(); - -#define V(i, _, name) \ - target->Set(env->context(), \ - FIXED_ONE_BYTE_STRING(env->isolate(), #name), \ - Uint32::NewFromUnsigned(env->isolate(), i)).Check(); - - HEAP_STATISTICS_PROPERTIES(V) -#undef V - - // Export symbols used by v8.getHeapCodeStatistics() env->SetMethod( target, "updateHeapCodeStatisticsBuffer", UpdateHeapCodeStatisticsBuffer); - target - ->Set(env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "heapCodeStatisticsBuffer"), - binding_data->heap_code_statistics_buffer.GetJSArray()) - .Check(); - -#define V(i, _, name) \ - target->Set(env->context(), \ - FIXED_ONE_BYTE_STRING(env->isolate(), #name), \ - Uint32::NewFromUnsigned(env->isolate(), i)).Check(); - - HEAP_CODE_STATISTICS_PROPERTIES(V) -#undef V - size_t number_of_heap_spaces = env->isolate()->NumberOfHeapSpaces(); // Heap space names are extracted once and exposed to JavaScript to @@ -237,18 +207,15 @@ void Initialize(Local target, "updateHeapSpaceStatisticsBuffer", UpdateHeapSpaceStatisticsBuffer); - target - ->Set(env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), - "heapSpaceStatisticsBuffer"), - binding_data->heap_space_statistics_buffer.GetJSArray()) +#define V(i, _, name) \ + target \ + ->Set(env->context(), \ + FIXED_ONE_BYTE_STRING(env->isolate(), #name), \ + Uint32::NewFromUnsigned(env->isolate(), i)) \ .Check(); -#define V(i, _, name) \ - target->Set(env->context(), \ - FIXED_ONE_BYTE_STRING(env->isolate(), #name), \ - Uint32::NewFromUnsigned(env->isolate(), i)).Check(); - + HEAP_STATISTICS_PROPERTIES(V) + HEAP_CODE_STATISTICS_PROPERTIES(V) HEAP_SPACE_STATISTICS_PROPERTIES(V) #undef V @@ -256,6 +223,7 @@ void Initialize(Local target, env->SetMethod(target, "setFlagsFromString", SetFlagsFromString); } +} // namespace v8_utils } // namespace node -NODE_MODULE_CONTEXT_AWARE_INTERNAL(v8, node::Initialize) +NODE_MODULE_CONTEXT_AWARE_INTERNAL(v8, node::v8_utils::Initialize) diff --git a/src/node_v8.h b/src/node_v8.h new file mode 100644 index 00000000000000..745c6580b844f4 --- /dev/null +++ b/src/node_v8.h @@ -0,0 +1,36 @@ +#ifndef SRC_NODE_V8_H_ +#define SRC_NODE_V8_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "aliased_buffer.h" +#include "base_object.h" +#include "util.h" +#include "v8.h" + +namespace node { +class Environment; + +namespace v8_utils { +class BindingData : public BaseObject { + public: + BindingData(Environment* env, v8::Local obj); + + static constexpr FastStringKey type_name{"node::v8::BindingData"}; + + AliasedFloat64Array heap_statistics_buffer; + AliasedFloat64Array heap_space_statistics_buffer; + AliasedFloat64Array heap_code_statistics_buffer; + + void MemoryInfo(MemoryTracker* tracker) const override; + SET_SELF_SIZE(BindingData) + SET_MEMORY_INFO_NAME(BindingData) +}; + +} // namespace v8_utils + +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_NODE_V8_H_ diff --git a/src/node_version.h b/src/node_version.h index 8d364ec40e8d84..5419747db7b650 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -23,13 +23,13 @@ #define SRC_NODE_VERSION_H_ #define NODE_MAJOR_VERSION 14 -#define NODE_MINOR_VERSION 16 -#define NODE_PATCH_VERSION 2 +#define NODE_MINOR_VERSION 17 +#define NODE_PATCH_VERSION 0 #define NODE_VERSION_IS_LTS 1 #define NODE_VERSION_LTS_CODENAME "Fermium" -#define NODE_VERSION_IS_RELEASE 0 +#define NODE_VERSION_IS_RELEASE 1 #ifndef NODE_STRINGIFY #define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n) @@ -93,6 +93,6 @@ // The NAPI_VERSION provided by this version of the runtime. This is the version // which the Node binary being built supports. -#define NAPI_VERSION 7 +#define NAPI_VERSION 8 #endif // SRC_NODE_VERSION_H_ diff --git a/src/node_worker.cc b/src/node_worker.cc index 2e018c4177cd2e..dd132a87cfcbaa 100644 --- a/src/node_worker.cc +++ b/src/node_worker.cc @@ -765,6 +765,39 @@ void Worker::TakeHeapSnapshot(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(scheduled ? taker->object() : Local()); } +void Worker::LoopIdleTime(const FunctionCallbackInfo& args) { + Worker* w; + ASSIGN_OR_RETURN_UNWRAP(&w, args.This()); + + Mutex::ScopedLock lock(w->mutex_); + // Using w->is_stopped() here leads to a deadlock, and checking is_stopped() + // before locking the mutex is a race condition. So manually do the same + // check. + if (w->stopped_ || w->env_ == nullptr) + return args.GetReturnValue().Set(-1); + + uint64_t idle_time = uv_metrics_idle_time(w->env_->event_loop()); + args.GetReturnValue().Set(1.0 * idle_time / 1e6); +} + +void Worker::LoopStartTime(const FunctionCallbackInfo& args) { + Worker* w; + ASSIGN_OR_RETURN_UNWRAP(&w, args.This()); + + Mutex::ScopedLock lock(w->mutex_); + // Using w->is_stopped() here leads to a deadlock, and checking is_stopped() + // before locking the mutex is a race condition. So manually do the same + // check. + if (w->stopped_ || w->env_ == nullptr) + return args.GetReturnValue().Set(-1); + + double loop_start_time = w->env_->performance_state()->milestones[ + node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_START]; + CHECK_GE(loop_start_time, 0); + args.GetReturnValue().Set( + (loop_start_time - node::performance::timeOrigin) / 1e6); +} + namespace { // Return the MessagePort that is global for this Environment and communicates @@ -798,6 +831,8 @@ void InitWorker(Local target, env->SetProtoMethod(w, "unref", Worker::Unref); env->SetProtoMethod(w, "getResourceLimits", Worker::GetResourceLimits); env->SetProtoMethod(w, "takeHeapSnapshot", Worker::TakeHeapSnapshot); + env->SetProtoMethod(w, "loopIdleTime", Worker::LoopIdleTime); + env->SetProtoMethod(w, "loopStartTime", Worker::LoopStartTime); Local workerString = FIXED_ONE_BYTE_STRING(env->isolate(), "Worker"); diff --git a/src/node_worker.h b/src/node_worker.h index 658018bb0661f8..a59bdc3f5bae17 100644 --- a/src/node_worker.h +++ b/src/node_worker.h @@ -65,6 +65,8 @@ class Worker : public AsyncWrap { const v8::FunctionCallbackInfo& args); v8::Local GetResourceLimits(v8::Isolate* isolate) const; static void TakeHeapSnapshot(const v8::FunctionCallbackInfo& args); + static void LoopIdleTime(const v8::FunctionCallbackInfo& args); + static void LoopStartTime(const v8::FunctionCallbackInfo& args); private: void CreateEnvMessagePort(Environment* env); diff --git a/src/res/node.rc b/src/res/node.rc index 9403e68be70aca..7af45a03c229b2 100644 --- a/src/res/node.rc +++ b/src/res/node.rc @@ -52,7 +52,7 @@ BEGIN BEGIN VALUE "CompanyName", "Node.js" VALUE "ProductName", "Node.js" - VALUE "FileDescription", "Node.js: Server-side JavaScript" + VALUE "FileDescription", "Node.js JavaScript Runtime" VALUE "FileVersion", NODE_EXE_VERSION VALUE "ProductVersion", NODE_EXE_VERSION VALUE "OriginalFilename", "node.exe" diff --git a/src/string_bytes.cc b/src/string_bytes.cc index 68c7c06d8df1de..b03268c49afcbe 100644 --- a/src/string_bytes.cc +++ b/src/string_bytes.cc @@ -21,7 +21,7 @@ #include "string_bytes.h" -#include "base64.h" +#include "base64-inl.h" #include "env-inl.h" #include "node_buffer.h" #include "node_errors.h" diff --git a/src/string_search.h b/src/string_search.h index 3d06f32058f673..cd9ef320a81112 100644 --- a/src/string_search.h +++ b/src/string_search.h @@ -32,7 +32,7 @@ class Vector { // Returns true if the Vector is front-to-back, false if back-to-front. // In the latter case, v[0] corresponds to the *end* of the memory range. - size_t forward() const { return is_forward_; } + bool forward() const { return is_forward_; } // Access individual vector elements - checks bounds in debug mode. T& operator[](size_t index) const { @@ -458,7 +458,7 @@ size_t StringSearch::BoyerMooreHorspoolSearch( const size_t subject_length = subject.length(); const size_t pattern_length = pattern_.length(); int* char_occurrences = bad_char_shift_table_; - int64_t badness = -pattern_length; + int64_t badness = -static_cast(pattern_length); // How bad we are doing without a good-suffix table. Char last_char = pattern_[pattern_length - 1]; diff --git a/src/timer_wrap.cc b/src/timer_wrap.cc new file mode 100644 index 00000000000000..6660b8c958810c --- /dev/null +++ b/src/timer_wrap.cc @@ -0,0 +1,95 @@ +#include "env-inl.h" +#include "memory_tracker-inl.h" +#include "timer_wrap.h" +#include "uv.h" + +namespace node { + +TimerWrap::TimerWrap(Environment* env, const TimerCb& fn) + : env_(env), + fn_(fn) { + uv_timer_init(env->event_loop(), &timer_); + timer_.data = this; +} + +void TimerWrap::Stop() { + if (timer_.data == nullptr) return; + uv_timer_stop(&timer_); +} + +void TimerWrap::Close() { + timer_.data = nullptr; + env_->CloseHandle(reinterpret_cast(&timer_), TimerClosedCb); +} + +void TimerWrap::TimerClosedCb(uv_handle_t* handle) { + std::unique_ptr ptr( + ContainerOf(&TimerWrap::timer_, + reinterpret_cast(handle))); +} + +void TimerWrap::Update(uint64_t interval, uint64_t repeat) { + if (timer_.data == nullptr) return; + uv_timer_start(&timer_, OnTimeout, interval, repeat); +} + +void TimerWrap::Ref() { + if (timer_.data == nullptr) return; + uv_ref(reinterpret_cast(&timer_)); +} + +void TimerWrap::Unref() { + if (timer_.data == nullptr) return; + uv_unref(reinterpret_cast(&timer_)); +} + +void TimerWrap::OnTimeout(uv_timer_t* timer) { + TimerWrap* t = ContainerOf(&TimerWrap::timer_, timer); + t->fn_(); +} + +TimerWrapHandle::TimerWrapHandle( + Environment* env, + const TimerWrap::TimerCb& fn) { + timer_ = new TimerWrap(env, fn); + env->AddCleanupHook(CleanupHook, this); +} + +void TimerWrapHandle::Stop() { + if (timer_ != nullptr) + return timer_->Stop(); +} + +void TimerWrapHandle::Close() { + if (timer_ != nullptr) { + timer_->env()->RemoveCleanupHook(CleanupHook, this); + timer_->Close(); + } + timer_ = nullptr; +} + +void TimerWrapHandle::Ref() { + if (timer_ != nullptr) + timer_->Ref(); +} + +void TimerWrapHandle::Unref() { + if (timer_ != nullptr) + timer_->Unref(); +} + +void TimerWrapHandle::Update(uint64_t interval, uint64_t repeat) { + if (timer_ != nullptr) + timer_->Update(interval, repeat); +} + +void TimerWrapHandle::MemoryInfo(MemoryTracker* tracker) const { + if (timer_ != nullptr) + tracker->TrackField("timer", *timer_); +} + +void TimerWrapHandle::CleanupHook(void* data) { + static_cast(data)->Close(); +} + +} // namespace node diff --git a/src/timer_wrap.h b/src/timer_wrap.h new file mode 100644 index 00000000000000..b2c20bf24d8746 --- /dev/null +++ b/src/timer_wrap.h @@ -0,0 +1,84 @@ +#ifndef SRC_TIMER_WRAP_H_ +#define SRC_TIMER_WRAP_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "memory_tracker.h" +#include "env.h" +#include "uv.h" + +#include + +namespace node { + +// Utility class that makes working with libuv timers a bit easier. +class TimerWrap final : public MemoryRetainer { + public: + using TimerCb = std::function; + + TimerWrap(Environment* env, const TimerCb& fn); + TimerWrap(const TimerWrap&) = delete; + + inline Environment* env() const { return env_; } + + // Stop calling the timer callback. + void Stop(); + // Render the timer unusable and delete this object. + void Close(); + + // Starts / Restarts the Timer + void Update(uint64_t interval, uint64_t repeat = 0); + + void Ref(); + void Unref(); + + SET_NO_MEMORY_INFO(); + SET_MEMORY_INFO_NAME(TimerWrap) + SET_SELF_SIZE(TimerWrap) + + private: + static void TimerClosedCb(uv_handle_t* handle); + static void OnTimeout(uv_timer_t* timer); + ~TimerWrap() = default; + + Environment* env_; + TimerCb fn_; + uv_timer_t timer_; + + friend std::unique_ptr::deleter_type; +}; + +class TimerWrapHandle : public MemoryRetainer { + public: + TimerWrapHandle( + Environment* env, + const TimerWrap::TimerCb& fn); + + TimerWrapHandle(const TimerWrapHandle&) = delete; + + ~TimerWrapHandle() { Close(); } + + void Update(uint64_t interval, uint64_t repeat = 0); + + void Ref(); + void Unref(); + + void Stop(); + void Close(); + + void MemoryInfo(node::MemoryTracker* tracker) const override; + + SET_MEMORY_INFO_NAME(TimerWrapHandle) + SET_SELF_SIZE(TimerWrapHandle) + + private: + static void CleanupHook(void* data); + + TimerWrap* timer_; +}; + +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_TIMER_WRAP_H_ diff --git a/test/abort/abort.status b/test/abort/abort.status new file mode 100644 index 00000000000000..e56c24ff433d63 --- /dev/null +++ b/test/abort/abort.status @@ -0,0 +1,11 @@ +prefix abort + +# To mark a test as flaky, list the test name in the appropriate section +# below, without ".js", followed by ": PASS,FLAKY". Example: +# sample-test : PASS,FLAKY + +[true] # This section applies to all platforms + +[$system==ibmi] +# https://github.com/nodejs/node/issues/34410 +test-addon-register-signal-handler: PASS,FLAKY diff --git a/test/abort/test-signal-handler.js b/test/abort/test-signal-handler.js index f68e6881f6e5b6..6ee7e4098af702 100644 --- a/test/abort/test-signal-handler.js +++ b/test/abort/test-signal-handler.js @@ -17,7 +17,8 @@ if (process.argv[2] === 'child') { const child = spawnSync(process.execPath, ['--expose-internals', __filename, 'child'], { stdio: 'inherit' }); - // FreeBSD and macOS use SIGILL for the kind of crash we're causing here. - assert(child.signal === 'SIGSEGV' || child.signal === 'SIGILL', - `child.signal = ${child.signal}`); + // FreeBSD uses SIGILL for this kind of crash. + // macOS uses SIGILL or SIGTRAP (arm64) for this kind of crash. + assert(child.signal === 'SIGSEGV' || child.signal === 'SIGILL' || + child.signal === 'SIGTRAP', `child.signal = ${child.signal}`); } diff --git a/test/addons/addon.status b/test/addons/addons.status similarity index 83% rename from test/addons/addon.status rename to test/addons/addons.status index 916f64e6be6a20..74c29ecd99888b 100644 --- a/test/addons/addon.status +++ b/test/addons/addons.status @@ -14,3 +14,5 @@ zlib-binding/test: PASS,FLAKY [$system==ibmi] openssl-binding/test: SKIP zlib-binding/test: SKIP +# https://github.com/nodejs/node/issues/34410 +register-signal-handler/test: PASS,FLAKY diff --git a/test/addons/cpu-profiler-crash/binding.cc b/test/addons/cpu-profiler-crash/binding.cc new file mode 100644 index 00000000000000..57358e82175e29 --- /dev/null +++ b/test/addons/cpu-profiler-crash/binding.cc @@ -0,0 +1,16 @@ +#include "node.h" +#include "v8.h" +#include "v8-profiler.h" + +static void StartCpuProfiler(const v8::FunctionCallbackInfo& args) { + v8::CpuProfiler* profiler = v8::CpuProfiler::New(args.GetIsolate()); + v8::Local profile_title = v8::String::NewFromUtf8( + args.GetIsolate(), + "testing", + v8::NewStringType::kInternalized).ToLocalChecked(); + profiler->StartProfiling(profile_title, true); +} + +NODE_MODULE_INIT(/* exports, module, context */) { + NODE_SET_METHOD(exports, "startCpuProfiler", StartCpuProfiler); +} diff --git a/test/addons/cpu-profiler-crash/binding.gyp b/test/addons/cpu-profiler-crash/binding.gyp new file mode 100644 index 00000000000000..55fbe7050f18e4 --- /dev/null +++ b/test/addons/cpu-profiler-crash/binding.gyp @@ -0,0 +1,9 @@ +{ + 'targets': [ + { + 'target_name': 'binding', + 'sources': [ 'binding.cc' ], + 'includes': ['../common.gypi'], + } + ] +} diff --git a/test/addons/cpu-profiler-crash/test.js b/test/addons/cpu-profiler-crash/test.js new file mode 100644 index 00000000000000..e7391ebfcc9a5c --- /dev/null +++ b/test/addons/cpu-profiler-crash/test.js @@ -0,0 +1,20 @@ +'use strict'; + +// This test crashes most of the time unless the v8 patch: +// https://github.com/v8/v8/commit/beebee4f80ff2eb91187ef1e8fa00b8ff82a20b3 +// is applied. +const common = require('../../common'); +const bindings = require(`./build/${common.buildType}/binding`); + +function fn() { setImmediate(fn); } +setInterval(function() { + for (let i = 0; i < 1000000; i++) + fn(); + clearInterval(this); + setTimeout(process.reallyExit, 2000); +}, 0); + + +setTimeout(() => { + bindings.startCpuProfiler(); +}, 1000); diff --git a/test/addons/make-callback-domain-warning/test.js b/test/addons/make-callback-domain-warning/test.js index 0377415e1269aa..e6eaa9d337c179 100644 --- a/test/addons/make-callback-domain-warning/test.js +++ b/test/addons/make-callback-domain-warning/test.js @@ -6,7 +6,7 @@ const domain = require('domain'); const binding = require(`./build/${common.buildType}/binding`); function makeCallback(object, cb) { - binding.makeCallback(object, () => setImmediate(cb)); + binding.makeCallback(object, function someMethod() { setImmediate(cb); }); } let latestWarning = null; @@ -16,8 +16,14 @@ process.on('warning', (warning) => { const d = domain.create(); +class Resource { + constructor(domain) { + this.domain = domain; + } +} + // When domain is disabled, no warning will be emitted -makeCallback({ domain: d }, common.mustCall(() => { +makeCallback(new Resource(d), common.mustCall(() => { assert.strictEqual(latestWarning, null); d.run(common.mustCall(() => { @@ -26,7 +32,9 @@ makeCallback({ domain: d }, common.mustCall(() => { assert.strictEqual(latestWarning, null); // Warning is emitted when domain property is used and domain is enabled - makeCallback({ domain: d }, common.mustCall(() => { + makeCallback(new Resource(d), common.mustCall(() => { + assert.match(latestWarning.message, + /Triggered by calling someMethod on Resource\./); assert.strictEqual(latestWarning.name, 'DeprecationWarning'); assert.strictEqual(latestWarning.code, 'DEP0097'); })); diff --git a/test/addons/testcfg.py b/test/addons/testcfg.py index 908fd6a475cc39..6c61081fbb0bac 100644 --- a/test/addons/testcfg.py +++ b/test/addons/testcfg.py @@ -3,4 +3,4 @@ import testpy def GetConfiguration(context, root): - return testpy.AddonTestConfiguration(context, root, 'addon') + return testpy.AddonTestConfiguration(context, root, 'addons') diff --git a/test/addons/worker-buffer-callback/test.js b/test/addons/worker-buffer-callback/test.js index b04984f1576432..08625b72384eb1 100644 --- a/test/addons/worker-buffer-callback/test.js +++ b/test/addons/worker-buffer-callback/test.js @@ -5,7 +5,7 @@ const { MessageChannel } = require('worker_threads'); const { buffer } = require(`./build/${common.buildType}/binding`); // Test that buffers allocated with a free callback through our APIs are not -// transfered. +// transferred. const { port1 } = new MessageChannel(); const origByteLength = buffer.byteLength; diff --git a/test/async-hooks/test-async-local-storage-enable-disable.js b/test/async-hooks/test-async-local-storage-enable-disable.js index 15ab2f5d9ebbb2..c7248d6cec40a8 100644 --- a/test/async-hooks/test-async-local-storage-enable-disable.js +++ b/test/async-hooks/test-async-local-storage-enable-disable.js @@ -24,8 +24,8 @@ asyncLocalStorage.run(new Map(), () => { process.nextTick(() => { assert.strictEqual(asyncLocalStorage.getStore(), undefined); - asyncLocalStorage.run(new Map(), () => { - assert.notStrictEqual(asyncLocalStorage.getStore(), undefined); + asyncLocalStorage.run(new Map().set('bar', 'foo'), () => { + assert.strictEqual(asyncLocalStorage.getStore().get('bar'), 'foo'); }); }); }); diff --git a/test/async-hooks/test-fseventwrap.js b/test/async-hooks/test-fseventwrap.js index 60928562872549..12a439f8033cbc 100644 --- a/test/async-hooks/test-fseventwrap.js +++ b/test/async-hooks/test-fseventwrap.js @@ -11,7 +11,7 @@ if (!common.isMainThread) common.skip('Worker bootstrapping works differently -> different async IDs'); if (common.isIBMi) - common.skip('IBMi does not suppport fs.watch()'); + common.skip('IBMi does not support fs.watch()'); const hooks = initHooks(); diff --git a/test/benchmark/benchmark.status b/test/benchmark/benchmark.status index 0db4181af0d760..6a966743aab26b 100644 --- a/test/benchmark/benchmark.status +++ b/test/benchmark/benchmark.status @@ -19,7 +19,3 @@ prefix benchmark [$system==aix] [$arch==arm] -# https://github.com/nodejs/node/issues/34266 -test-benchmark-fs: SKIP -# https://github.com/nodejs/build/issues/2382 -test-benchmark-napi: SKIP diff --git a/test/cctest/gtest/gtest-all.cc b/test/cctest/gtest/gtest-all.cc index ffa5f77859ab5f..69925f4f68d392 100644 --- a/test/cctest/gtest/gtest-all.cc +++ b/test/cctest/gtest/gtest-all.cc @@ -1995,7 +1995,7 @@ bool UnitTestOptions::MatchesFilter( return false; } - // Skips the pattern separater (the ':' character). + // Skips the pattern separator (the ':' character). cur_pattern++; } } diff --git a/test/cctest/test_base64.cc b/test/cctest/test_base64.cc index be61618f9086ed..e92498da61b871 100644 --- a/test/cctest/test_base64.cc +++ b/test/cctest/test_base64.cc @@ -1,4 +1,4 @@ -#include "base64.h" +#include "base64-inl.h" #include #include diff --git a/test/cctest/test_environment.cc b/test/cctest/test_environment.cc index 27706044b800f6..fd4adf17c83ac9 100644 --- a/test/cctest/test_environment.cc +++ b/test/cctest/test_environment.cc @@ -1,13 +1,17 @@ #include "node_buffer.h" #include "node_internals.h" #include "libplatform/libplatform.h" +#include "util.h" #include #include "gtest/gtest.h" #include "node_test_fixture.h" +#include +#include using node::AtExit; using node::RunAtExit; +using node::USE; static bool called_cb_1 = false; static bool called_cb_2 = false; @@ -66,7 +70,34 @@ TEST_F(EnvironmentTest, EnvironmentWithESMLoader) { "})()"); } +class RedirectStdErr { + public: + explicit RedirectStdErr(const char* filename) : filename_(filename) { + fflush(stderr); + fgetpos(stderr, &pos_); + fd_ = dup(fileno(stderr)); + USE(freopen(filename_, "w", stderr)); + } + + ~RedirectStdErr() { + fflush(stderr); + dup2(fd_, fileno(stderr)); + close(fd_); + remove(filename_); + clearerr(stderr); + fsetpos(stderr, &pos_); + } + + private: + int fd_; + fpos_t pos_; + const char* filename_; +}; + TEST_F(EnvironmentTest, EnvironmentWithNoESMLoader) { + // The following line will cause stderr to get redirected to avoid the + // error that would otherwise be printed to the console by this test. + RedirectStdErr redirect_scope("environment_test.log"); const v8::HandleScope handle_scope(isolate_); Argv argv; Env env {handle_scope, argv, node::EnvironmentFlags::kNoRegisterESMLoader}; @@ -559,3 +590,62 @@ TEST_F(EnvironmentTest, SetImmediateMicrotasks) { EXPECT_EQ(called, 1); } + +#ifndef _WIN32 // No SIGINT on Windows. +TEST_F(NodeZeroIsolateTestFixture, CtrlCWithOnlySafeTerminationTest) { + // We need to go through the whole setup dance here because we want to + // set only_terminate_in_safe_scope. + // Allocate and initialize Isolate. + v8::Isolate::CreateParams create_params; + create_params.array_buffer_allocator = allocator.get(); + create_params.only_terminate_in_safe_scope = true; + v8::Isolate* isolate = v8::Isolate::Allocate(); + CHECK_NOT_NULL(isolate); + platform->RegisterIsolate(isolate, ¤t_loop); + v8::Isolate::Initialize(isolate, create_params); + + // Try creating Context + IsolateData + Environment. + { + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope handle_scope(isolate); + + auto context = node::NewContext(isolate); + CHECK(!context.IsEmpty()); + v8::Context::Scope context_scope(context); + + std::unique_ptr + isolate_data{node::CreateIsolateData(isolate, + ¤t_loop, + platform.get()), + node::FreeIsolateData}; + CHECK(isolate_data); + + std::unique_ptr + environment{node::CreateEnvironment(isolate_data.get(), + context, + {}, + {}), + node::FreeEnvironment}; + CHECK(environment); + + v8::Local main_ret = + node::LoadEnvironment(environment.get(), + "'use strict';\n" + "const { runInThisContext } = require('vm');\n" + "try {\n" + " runInThisContext(" + " `process.kill(process.pid, 'SIGINT'); while(true){}`, " + " { breakOnSigint: true });\n" + " return 'unreachable';\n" + "} catch (err) {\n" + " return err.code;\n" + "}").ToLocalChecked(); + node::Utf8Value main_ret_str(isolate, main_ret); + EXPECT_EQ(std::string(*main_ret_str), "ERR_SCRIPT_EXECUTION_INTERRUPTED"); + } + + // Cleanup. + platform->UnregisterIsolate(isolate); + isolate->Dispose(); +} +#endif // _WIN32 diff --git a/test/cctest/test_node_postmortem_metadata.cc b/test/cctest/test_node_postmortem_metadata.cc index 3fb67ecbca265e..4cee7db4c8ee5b 100644 --- a/test/cctest/test_node_postmortem_metadata.cc +++ b/test/cctest/test_node_postmortem_metadata.cc @@ -14,6 +14,7 @@ extern uintptr_t nodedbg_offset_Environment__handle_wrap_queue___Environment_HandleWrapQueue; extern int debug_symbols_generated; extern int nodedbg_const_ContextEmbedderIndex__kEnvironment__int; +extern int nodedbg_const_BaseObject__kInternalFieldCount__int; extern uintptr_t nodedbg_offset_Environment_HandleWrapQueue__head___ListNode_HandleWrap; extern uintptr_t @@ -68,6 +69,12 @@ TEST_F(DebugSymbolsTest, ContextEmbedderEnvironmentIndex) { kEnvironmentIndex); } +TEST_F(DebugSymbolsTest, BaseObjectkInternalFieldCount) { + int kInternalFieldCount = node::BaseObject::kInternalFieldCount; + EXPECT_EQ(nodedbg_const_BaseObject__kInternalFieldCount__int, + kInternalFieldCount); +} + TEST_F(DebugSymbolsTest, ExternalStringDataOffset) { EXPECT_EQ(nodedbg_offset_ExternalString__data__uintptr_t, NODE_OFF_EXTSTR_DATA); @@ -89,7 +96,8 @@ TEST_F(DebugSymbolsTest, BaseObjectPersistentHandle) { Env env{handle_scope, argv}; v8::Local obj_templ = v8::ObjectTemplate::New(isolate_); - obj_templ->SetInternalFieldCount(1); + obj_templ->SetInternalFieldCount( + nodedbg_const_BaseObject__kInternalFieldCount__int); v8::Local object = obj_templ->NewInstance(env.context()).ToLocalChecked(); @@ -139,7 +147,8 @@ TEST_F(DebugSymbolsTest, HandleWrapList) { uv_tcp_t handle; auto obj_template = v8::FunctionTemplate::New(isolate_); - obj_template->InstanceTemplate()->SetInternalFieldCount(1); + obj_template->InstanceTemplate()->SetInternalFieldCount( + nodedbg_const_BaseObject__kInternalFieldCount__int); v8::Local object = obj_template->GetFunction(env.context()) .ToLocalChecked() @@ -171,7 +180,8 @@ TEST_F(DebugSymbolsTest, ReqWrapList) { tail = *reinterpret_cast(tail); auto obj_template = v8::FunctionTemplate::New(isolate_); - obj_template->InstanceTemplate()->SetInternalFieldCount(1); + obj_template->InstanceTemplate()->SetInternalFieldCount( + nodedbg_const_BaseObject__kInternalFieldCount__int); v8::Local object = obj_template->GetFunction(env.context()) .ToLocalChecked() diff --git a/test/cctest/test_url.cc b/test/cctest/test_url.cc index 2e78b24a5e3424..ad729ab1c7ffbd 100644 --- a/test/cctest/test_url.cc +++ b/test/cctest/test_url.cc @@ -81,6 +81,52 @@ TEST_F(URLTest, Base3) { EXPECT_EQ(simple.path(), "/baz"); } +TEST_F(URLTest, Base4) { + const char* input = "\\x"; + const char* base = "http://example.org/foo/bar"; + + URL simple(input, strlen(input), base, strlen(base)); + + EXPECT_FALSE(simple.flags() & URL_FLAGS_FAILED); + EXPECT_EQ(simple.protocol(), "http:"); + EXPECT_EQ(simple.host(), "example.org"); + EXPECT_EQ(simple.path(), "/x"); +} + +TEST_F(URLTest, Base5) { + const char* input = "/x"; + const char* base = "http://example.org/foo/bar"; + + URL simple(input, strlen(input), base, strlen(base)); + + EXPECT_FALSE(simple.flags() & URL_FLAGS_FAILED); + EXPECT_EQ(simple.protocol(), "http:"); + EXPECT_EQ(simple.host(), "example.org"); + EXPECT_EQ(simple.path(), "/x"); +} + +TEST_F(URLTest, Base6) { + const char* input = "\\\\x"; + const char* base = "http://example.org/foo/bar"; + + URL simple(input, strlen(input), base, strlen(base)); + + EXPECT_FALSE(simple.flags() & URL_FLAGS_FAILED); + EXPECT_EQ(simple.protocol(), "http:"); + EXPECT_EQ(simple.host(), "x"); +} + +TEST_F(URLTest, Base7) { + const char* input = "//x"; + const char* base = "http://example.org/foo/bar"; + + URL simple(input, strlen(input), base, strlen(base)); + + EXPECT_FALSE(simple.flags() & URL_FLAGS_FAILED); + EXPECT_EQ(simple.protocol(), "http:"); + EXPECT_EQ(simple.host(), "x"); +} + TEST_F(URLTest, TruncatedAfterProtocol) { char input[2] = { 'q', ':' }; URL simple(input, sizeof(input)); diff --git a/test/common/README.md b/test/common/README.md index 07bc3e180df3f2..f064ab6e7e14be 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -39,12 +39,12 @@ The `benchmark` module is used by tests to run benchmarks. The `common` module is used by tests for consistency across repeated tasks. -### `allowGlobals(...whitelist)` +### `allowGlobals(...allowlist)` -* `whitelist` [<Array>][] Array of Globals +* `allowlist` [<Array>][] Array of Globals * return [<Array>][] -Takes `whitelist` and concats that with predefined `knownGlobals`. +Takes `allowlist` and concats that with predefined `knownGlobals`. ### `canCreateSymLink()` @@ -600,7 +600,7 @@ If set, crypto tests are skipped. ### `NODE_TEST_KNOWN_GLOBALS` A comma-separated list of variables names that are appended to the global -variable whitelist. Alternatively, if `NODE_TEST_KNOWN_GLOBALS` is set to `'0'`, +variable allowlist. Alternatively, if `NODE_TEST_KNOWN_GLOBALS` is set to `'0'`, global leak detection is disabled. ## Fixtures Module @@ -944,13 +944,18 @@ The realpath of the testing temporary directory. Deletes and recreates the testing temporary directory. -The first time `refresh()` runs, it adds a listener to process `'exit'` that +The first time `refresh()` runs, it adds a listener to process `'exit'` that cleans the temporary directory. Thus, every file under `tmpdir.path` needs to be closed before the test completes. A good way to do this is to add a listener to process `'beforeExit'`. If a file needs to be left open until Node.js completes, use a child process and call `refresh()` only in the parent. +It is usually only necessary to call `refresh()` once in a test file. +Avoid calling it more than once in an asynchronous context as one call +might refresh the temporary directory of a different context, causing +the test to fail somewhat mysteriously. + ## UDP pair helper The `common/udppair` module exports a function `makeUDPPair` and a class diff --git a/test/common/dns.js b/test/common/dns.js index d8a703cc53be86..5d05cdc4b51027 100644 --- a/test/common/dns.js +++ b/test/common/dns.js @@ -13,7 +13,8 @@ const types = { PTR: 12, MX: 15, TXT: 16, - ANY: 255 + ANY: 255, + CAA: 257 }; const classes = { @@ -269,6 +270,14 @@ function writeDNSPacket(parsed) { ])); break; } + case 'CAA': + { + rdLengthBuf[0] = 5 + rr.issue.length + 2; + buffers.push(Buffer.from([Number(rr.critical)])); + buffers.push(Buffer.from([Number(5)])); + buffers.push(Buffer.from('issue' + rr.issue)); + break; + } default: throw new Error(`Unknown RR type ${rr.type}`); } diff --git a/test/common/index.js b/test/common/index.js index 95ffa28a2cc3d4..4c7a403d86acd9 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -275,8 +275,8 @@ if (global.gc) { knownGlobals.push(global.gc); } -function allowGlobals(...whitelist) { - knownGlobals = knownGlobals.concat(whitelist); +function allowGlobals(...allowlist) { + knownGlobals = knownGlobals.concat(allowlist); } if (process.env.NODE_TEST_KNOWN_GLOBALS !== '0') { diff --git a/test/common/internet.js b/test/common/internet.js index 88153960f0d59c..1c53587fb6dbf7 100644 --- a/test/common/internet.js +++ b/test/common/internet.js @@ -22,6 +22,9 @@ const addresses = { INVALID_HOST: 'something.invalid', // A host with MX records registered MX_HOST: 'nodejs.org', + // On some systems, .invalid returns a server failure/try again rather than + // record not found. Use this to guarantee record not found. + NOT_FOUND: 'come.on.fhqwhgads', // A host with SRV records registered SRV_HOST: '_jabber._tcp.google.com', // A host with PTR records registered @@ -30,6 +33,8 @@ const addresses = { NAPTR_HOST: 'sip2sip.info', // A host with SOA records registered SOA_HOST: 'nodejs.org', + // A host with CAA record registred + CAA_HOST: 'google.com', // A host with CNAME records registered CNAME_HOST: 'blog.nodejs.org', // A host with NS records registered diff --git a/test/common/tls.js b/test/common/tls.js index e7cacde7456707..5094755c240afd 100644 --- a/test/common/tls.js +++ b/test/common/tls.js @@ -15,9 +15,9 @@ class TestTLSSocket extends net.Socket { this.handshake_list = []; // AES128-GCM-SHA256 this.ciphers = Buffer.from('000002009c0', 'hex'); - this.pre_master_secret = + this.pre_primary_secret = Buffer.concat([this.version, crypto.randomBytes(46)]); - this.master_secret = null; + this.primary_secret = null; this.write_seq = 0; this.client_random = crypto.randomBytes(32); @@ -26,12 +26,12 @@ class TestTLSSocket extends net.Socket { }); this.on('server_random', (server_random) => { - this.master_secret = PRF12('sha256', this.pre_master_secret, - 'master secret', - Buffer.concat([this.client_random, - server_random]), - 48); - const key_block = PRF12('sha256', this.master_secret, + this.primary_secret = PRF12('sha256', this.pre_primary_secret, + 'primary secret', + Buffer.concat([this.client_random, + server_random]), + 48); + const key_block = PRF12('sha256', this.primary_secret, 'key expansion', Buffer.concat([server_random, this.client_random]), @@ -51,14 +51,14 @@ class TestTLSSocket extends net.Socket { } createClientKeyExchange() { - const encrypted_pre_master_secret = crypto.publicEncrypt({ + const encrypted_pre_primary_secret = crypto.publicEncrypt({ key: this.server_cert, padding: crypto.constants.RSA_PKCS1_PADDING - }, this.pre_master_secret); + }, this.pre_primary_secret); const length = Buffer.alloc(2); - length.writeUIntBE(encrypted_pre_master_secret.length, 0, 2); + length.writeUIntBE(encrypted_pre_primary_secret.length, 0, 2); const msg = addHandshakeHeader(0x10, Buffer.concat([ - length, encrypted_pre_master_secret])); + length, encrypted_pre_primary_secret])); this.emit('handshake', msg); return addRecordHeader(0x16, msg); } @@ -67,7 +67,7 @@ class TestTLSSocket extends net.Socket { const shasum = crypto.createHash('sha256'); shasum.update(Buffer.concat(this.handshake_list)); const message_hash = shasum.digest(); - const r = PRF12('sha256', this.master_secret, + const r = PRF12('sha256', this.primary_secret, 'client finished', message_hash, 12); const msg = addHandshakeHeader(0x14, r); this.emit('handshake', msg); diff --git a/test/doctool/test-doctool-html.js b/test/doctool/test-doctool-html.js index 1608eb04970222..2fa4766c837a27 100644 --- a/test/doctool/test-doctool-html.js +++ b/test/doctool/test-doctool-html.js @@ -44,7 +44,7 @@ function toHTML({ input, filename, nodeVersion, versions }) { .use(html.preprocessText, { nodeVersion }) .use(html.preprocessElements, { filename }) .use(html.buildToc, { filename, apilinks: {} }) - .use(remark2rehype, { allowDangerousHTML: true }) + .use(remark2rehype, { allowDangerousHtml: true }) .use(raw) .use(htmlStringify) .processSync(input); @@ -60,24 +60,25 @@ function toHTML({ input, filename, nodeVersion, versions }) { const testData = [ { file: fixtures.path('order_of_end_tags_5873.md'), - html: '

Static method: Buffer.from(array) ' + + html: '

Static method: Buffer.from(array) ' + '#

' + + 'id="foo_static_method_buffer_from_array">#

' + '' }, { file: fixtures.path('doc_with_yaml.md'), - html: '

Sample Markdown with YAML info' + + html: '

Sample Markdown with YAML info' + '#

' + - '

Foobar#

' + + ' id="foo_sample_markdown_with_yaml_info">#' + + '

Foobar#

' + ' ' + '

Describe Foobar in more detail here.

' + - '

Foobar II#

' + + '

Foobar II#

' + '

Describe Foobar II in more detail here.' + 'fg(1)' + - '

Deprecated thingy

' + + '

Deprecated thingy#' + - '

' + + '

Something#

' + ' ' + - '

Describe Something in more detail here.

' + '

Describe Something in more detail here.

' }, { file: fixtures.path('sample_document.md'), @@ -108,18 +111,20 @@ const testData = [ }, { file: fixtures.path('document_with_links.md'), - html: '

Usage and ExampleUsage and Example#' + - '

Usage#

node \\[options\\] index.js' + + '

Usage#

node \\[options\\] index.js' + '

Please see the' + - 'Command Line Optionsdocument for more information.

' + + 'Command Line Optionsdocument for more information.

' + + '

' + 'Example' + - '#

An example of a' + + '#

An example of a' + 'webserverwritten with Node.js which responds with' + - '\'Hello, World!\':

See also#

Check' + - 'out alsothis guide

' + '\'Hello, World!\':

' + + '

See also#

Check' + + 'out alsothis guide

' }, { file: fixtures.path('document_with_special_heading.md'), diff --git a/test/es-module/test-esm-data-urls.js b/test/es-module/test-esm-data-urls.js index 61da442c9e081c..eb2b65e9fb224a 100644 --- a/test/es-module/test-esm-data-urls.js +++ b/test/es-module/test-esm-data-urls.js @@ -99,7 +99,7 @@ function createBase64URL(mime, body) { await import(plainESMURL); common.mustNotCall()(); } catch (e) { - assert.strictEqual(e.code, 'ERR_INVALID_RETURN_PROPERTY_VALUE'); + assert.strictEqual(e.code, 'ERR_INVALID_MODULE_SPECIFIER'); } } })().then(common.mustCall()); diff --git a/test/es-module/test-esm-dynamic-import.js b/test/es-module/test-esm-dynamic-import.js index 6f8757da1b914e..6e64f86423c66b 100644 --- a/test/es-module/test-esm-dynamic-import.js +++ b/test/es-module/test-esm-dynamic-import.js @@ -1,7 +1,6 @@ 'use strict'; const common = require('../common'); const assert = require('assert'); -const { URL } = require('url'); const relativePath = '../fixtures/es-modules/test-esm-ok.mjs'; const absolutePath = require.resolve('../fixtures/es-modules/test-esm-ok.mjs'); diff --git a/test/es-module/test-esm-invalid-data-urls.js b/test/es-module/test-esm-invalid-data-urls.js new file mode 100644 index 00000000000000..67f0bfe4e25588 --- /dev/null +++ b/test/es-module/test-esm-invalid-data-urls.js @@ -0,0 +1,24 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +(async () => { + await assert.rejects(import('data:text/plain,export default0'), { + code: 'ERR_INVALID_MODULE_SPECIFIER', + message: + 'Invalid module "data:text/plain,export default0" has an unsupported ' + + 'MIME type "text/plain"', + }); + await assert.rejects(import('data:text/plain;base64,'), { + code: 'ERR_INVALID_MODULE_SPECIFIER', + message: + 'Invalid module "data:text/plain;base64," has an unsupported ' + + 'MIME type "text/plain"', + }); + await assert.rejects(import('data:application/json,[]'), { + code: 'ERR_INVALID_MODULE_SPECIFIER', + message: + 'Invalid module "data:application/json,[]" has an unsupported ' + + 'MIME type "application/json"', + }); +})().then(common.mustCall()); diff --git a/test/es-module/test-esm-loader-modulemap.js b/test/es-module/test-esm-loader-modulemap.js index a4d56a2c2fda1c..2d74cd385be52b 100644 --- a/test/es-module/test-esm-loader-modulemap.js +++ b/test/es-module/test-esm-loader-modulemap.js @@ -7,7 +7,6 @@ require('../common'); const assert = require('assert'); -const { URL } = require('url'); const { Loader } = require('internal/modules/esm/loader'); const ModuleMap = require('internal/modules/esm/module_map'); const ModuleJob = require('internal/modules/esm/module_job'); diff --git a/test/fixtures/child-process-stay-alive-forever.js b/test/fixtures/child-process-stay-alive-forever.js new file mode 100644 index 00000000000000..fda313eba074c1 --- /dev/null +++ b/test/fixtures/child-process-stay-alive-forever.js @@ -0,0 +1,3 @@ +setInterval(() => { + // Starting an interval to stay alive. +}, 1000); diff --git a/test/fixtures/ispreloading.js b/test/fixtures/ispreloading.js new file mode 100644 index 00000000000000..a936bbb6761cc5 --- /dev/null +++ b/test/fixtures/ispreloading.js @@ -0,0 +1,2 @@ +const assert = require('assert'); +assert(module.isPreloading); diff --git a/test/fixtures/policy/dependencies/dependencies-scopes-and-resources-policy.json b/test/fixtures/policy/dependencies/dependencies-scopes-and-resources-policy.json new file mode 100644 index 00000000000000..0cf33b16363352 --- /dev/null +++ b/test/fixtures/policy/dependencies/dependencies-scopes-and-resources-policy.json @@ -0,0 +1,14 @@ +{ + "resources": { + "../multi-deps.js": { + "integrity": true, + "cascade": true + } + }, + "scopes": { + "../": { + "integrity": true, + "dependencies": true + } + } +} diff --git a/test/fixtures/policy/multi-deps.js b/test/fixtures/policy/multi-deps.js new file mode 100644 index 00000000000000..51cdf61783989a --- /dev/null +++ b/test/fixtures/policy/multi-deps.js @@ -0,0 +1,3 @@ +'use strict'; +require('fs'); +require('process'); diff --git a/test/fixtures/process-exit-code-cases.js b/test/fixtures/process-exit-code-cases.js index c8c4a2ebe6c7ff..05b01afd8f4630 100644 --- a/test/fixtures/process-exit-code-cases.js +++ b/test/fixtures/process-exit-code-cases.js @@ -2,112 +2,135 @@ const assert = require('assert'); -const cases = []; -module.exports = cases; +function getTestCases(isWorker = false) { + const cases = []; + function exitsOnExitCodeSet() { + process.exitCode = 42; + process.on('exit', (code) => { + assert.strictEqual(process.exitCode, 42); + assert.strictEqual(code, 42); + }); + } + cases.push({ func: exitsOnExitCodeSet, result: 42 }); -function exitsOnExitCodeSet() { - process.exitCode = 42; - process.on('exit', (code) => { - assert.strictEqual(process.exitCode, 42); - assert.strictEqual(code, 42); - }); -} -cases.push({ func: exitsOnExitCodeSet, result: 42 }); + function changesCodeViaExit() { + process.exitCode = 99; + process.on('exit', (code) => { + assert.strictEqual(process.exitCode, 42); + assert.strictEqual(code, 42); + }); + process.exit(42); + } + cases.push({ func: changesCodeViaExit, result: 42 }); -function changesCodeViaExit() { - process.exitCode = 99; - process.on('exit', (code) => { - assert.strictEqual(process.exitCode, 42); - assert.strictEqual(code, 42); - }); - process.exit(42); -} -cases.push({ func: changesCodeViaExit, result: 42 }); + function changesCodeZeroExit() { + process.exitCode = 99; + process.on('exit', (code) => { + assert.strictEqual(process.exitCode, 0); + assert.strictEqual(code, 0); + }); + process.exit(0); + } + cases.push({ func: changesCodeZeroExit, result: 0 }); -function changesCodeZeroExit() { - process.exitCode = 99; - process.on('exit', (code) => { - assert.strictEqual(process.exitCode, 0); - assert.strictEqual(code, 0); + function exitWithOneOnUncaught() { + process.exitCode = 99; + process.on('exit', (code) => { + // cannot use assert because it will be uncaughtException -> 1 exit code + // that will render this test useless + if (code !== 1 || process.exitCode !== 1) { + console.log('wrong code! expected 1 for uncaughtException'); + process.exit(99); + } + }); + throw new Error('ok'); + } + cases.push({ + func: exitWithOneOnUncaught, + result: 1, + error: /^Error: ok$/, }); - process.exit(0); -} -cases.push({ func: changesCodeZeroExit, result: 0 }); -function exitWithOneOnUncaught() { - process.exitCode = 99; - process.on('exit', (code) => { - // cannot use assert because it will be uncaughtException -> 1 exit code - // that will render this test useless - if (code !== 1 || process.exitCode !== 1) { - console.log('wrong code! expected 1 for uncaughtException'); - process.exit(99); - } - }); - throw new Error('ok'); -} -cases.push({ - func: exitWithOneOnUncaught, - result: 1, - error: /^Error: ok$/, -}); + function changeCodeInsideExit() { + process.exitCode = 95; + process.on('exit', (code) => { + assert.strictEqual(process.exitCode, 95); + assert.strictEqual(code, 95); + process.exitCode = 99; + }); + } + cases.push({ func: changeCodeInsideExit, result: 99 }); -function changeCodeInsideExit() { - process.exitCode = 95; - process.on('exit', (code) => { - assert.strictEqual(process.exitCode, 95); - assert.strictEqual(code, 95); - process.exitCode = 99; - }); -} -cases.push({ func: changeCodeInsideExit, result: 99 }); + function zeroExitWithUncaughtHandler() { + process.on('exit', (code) => { + assert.strictEqual(process.exitCode, 0); + assert.strictEqual(code, 0); + }); + process.on('uncaughtException', () => { }); + throw new Error('ok'); + } + cases.push({ func: zeroExitWithUncaughtHandler, result: 0 }); -function zeroExitWithUncaughtHandler() { - process.on('exit', (code) => { - assert.strictEqual(process.exitCode, 0); - assert.strictEqual(code, 0); - }); - process.on('uncaughtException', () => {}); - throw new Error('ok'); -} -cases.push({ func: zeroExitWithUncaughtHandler, result: 0 }); + function changeCodeInUncaughtHandler() { + process.on('exit', (code) => { + assert.strictEqual(process.exitCode, 97); + assert.strictEqual(code, 97); + }); + process.on('uncaughtException', () => { + process.exitCode = 97; + }); + throw new Error('ok'); + } + cases.push({ func: changeCodeInUncaughtHandler, result: 97 }); -function changeCodeInUncaughtHandler() { - process.on('exit', (code) => { - assert.strictEqual(process.exitCode, 97); - assert.strictEqual(code, 97); + function changeCodeInExitWithUncaught() { + process.on('exit', (code) => { + assert.strictEqual(process.exitCode, 1); + assert.strictEqual(code, 1); + process.exitCode = 98; + }); + throw new Error('ok'); + } + cases.push({ + func: changeCodeInExitWithUncaught, + result: 98, + error: /^Error: ok$/, }); - process.on('uncaughtException', () => { - process.exitCode = 97; + + function exitWithZeroInExitWithUncaught() { + process.on('exit', (code) => { + assert.strictEqual(process.exitCode, 1); + assert.strictEqual(code, 1); + process.exitCode = 0; + }); + throw new Error('ok'); + } + cases.push({ + func: exitWithZeroInExitWithUncaught, + result: 0, + error: /^Error: ok$/, }); - throw new Error('ok'); -} -cases.push({ func: changeCodeInUncaughtHandler, result: 97 }); -function changeCodeInExitWithUncaught() { - process.on('exit', (code) => { - assert.strictEqual(process.exitCode, 1); - assert.strictEqual(code, 1); - process.exitCode = 98; + function exitWithThrowInUncaughtHandler() { + process.on('uncaughtException', () => { + throw new Error('ok') + }); + throw new Error('bad'); + } + cases.push({ + func: exitWithThrowInUncaughtHandler, + result: isWorker ? 1 : 7, + error: /^Error: ok$/, }); - throw new Error('ok'); -} -cases.push({ - func: changeCodeInExitWithUncaught, - result: 98, - error: /^Error: ok$/, -}); -function exitWithZeroInExitWithUncaught() { - process.on('exit', (code) => { - assert.strictEqual(process.exitCode, 1); - assert.strictEqual(code, 1); - process.exitCode = 0; + function exitWithUndefinedFatalException() { + process._fatalException = undefined; + throw new Error('ok'); + } + cases.push({ + func: exitWithUndefinedFatalException, + result: 6, }); - throw new Error('ok'); + return cases; } -cases.push({ - func: exitWithZeroInExitWithUncaught, - result: 0, - error: /^Error: ok$/, -}); +exports.getTestCases = getTestCases; diff --git a/test/fixtures/source-map/icu.js b/test/fixtures/source-map/icu.js new file mode 100644 index 00000000000000..a29f188ae86189 --- /dev/null +++ b/test/fixtures/source-map/icu.js @@ -0,0 +1,13 @@ +const React = { + createElement: () => { + "あ 🐕 🐕", function (e) { + throw e; + }(Error("an error")); + } +}; +const profile = /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("img", { + src: "avatar.png", + className: "profile" +}), /*#__PURE__*/React.createElement("h3", null, ["hello"])); + +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImljdS5qc3giXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsTUFBTSxLQUFLLEdBQUc7QUFDWixFQUFBLGFBQWEsRUFBRSxNQUFNO0FBQ2xCO0FBQUE7QUFBQSxNQUFpQixLQUFLLENBQUMsVUFBRCxDQUF0QixDQUFEO0FBQ0Q7QUFIVyxDQUFkO0FBTUEsTUFBTSxPQUFPLGdCQUNYLDhDQUNFO0FBQUssRUFBQSxHQUFHLEVBQUMsWUFBVDtBQUFzQixFQUFBLFNBQVMsRUFBQztBQUFoQyxFQURGLGVBRUUsZ0NBQUssQ0FBQyxPQUFELENBQUwsQ0FGRixDQURGIiwiZmlsZSI6InN0ZG91dCIsInNvdXJjZXNDb250ZW50IjpbImNvbnN0IFJlYWN0ID0ge1xuICBjcmVhdGVFbGVtZW50OiAoKSA9PiB7XG4gICAgKFwi44GCIPCfkJUg8J+QlVwiLCB0aHJvdyBFcnJvcihcImFuIGVycm9yXCIpKTtcbiAgfVxufTtcblxuY29uc3QgcHJvZmlsZSA9IChcbiAgPGRpdj5cbiAgICA8aW1nIHNyYz1cImF2YXRhci5wbmdcIiBjbGFzc05hbWU9XCJwcm9maWxlXCIgLz5cbiAgICA8aDM+e1tcImhlbGxvXCJdfTwvaDM+XG4gIDwvZGl2PlxuKTtcbiJdfQ== diff --git a/test/fixtures/source-map/icu.jsx b/test/fixtures/source-map/icu.jsx new file mode 100644 index 00000000000000..45325058b51222 --- /dev/null +++ b/test/fixtures/source-map/icu.jsx @@ -0,0 +1,12 @@ +const React = { + createElement: () => { + ("あ 🐕 🐕", throw Error("an error")); + } +}; + +const profile = ( +
+ +

{["hello"]}

+
+); diff --git a/test/fixtures/source-map/tabs.coffee b/test/fixtures/source-map/tabs.coffee new file mode 100644 index 00000000000000..abc5baab636e8a --- /dev/null +++ b/test/fixtures/source-map/tabs.coffee @@ -0,0 +1,29 @@ +# Assignment: +number = 42 +opposite = true + +# Conditions: +number = -42 if opposite + +# Functions: +square = (x) -> x * x + +# Arrays: +list = [1, 2, 3, 4, 5] + +# Objects: +math = + root: Math.sqrt + square: square + cube: (x) -> x * square x + +# Splats: +race = (winner, runners...) -> + print winner, runners + +# Existence: +if true + alert "I knew it!" + +# Array comprehensions: +cubes = (math.cube num for num in list) diff --git a/test/fixtures/source-map/tabs.js b/test/fixtures/source-map/tabs.js new file mode 100644 index 00000000000000..4e8ebf149dfd16 --- /dev/null +++ b/test/fixtures/source-map/tabs.js @@ -0,0 +1,56 @@ +// Generated by CoffeeScript 2.5.1 +(function() { + // Assignment: + var cubes, list, math, num, number, opposite, race, square; + + number = 42; + + opposite = true; + + if (opposite) { + // Conditions: + number = -42; + } + + // Functions: + square = function(x) { + return x * x; + }; + + // Arrays: + list = [1, 2, 3, 4, 5]; + + // Objects: + math = { + root: Math.sqrt, + square: square, + cube: function(x) { + return x * square(x); + } + }; + + // Splats: + race = function(winner, ...runners) { + return print(winner, runners); + }; + + // Existence: + if (true) { + alert("I knew it!"); + } + + // Array comprehensions: + cubes = (function() { + var i, len, results; + results = []; + for (i = 0, len = list.length; i < len; i++) { + num = list[i]; + results.push(math.cube(num)); + } + return results; + })(); + +}).call(this); + +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGFicy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInRhYnMuY29mZmVlIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBYTtFQUFBO0FBQUEsTUFBQSxLQUFBLEVBQUEsSUFBQSxFQUFBLElBQUEsRUFBQSxHQUFBLEVBQUEsTUFBQSxFQUFBLFFBQUEsRUFBQSxJQUFBLEVBQUE7O0VBQ2IsTUFBQSxHQUFXOztFQUNYLFFBQUEsR0FBVzs7RUFHWCxJQUFnQixRQUFoQjs7SUFBQSxNQUFBLEdBQVMsQ0FBQyxHQUFWO0dBTGE7OztFQVFiLE1BQUEsR0FBUyxRQUFBLENBQUMsQ0FBRCxDQUFBO1dBQU8sQ0FBQSxHQUFJO0VBQVgsRUFSSTs7O0VBV2IsSUFBQSxHQUFPLENBQUMsQ0FBRCxFQUFJLENBQUosRUFBTyxDQUFQLEVBQVUsQ0FBVixFQUFhLENBQWIsRUFYTTs7O0VBY2IsSUFBQSxHQUNDO0lBQUEsSUFBQSxFQUFRLElBQUksQ0FBQyxJQUFiO0lBQ0EsTUFBQSxFQUFRLE1BRFI7SUFFQSxJQUFBLEVBQVEsUUFBQSxDQUFDLENBQUQsQ0FBQTthQUFPLENBQUEsR0FBSSxNQUFBLENBQU8sQ0FBUDtJQUFYO0VBRlIsRUFmWTs7O0VBb0JiLElBQUEsR0FBTyxRQUFBLENBQUMsTUFBRCxFQUFBLEdBQVMsT0FBVCxDQUFBO1dBQ04sS0FBQSxDQUFNLE1BQU4sRUFBYyxPQUFkO0VBRE0sRUFwQk07OztFQXdCYixJQUFHLElBQUg7SUFDQyxLQUFBLENBQU0sWUFBTixFQUREO0dBeEJhOzs7RUE0QmIsS0FBQTs7QUFBUztJQUFBLEtBQUEsc0NBQUE7O21CQUFBLElBQUksQ0FBQyxJQUFMLENBQVUsR0FBVjtJQUFBLENBQUE7OztBQTVCSSIsInNvdXJjZXNDb250ZW50IjpbIiMgQXNzaWdubWVudDpcbm51bWJlciAgID0gNDJcbm9wcG9zaXRlID0gdHJ1ZVxuXG4jIENvbmRpdGlvbnM6XG5udW1iZXIgPSAtNDIgaWYgb3Bwb3NpdGVcblxuIyBGdW5jdGlvbnM6XG5zcXVhcmUgPSAoeCkgLT4geCAqIHhcblxuIyBBcnJheXM6XG5saXN0ID0gWzEsIDIsIDMsIDQsIDVdXG5cbiMgT2JqZWN0czpcbm1hdGggPVxuXHRyb290OiAgIE1hdGguc3FydFxuXHRzcXVhcmU6IHNxdWFyZVxuXHRjdWJlOiAgICh4KSAtPiB4ICogc3F1YXJlIHhcblxuIyBTcGxhdHM6XG5yYWNlID0gKHdpbm5lciwgcnVubmVycy4uLikgLT5cblx0cHJpbnQgd2lubmVyLCBydW5uZXJzXG5cbiMgRXhpc3RlbmNlOlxuaWYgdHJ1ZVxuXHRhbGVydCBcIkkga25ldyBpdCFcIlxuXG4jIEFycmF5IGNvbXByZWhlbnNpb25zOlxuY3ViZXMgPSAobWF0aC5jdWJlIG51bSBmb3IgbnVtIGluIGxpc3QpXG4iXX0= +//# sourceURL=/Users/bencoe/oss/coffee-script-test/tabs.coffee diff --git a/test/fixtures/source-map/webpack.js b/test/fixtures/source-map/webpack.js new file mode 100644 index 00000000000000..c8bf26ab6f6f59 --- /dev/null +++ b/test/fixtures/source-map/webpack.js @@ -0,0 +1,2 @@ +!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=0)}([function(e,t){const r=()=>{n()},n=()=>{o()},o=()=>{throw new Error("oh no!")};r()}]); +//# sourceMappingURL=webpack.js.map diff --git a/test/fixtures/source-map/webpack.js.map b/test/fixtures/source-map/webpack.js.map new file mode 100644 index 00000000000000..c0849ff6f0a3ba --- /dev/null +++ b/test/fixtures/source-map/webpack.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./webpack.js"],"names":["installedModules","__webpack_require__","moduleId","exports","module","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","functionB","functionC","functionD","Error"],"mappings":"aACE,IAAIA,EAAmB,GAGvB,SAASC,EAAoBC,GAG5B,GAAGF,EAAiBE,GACnB,OAAOF,EAAiBE,GAAUC,QAGnC,IAAIC,EAASJ,EAAiBE,GAAY,CACzCG,EAAGH,EACHI,GAAG,EACHH,QAAS,IAUV,OANAI,EAAQL,GAAUM,KAAKJ,EAAOD,QAASC,EAAQA,EAAOD,QAASF,GAG/DG,EAAOE,GAAI,EAGJF,EAAOD,QAKfF,EAAoBQ,EAAIF,EAGxBN,EAAoBS,EAAIV,EAGxBC,EAAoBU,EAAI,SAASR,EAASS,EAAMC,GAC3CZ,EAAoBa,EAAEX,EAASS,IAClCG,OAAOC,eAAeb,EAASS,EAAM,CAAEK,YAAY,EAAMC,IAAKL,KAKhEZ,EAAoBkB,EAAI,SAAShB,GACX,oBAAXiB,QAA0BA,OAAOC,aAC1CN,OAAOC,eAAeb,EAASiB,OAAOC,YAAa,CAAEC,MAAO,WAE7DP,OAAOC,eAAeb,EAAS,aAAc,CAAEmB,OAAO,KAQvDrB,EAAoBsB,EAAI,SAASD,EAAOE,GAEvC,GADU,EAAPA,IAAUF,EAAQrB,EAAoBqB,IAC/B,EAAPE,EAAU,OAAOF,EACpB,GAAW,EAAPE,GAA8B,iBAAVF,GAAsBA,GAASA,EAAMG,WAAY,OAAOH,EAChF,IAAII,EAAKX,OAAOY,OAAO,MAGvB,GAFA1B,EAAoBkB,EAAEO,GACtBX,OAAOC,eAAeU,EAAI,UAAW,CAAET,YAAY,EAAMK,MAAOA,IACtD,EAAPE,GAA4B,iBAATF,EAAmB,IAAI,IAAIM,KAAON,EAAOrB,EAAoBU,EAAEe,EAAIE,EAAK,SAASA,GAAO,OAAON,EAAMM,IAAQC,KAAK,KAAMD,IAC9I,OAAOF,GAIRzB,EAAoB6B,EAAI,SAAS1B,GAChC,IAAIS,EAAST,GAAUA,EAAOqB,WAC7B,WAAwB,OAAOrB,EAAgB,SAC/C,WAA8B,OAAOA,GAEtC,OADAH,EAAoBU,EAAEE,EAAQ,IAAKA,GAC5BA,GAIRZ,EAAoBa,EAAI,SAASiB,EAAQC,GAAY,OAAOjB,OAAOkB,UAAUC,eAAe1B,KAAKuB,EAAQC,IAGzG/B,EAAoBkC,EAAI,GAIjBlC,EAAoBA,EAAoBmC,EAAI,G,gBClFrD,MAIMC,EAAY,KAChBC,KAGIA,EAAY,KAChBC,KAGIA,EAAY,KAChB,MAAM,IAAIC,MAAM,WAZhBH","file":"webpack.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","const functionA = () => {\n functionB()\n}\n\nconst functionB = () => {\n functionC()\n}\n\nconst functionC = () => {\n functionD()\n}\n\nconst functionD = () => {\n throw new Error('oh no!')\n}\n\nfunctionA()\n"],"sourceRoot":""} \ No newline at end of file diff --git a/test/fixtures/worker-preload.js b/test/fixtures/worker-preload.js new file mode 100644 index 00000000000000..6e5c4a31d2f20e --- /dev/null +++ b/test/fixtures/worker-preload.js @@ -0,0 +1,9 @@ +const { + Worker, + workerData, + threadId +} = require('worker_threads'); + +if (threadId < 2) { + new Worker('1 + 1', { eval: true }); +} diff --git a/test/fixtures/wpt/README.md b/test/fixtures/wpt/README.md index a728d94361ca9c..b7ce4b81443080 100644 --- a/test/fixtures/wpt/README.md +++ b/test/fixtures/wpt/README.md @@ -15,9 +15,11 @@ Last update: - url: https://github.com/web-platform-tests/wpt/tree/418f7fabeb/url - resources: https://github.com/web-platform-tests/wpt/tree/e1fddfbf80/resources - interfaces: https://github.com/web-platform-tests/wpt/tree/8ada332aea/interfaces +- html/webappapis/atob: https://github.com/web-platform-tests/wpt/tree/f267e1dca6/html/webappapis/atob - html/webappapis/microtask-queuing: https://github.com/web-platform-tests/wpt/tree/0c3bed38df/html/webappapis/microtask-queuing - html/webappapis/timers: https://github.com/web-platform-tests/wpt/tree/ddfe9c089b/html/webappapis/timers - hr-time: https://github.com/web-platform-tests/wpt/tree/a5d1774ecf/hr-time +- dom/abort: https://github.com/web-platform-tests/wpt/tree/1728d198c9/dom/abort [Web Platform Tests]: https://github.com/web-platform-tests/wpt [`git node wpt`]: https://github.com/nodejs/node-core-utils/blob/master/docs/git-node.md#git-node-wpt diff --git a/test/fixtures/wpt/dom/abort/event.any.js b/test/fixtures/wpt/dom/abort/event.any.js new file mode 100644 index 00000000000000..2589ba1ce45091 --- /dev/null +++ b/test/fixtures/wpt/dom/abort/event.any.js @@ -0,0 +1,72 @@ +test(t => { + const c = new AbortController(), + s = c.signal; + let state = "begin"; + + assert_false(s.aborted); + + s.addEventListener("abort", + t.step_func(e => { + assert_equals(state, "begin"); + state = "aborted"; + }) + ); + c.abort(); + + assert_equals(state, "aborted"); + assert_true(s.aborted); + + c.abort(); +}, "AbortController abort() should fire event synchronously"); + +test(t => { + const controller = new AbortController(); + const signal = controller.signal; + assert_equals(controller.signal, signal, + "value of controller.signal should not have changed"); + controller.abort(); + assert_equals(controller.signal, signal, + "value of controller.signal should still not have changed"); +}, "controller.signal should always return the same object"); + +test(t => { + const controller = new AbortController(); + const signal = controller.signal; + let eventCount = 0; + signal.onabort = () => { + ++eventCount; + }; + controller.abort(); + assert_true(signal.aborted); + assert_equals(eventCount, 1, "event handler should have been called once"); + controller.abort(); + assert_true(signal.aborted); + assert_equals(eventCount, 1, + "event handler should not have been called again"); +}, "controller.abort() should do nothing the second time it is called"); + +test(t => { + const controller = new AbortController(); + controller.abort(); + controller.signal.onabort = + t.unreached_func("event handler should not be called"); +}, "event handler should not be called if added after controller.abort()"); + +test(t => { + const controller = new AbortController(); + const signal = controller.signal; + signal.onabort = t.step_func(e => { + assert_equals(e.type, "abort", "event type should be abort"); + assert_equals(e.target, signal, "event target should be signal"); + assert_false(e.bubbles, "event should not bubble"); + assert_true(e.isTrusted, "event should be trusted"); + }); + controller.abort(); +}, "the abort event should have the right properties"); + +test(t => { + const signal = AbortSignal.abort(); + assert_true(signal.aborted); +}, "the AbortSignal.abort() static returns an already aborted signal"); + +done(); diff --git a/test/fixtures/wpt/html/webappapis/atob/base64.any.js b/test/fixtures/wpt/html/webappapis/atob/base64.any.js new file mode 100644 index 00000000000000..7f433f4d8a9ee2 --- /dev/null +++ b/test/fixtures/wpt/html/webappapis/atob/base64.any.js @@ -0,0 +1,163 @@ +/** + * btoa() as defined by the HTML5 spec, which mostly just references RFC4648. + */ +function mybtoa(s) { + // String conversion as required by WebIDL. + s = String(s); + + // "The btoa() method must throw an INVALID_CHARACTER_ERR exception if the + // method's first argument contains any character whose code point is + // greater than U+00FF." + for (var i = 0; i < s.length; i++) { + if (s.charCodeAt(i) > 255) { + return "INVALID_CHARACTER_ERR"; + } + } + + var out = ""; + for (var i = 0; i < s.length; i += 3) { + var groupsOfSix = [undefined, undefined, undefined, undefined]; + groupsOfSix[0] = s.charCodeAt(i) >> 2; + groupsOfSix[1] = (s.charCodeAt(i) & 0x03) << 4; + if (s.length > i + 1) { + groupsOfSix[1] |= s.charCodeAt(i + 1) >> 4; + groupsOfSix[2] = (s.charCodeAt(i + 1) & 0x0f) << 2; + } + if (s.length > i + 2) { + groupsOfSix[2] |= s.charCodeAt(i + 2) >> 6; + groupsOfSix[3] = s.charCodeAt(i + 2) & 0x3f; + } + for (var j = 0; j < groupsOfSix.length; j++) { + if (typeof groupsOfSix[j] == "undefined") { + out += "="; + } else { + out += btoaLookup(groupsOfSix[j]); + } + } + } + return out; +} + +/** + * Lookup table for mybtoa(), which converts a six-bit number into the + * corresponding ASCII character. + */ +function btoaLookup(idx) { + if (idx < 26) { + return String.fromCharCode(idx + 'A'.charCodeAt(0)); + } + if (idx < 52) { + return String.fromCharCode(idx - 26 + 'a'.charCodeAt(0)); + } + if (idx < 62) { + return String.fromCharCode(idx - 52 + '0'.charCodeAt(0)); + } + if (idx == 62) { + return '+'; + } + if (idx == 63) { + return '/'; + } + // Throw INVALID_CHARACTER_ERR exception here -- won't be hit in the tests. +} + +function btoaException(input) { + input = String(input); + for (var i = 0; i < input.length; i++) { + if (input.charCodeAt(i) > 255) { + return true; + } + } + return false; +} + +function testBtoa(input) { + // "The btoa() method must throw an INVALID_CHARACTER_ERR exception if the + // method's first argument contains any character whose code point is + // greater than U+00FF." + var normalizedInput = String(input); + for (var i = 0; i < normalizedInput.length; i++) { + if (normalizedInput.charCodeAt(i) > 255) { + assert_throws_dom("InvalidCharacterError", function() { btoa(input); }, + "Code unit " + i + " has value " + normalizedInput.charCodeAt(i) + ", which is greater than 255"); + return; + } + } + assert_equals(btoa(input), mybtoa(input)); + assert_equals(atob(btoa(input)), String(input), "atob(btoa(input)) must be the same as String(input)"); +} + +var tests = ["עברית", "", "ab", "abc", "abcd", "abcde", + // This one is thrown in because IE9 seems to fail atob(btoa()) on it. Or + // possibly to fail btoa(). I actually can't tell what's happening here, + // but it doesn't hurt. + "\xff\xff\xc0", + // Is your DOM implementation binary-safe? + "\0a", "a\0b", + // WebIDL tests. + undefined, null, 7, 12, 1.5, true, false, NaN, +Infinity, -Infinity, 0, -0, + {toString: function() { return "foo" }}, +]; +for (var i = 0; i < 258; i++) { + tests.push(String.fromCharCode(i)); +} +tests.push(String.fromCharCode(10000)); +tests.push(String.fromCharCode(65534)); +tests.push(String.fromCharCode(65535)); + +// This is supposed to be U+10000. +tests.push(String.fromCharCode(0xd800, 0xdc00)); +tests = tests.map( + function(elem) { + var expected = mybtoa(elem); + if (expected === "INVALID_CHARACTER_ERR") { + return ["btoa(" + format_value(elem) + ") must raise INVALID_CHARACTER_ERR", elem]; + } + return ["btoa(" + format_value(elem) + ") == " + format_value(mybtoa(elem)), elem]; + } +); + +var everything = ""; +for (var i = 0; i < 256; i++) { + everything += String.fromCharCode(i); +} +tests.push(["btoa(first 256 code points concatenated)", everything]); + +generate_tests(testBtoa, tests); + +promise_test(() => fetch("../../../fetch/data-urls/resources/base64.json").then(res => res.json()).then(runAtobTests), "atob() setup."); + +const idlTests = [ + [undefined, null], + [null, [158, 233, 101]], + [7, null], + [12, [215]], + [1.5, null], + [true, [182, 187]], + [false, null], + [NaN, [53, 163]], + [+Infinity, [34, 119, 226, 158, 43, 114]], + [-Infinity, null], + [0, null], + [-0, null], + [{toString: function() { return "foo" }}, [126, 138]], + [{toString: function() { return "abcd" }}, [105, 183, 29]] +]; + +function runAtobTests(tests) { + const allTests = tests.concat(idlTests); + for(let i = 0; i < allTests.length; i++) { + const input = allTests[i][0], + output = allTests[i][1]; + test(() => { + if(output === null) { + assert_throws_dom("InvalidCharacterError", () => globalThis.atob(input)); + } else { + const result = globalThis.atob(input); + for(let ii = 0; ii < output.length; ii++) { + assert_equals(result.charCodeAt(ii), output[ii]); + } + } + }, "atob(" + format_value(input) + ")"); + } +} diff --git a/test/fixtures/wpt/versions.json b/test/fixtures/wpt/versions.json index b6c9a36fa641c9..4a3e4b84d1f339 100644 --- a/test/fixtures/wpt/versions.json +++ b/test/fixtures/wpt/versions.json @@ -19,6 +19,10 @@ "commit": "8ada332aeac03764f73ec7ff66f682c5c0b454b4", "path": "interfaces" }, + "html/webappapis/atob": { + "commit": "f267e1dca6f57a9f4d69f32a6920adfdb3268656", + "path": "html/webappapis/atob" + }, "html/webappapis/microtask-queuing": { "commit": "0c3bed38df6d9dcd1441873728fb5c1bb59c92df", "path": "html/webappapis/microtask-queuing" @@ -30,5 +34,9 @@ "hr-time": { "commit": "a5d1774ecf41751d1c9357c27c709ee33bf3e279", "path": "hr-time" + }, + "dom/abort": { + "commit": "1728d198c92834d92f7f399ef35e7823d5bfa0e4", + "path": "dom/abort" } -} \ No newline at end of file +} diff --git a/test/internet/test-dns-lookup.js b/test/internet/test-dns-lookup.js index df5ccf99a56fc3..6939698d392317 100644 --- a/test/internet/test-dns-lookup.js +++ b/test/internet/test-dns-lookup.js @@ -8,30 +8,30 @@ const { addresses } = require('../common/internet'); const assert = require('assert'); assert.rejects( - dnsPromises.lookup(addresses.INVALID_HOST, { + dnsPromises.lookup(addresses.NOT_FOUND, { hints: 0, family: 0, all: false }), { code: 'ENOTFOUND', - message: `getaddrinfo ENOTFOUND ${addresses.INVALID_HOST}` + message: `getaddrinfo ENOTFOUND ${addresses.NOT_FOUND}` } ); assert.rejects( - dnsPromises.lookup(addresses.INVALID_HOST, { + dnsPromises.lookup(addresses.NOT_FOUND, { hints: 0, family: 0, all: true }), { code: 'ENOTFOUND', - message: `getaddrinfo ENOTFOUND ${addresses.INVALID_HOST}` + message: `getaddrinfo ENOTFOUND ${addresses.NOT_FOUND}` } ); -dns.lookup(addresses.INVALID_HOST, { +dns.lookup(addresses.NOT_FOUND, { hints: 0, family: 0, all: true @@ -39,8 +39,8 @@ dns.lookup(addresses.INVALID_HOST, { assert.strictEqual(error.code, 'ENOTFOUND'); assert.strictEqual( error.message, - `getaddrinfo ENOTFOUND ${addresses.INVALID_HOST}` + `getaddrinfo ENOTFOUND ${addresses.NOT_FOUND}` ); assert.strictEqual(error.syscall, 'getaddrinfo'); - assert.strictEqual(error.hostname, addresses.INVALID_HOST); + assert.strictEqual(error.hostname, addresses.NOT_FOUND); })); diff --git a/test/internet/test-dns.js b/test/internet/test-dns.js index c74acc50dd7d3d..cb5069ce1c0288 100644 --- a/test/internet/test-dns.js +++ b/test/internet/test-dns.js @@ -91,7 +91,7 @@ TEST(async function test_resolve4_ttl(done) { assert.strictEqual(typeof item, 'object'); assert.strictEqual(typeof item.ttl, 'number'); assert.strictEqual(typeof item.address, 'string'); - assert.ok(item.ttl > 0); + assert.ok(item.ttl >= 0); assert.ok(isIPv4(item.address)); } } @@ -119,7 +119,7 @@ TEST(async function test_resolve6_ttl(done) { assert.strictEqual(typeof item, 'object'); assert.strictEqual(typeof item.ttl, 'number'); assert.strictEqual(typeof item.address, 'string'); - assert.ok(item.ttl > 0); + assert.ok(item.ttl >= 0); assert.ok(isIPv6(item.address)); } } @@ -163,13 +163,13 @@ TEST(async function test_resolveMx(done) { }); TEST(function test_resolveMx_failure(done) { - dnsPromises.resolveMx(addresses.INVALID_HOST) + dnsPromises.resolveMx(addresses.NOT_FOUND) .then(common.mustNotCall()) .catch(common.mustCall((err) => { assert.strictEqual(err.code, 'ENOTFOUND'); })); - const req = dns.resolveMx(addresses.INVALID_HOST, function(err, result) { + const req = dns.resolveMx(addresses.NOT_FOUND, function(err, result) { assert.ok(err instanceof Error); assert.strictEqual(err.code, 'ENOTFOUND'); @@ -203,13 +203,13 @@ TEST(async function test_resolveNs(done) { }); TEST(function test_resolveNs_failure(done) { - dnsPromises.resolveNs(addresses.INVALID_HOST) + dnsPromises.resolveNs(addresses.NOT_FOUND) .then(common.mustNotCall()) .catch(common.mustCall((err) => { assert.strictEqual(err.code, 'ENOTFOUND'); })); - const req = dns.resolveNs(addresses.INVALID_HOST, function(err, result) { + const req = dns.resolveNs(addresses.NOT_FOUND, function(err, result) { assert.ok(err instanceof Error); assert.strictEqual(err.code, 'ENOTFOUND'); @@ -247,13 +247,13 @@ TEST(async function test_resolveSrv(done) { }); TEST(function test_resolveSrv_failure(done) { - dnsPromises.resolveSrv(addresses.INVALID_HOST) + dnsPromises.resolveSrv(addresses.NOT_FOUND) .then(common.mustNotCall()) .catch(common.mustCall((err) => { assert.strictEqual(err.code, 'ENOTFOUND'); })); - const req = dns.resolveSrv(addresses.INVALID_HOST, function(err, result) { + const req = dns.resolveSrv(addresses.NOT_FOUND, function(err, result) { assert.ok(err instanceof Error); assert.strictEqual(err.code, 'ENOTFOUND'); @@ -287,13 +287,13 @@ TEST(async function test_resolvePtr(done) { }); TEST(function test_resolvePtr_failure(done) { - dnsPromises.resolvePtr(addresses.INVALID_HOST) + dnsPromises.resolvePtr(addresses.NOT_FOUND) .then(common.mustNotCall()) .catch(common.mustCall((err) => { assert.strictEqual(err.code, 'ENOTFOUND'); })); - const req = dns.resolvePtr(addresses.INVALID_HOST, function(err, result) { + const req = dns.resolvePtr(addresses.NOT_FOUND, function(err, result) { assert.ok(err instanceof Error); assert.strictEqual(err.code, 'ENOTFOUND'); @@ -332,13 +332,13 @@ TEST(async function test_resolveNaptr(done) { }); TEST(function test_resolveNaptr_failure(done) { - dnsPromises.resolveNaptr(addresses.INVALID_HOST) + dnsPromises.resolveNaptr(addresses.NOT_FOUND) .then(common.mustNotCall()) .catch(common.mustCall((err) => { assert.strictEqual(err.code, 'ENOTFOUND'); })); - const req = dns.resolveNaptr(addresses.INVALID_HOST, function(err, result) { + const req = dns.resolveNaptr(addresses.NOT_FOUND, function(err, result) { assert.ok(err instanceof Error); assert.strictEqual(err.code, 'ENOTFOUND'); @@ -381,13 +381,53 @@ TEST(async function test_resolveSoa(done) { }); TEST(function test_resolveSoa_failure(done) { - dnsPromises.resolveSoa(addresses.INVALID_HOST) + dnsPromises.resolveSoa(addresses.NOT_FOUND) .then(common.mustNotCall()) .catch(common.mustCall((err) => { assert.strictEqual(err.code, 'ENOTFOUND'); })); - const req = dns.resolveSoa(addresses.INVALID_HOST, function(err, result) { + const req = dns.resolveSoa(addresses.NOT_FOUND, function(err, result) { + assert.ok(err instanceof Error); + assert.strictEqual(err.code, 'ENOTFOUND'); + + assert.strictEqual(result, undefined); + + done(); + }); + + checkWrap(req); +}); + +TEST(async function test_resolveCaa(done) { + function validateResult(result) { + assert.ok(Array.isArray(result), + `expected array, got ${util.inspect(result)}`); + assert.strictEqual(result.length, 1); + assert.strictEqual(typeof result[0].critical, 'number'); + assert.strictEqual(result[0].critical, 0); + assert.strictEqual(result[0].issue, 'pki.goog'); + } + + validateResult(await dnsPromises.resolveCaa(addresses.CAA_HOST)); + + const req = dns.resolveCaa(addresses.CAA_HOST, function(err, records) { + assert.ifError(err); + validateResult(records); + done(); + }); + + checkWrap(req); +}); + +TEST(function test_resolveCaa_failure(done) { + dnsPromises.resolveTxt(addresses.NOT_FOUND) + .then(common.mustNotCall()) + .catch(common.mustCall((err) => { + assert.strictEqual(err.code, 'ENOTFOUND'); + })); + + const req = dns.resolveCaa(addresses.NOT_FOUND, function(err, result) { assert.ok(err instanceof Error); assert.strictEqual(err.code, 'ENOTFOUND'); @@ -421,13 +461,13 @@ TEST(async function test_resolveCname(done) { }); TEST(function test_resolveCname_failure(done) { - dnsPromises.resolveCname(addresses.INVALID_HOST) + dnsPromises.resolveCname(addresses.NOT_FOUND) .then(common.mustNotCall()) .catch(common.mustCall((err) => { assert.strictEqual(err.code, 'ENOTFOUND'); })); - const req = dns.resolveCname(addresses.INVALID_HOST, function(err, result) { + const req = dns.resolveCname(addresses.NOT_FOUND, function(err, result) { assert.ok(err instanceof Error); assert.strictEqual(err.code, 'ENOTFOUND'); @@ -459,13 +499,13 @@ TEST(async function test_resolveTxt(done) { }); TEST(function test_resolveTxt_failure(done) { - dnsPromises.resolveTxt(addresses.INVALID_HOST) + dnsPromises.resolveTxt(addresses.NOT_FOUND) .then(common.mustNotCall()) .catch(common.mustCall((err) => { assert.strictEqual(err.code, 'ENOTFOUND'); })); - const req = dns.resolveTxt(addresses.INVALID_HOST, function(err, result) { + const req = dns.resolveTxt(addresses.NOT_FOUND, function(err, result) { assert.ok(err instanceof Error); assert.strictEqual(err.code, 'ENOTFOUND'); @@ -479,16 +519,16 @@ TEST(function test_resolveTxt_failure(done) { TEST(function test_lookup_failure(done) { - dnsPromises.lookup(addresses.INVALID_HOST, 4) + dnsPromises.lookup(addresses.NOT_FOUND, 4) .then(common.mustNotCall()) .catch(common.expectsError({ code: dns.NOTFOUND })); - const req = dns.lookup(addresses.INVALID_HOST, 4, (err) => { + const req = dns.lookup(addresses.NOT_FOUND, 4, (err) => { assert.ok(err instanceof Error); assert.strictEqual(err.code, dns.NOTFOUND); assert.strictEqual(err.code, 'ENOTFOUND'); assert.ok(!/ENOENT/.test(err.message)); - assert.ok(err.message.includes(addresses.INVALID_HOST)); + assert.ok(err.message.includes(addresses.NOT_FOUND)); done(); }); @@ -632,18 +672,18 @@ TEST(function test_reverse_failure(done) { TEST(function test_lookup_failure(done) { - dnsPromises.lookup(addresses.INVALID_HOST) + dnsPromises.lookup(addresses.NOT_FOUND) .then(common.mustNotCall()) .catch(common.expectsError({ code: 'ENOTFOUND', - hostname: addresses.INVALID_HOST + hostname: addresses.NOT_FOUND })); - const req = dns.lookup(addresses.INVALID_HOST, (err) => { + const req = dns.lookup(addresses.NOT_FOUND, (err) => { assert(err instanceof Error); assert.strictEqual(err.code, 'ENOTFOUND'); // Silly error code... - assert.strictEqual(err.hostname, addresses.INVALID_HOST); - assert.ok(err.message.includes(addresses.INVALID_HOST)); + assert.strictEqual(err.hostname, addresses.NOT_FOUND); + assert.ok(err.message.includes(addresses.NOT_FOUND)); done(); }); @@ -653,7 +693,7 @@ TEST(function test_lookup_failure(done) { TEST(function test_resolve_failure(done) { - const req = dns.resolve4(addresses.INVALID_HOST, (err) => { + const req = dns.resolve4(addresses.NOT_FOUND, (err) => { assert(err instanceof Error); switch (err.code) { @@ -665,8 +705,8 @@ TEST(function test_resolve_failure(done) { break; } - assert.strictEqual(err.hostname, addresses.INVALID_HOST); - assert.ok(err.message.includes(addresses.INVALID_HOST)); + assert.strictEqual(err.hostname, addresses.NOT_FOUND); + assert.ok(err.message.includes(addresses.NOT_FOUND)); done(); }); diff --git a/test/internet/test-trace-events-dns.js b/test/internet/test-trace-events-dns.js index 9e5a0ccb026c1a..8c6bd249cbf2a7 100644 --- a/test/internet/test-trace-events-dns.js +++ b/test/internet/test-trace-events-dns.js @@ -29,6 +29,7 @@ const tests = { 'resolveAny': 'dns.resolveAny("example.com", (err, res) => {});', 'resolve4': 'dns.resolve4("example.com", (err, res) => {});', 'resolve6': 'dns.resolve6("example.com", (err, res) => {});', + 'resolveCaa': 'dns.resolveCaa("example.com", (err, res) => {});', 'resolveCname': 'dns.resolveCname("example.com", (err, res) => {});', 'resolveMx': 'dns.resolveMx("example.com", (err, res) => {});', 'resolveNs': 'dns.resolveNs("example.com", (err, res) => {});', diff --git a/test/js-native-api/test_exception/testFinalizerException.js b/test/js-native-api/test_exception/testFinalizerException.js index 1ba74ba4802a0f..4b6993fde47164 100644 --- a/test/js-native-api/test_exception/testFinalizerException.js +++ b/test/js-native-api/test_exception/testFinalizerException.js @@ -29,4 +29,4 @@ const child = spawnSync(process.execPath, [ '--expose-gc', __filename, 'child' ]); assert.strictEqual(child.signal, null); -assert.match(child.stderr.toString(), /Error during Finalize/m); +assert.match(child.stderr.toString(), /Error during Finalize/); diff --git a/test/js-native-api/test_general/test.js b/test/js-native-api/test_general/test.js index 4dbdc37c918c37..76527bc81c7cd8 100644 --- a/test/js-native-api/test_general/test.js +++ b/test/js-native-api/test_general/test.js @@ -33,7 +33,7 @@ assert.notStrictEqual(test_general.testGetPrototype(baseObject), test_general.testGetPrototype(extendedObject)); // Test version management functions -assert.strictEqual(test_general.testGetVersion(), 7); +assert.strictEqual(test_general.testGetVersion(), 8); [ 123, diff --git a/test/js-native-api/test_object/test_object.c b/test/js-native-api/test_object/test_object.c index 70a85e03c725dd..9a90b5689e074d 100644 --- a/test/js-native-api/test_object/test_object.c +++ b/test/js-native-api/test_object/test_object.c @@ -1,4 +1,3 @@ -#define NAPI_EXPERIMENTAL #include #include "../common.h" #include diff --git a/test/js-native-api/test_reference_double_free/binding.gyp b/test/js-native-api/test_reference_double_free/binding.gyp new file mode 100644 index 00000000000000..864846765d0132 --- /dev/null +++ b/test/js-native-api/test_reference_double_free/binding.gyp @@ -0,0 +1,11 @@ +{ + "targets": [ + { + "target_name": "test_reference_double_free", + "sources": [ + "../entry_point.c", + "test_reference_double_free.c" + ] + } + ] +} diff --git a/test/js-native-api/test_reference_double_free/test.js b/test/js-native-api/test_reference_double_free/test.js new file mode 100644 index 00000000000000..242d850ba9f677 --- /dev/null +++ b/test/js-native-api/test_reference_double_free/test.js @@ -0,0 +1,11 @@ +'use strict'; + +// This test makes no assertions. It tests a fix without which it will crash +// with a double free. + +const { buildType } = require('../../common'); + +const addon = require(`./build/${buildType}/test_reference_double_free`); + +{ new addon.MyObject(true); } +{ new addon.MyObject(false); } diff --git a/test/js-native-api/test_reference_double_free/test_reference_double_free.c b/test/js-native-api/test_reference_double_free/test_reference_double_free.c new file mode 100644 index 00000000000000..f38867d220ae68 --- /dev/null +++ b/test/js-native-api/test_reference_double_free/test_reference_double_free.c @@ -0,0 +1,55 @@ +#include +#include +#include "../common.h" + +static size_t g_call_count = 0; + +static void Destructor(napi_env env, void* data, void* nothing) { + napi_ref* ref = data; + NAPI_CALL_RETURN_VOID(env, napi_delete_reference(env, *ref)); + free(ref); +} + +static void NoDeleteDestructor(napi_env env, void* data, void* hint) { + napi_ref* ref = data; + size_t* call_count = hint; + + // This destructor must be called exactly once. + if ((*call_count) > 0) abort(); + *call_count = ((*call_count) + 1); + free(ref); +} + +static napi_value New(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value js_this, js_delete; + bool delete; + napi_ref* ref = malloc(sizeof(*ref)); + + NAPI_CALL(env, + napi_get_cb_info(env, info, &argc, &js_delete, &js_this, NULL)); + NAPI_CALL(env, napi_get_value_bool(env, js_delete, &delete)); + + if (delete) { + NAPI_CALL(env, + napi_wrap(env, js_this, ref, Destructor, NULL, ref)); + } else { + NAPI_CALL(env, + napi_wrap(env, js_this, ref, NoDeleteDestructor, &g_call_count, ref)); + } + NAPI_CALL(env, napi_reference_ref(env, *ref, NULL)); + + return js_this; +} + +EXTERN_C_START +napi_value Init(napi_env env, napi_value exports) { + napi_value myobj_ctor; + NAPI_CALL(env, + napi_define_class( + env, "MyObject", NAPI_AUTO_LENGTH, New, NULL, 0, NULL, &myobj_ctor)); + NAPI_CALL(env, + napi_set_named_property(env, exports, "MyObject", myobj_ctor)); + return exports; +} +EXTERN_C_END diff --git a/test/known_issues/known_issues.status b/test/known_issues/known_issues.status index e0f0a456089bf2..690e6e9840b77c 100644 --- a/test/known_issues/known_issues.status +++ b/test/known_issues/known_issues.status @@ -15,16 +15,32 @@ test-vm-timeout-escape-queuemicrotask: SKIP [$system==win32] [$system==linux] +# Windows-specific test +test-path-posix-relative-on-windows: SKIP [$system==macos] +# Windows-specific test +test-path-posix-relative-on-windows: SKIP [$system==solaris] +# Windows-specific test +test-path-posix-relative-on-windows: SKIP [$system==freebsd] +# Windows-specific test +test-path-posix-relative-on-windows: SKIP [$system==aix] +# Windows-specific test +test-path-posix-relative-on-windows: SKIP [$arch==arm] # The Raspberry Pis are too slow to run this test. # See https://github.com/nodejs/build/issues/2227#issuecomment-608334574 test-crypto-authenticated-stream: SKIP +# Windows-specific test +test-path-posix-relative-on-windows: SKIP + +[$system==ibmi] +# Windows-specific test +test-path-posix-relative-on-windows: SKIP diff --git a/test/known_issues/test-path-posix-relative-on-windows.js b/test/known_issues/test-path-posix-relative-on-windows.js new file mode 100644 index 00000000000000..bcaaca8b18a1ef --- /dev/null +++ b/test/known_issues/test-path-posix-relative-on-windows.js @@ -0,0 +1,10 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const path = require('path'); + +// Refs: https://github.com/nodejs/node/issues/13683 + +const relativePath = path.posix.relative('a/b/c', '../../x'); +assert.match(relativePath, /^(\.\.\/){3,5}x$/); diff --git a/test/message/source_map_reference_error_tabs.js b/test/message/source_map_reference_error_tabs.js new file mode 100644 index 00000000000000..f6a39d9d497449 --- /dev/null +++ b/test/message/source_map_reference_error_tabs.js @@ -0,0 +1,5 @@ +// Flags: --enable-source-maps + +'use strict'; +require('../common'); +require('../fixtures/source-map/tabs.js'); diff --git a/test/message/source_map_reference_error_tabs.out b/test/message/source_map_reference_error_tabs.out new file mode 100644 index 00000000000000..b02370ad34b6d3 --- /dev/null +++ b/test/message/source_map_reference_error_tabs.out @@ -0,0 +1,17 @@ +*tabs.coffee:26 + alert "I knew it!" + ^ + +ReferenceError: alert is not defined + at Object. (*tabs.coffee:39:5) + -> *tabs.coffee:26:2 + at Object. (*tabs.coffee:53:4) + -> *tabs.coffee:1:14 + at Module._compile (internal/modules/cjs/loader.js:* + at Object.Module._extensions..js (internal/modules/cjs/loader.js:* + at Module.load (internal/modules/cjs/loader.js:* + at Function.Module._load (internal/modules/cjs/loader.js:* + at Module.require (internal/modules/cjs/loader.js:* + at require (internal/modules/cjs/helpers.js:* + at Object. (*source_map_reference_error_tabs.js:* + at Module._compile (internal/modules/cjs/loader.js:* diff --git a/test/message/source_map_throw_catch.out b/test/message/source_map_throw_catch.out index 63e3eca3eff0ce..a8e71a934f5e47 100644 --- a/test/message/source_map_throw_catch.out +++ b/test/message/source_map_throw_catch.out @@ -1,4 +1,7 @@ reachable +*typescript-throw.ts:18 + throw Error('an exception'); + ^ Error: an exception at branch (*typescript-throw.js:20:15) -> *typescript-throw.ts:18:11 diff --git a/test/message/source_map_throw_first_tick.out b/test/message/source_map_throw_first_tick.out index 7f11d9fbd969be..2532c38d6f722c 100644 --- a/test/message/source_map_throw_first_tick.out +++ b/test/message/source_map_throw_first_tick.out @@ -1,4 +1,7 @@ reachable +*typescript-throw.ts:18 + throw Error('an exception'); + ^ Error: an exception at branch (*typescript-throw.js:20:15) -> *typescript-throw.ts:18:11 diff --git a/test/message/source_map_throw_icu.js b/test/message/source_map_throw_icu.js new file mode 100644 index 00000000000000..00298edc5ed81a --- /dev/null +++ b/test/message/source_map_throw_icu.js @@ -0,0 +1,5 @@ +// Flags: --enable-source-maps + +'use strict'; +require('../common'); +require('../fixtures/source-map/icu'); diff --git a/test/message/source_map_throw_icu.out b/test/message/source_map_throw_icu.out new file mode 100644 index 00000000000000..25043d62c1a1ea --- /dev/null +++ b/test/message/source_map_throw_icu.out @@ -0,0 +1,17 @@ +*icu.jsx:3 + ("********", throw Error("an error")); + ^ + +Error: an error + at Object.createElement (*icu.js:5:7) + -> *icu.jsx:3:23 + at Object. (*icu.js:8:82) + -> *icu.jsx:9:5 + at Module._compile (internal/modules/cjs/loader.js:* + at Object.Module._extensions..js (internal/modules/cjs/loader.js:* + at Module.load (internal/modules/cjs/loader.js:* + at Function.Module._load (internal/modules/cjs/loader.js:* + at Module.require (internal/modules/cjs/loader.js:* + at require (internal/modules/cjs/helpers.js:* + at Object. (*source_map_throw_icu.js:* + at Module._compile (internal/modules/cjs/loader.js:* diff --git a/test/message/source_map_throw_set_immediate.out b/test/message/source_map_throw_set_immediate.out index 6b169d7e025f7e..488d5381d5510b 100644 --- a/test/message/source_map_throw_set_immediate.out +++ b/test/message/source_map_throw_set_immediate.out @@ -1,6 +1,6 @@ -*uglify-throw.js:1 -setImmediate(function(){!function(){throw Error("goodbye")}()}); - ^ +*uglify-throw-original.js:5 + throw Error('goodbye'); + ^ Error: goodbye at *uglify-throw.js:1:43 diff --git a/test/message/v8_warning.out b/test/message/v8_warning.out index b006af0518f0f4..6c419c83cddf28 100644 --- a/test/message/v8_warning.out +++ b/test/message/v8_warning.out @@ -1,2 +1,2 @@ (node:*) V8: *v8_warning.js:* Invalid asm.js: Invalid return type -(Use `node --trace-warnings ...` to show where the warning was created) +(Use `* --trace-warnings ...` to show where the warning was created) diff --git a/test/node-api/test_async_cleanup_hook/binding.c b/test/node-api/test_async_cleanup_hook/binding.c index 7bbde56bb0ec88..4e88479574fb01 100644 --- a/test/node-api/test_async_cleanup_hook/binding.c +++ b/test/node-api/test_async_cleanup_hook/binding.c @@ -1,4 +1,3 @@ -#define NAPI_EXPERIMENTAL #include "node_api.h" #include "assert.h" #include "uv.h" diff --git a/test/node-api/test_env_teardown_gc/binding.c b/test/node-api/test_env_teardown_gc/binding.c new file mode 100644 index 00000000000000..f894690b2470b2 --- /dev/null +++ b/test/node-api/test_env_teardown_gc/binding.c @@ -0,0 +1,37 @@ +#include +#include +#include "../../js-native-api/common.h" + +static void MyObject_fini(napi_env env, void* data, void* hint) { + napi_ref* ref = data; + napi_value global; + napi_value cleanup; + NAPI_CALL_RETURN_VOID(env, napi_get_global(env, &global)); + NAPI_CALL_RETURN_VOID( + env, napi_get_named_property(env, global, "cleanup", &cleanup)); + napi_status status = napi_call_function(env, global, cleanup, 0, NULL, NULL); + // We may not be allowed to call into JS, in which case a pending exception + // will be returned. + NAPI_ASSERT_RETURN_VOID(env, + status == napi_ok || status == napi_pending_exception, + "Unexpected status for napi_call_function"); + NAPI_CALL_RETURN_VOID(env, napi_delete_reference(env, *ref)); + free(ref); +} + +static napi_value MyObject(napi_env env, napi_callback_info info) { + napi_value js_this; + napi_ref* ref = malloc(sizeof(*ref)); + NAPI_CALL(env, napi_get_cb_info(env, info, NULL, NULL, &js_this, NULL)); + NAPI_CALL(env, napi_wrap(env, js_this, ref, MyObject_fini, NULL, ref)); + return NULL; +} + +NAPI_MODULE_INIT() { + napi_value ctor; + NAPI_CALL( + env, napi_define_class( + env, "MyObject", NAPI_AUTO_LENGTH, MyObject, NULL, 0, NULL, &ctor)); + NAPI_CALL(env, napi_set_named_property(env, exports, "MyObject", ctor)); + return exports; +} diff --git a/test/node-api/test_env_teardown_gc/binding.gyp b/test/node-api/test_env_teardown_gc/binding.gyp new file mode 100644 index 00000000000000..413621ade335a1 --- /dev/null +++ b/test/node-api/test_env_teardown_gc/binding.gyp @@ -0,0 +1,8 @@ +{ + 'targets': [ + { + 'target_name': 'binding', + 'sources': [ 'binding.c' ] + } + ] +} diff --git a/test/node-api/test_env_teardown_gc/test.js b/test/node-api/test_env_teardown_gc/test.js new file mode 100644 index 00000000000000..8f1c4f4038fbab --- /dev/null +++ b/test/node-api/test_env_teardown_gc/test.js @@ -0,0 +1,14 @@ +'use strict'; +// Flags: --expose-gc + +process.env.NODE_TEST_KNOWN_GLOBALS = 0; + +const common = require('../../common'); +const binding = require(`./build/${common.buildType}/binding`); + +global.it = new binding.MyObject(); + +global.cleanup = () => { + delete global.it; + global.gc(); +}; diff --git a/test/node-api/test_threadsafe_function/binding.c b/test/node-api/test_threadsafe_function/binding.c index c9c526153804c6..b016dfa6c36656 100644 --- a/test/node-api/test_threadsafe_function/binding.c +++ b/test/node-api/test_threadsafe_function/binding.c @@ -130,7 +130,7 @@ static void call_js(napi_env env, napi_value cb, void* hint, void* data) { } static napi_ref alt_ref; -// Getting the data into JS with the alternative referece +// Getting the data into JS with the alternative reference static void call_ref(napi_env env, napi_value _, void* hint, void* data) { if (!(env == NULL || alt_ref == NULL)) { napi_value fn, argv, undefined; diff --git a/test/parallel/parallel.status b/test/parallel/parallel.status index baac6fd63edad2..d922a520945fe6 100644 --- a/test/parallel/parallel.status +++ b/test/parallel/parallel.status @@ -5,36 +5,18 @@ prefix parallel # sample-test : PASS,FLAKY [true] # This section applies to all platforms -# https://github.com/nodejs/node/issues/35881 -test-http2-respond-file-error-pipe-offset: PASS,FLAKY [$system==win32] -# https://github.com/nodejs/node/issues/20750 -test-http2-client-upload: PASS,FLAKY -# https://github.com/nodejs/node/issues/20750 -test-http2-client-upload-reject: PASS,FLAKY -# https://github.com/nodejs/node/issues/30847 -test-http2-compat-client-upload-reject: PASS,FLAKY -# https://github.com/nodejs/node/issues/30845 -test-http2-multistream-destroy-on-read-tls: PASS,FLAKY -# https://github.com/nodejs/node/issues/20750 -test-http2-pipe: PASS,FLAKY -# https://github.com/nodejs/node/issues/20750 -test-stream-pipeline-http2: PASS,FLAKY # https://github.com/nodejs/node/issues/24497 test-timers-immediate-queue: PASS,FLAKY # https://github.com/nodejs/node/issues/23277 test-worker-memory: PASS,FLAKY -# https://github.com/nodejs/node/issues/30846 -test-worker-message-port-transfer-terminate: PASS,FLAKY [$system==linux] [$system==macos] [$arch==arm || $arch==arm64] -# https://github.com/nodejs/node/issues/26610 -test-async-hooks-http-parser-destroy: PASS,FLAKY # https://github.com/nodejs/node/pull/31178 test-crypto-dh-stateless: SKIP test-crypto-keygen: SKIP @@ -73,3 +55,7 @@ test-net-write-after-end-nt: SKIP test-tls-env-extra-ca: SKIP # https://github.com/nodejs/node/pull/34209 test-dgram-error-message-address: SKIP +# https://github.com/nodejs/node/issues/36929 +test-crypto-secure-heap: SKIP +# https://github.com/nodejs/node/issues/36925 +test-fs-read-type: SKIP diff --git a/test/parallel/test-abortcontroller.js b/test/parallel/test-abortcontroller.js new file mode 100644 index 00000000000000..9df6f78b6165cc --- /dev/null +++ b/test/parallel/test-abortcontroller.js @@ -0,0 +1,135 @@ +// Flags: --no-warnings --expose-internals --experimental-abortcontroller +'use strict'; + +const common = require('../common'); + +const { ok, strictEqual, throws } = require('assert'); +const { Event } = require('internal/event_target'); + +{ + // Tests that abort is fired with the correct event type on AbortControllers + const ac = new AbortController(); + ok(ac.signal); + ac.signal.onabort = common.mustCall((event) => { + ok(event); + strictEqual(event.type, 'abort'); + }); + ac.signal.addEventListener('abort', common.mustCall((event) => { + ok(event); + strictEqual(event.type, 'abort'); + }), { once: true }); + ac.abort(); + ac.abort(); + ok(ac.signal.aborted); +} + +{ + // Tests that abort events are trusted + const ac = new AbortController(); + ac.signal.addEventListener('abort', common.mustCall((event) => { + ok(event.isTrusted); + })); + ac.abort(); +} + +{ + // Tests that abort events have the same `isTrusted` reference + const first = new AbortController(); + const second = new AbortController(); + let ev1, ev2; + const ev3 = new Event('abort'); + first.signal.addEventListener('abort', common.mustCall((event) => { + ev1 = event; + })); + second.signal.addEventListener('abort', common.mustCall((event) => { + ev2 = event; + })); + first.abort(); + second.abort(); + const firstTrusted = Reflect.getOwnPropertyDescriptor(ev1, 'isTrusted').get; + const secondTrusted = Reflect.getOwnPropertyDescriptor(ev2, 'isTrusted').get; + const untrusted = Reflect.getOwnPropertyDescriptor(ev3, 'isTrusted').get; + strictEqual(firstTrusted, secondTrusted); + strictEqual(untrusted, firstTrusted); +} + +{ + // Tests that AbortSignal is impossible to construct manually + const ac = new AbortController(); + throws( + () => new ac.signal.constructor(), + /^TypeError: Illegal constructor$/ + ); +} +{ + // Symbol.toStringTag + const toString = (o) => Object.prototype.toString.call(o); + const ac = new AbortController(); + strictEqual(toString(ac), '[object AbortController]'); + strictEqual(toString(ac.signal), '[object AbortSignal]'); +} + +{ + const signal = AbortSignal.abort(); + ok(signal.aborted); +} + +{ + // Test that AbortController properties and methods validate the receiver + const acSignalGet = Object.getOwnPropertyDescriptor( + AbortController.prototype, + 'signal' + ).get; + const acAbort = AbortController.prototype.abort; + + const goodController = new AbortController(); + ok(acSignalGet.call(goodController)); + acAbort.call(goodController); + + const badAbortControllers = [ + null, + undefined, + 0, + NaN, + true, + 'AbortController', + Object.create(AbortController.prototype) + ]; + for (const badController of badAbortControllers) { + throws( + () => acSignalGet.call(badController), + { code: 'ERR_INVALID_THIS', name: 'TypeError' } + ); + throws( + () => acAbort.call(badController), + { code: 'ERR_INVALID_THIS', name: 'TypeError' } + ); + } +} + +{ + // Test that AbortSignal properties validate the receiver + const signalAbortedGet = Object.getOwnPropertyDescriptor( + AbortSignal.prototype, + 'aborted' + ).get; + + const goodSignal = new AbortController().signal; + strictEqual(signalAbortedGet.call(goodSignal), false); + + const badAbortSignals = [ + null, + undefined, + 0, + NaN, + true, + 'AbortSignal', + Object.create(AbortSignal.prototype) + ]; + for (const badSignal of badAbortSignals) { + throws( + () => signalAbortedGet.call(badSignal), + { code: 'ERR_INVALID_THIS', name: 'TypeError' } + ); + } +} diff --git a/test/parallel/test-arm-math-illegal-instruction.js b/test/parallel/test-arm-math-illegal-instruction.js index 2c4cdd263018a0..4bf881d1b385a3 100644 --- a/test/parallel/test-arm-math-illegal-instruction.js +++ b/test/parallel/test-arm-math-illegal-instruction.js @@ -6,30 +6,10 @@ require('../common'); // See https://github.com/nodejs/node/issues/1376 // and https://code.google.com/p/v8/issues/detail?id=4019 -Math.abs(-0.5); -Math.acos(-0.5); -Math.acosh(-0.5); -Math.asin(-0.5); -Math.asinh(-0.5); -Math.atan(-0.5); -Math.atanh(-0.5); -Math.cbrt(-0.5); -Math.ceil(-0.5); -Math.cos(-0.5); -Math.cosh(-0.5); -Math.exp(-0.5); -Math.expm1(-0.5); -Math.floor(-0.5); -Math.fround(-0.5); -Math.log(-0.5); -Math.log10(-0.5); -Math.log1p(-0.5); -Math.log2(-0.5); -Math.round(-0.5); -Math.sign(-0.5); -Math.sin(-0.5); -Math.sinh(-0.5); -Math.sqrt(-0.5); -Math.tan(-0.5); -Math.tanh(-0.5); -Math.trunc(-0.5); +// Iterate over all Math functions +Object.getOwnPropertyNames(Math).forEach((functionName) => { + if (!/[A-Z]/.test(functionName)) { + // The function names don't have capital letters. + Math[functionName](-0.5); + } +}); diff --git a/test/parallel/test-assert-calltracker-calls.js b/test/parallel/test-assert-calltracker-calls.js index 4de23d4658468a..99db4ee284be81 100644 --- a/test/parallel/test-assert-calltracker-calls.js +++ b/test/parallel/test-assert-calltracker-calls.js @@ -64,3 +64,17 @@ assert.throws( () => callsfunc(), { message: msg } ); + +{ + const tracker = new assert.CallTracker(); + const callsNoop = tracker.calls(1); + callsNoop(); + tracker.verify(); +} + +{ + const tracker = new assert.CallTracker(); + const callsNoop = tracker.calls(undefined, 1); + callsNoop(); + tracker.verify(); +} diff --git a/test/parallel/test-assert-calltracker-report.js b/test/parallel/test-assert-calltracker-report.js index 79186c88fd058b..87ef0bff17fc0b 100644 --- a/test/parallel/test-assert-calltracker-report.js +++ b/test/parallel/test-assert-calltracker-report.js @@ -11,22 +11,16 @@ function foo() {} const callsfoo = tracker.calls(foo, 1); // Ensures that foo was added to the callChecks array. -if (tracker.report()[0].operator !== 'foo') { - process.exit(1); -} +assert.strictEqual(tracker.report()[0].operator, 'foo'); callsfoo(); // Ensures that foo was removed from the callChecks array after being called the // expected number of times. -if (typeof tracker.report()[0] === undefined) { - process.exit(1); -} +assert.strictEqual(typeof tracker.report()[0], 'undefined'); callsfoo(); // Ensures that foo was added back to the callChecks array after being called // more than the expected number of times. -if (tracker.report()[0].operator !== 'foo') { - process.exit(1); -} +assert.strictEqual(tracker.report()[0].operator, 'foo'); diff --git a/test/parallel/test-assert-deep.js b/test/parallel/test-assert-deep.js index 73af4b3e226929..b14179c85c8a8f 100644 --- a/test/parallel/test-assert-deep.js +++ b/test/parallel/test-assert-deep.js @@ -1194,3 +1194,13 @@ assert.throws( Object.setPrototypeOf(b, null); assertNotDeepOrStrict(a, b, assert.AssertionError); } + +{ + // Verify commutativity + // Regression test for https://github.com/nodejs/node/issues/37710 + const a = { x: 1 }; + const b = { y: 1 }; + Object.defineProperty(b, 'x', { value: 1 }); + + assertNotDeepOrStrict(a, b); +} diff --git a/test/parallel/test-assert.js b/test/parallel/test-assert.js index cbbf091f04b8a7..7613449c92b477 100644 --- a/test/parallel/test-assert.js +++ b/test/parallel/test-assert.js @@ -1282,7 +1282,7 @@ assert.throws( ); assert.throws( - () => a.notStrictEqual(5n), + () => a.notStrictEqual(5n), // eslint-disable-line no-restricted-syntax { code: 'ERR_MISSING_ARGS' } ); diff --git a/test/parallel/test-async-hooks-fatal-error.js b/test/parallel/test-async-hooks-fatal-error.js new file mode 100644 index 00000000000000..40c2edf329df6e --- /dev/null +++ b/test/parallel/test-async-hooks-fatal-error.js @@ -0,0 +1,53 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const childProcess = require('child_process'); +const os = require('os'); + +if (process.argv[2] === 'child') { + child(process.argv[3], process.argv[4]); +} else { + main(); +} + +function child(type, valueType) { + const { createHook } = require('async_hooks'); + const fs = require('fs'); + + createHook({ + [type]() { + if (valueType === 'symbol') { + throw Symbol('foo'); + } + // eslint-disable-next-line no-throw-literal + throw null; + } + }).enable(); + + // Trigger `promiseResolve`. + new Promise((resolve) => resolve()) + // Trigger `after`/`destroy`. + .then(() => fs.promises.readFile(__filename, 'utf8')) + // Make process exit with code 0 if no error caught. + .then(() => process.exit(0)); +} + +function main() { + const types = [ 'init', 'before', 'after', 'destroy', 'promiseResolve' ]; + const valueTypes = [ + [ 'null', 'Error: null' ], + [ 'symbol', 'Error: Symbol(foo)' ], + ]; + for (const type of types) { + for (const [valueType, expect] of valueTypes) { + const cp = childProcess.spawnSync( + process.execPath, + [ __filename, 'child', type, valueType ], + { + encoding: 'utf8', + }); + assert.strictEqual(cp.status, 1, type); + assert.strictEqual(cp.stderr.trim().split(os.EOL)[0], expect, type); + } + } +} diff --git a/test/parallel/test-async-hooks-run-in-async-scope-this-arg.js b/test/parallel/test-async-hooks-run-in-async-scope-this-arg.js new file mode 100644 index 00000000000000..a5016da9d5fcd3 --- /dev/null +++ b/test/parallel/test-async-hooks-run-in-async-scope-this-arg.js @@ -0,0 +1,17 @@ +'use strict'; + +// Test that passing thisArg to runInAsyncScope() works. + +const common = require('../common'); +const assert = require('assert'); +const { AsyncResource } = require('async_hooks'); + +const thisArg = {}; + +const res = new AsyncResource('fhqwhgads'); + +function callback() { + assert.strictEqual(this, thisArg); +} + +res.runInAsyncScope(common.mustCall(callback), thisArg); diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index 5fec2699bebe48..36d296ec0aa415 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -31,6 +31,7 @@ const expectedModules = new Set([ 'Internal Binding types', 'Internal Binding url', 'Internal Binding util', + 'NativeModule async_hooks', 'NativeModule buffer', 'NativeModule events', 'NativeModule fs', @@ -93,6 +94,7 @@ const expectedModules = new Set([ if (!common.isMainThread) { [ 'Internal Binding messaging', + 'Internal Binding performance', 'Internal Binding symbols', 'Internal Binding worker', 'NativeModule internal/streams/duplex', diff --git a/test/parallel/test-buffer-pool-untransferable.js b/test/parallel/test-buffer-pool-untransferable.js index 96e109c113fee8..d39b03435fa5b7 100644 --- a/test/parallel/test-buffer-pool-untransferable.js +++ b/test/parallel/test-buffer-pool-untransferable.js @@ -15,6 +15,6 @@ const length = a.length; const { port1 } = new MessageChannel(); port1.postMessage(a, [ a.buffer ]); -// Verify that the pool ArrayBuffer has not actually been transfered: +// Verify that the pool ArrayBuffer has not actually been transferred: assert.strictEqual(a.buffer, b.buffer); assert.strictEqual(a.length, length); diff --git a/test/parallel/test-child-process-cwd.js b/test/parallel/test-child-process-cwd.js index fb782234539db4..232f1d0f3d5316 100644 --- a/test/parallel/test-child-process-cwd.js +++ b/test/parallel/test-child-process-cwd.js @@ -28,11 +28,14 @@ const assert = require('assert'); const { spawn } = require('child_process'); // Spawns 'pwd' with given options, then test +// - whether the child pid is undefined or number, // - whether the exit code equals expectCode, // - optionally whether the trimmed stdout result matches expectData -function testCwd(options, expectCode = 0, expectData) { +function testCwd(options, expectPidType, expectCode = 0, expectData) { const child = spawn(...common.pwdCommand, options); + assert.strictEqual(typeof child.pid, expectPidType); + child.stdout.setEncoding('utf8'); // No need to assert callback since `data` is asserted. @@ -57,18 +60,18 @@ function testCwd(options, expectCode = 0, expectData) { // Assume does-not-exist doesn't exist, expect exitCode=-1 and errno=ENOENT { - testCwd({ cwd: 'does-not-exist' }, -1) + testCwd({ cwd: 'does-not-exist' }, 'undefined', -1) .on('error', common.mustCall(function(e) { assert.strictEqual(e.code, 'ENOENT'); })); } // Assume these exist, and 'pwd' gives us the right directory back -testCwd({ cwd: tmpdir.path }, 0, tmpdir.path); +testCwd({ cwd: tmpdir.path }, 'number', 0, tmpdir.path); const shouldExistDir = common.isWindows ? process.env.windir : '/dev'; -testCwd({ cwd: shouldExistDir }, 0, shouldExistDir); +testCwd({ cwd: shouldExistDir }, 'number', 0, shouldExistDir); // Spawn() shouldn't try to chdir() to invalid arg, so this should just work -testCwd({ cwd: '' }); -testCwd({ cwd: undefined }); -testCwd({ cwd: null }); +testCwd({ cwd: '' }, 'number'); +testCwd({ cwd: undefined }, 'number'); +testCwd({ cwd: null }, 'number'); diff --git a/test/parallel/test-child-process-exec-error.js b/test/parallel/test-child-process-exec-error.js index eaddeb6614e921..cd45f3071c2920 100644 --- a/test/parallel/test-child-process-exec-error.js +++ b/test/parallel/test-child-process-exec-error.js @@ -24,17 +24,21 @@ const common = require('../common'); const assert = require('assert'); const child_process = require('child_process'); -function test(fn, code) { - fn('does-not-exist', common.mustCall(function(err) { +function test(fn, code, expectPidType = 'number') { + const child = fn('does-not-exist', common.mustCall(function(err) { assert.strictEqual(err.code, code); assert(err.cmd.includes('does-not-exist')); })); + + assert.strictEqual(typeof child.pid, expectPidType); } +// With `shell: true`, expect pid (of the shell) if (common.isWindows) { - test(child_process.exec, 1); // Exit code of cmd.exe + test(child_process.exec, 1, 'number'); // Exit code of cmd.exe } else { - test(child_process.exec, 127); // Exit code of /bin/sh + test(child_process.exec, 127, 'number'); // Exit code of /bin/sh } -test(child_process.execFile, 'ENOENT'); +// With `shell: false`, expect no pid +test(child_process.execFile, 'ENOENT', 'undefined'); diff --git a/test/parallel/test-child-process-execfile.js b/test/parallel/test-child-process-execfile.js index bc0495b3332384..ca430d17cfe1bb 100644 --- a/test/parallel/test-child-process-execfile.js +++ b/test/parallel/test-child-process-execfile.js @@ -1,12 +1,15 @@ +// Flags: --experimental-abortcontroller 'use strict'; const common = require('../common'); const assert = require('assert'); const execFile = require('child_process').execFile; +const { getEventListeners } = require('events'); const { getSystemErrorName } = require('util'); const fixtures = require('../common/fixtures'); const fixture = fixtures.path('exit.js'); +const echoFixture = fixtures.path('echo.js'); const execOpts = { encoding: 'utf8', shell: true }; { @@ -45,3 +48,37 @@ const execOpts = { encoding: 'utf8', shell: true }; // Verify the shell option works properly execFile(process.execPath, [fixture, 0], execOpts, common.mustSucceed()); } + +{ + // Verify that the signal option works properly + const ac = new AbortController(); + const { signal } = ac; + + const callback = common.mustCall((err) => { + assert.strictEqual(err.code, 'ABORT_ERR'); + assert.strictEqual(err.name, 'AbortError'); + }); + execFile(process.execPath, [echoFixture, 0], { signal }, callback); + ac.abort(); +} + +{ + // Verify that if something different than Abortcontroller.signal + // is passed, ERR_INVALID_ARG_TYPE is thrown + assert.throws(() => { + const callback = common.mustNotCall(() => {}); + + execFile(process.execPath, [echoFixture, 0], { signal: 'hello' }, callback); + }, { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError' }); +} +{ + // Verify that the process completing removes the abort listener + const ac = new AbortController(); + const { signal } = ac; + + const callback = common.mustCall((err) => { + assert.strictEqual(getEventListeners(ac.signal).length, 0); + assert.strictEqual(err, null); + }); + execFile(process.execPath, [fixture, 0], { signal }, callback); +} diff --git a/test/parallel/test-child-process-fork-abort-signal.js b/test/parallel/test-child-process-fork-abort-signal.js new file mode 100644 index 00000000000000..809821738d1b9d --- /dev/null +++ b/test/parallel/test-child-process-fork-abort-signal.js @@ -0,0 +1,34 @@ +// Flags: --experimental-abortcontroller +'use strict'; + +const { mustCall } = require('../common'); +const { strictEqual } = require('assert'); +const fixtures = require('../common/fixtures'); +const { fork } = require('child_process'); + +{ + // Test aborting a forked child_process after calling fork + const ac = new AbortController(); + const { signal } = ac; + const cp = fork(fixtures.path('child-process-stay-alive-forever.js'), { + signal + }); + cp.on('exit', mustCall()); + cp.on('error', mustCall((err) => { + strictEqual(err.name, 'AbortError'); + })); + process.nextTick(() => ac.abort()); +} +{ + // Test passing an already aborted signal to a forked child_process + const ac = new AbortController(); + const { signal } = ac; + ac.abort(); + const cp = fork(fixtures.path('child-process-stay-alive-forever.js'), { + signal + }); + cp.on('exit', mustCall()); + cp.on('error', mustCall((err) => { + strictEqual(err.name, 'AbortError'); + })); +} diff --git a/test/parallel/test-child-process-pipe-dataflow.js b/test/parallel/test-child-process-pipe-dataflow.js index 5f425a6f3d087c..2e2edc65e9e0d5 100644 --- a/test/parallel/test-child-process-pipe-dataflow.js +++ b/test/parallel/test-child-process-pipe-dataflow.js @@ -61,8 +61,13 @@ const MB = KB * KB; })); }); + let wcBuf = ''; wc.stdout.on('data', common.mustCall((data) => { - // Grep always adds one extra byte at the end. - assert.strictEqual(data.toString().trim(), (MB + 1).toString()); + wcBuf += data; })); + + process.on('exit', () => { + // Grep always adds one extra byte at the end. + assert.strictEqual(wcBuf.trim(), (MB + 1).toString()); + }); } diff --git a/test/parallel/test-child-process-spawn-controller.js b/test/parallel/test-child-process-spawn-controller.js new file mode 100644 index 00000000000000..2ac3a0ddd514a7 --- /dev/null +++ b/test/parallel/test-child-process-spawn-controller.js @@ -0,0 +1,22 @@ +// Flags: --experimental-abortcontroller +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); + +// Verify that passing an AbortSignal works +const controller = new AbortController(); +const { signal } = controller; + +const echo = cp.spawn('echo', ['fun'], { + encoding: 'utf8', + shell: true, + signal +}); + +echo.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); +})); + +controller.abort(); diff --git a/test/parallel/test-child-process-spawn-error.js b/test/parallel/test-child-process-spawn-error.js index d6560ee9cc5734..ed1c8fac9f97ed 100644 --- a/test/parallel/test-child-process-spawn-error.js +++ b/test/parallel/test-child-process-spawn-error.js @@ -41,6 +41,11 @@ assert.strictEqual(enoentChild.stdio[0], enoentChild.stdin); assert.strictEqual(enoentChild.stdio[1], enoentChild.stdout); assert.strictEqual(enoentChild.stdio[2], enoentChild.stderr); +// Verify pid is not assigned. +assert.strictEqual(enoentChild.pid, undefined); + +enoentChild.on('spawn', common.mustNotCall()); + enoentChild.on('error', common.mustCall(function(err) { assert.strictEqual(err.code, 'ENOENT'); assert.strictEqual(getSystemErrorName(err.errno), 'ENOENT'); diff --git a/test/parallel/test-child-process-spawn-event.js b/test/parallel/test-child-process-spawn-event.js new file mode 100644 index 00000000000000..c025d8628681e8 --- /dev/null +++ b/test/parallel/test-child-process-spawn-event.js @@ -0,0 +1,27 @@ +'use strict'; +const common = require('../common'); +const spawn = require('child_process').spawn; +const assert = require('assert'); + +const subprocess = spawn('echo', ['ok']); + +let didSpawn = false; +subprocess.on('spawn', function() { + didSpawn = true; +}); +function mustCallAfterSpawn() { + return common.mustCall(function() { + assert.ok(didSpawn); + }); +} + +subprocess.on('error', common.mustNotCall()); +subprocess.on('spawn', common.mustCall()); +subprocess.stdout.on('data', mustCallAfterSpawn()); +subprocess.stdout.on('end', mustCallAfterSpawn()); +subprocess.stdout.on('close', mustCallAfterSpawn()); +subprocess.stderr.on('data', common.mustNotCall()); +subprocess.stderr.on('end', mustCallAfterSpawn()); +subprocess.stderr.on('close', mustCallAfterSpawn()); +subprocess.on('exit', mustCallAfterSpawn()); +subprocess.on('close', mustCallAfterSpawn()); diff --git a/test/known_issues/test-crypto-authenticated-stream.js b/test/parallel/test-crypto-authenticated-stream.js similarity index 98% rename from test/known_issues/test-crypto-authenticated-stream.js rename to test/parallel/test-crypto-authenticated-stream.js index 1e2a9edd7b0a6e..f29ee8932af804 100644 --- a/test/known_issues/test-crypto-authenticated-stream.js +++ b/test/parallel/test-crypto-authenticated-stream.js @@ -1,7 +1,9 @@ -/* eslint-disable node-core/crypto-check */ 'use strict'; // Refs: https://github.com/nodejs/node/issues/31733 const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + const assert = require('assert'); const crypto = require('crypto'); const fs = require('fs'); @@ -121,7 +123,6 @@ function test(config) { tmpdir.refresh(); -// OK test({ cipher: 'aes-128-ccm', aad: Buffer.alloc(1), @@ -131,7 +132,6 @@ test({ plaintextLength: 32768, }); -// Fails the fstream test. test({ cipher: 'aes-128-ccm', aad: Buffer.alloc(1), diff --git a/test/parallel/test-crypto-dh.js b/test/parallel/test-crypto-dh.js index f51ffba042421a..8a5fd21eea511e 100644 --- a/test/parallel/test-crypto-dh.js +++ b/test/parallel/test-crypto-dh.js @@ -471,3 +471,9 @@ assert.throws( 'crypto.getDiffieHellman(\'modp1\').setPublicKey(\'\') ' + 'failed to throw the expected error.' ); +assert.throws( + () => crypto.createDiffieHellman('', true), + { + code: 'ERR_INVALID_ARG_TYPE' + } +); diff --git a/test/parallel/test-crypto-random.js b/test/parallel/test-crypto-random.js index a5acd50ac3a708..3c3aa0ad00c9c4 100644 --- a/test/parallel/test-crypto-random.js +++ b/test/parallel/test-crypto-random.js @@ -31,8 +31,8 @@ const crypto = require('crypto'); const { kMaxLength } = require('buffer'); const { inspect } = require('util'); -const kMaxUint32 = Math.pow(2, 32) - 1; -const kMaxPossibleLength = Math.min(kMaxLength, kMaxUint32); +const kMaxInt32 = 2 ** 31 - 1; +const kMaxPossibleLength = Math.min(kMaxLength, kMaxInt32); common.expectWarning('DeprecationWarning', 'crypto.pseudoRandomBytes is deprecated.', 'DEP0115'); @@ -50,7 +50,7 @@ common.expectWarning('DeprecationWarning', assert.throws(() => f(value, common.mustNotCall()), errObj); }); - [-1, NaN, 2 ** 32].forEach((value) => { + [-1, NaN, 2 ** 32, 2 ** 31].forEach((value) => { const errObj = { code: 'ERR_OUT_OF_RANGE', name: 'RangeError', diff --git a/test/parallel/test-crypto-randomuuid.js b/test/parallel/test-crypto-randomuuid.js new file mode 100644 index 00000000000000..232dcec62c8f2e --- /dev/null +++ b/test/parallel/test-crypto-randomuuid.js @@ -0,0 +1,58 @@ +'use strict'; + +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { + randomUUID, +} = require('crypto'); + +const last = new Set([ + '00000000-0000-0000-0000-000000000000' +]); + +function testMatch(uuid) { + assert.match( + uuid, + /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/); +} + +// Generate a number of UUID's to make sure we're +// not just generating the same value over and over +// and to make sure the batching changes the random +// bytes. +for (let n = 0; n < 130; n++) { + const uuid = randomUUID(); + assert(!last.has(uuid)); + last.add(uuid); + assert.strictEqual(typeof uuid, 'string'); + assert.strictEqual(uuid.length, 36); + testMatch(uuid); + + // Check that version 4 identifier was populated. + assert.strictEqual( + Buffer.from(uuid.substr(14, 2), 'hex')[0] & 0x40, 0x40); + + // Check that clock_seq_hi_and_reserved was populated with reserved bits. + assert.strictEqual( + Buffer.from(uuid.substr(19, 2), 'hex')[0] & 0b1100_0000, 0b1000_0000); +} + +// Test non-buffered UUID's +{ + testMatch(randomUUID({ disableEntropyCache: true })); + testMatch(randomUUID({ disableEntropyCache: true })); + testMatch(randomUUID({ disableEntropyCache: true })); + testMatch(randomUUID({ disableEntropyCache: true })); + + assert.throws(() => randomUUID(1), { + code: 'ERR_INVALID_ARG_TYPE' + }); + + assert.throws(() => randomUUID({ disableEntropyCache: '' }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +} diff --git a/test/parallel/test-dgram-close-signal.js b/test/parallel/test-dgram-close-signal.js new file mode 100644 index 00000000000000..680d795a22e22d --- /dev/null +++ b/test/parallel/test-dgram-close-signal.js @@ -0,0 +1,34 @@ +// Flags: --experimental-abortcontroller +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +{ + // Test bad signal. + assert.throws( + () => dgram.createSocket({ type: 'udp4', signal: {} }), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); +} + +{ + // Test close. + const controller = new AbortController(); + const { signal } = controller; + const server = dgram.createSocket({ type: 'udp4', signal }); + server.on('close', common.mustCall()); + controller.abort(); +} + +{ + // Test close with pre-aborted signal. + const controller = new AbortController(); + controller.abort(); + const { signal } = controller; + const server = dgram.createSocket({ type: 'udp4', signal }); + server.on('close', common.mustCall()); +} diff --git a/test/parallel/test-diagnostics-channel-has-subscribers.js b/test/parallel/test-diagnostics-channel-has-subscribers.js new file mode 100644 index 00000000000000..de37267555089e --- /dev/null +++ b/test/parallel/test-diagnostics-channel-has-subscribers.js @@ -0,0 +1,10 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const { channel, hasSubscribers } = require('diagnostics_channel'); + +const dc = channel('test'); +assert.ok(!hasSubscribers('test')); + +dc.subscribe(() => {}); +assert.ok(hasSubscribers('test')); diff --git a/test/parallel/test-diagnostics-channel-http-server-start.js b/test/parallel/test-diagnostics-channel-http-server-start.js new file mode 100644 index 00000000000000..9a8136d4cc5839 --- /dev/null +++ b/test/parallel/test-diagnostics-channel-http-server-start.js @@ -0,0 +1,65 @@ +'use strict'; + +const common = require('../common'); +const { AsyncLocalStorage } = require('async_hooks'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); +const http = require('http'); + +const incomingStartChannel = dc.channel('http.server.request.start'); +const outgoingFinishChannel = dc.channel('http.server.response.finish'); + +const als = new AsyncLocalStorage(); +let context; + +// Bind requests to an AsyncLocalStorage context +incomingStartChannel.subscribe(common.mustCall((message) => { + als.enterWith(message); + context = message; +})); + +// When the request ends, verify the context has been maintained +// and that the messages contain the expected data +outgoingFinishChannel.subscribe(common.mustCall((message) => { + const data = { + request, + response, + server, + socket: request.socket + }; + + // Context is maintained + compare(als.getStore(), context); + + compare(context, data); + compare(message, data); +})); + +let request; +let response; + +const server = http.createServer(common.mustCall((req, res) => { + request = req; + response = res; + + setTimeout(() => { + res.end('done'); + }, 1); +})); + +server.listen(() => { + const { port } = server.address(); + http.get(`http://localhost:${port}`, (res) => { + res.resume(); + res.on('end', () => { + server.close(); + }); + }); +}); + +function compare(a, b) { + assert.strictEqual(a.request, b.request); + assert.strictEqual(a.response, b.response); + assert.strictEqual(a.socket, b.socket); + assert.strictEqual(a.server, b.server); +} diff --git a/test/parallel/test-diagnostics-channel-object-channel-pub-sub.js b/test/parallel/test-diagnostics-channel-object-channel-pub-sub.js new file mode 100644 index 00000000000000..cbc5b4d2e9a953 --- /dev/null +++ b/test/parallel/test-diagnostics-channel-object-channel-pub-sub.js @@ -0,0 +1,43 @@ +'use strict'; + +const common = require('../common'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); +const { Channel } = dc; + +const input = { + foo: 'bar' +}; + +// Should not have named channel +assert.ok(!dc.hasSubscribers('test')); + +// Individual channel objects can be created to avoid future lookups +const channel = dc.channel('test'); +assert.ok(channel instanceof Channel); + +// No subscribers yet, should not publish +assert.ok(!channel.hasSubscribers); + +const subscriber = common.mustCall((message, name) => { + assert.strictEqual(name, channel.name); + assert.deepStrictEqual(message, input); +}); + +// Now there's a subscriber, should publish +channel.subscribe(subscriber); +assert.ok(channel.hasSubscribers); + +// The ActiveChannel prototype swap should not fail instanceof +assert.ok(channel instanceof Channel); + +// Should trigger the subscriber once +channel.publish(input); + +// Should not publish after subscriber is unsubscribed +channel.unsubscribe(subscriber); +assert.ok(!channel.hasSubscribers); + +assert.throws(() => { + channel.subscribe(null); +}, { code: 'ERR_INVALID_ARG_TYPE' }); diff --git a/test/parallel/test-diagnostics-channel-safe-subscriber-errors.js b/test/parallel/test-diagnostics-channel-safe-subscriber-errors.js new file mode 100644 index 00000000000000..b0c5ab2480e374 --- /dev/null +++ b/test/parallel/test-diagnostics-channel-safe-subscriber-errors.js @@ -0,0 +1,29 @@ +'use strict'; + +const common = require('../common'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); + +const input = { + foo: 'bar' +}; + +const channel = dc.channel('fail'); + +const error = new Error('nope'); + +process.on('uncaughtException', common.mustCall((err) => { + assert.strictEqual(err, error); +})); + +channel.subscribe(common.mustCall((message, name) => { + throw error; +})); + +// The failing subscriber should not stop subsequent subscribers from running +channel.subscribe(common.mustCall()); + +// Publish should continue without throwing +const fn = common.mustCall(); +channel.publish(input); +fn(); diff --git a/test/parallel/test-diagnostics-channel-symbol-named.js b/test/parallel/test-diagnostics-channel-symbol-named.js new file mode 100644 index 00000000000000..96fe0fa53596e2 --- /dev/null +++ b/test/parallel/test-diagnostics-channel-symbol-named.js @@ -0,0 +1,28 @@ +'use strict'; + +const common = require('../common'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); + +const input = { + foo: 'bar' +}; + +const symbol = Symbol('test'); + +// Individual channel objects can be created to avoid future lookups +const channel = dc.channel(symbol); + +// Expect two successful publishes later +channel.subscribe(common.mustCall((message, name) => { + assert.strictEqual(name, symbol); + assert.deepStrictEqual(message, input); +})); + +channel.publish(input); + +{ + assert.throws(() => { + dc.channel(null); + }, /ERR_INVALID_ARG_TYPE/); +} diff --git a/test/parallel/test-dns-channel-cancel-promise.js b/test/parallel/test-dns-channel-cancel-promise.js new file mode 100644 index 00000000000000..382ac3dd508d68 --- /dev/null +++ b/test/parallel/test-dns-channel-cancel-promise.js @@ -0,0 +1,65 @@ +'use strict'; +const common = require('../common'); +const { promises: dnsPromises } = require('dns'); +const assert = require('assert'); +const dgram = require('dgram'); + +const server = dgram.createSocket('udp4'); +const resolver = new dnsPromises.Resolver(); + +const addMessageListener = () => { + server.removeAllListeners('message'); + + server.once('message', () => { + server.once('message', common.mustNotCall); + + resolver.cancel(); + }); +}; + +server.bind(0, common.mustCall(async () => { + resolver.setServers([`127.0.0.1:${server.address().port}`]); + + addMessageListener(); + + // Single promise + { + const hostname = 'example0.org'; + + await assert.rejects( + resolver.resolve4(hostname), + { + code: 'ECANCELLED', + syscall: 'queryA', + hostname + } + ); + } + + addMessageListener(); + + // Multiple promises + { + const assertions = []; + const assertionCount = 10; + + for (let i = 1; i <= assertionCount; i++) { + const hostname = `example${i}.org`; + + assertions.push( + assert.rejects( + resolver.resolve4(hostname), + { + code: 'ECANCELLED', + syscall: 'queryA', + hostname: hostname + } + ) + ); + } + + await Promise.all(assertions); + } + + server.close(); +})); diff --git a/test/parallel/test-dns-channel-cancel.js b/test/parallel/test-dns-channel-cancel.js index 0c59ee53161a5e..f92fb2e30a4d12 100644 --- a/test/parallel/test-dns-channel-cancel.js +++ b/test/parallel/test-dns-channel-cancel.js @@ -1,6 +1,5 @@ 'use strict'; const common = require('../common'); -const dnstools = require('../common/dns'); const { Resolver } = require('dns'); const assert = require('assert'); const dgram = require('dgram'); @@ -8,21 +7,45 @@ const dgram = require('dgram'); const server = dgram.createSocket('udp4'); const resolver = new Resolver(); -server.bind(0, common.mustCall(() => { +const desiredQueries = 11; +let finishedQueries = 0; + +const addMessageListener = () => { + server.removeAllListeners('message'); + + server.once('message', () => { + server.once('message', common.mustNotCall); + + resolver.cancel(); + }); +}; + +server.bind(0, common.mustCall(async () => { resolver.setServers([`127.0.0.1:${server.address().port}`]); - resolver.resolve4('example.org', common.mustCall((err, res) => { + + const callback = common.mustCall((err, res) => { assert.strictEqual(err.code, 'ECANCELLED'); assert.strictEqual(err.syscall, 'queryA'); - assert.strictEqual(err.hostname, 'example.org'); - server.close(); - })); -})); + assert.strictEqual(err.hostname, `example${finishedQueries}.org`); + + finishedQueries++; + if (finishedQueries === desiredQueries) { + server.close(); + } + }, desiredQueries); + + const next = (...args) => { + callback(...args); + + addMessageListener(); -server.on('message', common.mustCall((msg, { address, port }) => { - const parsed = dnstools.parseDNSPacket(msg); - const domain = parsed.questions[0].domain; - assert.strictEqual(domain, 'example.org'); + // Multiple queries + for (let i = 1; i < desiredQueries; i++) { + resolver.resolve4(`example${i}.org`, callback); + } + }; - // Do not send a reply. - resolver.cancel(); + // Single query + addMessageListener(); + resolver.resolve4('example0.org', next); })); diff --git a/test/parallel/test-dns-resolveany.js b/test/parallel/test-dns-resolveany.js index 0423d557a30b7d..7cc4a046ba7e2c 100644 --- a/test/parallel/test-dns-resolveany.js +++ b/test/parallel/test-dns-resolveany.js @@ -23,6 +23,11 @@ const answers = [ expire: 1800, minttl: 60 }, + { + type: 'CAA', + critical: 128, + issue: 'platynum.ch' + } ]; const server = dgram.createSocket('udp4'); diff --git a/test/parallel/test-dns-setlocaladdress.js b/test/parallel/test-dns-setlocaladdress.js new file mode 100644 index 00000000000000..25bece328f4a74 --- /dev/null +++ b/test/parallel/test-dns-setlocaladdress.js @@ -0,0 +1,40 @@ +'use strict'; +require('../common'); +const assert = require('assert'); + +const dns = require('dns'); +const resolver = new dns.Resolver(); +const promiseResolver = new dns.promises.Resolver(); + +// Verifies that setLocalAddress succeeds with IPv4 and IPv6 addresses +{ + resolver.setLocalAddress('127.0.0.1'); + resolver.setLocalAddress('::1'); + resolver.setLocalAddress('127.0.0.1', '::1'); + promiseResolver.setLocalAddress('127.0.0.1', '::1'); +} + +// Verify that setLocalAddress throws if called with an invalid address +{ + assert.throws(() => { + resolver.setLocalAddress('127.0.0.1', '127.0.0.1'); + }, Error); + assert.throws(() => { + resolver.setLocalAddress('::1', '::1'); + }, Error); + assert.throws(() => { + resolver.setLocalAddress('bad'); + }, Error); + assert.throws(() => { + resolver.setLocalAddress(123); + }, { code: 'ERR_INVALID_ARG_TYPE' }); + assert.throws(() => { + resolver.setLocalAddress('127.0.0.1', 42); + }, { code: 'ERR_INVALID_ARG_TYPE' }); + assert.throws(() => { + resolver.setLocalAddress(); + }, Error); + assert.throws(() => { + promiseResolver.setLocalAddress(); + }, Error); +} diff --git a/test/parallel/test-domain-dep0097.js b/test/parallel/test-domain-dep0097.js new file mode 100644 index 00000000000000..05b5c74b30d98e --- /dev/null +++ b/test/parallel/test-domain-dep0097.js @@ -0,0 +1,17 @@ +'use strict'; +const common = require('../common'); + +common.skipIfInspectorDisabled(); + +const assert = require('assert'); +const domain = require('domain'); +const inspector = require('inspector'); + +process.on('warning', common.mustCall((warning) => { + assert.strictEqual(warning.code, 'DEP0097'); + assert.match(warning.message, /Triggered by calling emit on process/); +})); + +domain.create().run(() => { + inspector.open(); +}); diff --git a/test/parallel/test-error-serdes.js b/test/parallel/test-error-serdes.js index f01dc0d6a8a81d..a65781c25eb261 100644 --- a/test/parallel/test-error-serdes.js +++ b/test/parallel/test-error-serdes.js @@ -49,3 +49,20 @@ assert.strictEqual(cycle(Function), '[Function: Function]'); assert.strictEqual(err.name, 'TypeError'); assert.strictEqual(err.code, 'ERR_INVALID_ARG_TYPE'); } + +{ + let called = false; + class DynamicError extends Error { + get type() { + called = true; + return 'dynamic'; + } + + get shouldIgnoreError() { + throw new Error(); + } + } + + serializeError(new DynamicError()); + assert.deepStrictEqual(called, true); +} diff --git a/test/parallel/test-event-on-async-iterator.js b/test/parallel/test-event-on-async-iterator.js index 5c02360250538c..a6dc4b9c81c04f 100644 --- a/test/parallel/test-event-on-async-iterator.js +++ b/test/parallel/test-event-on-async-iterator.js @@ -1,4 +1,4 @@ -// Flags: --expose-internals +// Flags: --expose-internals --no-warnings --experimental-abortcontroller 'use strict'; const common = require('../common'); @@ -248,6 +248,117 @@ async function nodeEventTarget() { clearInterval(interval); } +async function abortableOnBefore() { + const ee = new EventEmitter(); + const ac = new AbortController(); + ac.abort(); + [1, {}, null, false, 'hi'].forEach((signal) => { + assert.throws(() => on(ee, 'foo', { signal }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + assert.throws(() => on(ee, 'foo', { signal: ac.signal }), { + name: 'AbortError' + }); +} + +async function eventTargetAbortableOnBefore() { + const et = new EventTarget(); + const ac = new AbortController(); + ac.abort(); + [1, {}, null, false, 'hi'].forEach((signal) => { + assert.throws(() => on(et, 'foo', { signal }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + assert.throws(() => on(et, 'foo', { signal: ac.signal }), { + name: 'AbortError' + }); +} + +async function abortableOnAfter() { + const ee = new EventEmitter(); + const ac = new AbortController(); + + const i = setInterval(() => ee.emit('foo', 'foo'), 10); + + async function foo() { + for await (const f of on(ee, 'foo', { signal: ac.signal })) { + assert.strictEqual(f, 'foo'); + } + } + + foo().catch(common.mustCall((error) => { + assert.strictEqual(error.name, 'AbortError'); + })).finally(() => { + clearInterval(i); + }); + + process.nextTick(() => ac.abort()); +} + +async function eventTargetAbortableOnAfter() { + const et = new EventTarget(); + const ac = new AbortController(); + + const i = setInterval(() => et.dispatchEvent(new Event('foo')), 10); + + async function foo() { + for await (const f of on(et, 'foo', { signal: ac.signal })) { + assert(f); + } + } + + foo().catch(common.mustCall((error) => { + assert.strictEqual(error.name, 'AbortError'); + })).finally(() => { + clearInterval(i); + }); + + process.nextTick(() => ac.abort()); +} + +async function eventTargetAbortableOnAfter2() { + const et = new EventTarget(); + const ac = new AbortController(); + + const i = setInterval(() => et.dispatchEvent(new Event('foo')), 10); + + async function foo() { + for await (const f of on(et, 'foo', { signal: ac.signal })) { + assert(f); + // Cancel after a single event has been triggered. + ac.abort(); + } + } + + foo().catch(common.mustCall((error) => { + assert.strictEqual(error.name, 'AbortError'); + })).finally(() => { + clearInterval(i); + }); +} + +async function abortableOnAfterDone() { + const ee = new EventEmitter(); + const ac = new AbortController(); + + const i = setInterval(() => ee.emit('foo', 'foo'), 1); + let count = 0; + + async function foo() { + for await (const f of on(ee, 'foo', { signal: ac.signal })) { + assert.strictEqual(f[0], 'foo'); + if (++count === 5) + break; + } + ac.abort(); // No error will occur + } + + foo().finally(() => { + clearInterval(i); + }); +} async function run() { const funcs = [ @@ -260,7 +371,13 @@ async function run() { iterableThrow, eventTarget, errorListenerCount, - nodeEventTarget + nodeEventTarget, + abortableOnBefore, + abortableOnAfter, + eventTargetAbortableOnBefore, + eventTargetAbortableOnAfter, + eventTargetAbortableOnAfter2, + abortableOnAfterDone ]; for (const fn of funcs) { diff --git a/test/parallel/test-events-once.js b/test/parallel/test-events-once.js index 658a9964be73e1..867987da9ef386 100644 --- a/test/parallel/test-events-once.js +++ b/test/parallel/test-events-once.js @@ -1,9 +1,14 @@ 'use strict'; -// Flags: --expose-internals +// Flags: --expose-internals --no-warnings --experimental-abortcontroller const common = require('../common'); const { once, EventEmitter } = require('events'); -const { strictEqual, deepStrictEqual, fail } = require('assert'); +const { + strictEqual, + deepStrictEqual, + fail, + rejects, +} = require('assert'); const { EventTarget, Event } = require('internal/event_target'); async function onceAnEvent() { @@ -114,6 +119,81 @@ async function prioritizesEventEmitter() { process.nextTick(() => ee.emit('foo')); await once(ee, 'foo'); } + +async function abortSignalBefore() { + const ee = new EventEmitter(); + const ac = new AbortController(); + ee.on('error', common.mustNotCall()); + ac.abort(); + + await Promise.all([1, {}, 'hi', null, false].map((signal) => { + return rejects(once(ee, 'foo', { signal }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + })); + + return rejects(once(ee, 'foo', { signal: ac.signal }), { + name: 'AbortError' + }); +} + +async function abortSignalAfter() { + const ee = new EventEmitter(); + const ac = new AbortController(); + ee.on('error', common.mustNotCall()); + const r = rejects(once(ee, 'foo', { signal: ac.signal }), { + name: 'AbortError' + }); + process.nextTick(() => ac.abort()); + return r; +} + +async function abortSignalAfterEvent() { + const ee = new EventEmitter(); + const ac = new AbortController(); + process.nextTick(() => { + ee.emit('foo'); + ac.abort(); + }); + await once(ee, 'foo', { signal: ac.signal }); +} + +async function eventTargetAbortSignalBefore() { + const et = new EventTarget(); + const ac = new AbortController(); + ac.abort(); + + await Promise.all([1, {}, 'hi', null, false].map((signal) => { + return rejects(once(et, 'foo', { signal }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + })); + + return rejects(once(et, 'foo', { signal: ac.signal }), { + name: 'AbortError' + }); +} + +async function eventTargetAbortSignalAfter() { + const et = new EventTarget(); + const ac = new AbortController(); + const r = rejects(once(et, 'foo', { signal: ac.signal }), { + name: 'AbortError' + }); + process.nextTick(() => ac.abort()); + return r; +} + +async function eventTargetAbortSignalAfterEvent() { + const et = new EventTarget(); + const ac = new AbortController(); + process.nextTick(() => { + et.dispatchEvent(new Event('foo')); + ac.abort(); + }); + await once(et, 'foo', { signal: ac.signal }); +} + Promise.all([ onceAnEvent(), onceAnEventWithTwoArgs(), @@ -123,4 +203,10 @@ Promise.all([ onceWithEventTarget(), onceWithEventTargetError(), prioritizesEventEmitter(), + abortSignalBefore(), + abortSignalAfter(), + abortSignalAfterEvent(), + eventTargetAbortSignalBefore(), + eventTargetAbortSignalAfter(), + eventTargetAbortSignalAfterEvent(), ]).then(common.mustCall()); diff --git a/test/parallel/test-events-static-geteventlisteners.js b/test/parallel/test-events-static-geteventlisteners.js new file mode 100644 index 00000000000000..d5e8c457f5d57f --- /dev/null +++ b/test/parallel/test-events-static-geteventlisteners.js @@ -0,0 +1,38 @@ +// Flags: --expose-internals +'use strict'; + +const common = require('../common'); + +const { + deepStrictEqual, +} = require('assert'); + +const { getEventListeners, EventEmitter } = require('events'); +const { EventTarget } = require('internal/event_target'); + +// Test getEventListeners on EventEmitter +{ + const fn1 = common.mustNotCall(); + const fn2 = common.mustNotCall(); + const emitter = new EventEmitter(); + emitter.on('foo', fn1); + emitter.on('foo', fn2); + emitter.on('baz', fn1); + emitter.on('baz', fn1); + deepStrictEqual(getEventListeners(emitter, 'foo'), [fn1, fn2]); + deepStrictEqual(getEventListeners(emitter, 'bar'), []); + deepStrictEqual(getEventListeners(emitter, 'baz'), [fn1, fn1]); +} +// Test getEventListeners on EventTarget +{ + const fn1 = common.mustNotCall(); + const fn2 = common.mustNotCall(); + const target = new EventTarget(); + target.addEventListener('foo', fn1); + target.addEventListener('foo', fn2); + target.addEventListener('baz', fn1); + target.addEventListener('baz', fn1); + deepStrictEqual(getEventListeners(target, 'foo'), [fn1, fn2]); + deepStrictEqual(getEventListeners(target, 'bar'), []); + deepStrictEqual(getEventListeners(target, 'baz'), [fn1]); +} diff --git a/test/parallel/test-eventtarget-memoryleakwarning.js b/test/parallel/test-eventtarget-memoryleakwarning.js new file mode 100644 index 00000000000000..46c68b9979cfcb --- /dev/null +++ b/test/parallel/test-eventtarget-memoryleakwarning.js @@ -0,0 +1,81 @@ +// Flags: --no-warnings --expose-internals --experimental-abortcontroller +'use strict'; +const common = require('../common'); +const { + setMaxListeners, + EventEmitter +} = require('events'); +const assert = require('assert'); +const { MessageChannel } = require('worker_threads'); +const { EventTarget } = require('internal/event_target'); + +common.expectWarning({ + MaxListenersExceededWarning: [ + ['Possible EventTarget memory leak detected. 3 foo listeners added to ' + + 'EventTarget. Use events.setMaxListeners() ' + + 'to increase limit'], + ['Possible EventTarget memory leak detected. 3 foo listeners added to ' + + '[MessagePort [EventTarget]]. ' + + 'Use events.setMaxListeners() to increase ' + + 'limit'], + ['Possible EventTarget memory leak detected. 3 foo listeners added to ' + + '[MessagePort [EventTarget]]. ' + + 'Use events.setMaxListeners() to increase ' + + 'limit'], + ['Possible EventTarget memory leak detected. 3 foo listeners added to ' + + '[AbortSignal]. ' + + 'Use events.setMaxListeners() to increase ' + + 'limit'], + ], + ExperimentalWarning: [[ + 'AbortController is an experimental feature. This feature could change ' + + 'at any time' + ]] +}); + + +{ + const et = new EventTarget(); + setMaxListeners(2, et); + et.addEventListener('foo', () => {}); + et.addEventListener('foo', () => {}); + et.addEventListener('foo', () => {}); +} + +{ + // No warning emitted because prior limit was only for that + // one EventTarget. + const et = new EventTarget(); + et.addEventListener('foo', () => {}); + et.addEventListener('foo', () => {}); + et.addEventListener('foo', () => {}); +} + +{ + const mc = new MessageChannel(); + setMaxListeners(2, mc.port1); + mc.port1.addEventListener('foo', () => {}); + mc.port1.addEventListener('foo', () => {}); + mc.port1.addEventListener('foo', () => {}); +} + +{ + // Set the default for newly created EventTargets + setMaxListeners(2); + const mc = new MessageChannel(); + mc.port1.addEventListener('foo', () => {}); + mc.port1.addEventListener('foo', () => {}); + mc.port1.addEventListener('foo', () => {}); + + const ac = new AbortController(); + ac.signal.addEventListener('foo', () => {}); + ac.signal.addEventListener('foo', () => {}); + ac.signal.addEventListener('foo', () => {}); +} + +{ + // It works for EventEmitters also + const ee = new EventEmitter(); + setMaxListeners(2, ee); + assert.strictEqual(ee.getMaxListeners(), 2); +} diff --git a/test/parallel/test-eventtarget.js b/test/parallel/test-eventtarget.js index 9487c22e26d1af..1918394fbae5e1 100644 --- a/test/parallel/test-eventtarget.js +++ b/test/parallel/test-eventtarget.js @@ -256,13 +256,6 @@ let asyncTest = Promise.resolve(); {}, // No handleEvent function false ].forEach((i) => throws(() => target.addEventListener('foo', i), err(i))); - - [ - 'foo', - 1, - {}, // No handleEvent function - false - ].forEach((i) => throws(() => target.addEventListener('foo', i), err(i))); } { @@ -414,6 +407,7 @@ let asyncTest = Promise.resolve(); } { + // Event Statics strictEqual(Event.NONE, 0); strictEqual(Event.CAPTURING_PHASE, 1); strictEqual(Event.AT_TARGET, 2); @@ -424,6 +418,8 @@ let asyncTest = Promise.resolve(); strictEqual(e.eventPhase, Event.AT_TARGET); }), { once: true }); target.dispatchEvent(new Event('foo')); + // Event is a function + strictEqual(Event.length, 1); } { @@ -479,3 +475,57 @@ let asyncTest = Promise.resolve(); throws(() => eventTarget.addEventListener('foo', Symbol()), TypeError); }); } +{ + const eventTarget = new EventTarget(); + const event = new Event('foo'); + eventTarget.dispatchEvent(event); + strictEqual(event.target, eventTarget); +} +{ + // Event target exported keys + const eventTarget = new EventTarget(); + deepStrictEqual(Object.keys(eventTarget), []); + deepStrictEqual(Object.getOwnPropertyNames(eventTarget), []); + const parentKeys = Object.keys(Object.getPrototypeOf(eventTarget)).sort(); + const keys = ['addEventListener', 'dispatchEvent', 'removeEventListener']; + deepStrictEqual(parentKeys, keys); +} +{ + // Subclassing + class SubTarget extends EventTarget {} + const target = new SubTarget(); + target.addEventListener('foo', common.mustCall()); + target.dispatchEvent(new Event('foo')); +} +{ + // Test event order + const target = new EventTarget(); + let state = 0; + target.addEventListener('foo', common.mustCall(() => { + strictEqual(state, 0); + state++; + })); + target.addEventListener('foo', common.mustCall(() => { + strictEqual(state, 1); + })); + target.dispatchEvent(new Event('foo')); +} +{ + const target = new EventTarget(); + defineEventHandler(target, 'foo'); + const descriptor = Object.getOwnPropertyDescriptor(target, 'onfoo'); + strictEqual(descriptor.configurable, true); + strictEqual(descriptor.enumerable, true); +} +{ + const target = new EventTarget(); + defineEventHandler(target, 'foo'); + const output = []; + target.addEventListener('foo', () => output.push(1)); + target.onfoo = common.mustNotCall(); + target.addEventListener('foo', () => output.push(3)); + target.onfoo = () => output.push(2); + target.addEventListener('foo', () => output.push(4)); + target.dispatchEvent(new Event('foo')); + deepStrictEqual(output, [1, 2, 3, 4]); +} diff --git a/test/parallel/test-fs-exists.js b/test/parallel/test-fs-exists.js index cd2d9a712f3f58..75b7adfc3ee3d0 100644 --- a/test/parallel/test-fs-exists.js +++ b/test/parallel/test-fs-exists.js @@ -23,7 +23,6 @@ const common = require('../common'); const assert = require('assert'); const fs = require('fs'); -const { URL } = require('url'); const f = __filename; assert.throws(() => fs.exists(f), { code: 'ERR_INVALID_CALLBACK' }); diff --git a/test/parallel/test-fs-mkdir.js b/test/parallel/test-fs-mkdir.js index 5e28d6b944b6e0..4124a638a20234 100644 --- a/test/parallel/test-fs-mkdir.js +++ b/test/parallel/test-fs-mkdir.js @@ -53,6 +53,25 @@ function nextdir() { })); } +// fs.mkdir creates directory with mode passed as an options object +{ + const pathname = path.join(tmpdir.path, nextdir()); + + fs.mkdir(pathname, { mode: 0o777 }, common.mustCall(function(err) { + assert.strictEqual(err, null); + assert.strictEqual(fs.existsSync(pathname), true); + })); +} + +// fs.mkdirSync creates directory with mode passed as an options object +{ + const pathname = path.join(tmpdir.path, nextdir()); + + fs.mkdirSync(pathname, { mode: 0o777 }); + + assert.strictEqual(fs.existsSync(pathname), true); +} + // mkdirSync successfully creates directory from given path { const pathname = path.join(tmpdir.path, nextdir()); diff --git a/test/parallel/test-fs-null-bytes.js b/test/parallel/test-fs-null-bytes.js index beaea00969b630..d4548c02c07009 100644 --- a/test/parallel/test-fs-null-bytes.js +++ b/test/parallel/test-fs-null-bytes.js @@ -23,7 +23,6 @@ const common = require('../common'); const assert = require('assert'); const fs = require('fs'); -const URL = require('url').URL; function check(async, sync) { const argsSync = Array.prototype.slice.call(arguments, 2); diff --git a/test/parallel/test-fs-opendir.js b/test/parallel/test-fs-opendir.js index ab7b24b63e76b2..4c4681ef48e2ff 100644 --- a/test/parallel/test-fs-opendir.js +++ b/test/parallel/test-fs-opendir.js @@ -223,12 +223,11 @@ async function doAsyncIterInvalidCallbackTest() { } doAsyncIterInvalidCallbackTest().then(common.mustCall()); -// Check if directory already closed - throw an exception +// Check first call to close() - should not report an error. async function doAsyncIterDirClosedTest() { const dir = await fs.promises.opendir(testDir); await dir.close(); - - assert.throws(() => dir.close(), dirclosedError); + await assert.rejects(() => dir.close(), dirclosedError); } doAsyncIterDirClosedTest().then(common.mustCall()); @@ -245,6 +244,12 @@ async function doConcurrentAsyncAndSyncOps() { } doConcurrentAsyncAndSyncOps().then(common.mustCall()); +// Check read throw exceptions on invalid callback +{ + const dir = fs.opendirSync(testDir); + assert.throws(() => dir.read('INVALID_CALLBACK'), /ERR_INVALID_CALLBACK/); +} + // Check that concurrent read() operations don't do weird things. async function doConcurrentAsyncOps() { const dir = await fs.promises.opendir(testDir); @@ -267,3 +272,19 @@ async function doConcurrentAsyncMixedOps() { await promise2; } doConcurrentAsyncMixedOps().then(common.mustCall()); + +// Check if directory already closed - the callback should pass an error. +{ + const dir = fs.opendirSync(testDir); + dir.closeSync(); + dir.close(common.mustCall((error) => { + assert.strictEqual(error.code, dirclosedError.code); + })); +} + +// Check if directory already closed - throw an promise exception. +{ + const dir = fs.opendirSync(testDir); + dir.closeSync(); + assert.rejects(dir.close(), dirclosedError).then(common.mustCall()); +} diff --git a/test/parallel/test-fs-options-immutable.js b/test/parallel/test-fs-options-immutable.js index 17fc1cbeebd1ad..b993d776e6e460 100644 --- a/test/parallel/test-fs-options-immutable.js +++ b/test/parallel/test-fs-options-immutable.js @@ -44,7 +44,7 @@ if (common.canCreateSymLink()) { fs.appendFile(fileName, 'ABCD', options, common.mustCall(errHandler)); } -if (!common.isIBMi) { // IBMi does not suppport fs.watch() +if (!common.isIBMi) { // IBMi does not support fs.watch() const watch = fs.watch(__filename, options, common.mustNotCall()); watch.close(); } diff --git a/test/parallel/test-fs-promises-readfile.js b/test/parallel/test-fs-promises-readfile.js index ff25be75b84ff0..28a191d41442eb 100644 --- a/test/parallel/test-fs-promises-readfile.js +++ b/test/parallel/test-fs-promises-readfile.js @@ -1,3 +1,4 @@ +// Flags: --experimental-abortcontroller 'use strict'; const common = require('../common'); @@ -10,18 +11,21 @@ tmpdir.refresh(); const fn = path.join(tmpdir.path, 'large-file'); -async function validateReadFile() { - // Creating large buffer with random content - const buffer = Buffer.from( - Array.apply(null, { length: 16834 * 2 }) - .map(Math.random) - .map((number) => (number * (1 << 8))) - ); +// Creating large buffer with random content +const largeBuffer = Buffer.from( + Array.apply(null, { length: 16834 * 2 }) + .map(Math.random) + .map((number) => (number * (1 << 8))) +); +async function createLargeFile() { // Writing buffer to a file then try to read it - await writeFile(fn, buffer); + await writeFile(fn, largeBuffer); +} + +async function validateReadFile() { const readBuffer = await readFile(fn); - assert.strictEqual(readBuffer.equals(buffer), true); + assert.strictEqual(readBuffer.equals(largeBuffer), true); } async function validateReadFileProc() { @@ -39,6 +43,40 @@ async function validateReadFileProc() { assert.ok(hostname.length > 0); } -validateReadFile() - .then(() => validateReadFileProc()) - .then(common.mustCall()); +function validateReadFileAbortLogicBefore() { + const controller = new AbortController(); + const signal = controller.signal; + controller.abort(); + assert.rejects(readFile(fn, { signal }), { + name: 'AbortError' + }); +} + +function validateReadFileAbortLogicDuring() { + const controller = new AbortController(); + const signal = controller.signal; + process.nextTick(() => controller.abort()); + assert.rejects(readFile(fn, { signal }), { + name: 'AbortError' + }); +} + +async function validateWrongSignalParam() { + // Verify that if something different than Abortcontroller.signal + // is passed, ERR_INVALID_ARG_TYPE is thrown + + await assert.rejects(async () => { + const callback = common.mustNotCall(() => {}); + await readFile(fn, { signal: 'hello' }, callback); + }, { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError' }); + +} + +(async () => { + await createLargeFile(); + await validateReadFile(); + await validateReadFileProc(); + await validateReadFileAbortLogicBefore(); + await validateReadFileAbortLogicDuring(); + await validateWrongSignalParam(); +})().then(common.mustCall()); diff --git a/test/parallel/test-fs-promises-writefile.js b/test/parallel/test-fs-promises-writefile.js index 858e90bc6291de..de58395774ba0d 100644 --- a/test/parallel/test-fs-promises-writefile.js +++ b/test/parallel/test-fs-promises-writefile.js @@ -1,3 +1,4 @@ +// Flags: --experimental-abortcontroller 'use strict'; const common = require('../common'); @@ -11,6 +12,7 @@ const tmpDir = tmpdir.path; tmpdir.refresh(); const dest = path.resolve(tmpDir, 'tmp.txt'); +const otherDest = path.resolve(tmpDir, 'tmp-2.txt'); const buffer = Buffer.from('abc'.repeat(1000)); const buffer2 = Buffer.from('xyz'.repeat(1000)); @@ -20,6 +22,15 @@ async function doWrite() { assert.deepStrictEqual(data, buffer); } +async function doWriteWithCancel() { + const controller = new AbortController(); + const { signal } = controller; + process.nextTick(() => controller.abort()); + assert.rejects(fsPromises.writeFile(otherDest, buffer, { signal }), { + name: 'AbortError' + }); +} + async function doAppend() { await fsPromises.appendFile(dest, buffer2); const data = fs.readFileSync(dest); @@ -41,6 +52,7 @@ async function doReadWithEncoding() { } doWrite() + .then(doWriteWithCancel) .then(doAppend) .then(doRead) .then(doReadWithEncoding) diff --git a/test/parallel/test-fs-promises.js b/test/parallel/test-fs-promises.js index f23df685eac072..b6d4260a6484d0 100644 --- a/test/parallel/test-fs-promises.js +++ b/test/parallel/test-fs-promises.js @@ -183,24 +183,24 @@ async function getHandle(dest) { assert.rejects( async () => { - await chown(dest, 1, -1); + await chown(dest, 1, -2); }, { code: 'ERR_OUT_OF_RANGE', name: 'RangeError', message: 'The value of "gid" is out of range. ' + - 'It must be >= 0 && < 4294967296. Received -1' + 'It must be >= -1 && <= 4294967295. Received -2' }); assert.rejects( async () => { - await handle.chown(1, -1); + await handle.chown(1, -2); }, { code: 'ERR_OUT_OF_RANGE', name: 'RangeError', message: 'The value of "gid" is out of range. ' + - 'It must be >= 0 && < 4294967296. Received -1' + 'It must be >= -1 && <= 4294967295. Received -2' }); await handle.close(); diff --git a/test/parallel/test-fs-read-optional-params.js b/test/parallel/test-fs-read-optional-params.js index bae99da3c0ebd0..aac5f51d0bb2db 100644 --- a/test/parallel/test-fs-read-optional-params.js +++ b/test/parallel/test-fs-read-optional-params.js @@ -11,14 +11,14 @@ const expected = Buffer.from('xyz\n'); const defaultBufferAsync = Buffer.alloc(16384); const bufferAsOption = Buffer.allocUnsafe(expected.length); -// Test passing in an empty options object -fs.read(fd, { position: 0 }, common.mustCall((err, bytesRead, buffer) => { +// Test not passing in any options object +fs.read(fd, common.mustCall((err, bytesRead, buffer) => { assert.strictEqual(bytesRead, expected.length); assert.deepStrictEqual(defaultBufferAsync.length, buffer.length); })); -// Test not passing in any options object -fs.read(fd, common.mustCall((err, bytesRead, buffer) => { +// Test passing in an empty options object +fs.read(fd, { position: 0 }, common.mustCall((err, bytesRead, buffer) => { assert.strictEqual(bytesRead, expected.length); assert.deepStrictEqual(defaultBufferAsync.length, buffer.length); })); diff --git a/test/parallel/test-fs-readfile.js b/test/parallel/test-fs-readfile.js index 648bf692d1dcc8..9be20d1a003de0 100644 --- a/test/parallel/test-fs-readfile.js +++ b/test/parallel/test-fs-readfile.js @@ -1,3 +1,4 @@ +// Flags: --experimental-abortcontroller 'use strict'; const common = require('../common'); @@ -57,3 +58,29 @@ for (const e of fileInfo) { assert.deepStrictEqual(buf, e.contents); })); } +{ + // Test cancellation, before + const controller = new AbortController(); + const signal = controller.signal; + controller.abort(); + fs.readFile(fileInfo[0].name, { signal }, common.mustCall((err, buf) => { + assert.strictEqual(err.name, 'AbortError'); + })); +} +{ + // Test cancellation, during read + const controller = new AbortController(); + const signal = controller.signal; + fs.readFile(fileInfo[0].name, { signal }, common.mustCall((err, buf) => { + assert.strictEqual(err.name, 'AbortError'); + })); + process.nextTick(() => controller.abort()); +} +{ + // Verify that if something different than Abortcontroller.signal + // is passed, ERR_INVALID_ARG_TYPE is thrown + assert.throws(() => { + const callback = common.mustNotCall(() => {}); + fs.readFile(fileInfo[0].name, { signal: 'hello' }, callback); + }, { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError' }); +} diff --git a/test/parallel/test-fs-realpath-pipe.js b/test/parallel/test-fs-realpath-pipe.js index 7104e93ff123ed..a2e88cce422e07 100644 --- a/test/parallel/test-fs-realpath-pipe.js +++ b/test/parallel/test-fs-realpath-pipe.js @@ -12,6 +12,7 @@ const { spawnSync } = require('child_process'); for (const code of [ `require('fs').realpath('/dev/stdin', (err, resolvedPath) => { if (err) { + console.error(err); process.exit(1); } if (resolvedPath) { @@ -22,11 +23,17 @@ for (const code of [ if (require('fs').realpathSync('/dev/stdin')) { process.exit(2); } - } catch { + } catch (e) { + console.error(e); process.exit(1); }` ]) { - assert.strictEqual(spawnSync(process.execPath, ['-e', code], { + const child = spawnSync(process.execPath, ['-e', code], { stdio: 'pipe' - }).status, 2); + }); + if (child.status !== 2) { + console.log(code); + console.log(child.stderr.toString()); + } + assert.strictEqual(child.status, 2); } diff --git a/test/parallel/test-fs-stat-bigint.js b/test/parallel/test-fs-stat-bigint.js index f96bef192f07ab..3ce12ecc076156 100644 --- a/test/parallel/test-fs-stat-bigint.js +++ b/test/parallel/test-fs-stat-bigint.js @@ -122,6 +122,33 @@ if (!common.isWindows) { fs.closeSync(fd); } +{ + assert.throws( + () => fs.statSync('does_not_exist'), + { code: 'ENOENT' }); + assert.strictEqual( + fs.statSync('does_not_exist', { throwIfNoEntry: false }), + undefined); +} + +{ + assert.throws( + () => fs.lstatSync('does_not_exist'), + { code: 'ENOENT' }); + assert.strictEqual( + fs.lstatSync('does_not_exist', { throwIfNoEntry: false }), + undefined); +} + +{ + assert.throws( + () => fs.fstatSync(9999), + { code: 'EBADF' }); + assert.throws( + () => fs.fstatSync(9999, { throwIfNoEntry: false }), + { code: 'EBADF' }); +} + const runCallbackTest = (func, arg, done) => { const startTime = process.hrtime.bigint(); func(arg, { bigint: true }, common.mustCall((err, bigintStats) => { diff --git a/test/parallel/test-fs-symlink-buffer-path.js b/test/parallel/test-fs-symlink-buffer-path.js new file mode 100644 index 00000000000000..8cc9571258ed14 --- /dev/null +++ b/test/parallel/test-fs-symlink-buffer-path.js @@ -0,0 +1,60 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// 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 common = require('../common'); +if (!common.canCreateSymLink()) + common.skip('insufficient privileges'); + +const fixtures = require('../common/fixtures'); + +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +// Test creating and reading symbolic link +const linkData = fixtures.path('/cycles/root.js'); +const linkPath = path.join(tmpdir.path, 'symlink1.js'); + +let linkTime; +let fileTime; + +// Refs: https://github.com/nodejs/node/issues/34514 +fs.symlinkSync(Buffer.from(linkData), linkPath); + +fs.lstat(linkPath, common.mustSucceed((stats) => { + linkTime = stats.mtime.getTime(); +})); + +fs.stat(linkPath, common.mustSucceed((stats) => { + fileTime = stats.mtime.getTime(); +})); + +fs.readlink(linkPath, common.mustSucceed((destination) => { + assert.strictEqual(destination, linkData); +})); + +process.on('exit', () => { + assert.notStrictEqual(linkTime, fileTime); +}); diff --git a/test/parallel/test-fs-symlink.js b/test/parallel/test-fs-symlink.js index 7c413ca2fedf81..836dfeae17016c 100644 --- a/test/parallel/test-fs-symlink.js +++ b/test/parallel/test-fs-symlink.js @@ -85,6 +85,6 @@ const errObj = { assert.throws(() => fs.symlink('', '', '🍏', common.mustNotCall()), errObj); assert.throws(() => fs.symlinkSync('', '', '🍏'), errObj); -process.on('exit', function() { +process.on('exit', () => { assert.notStrictEqual(linkTime, fileTime); }); diff --git a/test/parallel/test-fs-utimes-y2K38.js b/test/parallel/test-fs-utimes-y2K38.js new file mode 100644 index 00000000000000..9e42e90feb1fd6 --- /dev/null +++ b/test/parallel/test-fs-utimes-y2K38.js @@ -0,0 +1,66 @@ +'use strict'; +const common = require('../common'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const assert = require('assert'); +const fs = require('fs'); + +// Check for Y2K38 support. For Windows, assume it's there. Windows +// doesn't have `touch` and `date -r` which are used in the check for support. +if (!common.isWindows) { + const testFilePath = `${tmpdir.path}/y2k38-test`; + const testFileDate = '204001020304'; + const { spawnSync } = require('child_process'); + const touchResult = spawnSync('touch', + ['-t', testFileDate, testFilePath], + { encoding: 'utf8' }); + if (touchResult.status !== 0) { + common.skip('File system appears to lack Y2K38 support (touch failed)'); + } + + // On some file systems that lack Y2K38 support, `touch` will succeed but + // the time will be incorrect. + const dateResult = spawnSync('date', + ['-r', testFilePath, '+%Y%m%d%H%M'], + { encoding: 'utf8' }); + if (dateResult.status === 0) { + if (dateResult.stdout.trim() !== testFileDate) { + common.skip('File system appears to lack Y2k38 support (date failed)'); + } + } else { + // On some platforms `date` may not support the `-r` option. Usually + // this will result in a non-zero status and usage information printed. + // In this case optimistically proceed -- the earlier `touch` succeeded + // but validation that the file has the correct time is not easily possible. + assert.match(dateResult.stderr, /[Uu]sage:/); + } +} + +// Ref: https://github.com/nodejs/node/issues/13255 +const path = `${tmpdir.path}/test-utimes-precision`; +fs.writeFileSync(path, ''); + +const Y2K38_mtime = 2 ** 31; +fs.utimesSync(path, Y2K38_mtime, Y2K38_mtime); +const Y2K38_stats = fs.statSync(path); +assert.strictEqual(Y2K38_stats.mtime.getTime() / 1000, Y2K38_mtime); + +if (common.isWindows) { + // This value would get converted to (double)1713037251359.9998 + const truncate_mtime = 1713037251360; + fs.utimesSync(path, truncate_mtime / 1000, truncate_mtime / 1000); + const truncate_stats = fs.statSync(path); + assert.strictEqual(truncate_stats.mtime.getTime(), truncate_mtime); + + // test Y2K38 for windows + // This value if treaded as a `signed long` gets converted to -2135622133469. + // POSIX systems stores timestamps in {long t_sec, long t_usec}. + // NTFS stores times in nanoseconds in a single `uint64_t`, so when libuv + // calculates (long)`uv_timespec_t.tv_sec` we get 2's complement. + const overflow_mtime = 2159345162531; + fs.utimesSync(path, overflow_mtime / 1000, overflow_mtime / 1000); + const overflow_stats = fs.statSync(path); + assert.strictEqual(overflow_stats.mtime.getTime(), overflow_mtime); +} diff --git a/test/parallel/test-fs-utimes.js b/test/parallel/test-fs-utimes.js index b81c5b6bf62940..8f8286f8a3343f 100644 --- a/test/parallel/test-fs-utimes.js +++ b/test/parallel/test-fs-utimes.js @@ -156,37 +156,6 @@ function runTests(iter) { } } -// Ref: https://github.com/nodejs/node/issues/13255 -const path = `${tmpdir.path}/test-utimes-precision`; -fs.writeFileSync(path, ''); - -// Test Y2K38 for all platforms [except 'arm', 'OpenBSD', 'SunOS' and 'IBMi'] -if (!process.arch.includes('arm') && - !common.isOpenBSD && !common.isSunOS && !common.isIBMi) { - const Y2K38_mtime = 2 ** 31; - fs.utimesSync(path, Y2K38_mtime, Y2K38_mtime); - const Y2K38_stats = fs.statSync(path); - assert.strictEqual(Y2K38_stats.mtime.getTime() / 1000, Y2K38_mtime); -} - -if (common.isWindows) { - // This value would get converted to (double)1713037251359.9998 - const truncate_mtime = 1713037251360; - fs.utimesSync(path, truncate_mtime / 1000, truncate_mtime / 1000); - const truncate_stats = fs.statSync(path); - assert.strictEqual(truncate_stats.mtime.getTime(), truncate_mtime); - - // test Y2K38 for windows - // This value if treaded as a `signed long` gets converted to -2135622133469. - // POSIX systems stores timestamps in {long t_sec, long t_usec}. - // NTFS stores times in nanoseconds in a single `uint64_t`, so when libuv - // calculates (long)`uv_timespec_t.tv_sec` we get 2's complement. - const overflow_mtime = 2159345162531; - fs.utimesSync(path, overflow_mtime / 1000, overflow_mtime / 1000); - const overflow_stats = fs.statSync(path); - assert.strictEqual(overflow_stats.mtime.getTime(), overflow_mtime); -} - const expectTypeError = { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError' diff --git a/test/parallel/test-fs-watch-abort-signal.js b/test/parallel/test-fs-watch-abort-signal.js new file mode 100644 index 00000000000000..e5589bc1140f8b --- /dev/null +++ b/test/parallel/test-fs-watch-abort-signal.js @@ -0,0 +1,32 @@ +// Flags: --expose-internals --experimental-abortcontroller +'use strict'; + +// Verify that AbortSignal integration works for fs.watch + +const common = require('../common'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +const fs = require('fs'); +const fixtures = require('../common/fixtures'); + + +{ + // Signal aborted after creating the watcher + const file = fixtures.path('empty.js'); + const ac = new AbortController(); + const { signal } = ac; + const watcher = fs.watch(file, { signal }); + watcher.once('close', common.mustCall()); + setImmediate(() => ac.abort()); +} +{ + // Signal aborted before creating the watcher + const file = fixtures.path('empty.js'); + const ac = new AbortController(); + const { signal } = ac; + ac.abort(); + const watcher = fs.watch(file, { signal }); + watcher.once('close', common.mustCall()); +} diff --git a/test/parallel/test-fs-whatwg-url.js b/test/parallel/test-fs-whatwg-url.js index a506e5eb942a54..829cfa92fafebd 100644 --- a/test/parallel/test-fs-whatwg-url.js +++ b/test/parallel/test-fs-whatwg-url.js @@ -6,7 +6,6 @@ const assert = require('assert'); const path = require('path'); const fs = require('fs'); const os = require('os'); -const URL = require('url').URL; function pathToFileURL(p) { if (!path.isAbsolute(p)) diff --git a/test/parallel/test-fs-write-file.js b/test/parallel/test-fs-write-file.js index e80861756ab2d8..129560e63601fa 100644 --- a/test/parallel/test-fs-write-file.js +++ b/test/parallel/test-fs-write-file.js @@ -19,6 +19,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. +// Flags: --experimental-abortcontroller 'use strict'; const common = require('../common'); const assert = require('assert'); @@ -66,3 +67,32 @@ fs.open(filename4, 'w+', common.mustSucceed((fd) => { })); })); })); + + +{ + // Test that writeFile is cancellable with an AbortSignal. + // Before the operation has started + const controller = new AbortController(); + const signal = controller.signal; + const filename3 = join(tmpdir.path, 'test3.txt'); + + fs.writeFile(filename3, s, { signal }, common.mustCall((err) => { + assert.strictEqual(err.name, 'AbortError'); + })); + + controller.abort(); +} + +{ + // Test that writeFile is cancellable with an AbortSignal. + // After the operation has started + const controller = new AbortController(); + const signal = controller.signal; + const filename4 = join(tmpdir.path, 'test5.txt'); + + fs.writeFile(filename4, s, { signal }, common.mustCall((err) => { + assert.strictEqual(err.name, 'AbortError'); + })); + + process.nextTick(() => controller.abort()); +} diff --git a/test/parallel/test-fs-write.js b/test/parallel/test-fs-write.js index bdee397eb58dd2..63f0245663c435 100644 --- a/test/parallel/test-fs-write.js +++ b/test/parallel/test-fs-write.js @@ -36,11 +36,13 @@ const fn4 = path.join(tmpdir.path, 'write4.txt'); const expected = 'ümlaut.'; const constants = fs.constants; -/* eslint-disable no-undef */ -common.allowGlobals(externalizeString, isOneByteString, x); +const { externalizeString, isOneByteString } = global; + +// Account for extra globals exposed by --expose_externalize_string. +common.allowGlobals(externalizeString, isOneByteString, global.x); { - const expected = 'ümlaut eins'; // Must be a unique string. + const expected = 'ümlaut sechzig'; // Must be a unique string. externalizeString(expected); assert.strictEqual(isOneByteString(expected), true); const fd = fs.openSync(fn, 'w'); @@ -50,7 +52,7 @@ common.allowGlobals(externalizeString, isOneByteString, x); } { - const expected = 'ümlaut zwei'; // Must be a unique string. + const expected = 'ümlaut neunzig'; // Must be a unique string. externalizeString(expected); assert.strictEqual(isOneByteString(expected), true); const fd = fs.openSync(fn, 'w'); @@ -60,7 +62,7 @@ common.allowGlobals(externalizeString, isOneByteString, x); } { - const expected = '中文 1'; // Must be a unique string. + const expected = 'Zhōngwén 1'; // Must be a unique string. externalizeString(expected); assert.strictEqual(isOneByteString(expected), false); const fd = fs.openSync(fn, 'w'); @@ -70,7 +72,7 @@ common.allowGlobals(externalizeString, isOneByteString, x); } { - const expected = '中文 2'; // Must be a unique string. + const expected = 'Zhōngwén 2'; // Must be a unique string. externalizeString(expected); assert.strictEqual(isOneByteString(expected), false); const fd = fs.openSync(fn, 'w'); @@ -78,7 +80,6 @@ common.allowGlobals(externalizeString, isOneByteString, x); fs.closeSync(fd); assert.strictEqual(fs.readFileSync(fn, 'utf8'), expected); } -/* eslint-enable no-undef */ fs.open(fn, 'w', 0o644, common.mustSucceed((fd) => { const done = common.mustSucceed((written) => { diff --git a/test/parallel/test-http-agent-scheduling.js b/test/parallel/test-http-agent-scheduling.js index 7214ef5f877d7e..737f535a0f0a92 100644 --- a/test/parallel/test-http-agent-scheduling.js +++ b/test/parallel/test-http-agent-scheduling.js @@ -56,11 +56,11 @@ function defaultTest() { bulkRequest(url, agent, (ports) => { makeRequest(url, agent, (port) => { - assert.strictEqual(ports[0], port); + assert.strictEqual(ports[ports.length - 1], port); makeRequest(url, agent, (port) => { - assert.strictEqual(ports[1], port); + assert.strictEqual(ports[ports.length - 1], port); makeRequest(url, agent, (port) => { - assert.strictEqual(ports[2], port); + assert.strictEqual(ports[ports.length - 1], port); server.close(); agent.destroy(); }); diff --git a/test/parallel/test-http-client-abort-destroy.js b/test/parallel/test-http-client-abort-destroy.js index 6db2ea5682e922..f46d10d367106e 100644 --- a/test/parallel/test-http-client-abort-destroy.js +++ b/test/parallel/test-http-client-abort-destroy.js @@ -1,3 +1,4 @@ +// Flags: --experimental-abortcontroller 'use strict'; const common = require('../common'); const http = require('http'); @@ -52,8 +53,7 @@ const assert = require('assert'); { // destroy - const server = http.createServer(common.mustNotCall((req, res) => { - })); + const server = http.createServer(common.mustNotCall()); server.listen(0, common.mustCall(() => { const options = { port: server.address().port }; @@ -69,3 +69,26 @@ const assert = require('assert'); assert.strictEqual(req.destroyed, true); })); } + + +{ + // Destroy with AbortSignal + + const server = http.createServer(common.mustNotCall()); + const controller = new AbortController(); + + server.listen(0, common.mustCall(() => { + const options = { port: server.address().port, signal: controller.signal }; + const req = http.get(options, common.mustNotCall()); + req.on('error', common.mustCall((err) => { + assert.strictEqual(err.code, 'ABORT_ERR'); + assert.strictEqual(err.name, 'AbortError'); + server.close(); + })); + assert.strictEqual(req.aborted, false); + assert.strictEqual(req.destroyed, false); + controller.abort(); + assert.strictEqual(req.aborted, false); + assert.strictEqual(req.destroyed, true); + })); +} diff --git a/test/parallel/test-http-client-get-url.js b/test/parallel/test-http-client-get-url.js index a72eea56538c4d..3b091a72eda493 100644 --- a/test/parallel/test-http-client-get-url.js +++ b/test/parallel/test-http-client-get-url.js @@ -24,7 +24,6 @@ const common = require('../common'); const assert = require('assert'); const http = require('http'); const url = require('url'); -const URL = url.URL; const testPath = '/foo?bar'; const server = http.createServer(common.mustCall((req, res) => { diff --git a/test/parallel/test-http-mutable-headers.js b/test/parallel/test-http-mutable-headers.js index c9f63acf4433f4..755a4dd66159e8 100644 --- a/test/parallel/test-http-mutable-headers.js +++ b/test/parallel/test-http-mutable-headers.js @@ -108,6 +108,10 @@ const s = http.createServer(common.mustCall((req, res) => { ['x-test-header', 'x-test-header2', 'set-cookie', 'x-test-array-header']); + assert.deepStrictEqual(res.getRawHeaderNames(), + ['x-test-header', 'X-TEST-HEADER2', + 'set-cookie', 'x-test-array-header']); + assert.strictEqual(res.hasHeader('x-test-header2'), true); assert.strictEqual(res.hasHeader('X-TEST-HEADER2'), true); assert.strictEqual(res.hasHeader('X-Test-Header2'), true); @@ -171,7 +175,10 @@ function nextTest() { let bufferedResponse = ''; - http.get({ port: s.address().port }, common.mustCall((response) => { + const req = http.get({ + port: s.address().port, + headers: { 'X-foo': 'bar' } + }, common.mustCall((response) => { switch (test) { case 'headers': assert.strictEqual(response.statusCode, 201); @@ -214,4 +221,10 @@ function nextTest() { common.mustCall(nextTest)(); })); })); + + assert.deepStrictEqual(req.getHeaderNames(), + ['x-foo', 'host']); + + assert.deepStrictEqual(req.getRawHeaderNames(), + ['X-foo', 'Host']); } diff --git a/test/parallel/test-http-req-close-robust-from-tampering.js b/test/parallel/test-http-req-close-robust-from-tampering.js new file mode 100644 index 00000000000000..46ae0c0e297158 --- /dev/null +++ b/test/parallel/test-http-req-close-robust-from-tampering.js @@ -0,0 +1,26 @@ +'use strict'; +const common = require('../common'); +const { createServer } = require('http'); +const { connect } = require('net'); + +// Make sure that calling the semi-private close() handlers manually doesn't +// cause an error. + +const server = createServer(common.mustCall((req, res) => { + req.client._events.close.forEach((fn) => { fn.bind(req)(); }); +})); + +server.unref(); + +server.listen(0, common.mustCall(() => { + const client = connect(server.address().port); + + const req = [ + 'POST / HTTP/1.1', + 'Content-Length: 11', + '', + 'hello world', + ].join('\r\n'); + + client.end(req); +})); diff --git a/test/parallel/test-http-reuse-socket.js b/test/parallel/test-http-reuse-socket.js new file mode 100644 index 00000000000000..f5cd002fdbf519 --- /dev/null +++ b/test/parallel/test-http-reuse-socket.js @@ -0,0 +1,51 @@ +'use strict'; +const common = require('../common'); +const http = require('http'); +const assert = require('assert'); +const Countdown = require('../common/countdown'); + +// The HEAD:204, GET:200 is the most pathological test case. +// GETs following a 204 response with a content-encoding header failed. +// Responses without bodies and without content-length or encoding caused +// the socket to be closed. +const codes = [204, 200, 200, 304, 200]; +const methods = ['HEAD', 'HEAD', 'GET', 'HEAD', 'GET']; + +const sockets = []; +const agent = new http.Agent(); +agent.maxSockets = 1; + +const countdown = new Countdown(codes.length, () => server.close()); + +const server = http.createServer(common.mustCall((req, res) => { + const code = codes.shift(); + assert.strictEqual(typeof code, 'number'); + assert.ok(code > 0); + res.writeHead(code, {}); + res.end(); +}, codes.length)); + +function nextRequest() { + const request = http.request({ + port: server.address().port, + path: '/', + agent: agent, + method: methods.shift() + }, common.mustCall((response) => { + response.on('end', common.mustCall(() => { + if (countdown.dec()) { + nextRequest(); + } + assert.strictEqual(sockets.length, 1); + })); + response.resume(); + })); + request.on('socket', common.mustCall((socket) => { + if (!sockets.includes(socket)) { + sockets.push(socket); + } + })); + request.end(); +} + +server.listen(0, common.mustCall(nextRequest)); diff --git a/test/parallel/test-http-server-request-timeout-delayed-body.js b/test/parallel/test-http-server-request-timeout-delayed-body.js index c6f26def86bad1..ec8ebbb5cd18eb 100644 --- a/test/parallel/test-http-server-request-timeout-delayed-body.js +++ b/test/parallel/test-http-server-request-timeout-delayed-body.js @@ -9,6 +9,7 @@ const { connect } = require('net'); // after server.requestTimeout if the client // pauses before start sending the body. +let sendDelayedRequestBody; const server = createServer(common.mustCall((req, res) => { let body = ''; req.setEncoding('utf-8'); @@ -22,6 +23,9 @@ const server = createServer(common.mustCall((req, res) => { res.write(body); res.end(); }); + + assert.strictEqual(typeof sendDelayedRequestBody, 'function'); + sendDelayedRequestBody(); })); // 0 seconds is the default @@ -44,9 +48,11 @@ server.listen(0, common.mustCall(() => { client.write('Connection: close\r\n'); client.write('\r\n'); - setTimeout(() => { - client.write('12345678901234567890\r\n\r\n'); - }, common.platformTimeout(2000)).unref(); + sendDelayedRequestBody = common.mustCall(() => { + setTimeout(() => { + client.write('12345678901234567890\r\n\r\n'); + }, common.platformTimeout(2000)).unref(); + }); const errOrEnd = common.mustCall(function(err) { console.log(err); diff --git a/test/parallel/test-http-server-request-timeout-delayed-headers.js b/test/parallel/test-http-server-request-timeout-delayed-headers.js index b2c282fd2fa122..f5d85b70514950 100644 --- a/test/parallel/test-http-server-request-timeout-delayed-headers.js +++ b/test/parallel/test-http-server-request-timeout-delayed-headers.js @@ -8,9 +8,12 @@ const { connect } = require('net'); // This test validates that the server returns 408 // after server.requestTimeout if the client // pauses before start sending the request. - +let sendDelayedRequestHeaders; const server = createServer(common.mustNotCall()); - +server.on('connection', common.mustCall(() => { + assert.strictEqual(typeof sendDelayedRequestHeaders, 'function'); + sendDelayedRequestHeaders(); +})); // 0 seconds is the default assert.strictEqual(server.requestTimeout, 0); const requestTimeout = common.platformTimeout(1000); @@ -39,10 +42,12 @@ server.listen(0, common.mustCall(() => { client.resume(); - setTimeout(() => { - client.write('POST / HTTP/1.1\r\n'); - client.write('Content-Length: 20\r\n'); - client.write('Connection: close\r\n\r\n'); - client.write('12345678901234567890\r\n\r\n'); - }, common.platformTimeout(2000)).unref(); + sendDelayedRequestHeaders = common.mustCall(() => { + setTimeout(() => { + client.write('POST / HTTP/1.1\r\n'); + client.write('Content-Length: 20\r\n'); + client.write('Connection: close\r\n\r\n'); + client.write('12345678901234567890\r\n\r\n'); + }, common.platformTimeout(2000)).unref(); + }); })); diff --git a/test/parallel/test-http-server-request-timeout-interrupted-body.js b/test/parallel/test-http-server-request-timeout-interrupted-body.js index 237e133fef7d94..7d70351fdc0833 100644 --- a/test/parallel/test-http-server-request-timeout-interrupted-body.js +++ b/test/parallel/test-http-server-request-timeout-interrupted-body.js @@ -8,7 +8,7 @@ const { connect } = require('net'); // This test validates that the server returns 408 // after server.requestTimeout if the client // pauses sending in the middle of the body. - +let sendDelayedRequestBody; const server = createServer(common.mustCall((req, res) => { let body = ''; req.setEncoding('utf-8'); @@ -22,6 +22,9 @@ const server = createServer(common.mustCall((req, res) => { res.write(body); res.end(); }); + + assert.strictEqual(typeof sendDelayedRequestBody, 'function'); + sendDelayedRequestBody(); })); // 0 seconds is the default @@ -57,7 +60,9 @@ server.listen(0, common.mustCall(() => { client.write('\r\n'); client.write('1234567890'); - setTimeout(() => { - client.write('1234567890\r\n\r\n'); - }, common.platformTimeout(2000)).unref(); + sendDelayedRequestBody = common.mustCall(() => { + setTimeout(() => { + client.write('1234567890\r\n\r\n'); + }, common.platformTimeout(2000)).unref(); + }); })); diff --git a/test/parallel/test-http-server-request-timeout-interrupted-headers.js b/test/parallel/test-http-server-request-timeout-interrupted-headers.js index 0b8a2c315ab0e2..1fa8946ffae70f 100644 --- a/test/parallel/test-http-server-request-timeout-interrupted-headers.js +++ b/test/parallel/test-http-server-request-timeout-interrupted-headers.js @@ -8,8 +8,12 @@ const { connect } = require('net'); // This test validates that the server returns 408 // after server.requestTimeout if the client // pauses sending in the middle of a header. - +let sendDelayedRequestHeaders; const server = createServer(common.mustNotCall()); +server.on('connection', common.mustCall(() => { + assert.strictEqual(typeof sendDelayedRequestHeaders, 'function'); + sendDelayedRequestHeaders(); +})); // 120 seconds is the default assert.strictEqual(server.requestTimeout, 0); @@ -42,7 +46,9 @@ server.listen(0, common.mustCall(() => { client.write('Connection: close\r\n'); client.write('X-CRASH: '); - setTimeout(() => { - client.write('1234567890\r\n\r\n'); - }, common.platformTimeout(2000)).unref(); + sendDelayedRequestHeaders = common.mustCall(() => { + setTimeout(() => { + client.write('1234567890\r\n\r\n'); + }, common.platformTimeout(2000)).unref(); + }); })); diff --git a/test/parallel/test-http-server-request-timeout-upgrade.js b/test/parallel/test-http-server-request-timeout-upgrade.js index dd7269621e2d47..87e8dbab131631 100644 --- a/test/parallel/test-http-server-request-timeout-upgrade.js +++ b/test/parallel/test-http-server-request-timeout-upgrade.js @@ -7,8 +7,12 @@ const { connect } = require('net'); // This test validates that the requestTimeoout // is disabled after the connection is upgraded. - +let sendDelayedRequestHeaders; const server = createServer(common.mustNotCall()); +server.on('connection', common.mustCall(() => { + assert.strictEqual(typeof sendDelayedRequestHeaders, 'function'); + sendDelayedRequestHeaders(); +})); // 0 seconds is the default assert.strictEqual(server.requestTimeout, 0); @@ -48,8 +52,10 @@ server.listen(0, common.mustCall(() => { client.write('Upgrade: WebSocket\r\n'); client.write('Connection: Upgrade\r\n\r\n'); - setTimeout(() => { - client.write('12345678901234567890'); - client.end(); - }, common.platformTimeout(2000)).unref(); + sendDelayedRequestHeaders = common.mustCall(() => { + setTimeout(() => { + client.write('12345678901234567890'); + client.end(); + }, common.platformTimeout(2000)).unref(); + }); })); diff --git a/test/parallel/test-http-set-header-chain.js b/test/parallel/test-http-set-header-chain.js new file mode 100644 index 00000000000000..aa9519129a9123 --- /dev/null +++ b/test/parallel/test-http-set-header-chain.js @@ -0,0 +1,29 @@ +'use strict'; +const common = require('../common'); +const http = require('http'); +const assert = require('assert'); +const expected = { + '__proto__': null, + 'testheader1': 'foo', + 'testheader2': 'bar', + 'testheader3': 'xyz' +}; +const server = http.createServer(common.mustCall((req, res) => { + let retval = res.setHeader('testheader1', 'foo'); + + // Test that the setHeader returns the same response object. + assert.strictEqual(retval, res); + + retval = res.setHeader('testheader2', 'bar').setHeader('testheader3', 'xyz'); + // Test that chaining works for setHeader. + assert.deepStrictEqual(res.getHeaders(), expected); + res.end('ok'); +})); +server.listen(0, () => { + http.get({ port: server.address().port }, common.mustCall((res) => { + res.on('data', () => {}); + res.on('end', common.mustCall(() => { + server.close(); + })); + })); +}); diff --git a/test/parallel/test-http2-altsvc.js b/test/parallel/test-http2-altsvc.js index 3a1a1cf62991b7..39a3ca97b78bf3 100644 --- a/test/parallel/test-http2-altsvc.js +++ b/test/parallel/test-http2-altsvc.js @@ -6,7 +6,6 @@ if (!common.hasCrypto) const assert = require('assert'); const http2 = require('http2'); -const { URL } = require('url'); const Countdown = require('../common/countdown'); const server = http2.createServer(); diff --git a/test/parallel/test-http2-clean-output.js b/test/parallel/test-http2-clean-output.js new file mode 100644 index 00000000000000..27b7c338c9a794 --- /dev/null +++ b/test/parallel/test-http2-clean-output.js @@ -0,0 +1,40 @@ +'use strict'; + +const { + hasCrypto, + mustCall, + skip +} = require('../common'); +if (!hasCrypto) + skip('missing crypto'); + +const { + strictEqual +} = require('assert'); +const { + createServer, + connect +} = require('http2'); +const { + spawnSync +} = require('child_process'); + +// Validate that there is no unexpected output when +// using http2 +if (process.argv[2] !== 'child') { + const { + stdout, stderr, status + } = spawnSync(process.execPath, [__filename, 'child'], { encoding: 'utf8' }); + strictEqual(stderr, ''); + strictEqual(stdout, ''); + strictEqual(status, 0); +} else { + const server = createServer(); + server.listen(0, mustCall(() => { + const client = connect(`http://localhost:${server.address().port}`); + client.on('connect', mustCall(() => { + client.close(); + server.close(); + })); + })); +} diff --git a/test/parallel/test-http2-client-destroy.js b/test/parallel/test-http2-client-destroy.js index 12da903c6535a0..0336c7910fa6a6 100644 --- a/test/parallel/test-http2-client-destroy.js +++ b/test/parallel/test-http2-client-destroy.js @@ -1,4 +1,4 @@ -// Flags: --expose-internals +// Flags: --expose-internals --experimental-abortcontroller 'use strict'; @@ -8,6 +8,7 @@ if (!common.hasCrypto) const assert = require('assert'); const h2 = require('http2'); const { kSocket } = require('internal/http2/util'); +const { kEvents } = require('internal/event_target'); const Countdown = require('../common/countdown'); { @@ -165,3 +166,76 @@ const Countdown = require('../common/countdown'); req.on('close', common.mustCall(() => server.close())); })); } + +// Destroy with AbortSignal +{ + const server = h2.createServer(); + const controller = new AbortController(); + + server.on('stream', common.mustNotCall()); + server.listen(0, common.mustCall(() => { + const client = h2.connect(`http://localhost:${server.address().port}`); + client.on('close', common.mustCall()); + + const { signal } = controller; + assert.strictEqual(signal[kEvents].get('abort'), undefined); + + client.on('error', common.mustCall(() => { + // After underlying stream dies, signal listener detached + assert.strictEqual(signal[kEvents].get('abort'), undefined); + })); + + const req = client.request({}, { signal }); + + req.on('error', common.mustCall((err) => { + assert.strictEqual(err.code, 'ABORT_ERR'); + assert.strictEqual(err.name, 'AbortError'); + })); + req.on('close', common.mustCall(() => server.close())); + + assert.strictEqual(req.aborted, false); + assert.strictEqual(req.destroyed, false); + // Signal listener attached + assert.strictEqual(signal[kEvents].get('abort').size, 1); + + controller.abort(); + + assert.strictEqual(req.aborted, false); + assert.strictEqual(req.destroyed, true); + })); +} +// Pass an already destroyed signal to abort immediately. +{ + const server = h2.createServer(); + const controller = new AbortController(); + + server.on('stream', common.mustNotCall()); + server.listen(0, common.mustCall(() => { + const client = h2.connect(`http://localhost:${server.address().port}`); + client.on('close', common.mustCall()); + + const { signal } = controller; + controller.abort(); + + assert.strictEqual(signal[kEvents].get('abort'), undefined); + + client.on('error', common.mustCall(() => { + // After underlying stream dies, signal listener detached + assert.strictEqual(signal[kEvents].get('abort'), undefined); + })); + + const req = client.request({}, { signal }); + // Signal already aborted, so no event listener attached. + assert.strictEqual(signal[kEvents].get('abort'), undefined); + + assert.strictEqual(req.aborted, false); + // Destroyed on same tick as request made + assert.strictEqual(req.destroyed, true); + + req.on('error', common.mustCall((err) => { + assert.strictEqual(err.code, 'ABORT_ERR'); + assert.strictEqual(err.name, 'AbortError'); + })); + req.on('close', common.mustCall(() => server.close())); + })); +} diff --git a/test/parallel/test-http2-connect-method.js b/test/parallel/test-http2-connect-method.js index d5b4c4bd274950..4ada9f47553528 100644 --- a/test/parallel/test-http2-connect-method.js +++ b/test/parallel/test-http2-connect-method.js @@ -6,7 +6,6 @@ if (!common.hasCrypto) const assert = require('assert'); const net = require('net'); const http2 = require('http2'); -const { URL } = require('url'); const { HTTP2_HEADER_METHOD, diff --git a/test/parallel/test-http2-create-client-connect.js b/test/parallel/test-http2-create-client-connect.js index 8a4fc9a1d0e075..5723fcecd6f069 100644 --- a/test/parallel/test-http2-create-client-connect.js +++ b/test/parallel/test-http2-create-client-connect.js @@ -9,7 +9,6 @@ if (!common.hasCrypto) const fixtures = require('../common/fixtures'); const h2 = require('http2'); const url = require('url'); -const URL = url.URL; { const server = h2.createServer(); diff --git a/test/parallel/test-http2-respond-file-error-pipe-offset.js b/test/parallel/test-http2-respond-file-error-pipe-offset.js index 21fccb905d7224..39876baaf51dab 100644 --- a/test/parallel/test-http2-respond-file-error-pipe-offset.js +++ b/test/parallel/test-http2-respond-file-error-pipe-offset.js @@ -56,4 +56,11 @@ server.listen(0, () => { req.end(); }); -fs.writeFile(pipeName, 'Hello, world!\n', common.mustSucceed()); +fs.writeFile(pipeName, 'Hello, world!\n', common.mustCall((err) => { + // It's possible for the reading end of the pipe to get the expected error + // and break everything down before we're finished, so allow `EPIPE` but + // no other errors. + if (err?.code !== 'EPIPE') { + assert.ifError(err); + } +})); diff --git a/test/parallel/test-http2-response-splitting.js b/test/parallel/test-http2-response-splitting.js index 9613eca9636ae4..a94b9ca4f72b35 100644 --- a/test/parallel/test-http2-response-splitting.js +++ b/test/parallel/test-http2-response-splitting.js @@ -9,7 +9,6 @@ if (!common.hasCrypto) common.skip('missing crypto'); const assert = require('assert'); const http2 = require('http2'); -const { URL } = require('url'); // Response splitting example, credit: Amit Klein, Safebreach const str = '/welcome?lang=bar%c4%8d%c4%8aContent­Length:%200%c4%8d%c4%8a%c' + diff --git a/test/parallel/test-http2-update-settings.js b/test/parallel/test-http2-update-settings.js new file mode 100644 index 00000000000000..e99ee2bcf150d8 --- /dev/null +++ b/test/parallel/test-http2-update-settings.js @@ -0,0 +1,59 @@ +'use strict'; + +// This test ensures that the Http2SecureServer and Http2Server +// settings are updated when the setting object is valid. +// When the setting object is invalid, this test ensures that +// updateSettings throws an exception. + +const common = require('../common'); +if (!common.hasCrypto) { common.skip('missing crypto'); } +const assert = require('assert'); +const http2 = require('http2'); + +testUpdateSettingsWith({ + server: http2.createSecureServer(), + newServerSettings: { + 'headerTableSize': 1, + 'initialWindowSize': 1, + 'maxConcurrentStreams': 1, + 'maxHeaderListSize': 1, + 'maxFrameSize': 16385, + 'enablePush': false, + 'enableConnectProtocol': true + } +}); +testUpdateSettingsWith({ + server: http2.createServer(), + newServerSettings: { + 'enablePush': false + } +}); + +function testUpdateSettingsWith({ server, newServerSettings }) { + const oldServerSettings = getServerSettings(server); + assert.notDeepStrictEqual(oldServerSettings, newServerSettings); + server.updateSettings(newServerSettings); + const updatedServerSettings = getServerSettings(server); + assert.deepStrictEqual(updatedServerSettings, { ...oldServerSettings, + ...newServerSettings }); + assert.throws(() => server.updateSettings(''), { + message: 'The "settings" argument must be of type object. ' + + 'Received type string (\'\')', + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + assert.throws(() => server.updateSettings({ + 'maxHeaderListSize': 'foo' + }), { + message: 'Invalid value for setting "maxHeaderListSize": foo', + code: 'ERR_HTTP2_INVALID_SETTING_VALUE', + name: 'RangeError' + }); +} + +function getServerSettings(server) { + const options = Object + .getOwnPropertySymbols(server) + .find((s) => s.toString() === 'Symbol(options)'); + return server[options].settings; +} diff --git a/test/parallel/test-https-abortcontroller.js b/test/parallel/test-https-abortcontroller.js new file mode 100644 index 00000000000000..3da2ab9ce25c28 --- /dev/null +++ b/test/parallel/test-https-abortcontroller.js @@ -0,0 +1,40 @@ +// Flags: --experimental-abortcontroller +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const fixtures = require('../common/fixtures'); +const https = require('https'); +const assert = require('assert'); +const { once } = require('events'); + +const options = { + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}; + +(async () => { + const { port, server } = await new Promise((resolve) => { + const server = https.createServer(options, () => {}); + server.listen(0, () => resolve({ port: server.address().port, server })); + }); + try { + const ac = new AbortController(); + const req = https.request({ + host: 'localhost', + port, + path: '/', + method: 'GET', + rejectUnauthorized: false, + signal: ac.signal, + }); + process.nextTick(() => ac.abort()); + const [ err ] = await once(req, 'error'); + assert.strictEqual(err.name, 'AbortError'); + assert.strictEqual(err.code, 'ABORT_ERR'); + } finally { + server.close(); + } +})().then(common.mustCall()); diff --git a/test/parallel/test-https-agent-additional-options.js b/test/parallel/test-https-agent-additional-options.js index 2b5446b9e3d194..70c3dee103b2c9 100644 --- a/test/parallel/test-https-agent-additional-options.js +++ b/test/parallel/test-https-agent-additional-options.js @@ -34,6 +34,8 @@ const updatedValues = new Map([ ['dhparam', fixtures.readKey('dh2048.pem')], ['ecdhCurve', 'secp384r1'], ['honorCipherOrder', true], + ['minVersion', 'TLSv1.1'], + ['maxVersion', 'TLSv1.3'], ['secureOptions', crypto.constants.SSL_OP_CIPHER_SERVER_PREFERENCE], ['secureProtocol', 'TLSv1_1_method'], ['sessionIdContext', 'sessionIdContext'], @@ -61,8 +63,8 @@ function variations(iter, port, cb) { server.close(); } else { // Save `value` for check the next time. - value = next.value.val; const [key, val] = next.value; + value = val; https.get({ ...getBaseOptions(port), [key]: val }, variations(iter, port, cb)); } diff --git a/test/parallel/test-https-agent-getname.js b/test/parallel/test-https-agent-getname.js index dabb08f074af9a..6f8c32b299a669 100644 --- a/test/parallel/test-https-agent-getname.js +++ b/test/parallel/test-https-agent-getname.js @@ -12,7 +12,7 @@ const agent = new https.Agent(); // empty options assert.strictEqual( agent.getName({}), - 'localhost:::::::::::::::::::' + 'localhost::::::::::::::::::::::' ); // Pass all options arguments @@ -34,11 +34,15 @@ const options = { secureOptions: 0, secureProtocol: 'secureProtocol', servername: 'localhost', - sessionIdContext: 'sessionIdContext' + sessionIdContext: 'sessionIdContext', + sigalgs: 'sigalgs', + privateKeyIdentifier: 'privateKeyIdentifier', + privateKeyEngine: 'privateKeyEngine', }; assert.strictEqual( agent.getName(options), '0.0.0.0:443:192.168.1.1:ca:cert:dynamic:ciphers:key:pfx:false:localhost:' + - '::secureProtocol:c,r,l:false:ecdhCurve:dhparam:0:sessionIdContext' + '::secureProtocol:c,r,l:false:ecdhCurve:dhparam:0:sessionIdContext:' + + '"sigalgs":privateKeyIdentifier:privateKeyEngine' ); diff --git a/test/parallel/test-https-agent.js b/test/parallel/test-https-agent.js index 4b2f9e73a1cd79..ce4bc6e5bdb86b 100644 --- a/test/parallel/test-https-agent.js +++ b/test/parallel/test-https-agent.js @@ -35,7 +35,7 @@ const options = { }; -const server = https.Server(options, function(req, res) { +const server = https.Server(options, (req, res) => { res.writeHead(200); res.end('hello world\n'); }); @@ -46,9 +46,9 @@ const N = 4; const M = 4; -server.listen(0, function() { +server.listen(0, () => { for (let i = 0; i < N; i++) { - setTimeout(function() { + setTimeout(() => { for (let j = 0; j < M; j++) { https.get({ path: '/', @@ -58,7 +58,7 @@ server.listen(0, function() { res.resume(); assert.strictEqual(res.statusCode, 200); if (++responses === N * M) server.close(); - }).on('error', function(e) { + }).on('error', (e) => { throw e; }); } @@ -67,6 +67,6 @@ server.listen(0, function() { }); -process.on('exit', function() { +process.on('exit', () => { assert.strictEqual(responses, N * M); }); diff --git a/test/parallel/test-https-client-get-url.js b/test/parallel/test-https-client-get-url.js index 76328775e80187..fb91a4f1e7cb8a 100644 --- a/test/parallel/test-https-client-get-url.js +++ b/test/parallel/test-https-client-get-url.js @@ -32,8 +32,6 @@ const assert = require('assert'); const https = require('https'); const url = require('url'); -const URL = url.URL; - const options = { key: fixtures.readKey('agent1-key.pem'), cert: fixtures.readKey('agent1-cert.pem') diff --git a/test/parallel/test-inspector-has-idle.js b/test/parallel/test-inspector-has-idle.js new file mode 100644 index 00000000000000..c14590353e6780 --- /dev/null +++ b/test/parallel/test-inspector-has-idle.js @@ -0,0 +1,43 @@ +'use strict'; +const common = require('../common'); + +common.skipIfInspectorDisabled(); + +const assert = require('assert'); +const { Session } = require('inspector'); +const { promisify } = require('util'); + +const sleep = promisify(setTimeout); + +async function test() { + const inspector = new Session(); + inspector.connect(); + + inspector.post('Profiler.enable'); + inspector.post('Profiler.start'); + + await sleep(1000); + + const { profile } = await new Promise((resolve, reject) => { + inspector.post('Profiler.stop', (err, params) => { + if (err) return reject(err); + resolve(params); + }); + }); + + let hasIdle = false; + for (const node of profile.nodes) { + if (node.callFrame.functionName === '(idle)') { + hasIdle = true; + break; + } + } + assert(hasIdle); + + inspector.post('Profiler.disable'); + inspector.disconnect(); +} + +test().then(common.mustCall(() => { + console.log('Done!'); +})); diff --git a/test/parallel/test-macos-app-sandbox.js b/test/parallel/test-macos-app-sandbox.js index 53484834a36758..e1e5460ab22cda 100644 --- a/test/parallel/test-macos-app-sandbox.js +++ b/test/parallel/test-macos-app-sandbox.js @@ -1,7 +1,7 @@ 'use strict'; const common = require('../common'); if (process.platform !== 'darwin') - common.skip('App Sandbox is only avaliable on Darwin'); + common.skip('App Sandbox is only available on Darwin'); const fixtures = require('../common/fixtures'); const tmpdir = require('../common/tmpdir'); diff --git a/test/parallel/test-nodeeventtarget.js b/test/parallel/test-nodeeventtarget.js index 0f9218f540860d..276bd9feb4da7c 100644 --- a/test/parallel/test-nodeeventtarget.js +++ b/test/parallel/test-nodeeventtarget.js @@ -11,6 +11,7 @@ const { deepStrictEqual, ok, strictEqual, + throws, } = require('assert'); const { on } = require('events'); @@ -145,6 +146,27 @@ const { on } = require('events'); target.on('foo', () => {}); target.on('foo', () => {}); } +{ + // Test NodeEventTarget emit + const emitter = new NodeEventTarget(); + emitter.addEventListener('foo', common.mustCall((e) => { + strictEqual(e.type, 'foo'); + strictEqual(e.detail, 'bar'); + ok(e instanceof Event); + }), { once: true }); + emitter.once('foo', common.mustCall((e, droppedAdditionalArgument) => { + strictEqual(e, 'bar'); + strictEqual(droppedAdditionalArgument, undefined); + })); + emitter.emit('foo', 'bar', 'baz'); +} +{ + // Test NodeEventTarget emit unsupported usage + const emitter = new NodeEventTarget(); + throws(() => { + emitter.emit(); + }, /ERR_INVALID_ARG_TYPE/); +} (async () => { // test NodeEventTarget async-iterability diff --git a/test/parallel/test-npm-install.js b/test/parallel/test-npm-install.js index 1eec5f57ad6db9..17deacdbc14d2a 100644 --- a/test/parallel/test-npm-install.js +++ b/test/parallel/test-npm-install.js @@ -42,6 +42,8 @@ const env = { ...process.env, PATH: path.dirname(process.execPath), NPM_CONFIG_PREFIX: path.join(npmSandbox, 'npm-prefix'), NPM_CONFIG_TMP: path.join(npmSandbox, 'npm-tmp'), + NPM_CONFIG_AUDIT: false, + NPM_CONFIG_UPDATE_NOTIFIER: false, HOME: homeDir, }; diff --git a/test/parallel/test-performance-eventlooputil.js b/test/parallel/test-performance-eventlooputil.js index 1a5d86db750a94..9ce7212729d574 100644 --- a/test/parallel/test-performance-eventlooputil.js +++ b/test/parallel/test-performance-eventlooputil.js @@ -1,8 +1,9 @@ 'use strict'; -require('../common'); +const { mustCall } = require('../common'); -const TIMEOUT = 50; +const TIMEOUT = 10; +const SPIN_DUR = 50; const assert = require('assert'); const { performance } = require('perf_hooks'); @@ -21,29 +22,39 @@ if (nodeTiming.loopStart === -1) { { idle: 0, active: 0, utilization: 0 }); } -// Place in setTimeout() to make sure there is some idle time, but not going to -// assert this since it could make the test flaky. -setTimeout(() => { - const t = Date.now(); +const nodeTimingProps = ['name', 'entryType', 'startTime', 'duration', + 'nodeStart', 'v8Start', 'environment', 'loopStart', + 'loopExit', 'bootstrapComplete', 'idleTime']; +for (const p of nodeTimingProps) + assert.ok(typeof JSON.parse(JSON.stringify(nodeTiming))[p] === + typeof nodeTiming[p]); + +setTimeout(mustCall(function r() { const elu1 = eventLoopUtilization(); - while (Date.now() - t < 50) { } + // Force idle time to accumulate before allowing test to continue. + if (elu1.idle <= 0) + return setTimeout(mustCall(r), 5); - const elu2 = eventLoopUtilization(); - const elu3 = eventLoopUtilization(elu1); - const elu4 = eventLoopUtilization(elu2, elu1); + const t = Date.now(); + while (Date.now() - t < SPIN_DUR) { } + + const elu2 = eventLoopUtilization(elu1); + const elu3 = eventLoopUtilization(); + const elu4 = eventLoopUtilization(elu3, elu1); - assert.strictEqual(elu3.idle, 0); + assert.strictEqual(elu2.idle, 0); assert.strictEqual(elu4.idle, 0); - assert.strictEqual(elu3.utilization, 1); + assert.strictEqual(elu2.utilization, 1); assert.strictEqual(elu4.utilization, 1); - assert.strictEqual(elu2.active - elu1.active, elu4.active); - assert.ok(elu2.active > elu3.active); - assert.ok(elu2.active > elu4.active); - assert.ok(elu3.active > elu4.active); + assert.strictEqual(elu3.active - elu1.active, elu4.active); + assert.ok(elu2.active > SPIN_DUR - 10, `${elu2.active} <= ${SPIN_DUR - 10}`); + assert.ok(elu2.active < elu4.active, `${elu2.active} >= ${elu4.active}`); + assert.ok(elu3.active > elu2.active, `${elu3.active} <= ${elu2.active}`); + assert.ok(elu3.active > elu4.active, `${elu3.active} <= ${elu4.active}`); - setTimeout(runIdleTimeTest, TIMEOUT); -}, 5); + setTimeout(mustCall(runIdleTimeTest), TIMEOUT); +}), 5); function runIdleTimeTest() { const idleTime = nodeTiming.idleTime; @@ -55,7 +66,7 @@ function runIdleTimeTest() { assert.strictEqual(elu1.idle, idleTime); assert.strictEqual(elu1.utilization, elu1.active / sum); - setTimeout(runCalcTest, TIMEOUT, elu1); + setTimeout(mustCall(runCalcTest), TIMEOUT, elu1); } function runCalcTest(elu1) { @@ -65,18 +76,20 @@ function runCalcTest(elu1) { const active_delta = elu2.active - elu1.active; const idle_delta = elu2.idle - elu1.idle; - assert.ok(elu2.idle >= 0); - assert.ok(elu2.active >= 0); - assert.ok(elu3.idle >= 0); - assert.ok(elu3.active >= 0); - assert.ok(elu2.idle + elu2.active > elu1.idle + elu2.active); - assert.ok(elu2.idle + elu2.active >= now - nodeTiming.loopStart); + assert.ok(elu2.idle >= 0, `${elu2.idle} < 0`); + assert.ok(elu2.active >= 0, `${elu2.active} < 0`); + assert.ok(elu3.idle >= 0, `${elu3.idle} < 0`); + assert.ok(elu3.active >= 0, `${elu3.active} < 0`); + assert.ok(elu2.idle + elu2.active > elu1.idle + elu1.active, + `${elu2.idle + elu2.active} <= ${elu1.idle + elu1.active}`); + assert.ok(elu2.idle + elu2.active >= now - nodeTiming.loopStart, + `${elu2.idle + elu2.active} < ${now - nodeTiming.loopStart}`); assert.strictEqual(elu3.active, elu2.active - elu1.active); assert.strictEqual(elu3.idle, elu2.idle - elu1.idle); assert.strictEqual(elu3.utilization, active_delta / (idle_delta + active_delta)); - setImmediate(runWorkerTest); + setImmediate(mustCall(runWorkerTest)); } function runWorkerTest() { @@ -90,10 +103,11 @@ function runWorkerTest() { const elu1 = eventLoopUtilization(); const worker = new Worker(__filename, { argv: [ 'iamalive' ] }); - worker.on('message', (msg) => { + worker.on('message', mustCall((msg) => { const elu2 = eventLoopUtilization(elu1); const data = JSON.parse(msg); - assert.ok(elu2.active + elu2.idle > data.active + data.idle); - }); + assert.ok(elu2.active + elu2.idle > data.active + data.idle, + `${elu2.active + elu2.idle} <= ${data.active + data.idle}`); + })); } diff --git a/test/parallel/test-policy-scopes.js b/test/parallel/test-policy-scopes.js index 129287c73c7659..43789713cc979a 100644 --- a/test/parallel/test-policy-scopes.js +++ b/test/parallel/test-policy-scopes.js @@ -10,8 +10,8 @@ const fixtures = require('../common/fixtures'); const assert = require('assert'); const { spawnSync } = require('child_process'); -const dep = fixtures.path('policy', 'main.mjs'); { + const dep = fixtures.path('policy', 'main.mjs'); const depPolicy = fixtures.path( 'policy', 'dependencies', @@ -24,3 +24,17 @@ const dep = fixtures.path('policy', 'main.mjs'); ); assert.strictEqual(status, 0); } +{ + const dep = fixtures.path('policy', 'multi-deps.js'); + const depPolicy = fixtures.path( + 'policy', + 'dependencies', + 'dependencies-scopes-and-resources-policy.json'); + const { status } = spawnSync( + process.execPath, + [ + '--experimental-policy', depPolicy, dep, + ] + ); + assert.strictEqual(status, 0); +} diff --git a/test/parallel/test-preload-worker.js b/test/parallel/test-preload-worker.js new file mode 100644 index 00000000000000..3e9134b470cf37 --- /dev/null +++ b/test/parallel/test-preload-worker.js @@ -0,0 +1,10 @@ +'use strict'; + +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const worker = fixtures.path('worker-preload.js'); +const { exec } = require('child_process'); +const kNodeBinary = process.argv[0]; + + +exec(`"${kNodeBinary}" -r "${worker}" -pe "1+1"`, common.mustSucceed()); diff --git a/test/parallel/test-preload.js b/test/parallel/test-preload.js index 29a2c682606541..df83d74e23bbb5 100644 --- a/test/parallel/test-preload.js +++ b/test/parallel/test-preload.js @@ -27,6 +27,19 @@ const fixtureE = fixtures.path('intrinsic-mutation.js'); const fixtureF = fixtures.path('print-intrinsic-mutation-name.js'); const fixtureG = fixtures.path('worker-from-argv.js'); const fixtureThrows = fixtures.path('throws_error4.js'); +const fixtureIsPreloading = fixtures.path('ispreloading.js'); + +// Assert that module.isPreloading is false here +assert(!module.isPreloading); + +// Test that module.isPreloading is set in preloaded module +// Test preloading a single module works +childProcess.exec( + `"${nodeBinary}" ${preloadOption([fixtureIsPreloading])} "${fixtureB}"`, + function(err, stdout, stderr) { + assert.ifError(err); + assert.strictEqual(stdout, 'B\n'); + }); // Test preloading a single module works childProcess.exec(`"${nodeBinary}" ${preloadOption([fixtureA])} "${fixtureB}"`, diff --git a/test/parallel/test-process-beforeexit.js b/test/parallel/test-process-beforeexit.js index 215e73dc0650f3..7b7ba48f7d8cb4 100644 --- a/test/parallel/test-process-beforeexit.js +++ b/test/parallel/test-process-beforeexit.js @@ -49,12 +49,24 @@ function tryListen() { // Test that a function invoked from the beforeExit handler can use a timer // to keep the event loop open, which can use another timer to keep the event // loop open, etc. +// +// After N times, call function `tryNextTick` to test behaviors of the +// `process.nextTick`. function tryRepeatedTimer() { const N = 5; let n = 0; const repeatedTimer = common.mustCall(function() { if (++n < N) setTimeout(repeatedTimer, 1); + else // n == N + process.once('beforeExit', common.mustCall(tryNextTick)); }, N); setTimeout(repeatedTimer, 1); } + +// Test if the callback of `process.nextTick` can be invoked. +function tryNextTick() { + process.nextTick(common.mustCall(function() { + process.once('beforeExit', common.mustNotCall()); + })); +} diff --git a/test/parallel/test-process-binding-internalbinding-whitelist.js b/test/parallel/test-process-binding-internalbinding-allowlist.js similarity index 94% rename from test/parallel/test-process-binding-internalbinding-whitelist.js rename to test/parallel/test-process-binding-internalbinding-allowlist.js index 9768ef66741aa3..10667b843cde88 100644 --- a/test/parallel/test-process-binding-internalbinding-whitelist.js +++ b/test/parallel/test-process-binding-internalbinding-allowlist.js @@ -4,7 +4,7 @@ const common = require('../common'); const assert = require('assert'); -// Assert that whitelisted internalBinding modules are accessible via +// Assert that allowed internalBinding modules are accessible via // process.binding(). assert(process.binding('async_wrap')); assert(process.binding('buffer')); diff --git a/test/parallel/test-process-exit-code.js b/test/parallel/test-process-exit-code.js index 2f658a172cdc5a..51d23c35c5665e 100644 --- a/test/parallel/test-process-exit-code.js +++ b/test/parallel/test-process-exit-code.js @@ -24,7 +24,8 @@ require('../common'); const assert = require('assert'); const debug = require('util').debuglog('test'); -const testCases = require('../fixtures/process-exit-code-cases'); +const { getTestCases } = require('../fixtures/process-exit-code-cases'); +const testCases = getTestCases(false); if (!process.argv[2]) { parent(); diff --git a/test/parallel/test-process-uid-gid.js b/test/parallel/test-process-uid-gid.js index 6ca2e009571ef0..0e170620b7f237 100644 --- a/test/parallel/test-process-uid-gid.js +++ b/test/parallel/test-process-uid-gid.js @@ -51,6 +51,13 @@ assert.throws(() => { message: 'User identifier does not exist: fhqwhgadshgnsdhjsdbkhsdabkfabkveyb' }); +// Passing -0 shouldn't crash the process +// Refs: https://github.com/nodejs/node/issues/32750 +try { process.setuid(-0); } catch {} +try { process.seteuid(-0); } catch {} +try { process.setgid(-0); } catch {} +try { process.setegid(-0); } catch {} + // If we're not running as super user... if (process.getuid() !== 0) { // Should not throw. @@ -79,6 +86,7 @@ try { } process.setgid('nogroup'); } + const newgid = process.getgid(); assert.notStrictEqual(newgid, oldgid); diff --git a/test/parallel/test-querystring.js b/test/parallel/test-querystring.js index 58554f0d85c438..7a001f6a029c42 100644 --- a/test/parallel/test-querystring.js +++ b/test/parallel/test-querystring.js @@ -273,6 +273,24 @@ qsWeirdObjects.forEach((testCase) => { assert.strictEqual(qs.stringify(testCase[0]), testCase[1]); }); +// BigInt values + +assert.strictEqual(qs.stringify({ foo: 2n ** 1023n }), + 'foo=' + 2n ** 1023n); +assert.strictEqual(qs.stringify([0n, 1n, 2n]), + '0=0&1=1&2=2'); + +assert.strictEqual(qs.stringify({ foo: 2n ** 1023n }, + null, + null, + { encodeURIComponent: (c) => c }), + 'foo=' + 2n ** 1023n); +assert.strictEqual(qs.stringify([0n, 1n, 2n], + null, + null, + { encodeURIComponent: (c) => c }), + '0=0&1=1&2=2'); + // Invalid surrogate pair throws URIError assert.throws( () => qs.stringify({ foo: '\udc00' }), diff --git a/test/parallel/test-readline-async-iterators.js b/test/parallel/test-readline-async-iterators.js index c3883e4f369fde..f803e17ea71a8a 100644 --- a/test/parallel/test-readline-async-iterators.js +++ b/test/parallel/test-readline-async-iterators.js @@ -39,7 +39,7 @@ async function testSimple() { expectedLines.pop(); } assert.deepStrictEqual(iteratedLines, expectedLines); - assert.strictEqual(iteratedLines.join(''), fileContent.replace(/\n/gm, '')); + assert.strictEqual(iteratedLines.join(''), fileContent.replace(/\n/g, '')); } } diff --git a/test/parallel/test-readline-interface.js b/test/parallel/test-readline-interface.js index e7247a65c04319..da76e54c125b59 100644 --- a/test/parallel/test-readline-interface.js +++ b/test/parallel/test-readline-interface.js @@ -63,6 +63,12 @@ function assertCursorRowsAndCols(rli, rows, cols) { assert.strictEqual(cursorPos.cols, cols); } +{ + const input = new FakeInput(); + const rl = readline.Interface({ input }); + assert(rl instanceof readline.Interface); +} + [ undefined, 50, @@ -657,6 +663,13 @@ function assertCursorRowsAndCols(rli, rows, cols) { rli.close(); } +// Close readline interface +{ + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('keypress', '.', { ctrl: true, name: 'c' }); + assert(rli.closed); +} + // Multi-line input cursor position { const [rli, fi] = getInterface({ terminal: true, prompt: '' }); @@ -898,6 +911,16 @@ for (let i = 0; i < 12; i++) { }); } + // Calling the getPrompt method + { + const expectedPrompts = ['$ ', '> ']; + const [rli] = getInterface({ terminal }); + for (const prompt of expectedPrompts) { + rli.setPrompt(prompt); + assert.strictEqual(rli.getPrompt(), prompt); + } + } + { const expected = terminal ? ['\u001b[1G', '\u001b[0J', '$ ', '\u001b[3G'] : @@ -920,7 +943,7 @@ for (let i = 0; i < 12; i++) { rl.prompt(); - assert.strictEqual(rl._prompt, '$ '); + assert.strictEqual(rl.getPrompt(), '$ '); } { diff --git a/test/parallel/test-repl-array-prototype-tempering.js b/test/parallel/test-repl-array-prototype-tempering.js new file mode 100644 index 00000000000000..907a6396ebeaef --- /dev/null +++ b/test/parallel/test-repl-array-prototype-tempering.js @@ -0,0 +1,66 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { spawn } = require('child_process'); + +const replProcess = spawn(process.argv0, ['--interactive'], { + stdio: ['pipe', 'pipe', 'inherit'], + windowsHide: true, +}); + +replProcess.on('error', common.mustNotCall()); + +const replReadyState = (async function* () { + let ready; + const SPACE = ' '.charCodeAt(); + const BRACKET = '>'.charCodeAt(); + const DOT = '.'.charCodeAt(); + replProcess.stdout.on('data', (data) => { + ready = data[data.length - 1] === SPACE && ( + data[data.length - 2] === BRACKET || ( + data[data.length - 2] === DOT && + data[data.length - 3] === DOT && + data[data.length - 4] === DOT + )); + }); + + const processCrashed = new Promise((resolve, reject) => + replProcess.on('exit', reject) + ); + while (true) { + await Promise.race([new Promise(setImmediate), processCrashed]); + if (ready) { + ready = false; + yield; + } + } +})(); +async function writeLn(data, expectedOutput) { + await replReadyState.next(); + if (expectedOutput) { + replProcess.stdout.once('data', common.mustCall((data) => + assert.match(data.toString('utf8'), expectedOutput) + )); + } + await new Promise((resolve, reject) => replProcess.stdin.write( + `${data}\n`, + (err) => (err ? reject(err) : resolve()) + )); +} + +async function main() { + await writeLn( + 'Object.defineProperty(Array.prototype, "-1", ' + + '{ get() { return this[this.length - 1]; } });' + ); + + await writeLn( + '[3, 2, 1][-1];', + /^1\n(>\s)?$/ + ); + await writeLn('.exit'); + + assert(!replProcess.connected); +} + +main().then(common.mustCall()); diff --git a/test/parallel/test-repl-clear-immediate-crash.js b/test/parallel/test-repl-clear-immediate-crash.js new file mode 100644 index 00000000000000..ce8aaf48e7fa0e --- /dev/null +++ b/test/parallel/test-repl-clear-immediate-crash.js @@ -0,0 +1,12 @@ +'use strict'; +const common = require('../common'); +const child_process = require('child_process'); +const assert = require('assert'); + +// Regression test for https://github.com/nodejs/node/issues/37806: +const proc = child_process.spawn(process.execPath, ['-i']); +proc.on('error', common.mustNotCall()); +proc.on('exit', common.mustCall((code) => { + assert.strictEqual(code, 0); +})); +proc.stdin.write('clearImmediate({});\n.exit\n'); diff --git a/test/parallel/test-repl-no-terminal.js b/test/parallel/test-repl-no-terminal.js new file mode 100644 index 00000000000000..60f97b52e26942 --- /dev/null +++ b/test/parallel/test-repl-no-terminal.js @@ -0,0 +1,7 @@ +'use strict'; +const common = require('../common'); + +const repl = require('repl'); +const r = repl.start({ terminal: false }); +r.setupHistory('/nonexistent/file', common.mustSucceed()); +process.stdin.unref?.(); diff --git a/test/parallel/test-repl-options.js b/test/parallel/test-repl-options.js index ca3cf2a3a4b3af..256d48a8db8f9f 100644 --- a/test/parallel/test-repl-options.js +++ b/test/parallel/test-repl-options.js @@ -120,7 +120,7 @@ assert.throws(r3, { // 4, Verify that defaults are used when no arguments are provided const r4 = repl.start(); -assert.strictEqual(r4._prompt, '> '); +assert.strictEqual(r4.getPrompt(), '> '); assert.strictEqual(r4.input, process.stdin); assert.strictEqual(r4.output, process.stdout); assert.strictEqual(r4.terminal, !!r4.output.isTTY); diff --git a/test/parallel/test-repl-pretty-stack-custom-writer.js b/test/parallel/test-repl-pretty-stack-custom-writer.js new file mode 100644 index 00000000000000..877f8cb8077597 --- /dev/null +++ b/test/parallel/test-repl-pretty-stack-custom-writer.js @@ -0,0 +1,23 @@ +'use strict'; +require('../common'); +const { PassThrough } = require('stream'); +const assert = require('assert'); +const repl = require('repl'); + +{ + const input = new PassThrough(); + const output = new PassThrough(); + + const r = repl.start({ + prompt: '', + input, + output, + writer: String, + terminal: false, + useColors: false + }); + + r.write('throw new Error("foo[a]")\n'); + r.close(); + assert.strictEqual(output.read().toString(), 'Uncaught Error: foo[a]\n'); +} diff --git a/test/parallel/test-repl-tab-complete.js b/test/parallel/test-repl-tab-complete.js index 6f5cbe2becd5f7..164af30f7a7a61 100644 --- a/test/parallel/test-repl-tab-complete.js +++ b/test/parallel/test-repl-tab-complete.js @@ -51,7 +51,12 @@ function getNoResultsFunction() { const works = [['inner.one'], 'inner.o']; const putIn = new ArrayStream(); -const testMe = repl.start('', putIn); +const testMe = repl.start({ + prompt: '', + input: putIn, + output: process.stdout, + allowBlockingCompletions: true +}); // Some errors are passed to the domain, but do not callback testMe._domain.on('error', assert.ifError); diff --git a/test/parallel/test-repl.js b/test/parallel/test-repl.js index fb7006fbc1db48..257b66299ddffc 100644 --- a/test/parallel/test-repl.js +++ b/test/parallel/test-repl.js @@ -926,7 +926,7 @@ function event(ee, expected) { const data = inspect(expected, { compact: false }); const msg = `The REPL did not reply as expected for:\n\n${data}`; reject(new Error(msg)); - }, common.platformTimeout(1000)); + }, common.platformTimeout(9999)); ee.once('data', common.mustCall((...args) => { clearTimeout(timeout); resolve(...args); diff --git a/test/parallel/test-source-map-api.js b/test/parallel/test-source-map-api.js index 60bbb661e1c801..3f6c16acd4717a 100644 --- a/test/parallel/test-source-map-api.js +++ b/test/parallel/test-source-map-api.js @@ -1,11 +1,26 @@ // Flags: --enable-source-maps 'use strict'; -require('../common'); +const common = require('../common'); const assert = require('assert'); const { findSourceMap, SourceMap } = require('module'); const { readFileSync } = require('fs'); +// It should throw with invalid args. +{ + [1, true, 'foo'].forEach((invalidArg) => + assert.throws( + () => new SourceMap(invalidArg), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "payload" argument must be of type object.' + + common.invalidArgTypeHelper(invalidArg) + } + ) + ); +} + // findSourceMap() can lookup source-maps based on URIs, in the // non-exceptional case. { diff --git a/test/parallel/test-source-map-enable.js b/test/parallel/test-source-map-enable.js index 71130441438dcc..0887ae8811c45b 100644 --- a/test/parallel/test-source-map-enable.js +++ b/test/parallel/test-source-map-enable.js @@ -8,6 +8,7 @@ const { dirname } = require('path'); const fs = require('fs'); const path = require('path'); const { spawnSync } = require('child_process'); +const { pathToFileURL } = require('url'); const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); @@ -88,8 +89,8 @@ function nextdir() { // Source-map should have been loaded from disk and sources should have been // rewritten, such that they're absolute paths. assert.strictEqual( - dirname( - `file://${require.resolve('../fixtures/source-map/disk-relative-path')}`), + dirname(pathToFileURL( + require.resolve('../fixtures/source-map/disk-relative-path')).href), dirname(sourceMap.data.sources[0]) ); } @@ -109,8 +110,8 @@ function nextdir() { // base64 JSON should have been decoded, and paths to sources should have // been rewritten such that they're absolute: assert.strictEqual( - dirname( - `file://${require.resolve('../fixtures/source-map/inline-base64')}`), + dirname(pathToFileURL( + require.resolve('../fixtures/source-map/inline-base64')).href), dirname(sourceMap.data.sources[0]) ); } @@ -265,6 +266,23 @@ function nextdir() { ); } +// Does not attempt to apply path resolution logic to absolute URLs +// with schemes. +// Refs: https://github.com/webpack/webpack/issues/9601 +// Refs: https://sourcemaps.info/spec.html#h.75yo6yoyk7x5 +{ + const output = spawnSync(process.execPath, [ + '--enable-source-maps', + require.resolve('../fixtures/source-map/webpack.js') + ]); + // Error in original context of source content: + assert.ok( + output.stderr.toString().match(/throw new Error\('oh no!'\)\r?\n.*\^/) + ); + // Rewritten stack trace: + assert.ok(output.stderr.toString().includes('webpack:///webpack.js:14:9')); +} + function getSourceMapFromCache(fixtureFile, coverageDirectory) { const jsonFiles = fs.readdirSync(coverageDirectory); for (const jsonFile of jsonFiles) { diff --git a/test/parallel/test-stream-buffer-list.js b/test/parallel/test-stream-buffer-list.js index 5b495a41e08545..d9d0405f4d5a45 100644 --- a/test/parallel/test-stream-buffer-list.js +++ b/test/parallel/test-stream-buffer-list.js @@ -48,3 +48,37 @@ const shifted = list.shift(); testIterator(list, 0); assert.strictEqual(shifted, buf); assert.deepStrictEqual(list, new BufferList()); + +{ + const list = new BufferList(); + list.push('foo'); + list.push('bar'); + list.push('foo'); + list.push('bar'); + assert.strictEqual(list.consume(6, true), 'foobar'); + assert.strictEqual(list.consume(6, true), 'foobar'); +} + +{ + const list = new BufferList(); + list.push('foo'); + list.push('bar'); + assert.strictEqual(list.consume(5, true), 'fooba'); +} + +{ + const list = new BufferList(); + list.push(buf); + list.push(buf); + list.push(buf); + list.push(buf); + assert.strictEqual(list.consume(6).toString(), 'foofoo'); + assert.strictEqual(list.consume(6).toString(), 'foofoo'); +} + +{ + const list = new BufferList(); + list.push(buf); + list.push(buf); + assert.strictEqual(list.consume(5).toString(), 'foofo'); +} diff --git a/test/parallel/test-stream-pipe-needDrain.js b/test/parallel/test-stream-pipe-needDrain.js new file mode 100644 index 00000000000000..0836c81da22438 --- /dev/null +++ b/test/parallel/test-stream-pipe-needDrain.js @@ -0,0 +1,32 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const Readable = require('_stream_readable'); +const Writable = require('_stream_writable'); + +// Pipe should pause temporarily if writable needs drain. +{ + const w = new Writable({ + write(buf, encoding, callback) { + process.nextTick(callback); + }, + highWaterMark: 1 + }); + + while (w.write('asd')); + + assert.strictEqual(w.writableNeedDrain, true); + + const r = new Readable({ + read() { + this.push('asd'); + this.push(null); + } + }); + + r.on('pause', common.mustCall(2)); + r.on('end', common.mustCall()); + + r.pipe(w); +} diff --git a/test/parallel/test-stream-pipeline.js b/test/parallel/test-stream-pipeline.js index 7cfdc4f4141571..302e99f22b4214 100644 --- a/test/parallel/test-stream-pipeline.js +++ b/test/parallel/test-stream-pipeline.js @@ -1232,3 +1232,24 @@ const net = require('net'); assert.strictEqual(res, '123'); })); } +{ + function createThenable() { + let counter = 0; + return { + get then() { + if (counter++) { + throw new Error('Cannot access `then` more than once'); + } + return Function.prototype; + }, + }; + } + + pipeline( + function* () { + yield 0; + }, + createThenable, + () => common.mustNotCall(), + ); +} diff --git a/test/parallel/test-timers-clear-object-does-not-throw-error.js b/test/parallel/test-timers-clear-object-does-not-throw-error.js new file mode 100644 index 00000000000000..9752f53abd0755 --- /dev/null +++ b/test/parallel/test-timers-clear-object-does-not-throw-error.js @@ -0,0 +1,8 @@ +'use strict'; +require('../common'); + +// This test makes sure clearing timers with +// objects doesn't throw +clearImmediate({}); +clearTimeout({}); +clearInterval({}); diff --git a/test/parallel/test-timers-promisified.js b/test/parallel/test-timers-promisified.js index 85e7093cfa185f..1b1e98d628543a 100644 --- a/test/parallel/test-timers-promisified.js +++ b/test/parallel/test-timers-promisified.js @@ -1,3 +1,4 @@ +// Flags: --no-warnings --experimental-abortcontroller 'use strict'; const common = require('../common'); const assert = require('assert'); @@ -9,6 +10,8 @@ const { promisify } = require('util'); const setTimeout = promisify(timers.setTimeout); const setImmediate = promisify(timers.setImmediate); +process.on('multipleResolves', common.mustNotCall()); + { const promise = setTimeout(1); promise.then(common.mustCall((value) => { @@ -36,3 +39,79 @@ const setImmediate = promisify(timers.setImmediate); assert.strictEqual(value, 'foobar'); })); } + +{ + const ac = new AbortController(); + const signal = ac.signal; + assert.rejects(setTimeout(10, undefined, { signal }), /AbortError/); + ac.abort(); +} + +{ + const ac = new AbortController(); + const signal = ac.signal; + ac.abort(); // Abort in advance + assert.rejects(setTimeout(10, undefined, { signal }), /AbortError/); +} + +{ + const ac = new AbortController(); + const signal = ac.signal; + assert.rejects(setImmediate(10, { signal }), /AbortError/); + ac.abort(); +} + +{ + const ac = new AbortController(); + const signal = ac.signal; + ac.abort(); // Abort in advance + assert.rejects(setImmediate(10, { signal }), /AbortError/); +} + +{ + // Check that aborting after resolve will not reject. + const ac = new AbortController(); + const signal = ac.signal; + setTimeout(10, undefined, { signal }).then(() => { + ac.abort(); + }); +} +{ + // Check that aborting after resolve will not reject. + const ac = new AbortController(); + const signal = ac.signal; + setImmediate(10, { signal }).then(() => { + ac.abort(); + }); +} + +{ + Promise.all( + [1, '', false, Infinity].map((i) => assert.rejects(setImmediate(10, i)), { + code: 'ERR_INVALID_ARG_TYPE' + })).then(common.mustCall()); + + Promise.all( + [1, '', false, Infinity, null, {}].map( + (signal) => assert.rejects(setImmediate(10, { signal })), { + code: 'ERR_INVALID_ARG_TYPE' + })).then(common.mustCall()); + + Promise.all( + [1, '', false, Infinity].map( + (i) => assert.rejects(setTimeout(10, null, i)), { + code: 'ERR_INVALID_ARG_TYPE' + })).then(common.mustCall()); + + Promise.all( + [1, '', false, Infinity, null, {}].map( + (signal) => assert.rejects(setTimeout(10, null, { signal })), { + code: 'ERR_INVALID_ARG_TYPE' + })).then(common.mustCall()); + + Promise.all( + [1, '', Infinity, null, {}].map( + (ref) => assert.rejects(setTimeout(10, null, { ref })), { + code: 'ERR_INVALID_ARG_TYPE' + })).then(common.mustCall()); +} diff --git a/test/parallel/test-url-format-whatwg.js b/test/parallel/test-url-format-whatwg.js index ab3d18bdc3aa25..9c86a7ae2c6910 100644 --- a/test/parallel/test-url-format-whatwg.js +++ b/test/parallel/test-url-format-whatwg.js @@ -6,7 +6,6 @@ if (!common.hasIntl) const assert = require('assert'); const url = require('url'); -const URL = url.URL; const myURL = new URL('http://xn--lck1c3crb1723bpq4a.com/a?a=b#c'); diff --git a/test/parallel/test-url-format.js b/test/parallel/test-url-format.js index 2d0132eb38a0bb..720a10a97a979b 100644 --- a/test/parallel/test-url-format.js +++ b/test/parallel/test-url-format.js @@ -148,6 +148,12 @@ const formatTests = { host: '[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616', pathname: '/s/stopButton' }, + 'http://[::]/': { + href: 'http://[::]/', + protocol: 'http:', + hostname: '[::]', + pathname: '/' + }, // Encode context-specific delimiters in path and query, but do not touch // other non-delimiter chars like `%`. diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 8bc14815be0803..ee67d2e79aec9a 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -210,6 +210,17 @@ assert(!/Object/.test( 'ArrayBuffer { (detached), byteLength: 0 }'); } +// Truncate output for ArrayBuffers using plural or singular bytes +{ + const ab = new ArrayBuffer(3); + assert.strictEqual(util.inspect(ab, { showHidden: true, maxArrayLength: 2 }), + 'ArrayBuffer { [Uint8Contents]' + + ': <00 00 ... 1 more byte>, byteLength: 3 }'); + assert.strictEqual(util.inspect(ab, { showHidden: true, maxArrayLength: 1 }), + 'ArrayBuffer { [Uint8Contents]' + + ': <00 ... 2 more bytes>, byteLength: 3 }'); +} + // Now do the same checks but from a different context. { const showHidden = false; @@ -2011,6 +2022,11 @@ assert.strictEqual(util.inspect('"\'${a}'), "'\"\\'${a}'"); rest[rest.length - 1] = rest[rest.length - 1].slice(0, -1); rest.length = 1; } + Object.setPrototypeOf(clazz, Map.prototype); + assert.strictEqual( + util.inspect(clazz), + ['[class', name, '[Map]', ...rest].join(' ') + ']' + ); Object.setPrototypeOf(clazz, null); assert.strictEqual( util.inspect(clazz), @@ -2910,6 +2926,12 @@ assert.strictEqual( assert.strictEqual(inspect(undetectable), '{}'); } +// Truncate output for Primitives with 1 character left +{ + assert.strictEqual(util.inspect('bl', { maxStringLength: 1 }), + "'b'... 1 more character"); +} + { const x = 'a'.repeat(1e6); assert.strictEqual( @@ -3001,3 +3023,81 @@ assert.strictEqual( // Consistency check. assert(fullObjectGraph(global).has(Function.prototype)); } + +{ + // Confirm that own constructor value displays correctly. + + function Fhqwhgads() {} + + const sterrance = new Fhqwhgads(); + sterrance.constructor = Fhqwhgads; + + assert.strictEqual( + util.inspect(sterrance, { showHidden: true }), + 'Fhqwhgads {\n' + + ' constructor: [Function: Fhqwhgads] {\n' + + ' [length]: 0,\n' + + " [name]: 'Fhqwhgads',\n" + + ' [prototype]: { [constructor]: [Circular *1] }\n' + + ' }\n' + + '}' + ); +} + +{ + // Confirm null prototype of generator prototype displays as expected. + + function getProtoOfProto() { + return Object.getPrototypeOf(Object.getPrototypeOf(function* () {})); + } + + function* generator() {} + + const generatorPrototype = Object.getPrototypeOf(generator); + const originalProtoOfProto = Object.getPrototypeOf(generatorPrototype); + assert.strictEqual(getProtoOfProto(), originalProtoOfProto); + Object.setPrototypeOf(generatorPrototype, null); + assert.notStrictEqual(getProtoOfProto, originalProtoOfProto); + + // This is the actual test. The other assertions in this block are about + // making sure the test is set up correctly and isn't polluting other tests. + assert.strictEqual( + util.inspect(generator, { showHidden: true }), + '[GeneratorFunction: generator] {\n' + + ' [length]: 0,\n' + + " [name]: 'generator',\n" + + " [prototype]: Object [Generator] { [Symbol(Symbol.toStringTag)]: 'Generator' },\n" + // eslint-disable-line max-len + " [Symbol(Symbol.toStringTag)]: 'GeneratorFunction'\n" + + '}' + ); + + // Reset so we don't pollute other tests + Object.setPrototypeOf(generatorPrototype, originalProtoOfProto); + assert.strictEqual(getProtoOfProto(), originalProtoOfProto); +} + +{ + // Test for when breakLength results in a single column. + const obj = Array(9).fill('fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf'); + assert.strictEqual( + util.inspect(obj, { breakLength: 256 }), + '[\n' + + " 'fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf',\n" + + " 'fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf',\n" + + " 'fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf',\n" + + " 'fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf',\n" + + " 'fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf',\n" + + " 'fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf',\n" + + " 'fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf',\n" + + " 'fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf',\n" + + " 'fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf'\n" + + ']' + ); +} + +{ + assert.strictEqual( + util.inspect({ ['__proto__']: { a: 1 } }), + "{ ['__proto__']: { a: 1 } }" + ); +} diff --git a/test/parallel/test-uv-errmap.js b/test/parallel/test-uv-errmap.js new file mode 100644 index 00000000000000..6a077551b63d77 --- /dev/null +++ b/test/parallel/test-uv-errmap.js @@ -0,0 +1,24 @@ +// Flags: --expose-internals +'use strict'; + +require('../common'); +const assert = require('assert'); +const { + getSystemErrorMap, + _errnoException +} = require('util'); + +const { internalBinding } = require('internal/test/binding'); +const uv = internalBinding('uv'); +const uvKeys = Object.keys(uv); + +const errMap = getSystemErrorMap(); + +uvKeys.forEach((key) => { + if (!key.startsWith('UV_')) + return; + + const err = _errnoException(uv[key]); + const name = uv.errname(uv[key]); + assert.strictEqual(errMap.get(err.errno)[0], name); +}); diff --git a/test/parallel/test-v8-serdes.js b/test/parallel/test-v8-serdes.js index 593fb34ad73594..f080f551396c8b 100644 --- a/test/parallel/test-v8-serdes.js +++ b/test/parallel/test-v8-serdes.js @@ -16,6 +16,7 @@ const objects = [ { bar: 'baz' }, new Uint8Array([1, 2, 3, 4]), new Uint32Array([1, 2, 3, 4]), + new DataView(new ArrayBuffer(42)), Buffer.from([1, 2, 3, 4]), undefined, null, @@ -25,11 +26,6 @@ const objects = [ const hostObject = new (internalBinding('js_stream').JSStream)(); -const serializerTypeError = - /^TypeError: Class constructor Serializer cannot be invoked without 'new'$/; -const deserializerTypeError = - /^TypeError: Class constructor Deserializer cannot be invoked without 'new'$/; - { const ser = new v8.DefaultSerializer(); ser.writeHeader(); @@ -186,8 +182,16 @@ const deserializerTypeError = } { - assert.throws(v8.Serializer, serializerTypeError); - assert.throws(v8.Deserializer, deserializerTypeError); + assert.throws(() => v8.Serializer(), { + constructor: TypeError, + message: "Class constructor Serializer cannot be invoked without 'new'", + code: 'ERR_CONSTRUCT_CALL_REQUIRED' + }); + assert.throws(() => v8.Deserializer(), { + constructor: TypeError, + message: "Class constructor Deserializer cannot be invoked without 'new'", + code: 'ERR_CONSTRUCT_CALL_REQUIRED' + }); } @@ -232,3 +236,10 @@ const deserializerTypeError = /^TypeError: buffer must be a TypedArray or a DataView$/, ); } + +{ + // Regression test for https://github.com/nodejs/node/issues/37978 + assert.throws(() => { + new v8.Deserializer(new v8.Serializer().releaseBuffer()).readDouble(); + }, /ReadDouble\(\) failed/); +} diff --git a/test/parallel/test-vm-module-link.js b/test/parallel/test-vm-module-link.js index 48f06439b6dacf..99235cf1b6c033 100644 --- a/test/parallel/test-vm-module-link.js +++ b/test/parallel/test-vm-module-link.js @@ -5,7 +5,6 @@ const common = require('../common'); const assert = require('assert'); -const { URL } = require('url'); const { SourceTextModule } = require('vm'); diff --git a/test/parallel/test-weakref.js b/test/parallel/test-weakref.js index 9dac8463760dac..ca7485aaa6a40c 100644 --- a/test/parallel/test-weakref.js +++ b/test/parallel/test-weakref.js @@ -1,6 +1,6 @@ 'use strict'; -// Flags: --expose-gc --harmony-weak-refs +// Flags: --expose-gc require('../common'); const assert = require('assert'); diff --git a/test/parallel/test-whatwg-url-constructor.js b/test/parallel/test-whatwg-url-constructor.js index d5b12a6021b772..3dc1c5986027e7 100644 --- a/test/parallel/test-whatwg-url-constructor.js +++ b/test/parallel/test-whatwg-url-constructor.js @@ -6,7 +6,6 @@ if (!common.hasIntl) { } const fixtures = require('../common/fixtures'); -const { URL, URLSearchParams } = require('url'); const { test, assert_equals, assert_true, assert_throws } = require('../common/wpt').harness; diff --git a/test/parallel/test-whatwg-url-custom-global.js b/test/parallel/test-whatwg-url-custom-global.js index c79723f0490e07..b99dfd8f3e7d94 100644 --- a/test/parallel/test-whatwg-url-custom-global.js +++ b/test/parallel/test-whatwg-url-custom-global.js @@ -4,7 +4,6 @@ require('../common'); const assert = require('assert'); -const { URL, URLSearchParams } = require('url'); assert.deepStrictEqual( Object.getOwnPropertyDescriptor(global, 'URL'), diff --git a/test/parallel/test-whatwg-url-custom-inspect.js b/test/parallel/test-whatwg-url-custom-inspect.js index 318b8b66d5672a..ad77f5725d30ed 100644 --- a/test/parallel/test-whatwg-url-custom-inspect.js +++ b/test/parallel/test-whatwg-url-custom-inspect.js @@ -9,7 +9,6 @@ if (!common.hasIntl) { } const util = require('util'); -const URL = require('url').URL; const assert = require('assert'); const url = new URL('https://username:password@host.name:8080/path/name/?que=ry#hash'); diff --git a/test/parallel/test-whatwg-url-custom-parsing.js b/test/parallel/test-whatwg-url-custom-parsing.js index 2799a9caef3efe..6d9a9a816eff83 100644 --- a/test/parallel/test-whatwg-url-custom-parsing.js +++ b/test/parallel/test-whatwg-url-custom-parsing.js @@ -8,7 +8,6 @@ if (!common.hasIntl) { common.skip('missing Intl'); } -const URL = require('url').URL; const assert = require('assert'); const fixtures = require('../common/fixtures'); diff --git a/test/parallel/test-whatwg-url-custom-properties.js b/test/parallel/test-whatwg-url-custom-properties.js index 23742a8e7493e2..0267c7fca41820 100644 --- a/test/parallel/test-whatwg-url-custom-properties.js +++ b/test/parallel/test-whatwg-url-custom-properties.js @@ -4,7 +4,6 @@ // Tests below are not from WPT. require('../common'); -const URL = require('url').URL; const assert = require('assert'); const urlToOptions = require('internal/url').urlToOptions; diff --git a/test/parallel/test-whatwg-url-custom-searchparams-append.js b/test/parallel/test-whatwg-url-custom-searchparams-append.js index cf93950ebd514c..5d2976a23cad53 100644 --- a/test/parallel/test-whatwg-url-custom-searchparams-append.js +++ b/test/parallel/test-whatwg-url-custom-searchparams-append.js @@ -4,7 +4,6 @@ require('../common'); const assert = require('assert'); -const URLSearchParams = require('url').URLSearchParams; { const params = new URLSearchParams(); diff --git a/test/parallel/test-whatwg-url-custom-searchparams-constructor.js b/test/parallel/test-whatwg-url-custom-searchparams-constructor.js index ab065814179d3f..878caed43ff0ab 100644 --- a/test/parallel/test-whatwg-url-custom-searchparams-constructor.js +++ b/test/parallel/test-whatwg-url-custom-searchparams-constructor.js @@ -4,7 +4,6 @@ require('../common'); const assert = require('assert'); -const URLSearchParams = require('url').URLSearchParams; function makeIterableFunc(array) { return Object.assign(() => {}, { diff --git a/test/parallel/test-whatwg-url-custom-searchparams-delete.js b/test/parallel/test-whatwg-url-custom-searchparams-delete.js index 5c3088b0363ff1..e84f10e3f93df8 100644 --- a/test/parallel/test-whatwg-url-custom-searchparams-delete.js +++ b/test/parallel/test-whatwg-url-custom-searchparams-delete.js @@ -4,7 +4,6 @@ require('../common'); const assert = require('assert'); -const { URL, URLSearchParams } = require('url'); { const params = new URLSearchParams(); diff --git a/test/parallel/test-whatwg-url-custom-searchparams-entries.js b/test/parallel/test-whatwg-url-custom-searchparams-entries.js index b70717ff2b78c1..6e5dabb1a768c4 100644 --- a/test/parallel/test-whatwg-url-custom-searchparams-entries.js +++ b/test/parallel/test-whatwg-url-custom-searchparams-entries.js @@ -2,7 +2,6 @@ require('../common'); const assert = require('assert'); -const URLSearchParams = require('url').URLSearchParams; // Tests below are not from WPT. const params = new URLSearchParams('a=b&c=d'); diff --git a/test/parallel/test-whatwg-url-custom-searchparams-foreach.js b/test/parallel/test-whatwg-url-custom-searchparams-foreach.js index b796cff9bc1b5c..0c035161dbea97 100644 --- a/test/parallel/test-whatwg-url-custom-searchparams-foreach.js +++ b/test/parallel/test-whatwg-url-custom-searchparams-foreach.js @@ -4,7 +4,6 @@ require('../common'); const assert = require('assert'); -const { URLSearchParams } = require('url'); { const params = new URLSearchParams(); diff --git a/test/parallel/test-whatwg-url-custom-searchparams-get.js b/test/parallel/test-whatwg-url-custom-searchparams-get.js index 1088fcc43d439a..4ce16805ceceb9 100644 --- a/test/parallel/test-whatwg-url-custom-searchparams-get.js +++ b/test/parallel/test-whatwg-url-custom-searchparams-get.js @@ -4,7 +4,6 @@ require('../common'); const assert = require('assert'); -const URLSearchParams = require('url').URLSearchParams; { const params = new URLSearchParams(); diff --git a/test/parallel/test-whatwg-url-custom-searchparams-getall.js b/test/parallel/test-whatwg-url-custom-searchparams-getall.js index 8d229a25979d6c..d3f271fcc5dc7b 100644 --- a/test/parallel/test-whatwg-url-custom-searchparams-getall.js +++ b/test/parallel/test-whatwg-url-custom-searchparams-getall.js @@ -4,7 +4,6 @@ require('../common'); const assert = require('assert'); -const URLSearchParams = require('url').URLSearchParams; { const params = new URLSearchParams(); diff --git a/test/parallel/test-whatwg-url-custom-searchparams-has.js b/test/parallel/test-whatwg-url-custom-searchparams-has.js index 4a76dda6d3dc48..1963e40057ef72 100644 --- a/test/parallel/test-whatwg-url-custom-searchparams-has.js +++ b/test/parallel/test-whatwg-url-custom-searchparams-has.js @@ -4,7 +4,6 @@ require('../common'); const assert = require('assert'); -const URLSearchParams = require('url').URLSearchParams; { const params = new URLSearchParams(); diff --git a/test/parallel/test-whatwg-url-custom-searchparams-inspect.js b/test/parallel/test-whatwg-url-custom-searchparams-inspect.js index 6cc22caea62436..c03890938d9cfe 100644 --- a/test/parallel/test-whatwg-url-custom-searchparams-inspect.js +++ b/test/parallel/test-whatwg-url-custom-searchparams-inspect.js @@ -5,7 +5,6 @@ require('../common'); const assert = require('assert'); const util = require('util'); -const URLSearchParams = require('url').URLSearchParams; const sp = new URLSearchParams('?a=a&b=b&b=c'); assert.strictEqual(util.inspect(sp), diff --git a/test/parallel/test-whatwg-url-custom-searchparams-keys.js b/test/parallel/test-whatwg-url-custom-searchparams-keys.js index 5a222c7428eac7..b65e71c9a24153 100644 --- a/test/parallel/test-whatwg-url-custom-searchparams-keys.js +++ b/test/parallel/test-whatwg-url-custom-searchparams-keys.js @@ -4,7 +4,6 @@ require('../common'); const assert = require('assert'); -const URLSearchParams = require('url').URLSearchParams; const params = new URLSearchParams('a=b&c=d'); const keys = params.keys(); diff --git a/test/parallel/test-whatwg-url-custom-searchparams-set.js b/test/parallel/test-whatwg-url-custom-searchparams-set.js index 39462bf4880065..106e94d6a249a9 100644 --- a/test/parallel/test-whatwg-url-custom-searchparams-set.js +++ b/test/parallel/test-whatwg-url-custom-searchparams-set.js @@ -4,7 +4,6 @@ require('../common'); const assert = require('assert'); -const URLSearchParams = require('url').URLSearchParams; { const params = new URLSearchParams(); diff --git a/test/parallel/test-whatwg-url-custom-searchparams-sort.js b/test/parallel/test-whatwg-url-custom-searchparams-sort.js index 49c3b065f957c6..e0b0c5c1ed12f6 100644 --- a/test/parallel/test-whatwg-url-custom-searchparams-sort.js +++ b/test/parallel/test-whatwg-url-custom-searchparams-sort.js @@ -3,7 +3,6 @@ // Tests below are not from WPT. require('../common'); -const { URL, URLSearchParams } = require('url'); const { test, assert_array_equals } = require('../common/wpt').harness; // TODO(joyeecheung): upstream this to WPT, if possible - even diff --git a/test/parallel/test-whatwg-url-custom-searchparams-stringifier.js b/test/parallel/test-whatwg-url-custom-searchparams-stringifier.js index 35307fa914975a..e46865e8b014eb 100644 --- a/test/parallel/test-whatwg-url-custom-searchparams-stringifier.js +++ b/test/parallel/test-whatwg-url-custom-searchparams-stringifier.js @@ -4,7 +4,6 @@ require('../common'); const assert = require('assert'); -const URLSearchParams = require('url').URLSearchParams; { const params = new URLSearchParams(); diff --git a/test/parallel/test-whatwg-url-custom-searchparams-values.js b/test/parallel/test-whatwg-url-custom-searchparams-values.js index eedad691fa351c..9c4bb05d0e587d 100644 --- a/test/parallel/test-whatwg-url-custom-searchparams-values.js +++ b/test/parallel/test-whatwg-url-custom-searchparams-values.js @@ -4,7 +4,6 @@ require('../common'); const assert = require('assert'); -const URLSearchParams = require('url').URLSearchParams; const params = new URLSearchParams('a=b&c=d'); const values = params.values(); diff --git a/test/parallel/test-whatwg-url-custom-searchparams.js b/test/parallel/test-whatwg-url-custom-searchparams.js index 39c8d87b6a60bf..272435b001a366 100644 --- a/test/parallel/test-whatwg-url-custom-searchparams.js +++ b/test/parallel/test-whatwg-url-custom-searchparams.js @@ -4,7 +4,6 @@ require('../common'); const assert = require('assert'); -const { URL, URLSearchParams } = require('url'); const fixtures = require('../common/fixtures'); const serialized = 'a=a&a=1&a=true&a=undefined&a=null&a=%EF%BF%BD' + diff --git a/test/parallel/test-whatwg-url-custom-setters.js b/test/parallel/test-whatwg-url-custom-setters.js index e10ebb9fe66968..b98bf5d8d3b393 100644 --- a/test/parallel/test-whatwg-url-custom-setters.js +++ b/test/parallel/test-whatwg-url-custom-setters.js @@ -9,7 +9,6 @@ if (!common.hasIntl) { } const assert = require('assert'); -const URL = require('url').URL; const { test, assert_equals } = require('../common/wpt').harness; const fixtures = require('../common/fixtures'); diff --git a/test/parallel/test-whatwg-url-custom-tostringtag.js b/test/parallel/test-whatwg-url-custom-tostringtag.js index 784a3ebc7728e5..54e5850a8f07b9 100644 --- a/test/parallel/test-whatwg-url-custom-tostringtag.js +++ b/test/parallel/test-whatwg-url-custom-tostringtag.js @@ -4,7 +4,6 @@ require('../common'); const assert = require('assert'); -const URL = require('url').URL; const toString = Object.prototype.toString; diff --git a/test/parallel/test-whatwg-url-origin.js b/test/parallel/test-whatwg-url-origin.js index 05bd1229177e15..532ff06bb1152f 100644 --- a/test/parallel/test-whatwg-url-origin.js +++ b/test/parallel/test-whatwg-url-origin.js @@ -6,7 +6,6 @@ if (!common.hasIntl) { } const fixtures = require('../common/fixtures'); -const URL = require('url').URL; const { test, assert_equals } = require('../common/wpt').harness; const request = { diff --git a/test/parallel/test-whatwg-url-override-hostname.js b/test/parallel/test-whatwg-url-override-hostname.js new file mode 100644 index 00000000000000..61e3412c6b7b53 --- /dev/null +++ b/test/parallel/test-whatwg-url-override-hostname.js @@ -0,0 +1,20 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +{ + const url = new (class extends URL { get hostname() { return 'bar.com'; } })('http://foo.com/'); + assert.strictEqual(url.href, 'http://foo.com/'); + assert.strictEqual(url.toString(), 'http://foo.com/'); + assert.strictEqual(url.toJSON(), 'http://foo.com/'); + assert.strictEqual(url.hash, ''); + assert.strictEqual(url.host, 'foo.com'); + assert.strictEqual(url.hostname, 'bar.com'); + assert.strictEqual(url.origin, 'http://foo.com'); + assert.strictEqual(url.password, ''); + assert.strictEqual(url.protocol, 'http:'); + assert.strictEqual(url.username, ''); + assert.strictEqual(url.search, ''); + assert.strictEqual(url.searchParams.toString(), ''); +} diff --git a/test/parallel/test-whatwg-url-setters.js b/test/parallel/test-whatwg-url-setters.js index 6634fdd4b844e5..8742ab8ed372de 100644 --- a/test/parallel/test-whatwg-url-setters.js +++ b/test/parallel/test-whatwg-url-setters.js @@ -6,7 +6,6 @@ if (!common.hasIntl) { common.skip('missing Intl'); } -const URL = require('url').URL; const { test, assert_equals } = require('../common/wpt').harness; const fixtures = require('../common/fixtures'); diff --git a/test/parallel/test-whatwg-url-toascii.js b/test/parallel/test-whatwg-url-toascii.js index c5f0f24d6031d1..e5180bfb344127 100644 --- a/test/parallel/test-whatwg-url-toascii.js +++ b/test/parallel/test-whatwg-url-toascii.js @@ -6,7 +6,6 @@ if (!common.hasIntl) { } const fixtures = require('../common/fixtures'); -const { URL } = require('url'); const { test, assert_equals, assert_throws } = require('../common/wpt').harness; const request = { diff --git a/test/parallel/test-worker-execargv-invalid.js b/test/parallel/test-worker-execargv-invalid.js index bbad565c4f0c5f..be8ab0b8c423b7 100644 --- a/test/parallel/test-worker-execargv-invalid.js +++ b/test/parallel/test-worker-execargv-invalid.js @@ -33,3 +33,17 @@ const { Worker } = require('worker_threads'); new Worker(__filename, { execArgv: ['--redirect-warnings'] }); }, expectedErr); } + +{ + const expectedErr = { + code: 'ERR_WORKER_INVALID_EXEC_ARGV', + name: 'Error' + }; + assert.throws(() => { + new Worker(__filename, { + env: { + NODE_OPTIONS: '--nonexistent-options' + } + }); + }, expectedErr); +} diff --git a/test/parallel/test-worker-exit-code.js b/test/parallel/test-worker-exit-code.js index 6107cd0316b250..db8986f9ef6c92 100644 --- a/test/parallel/test-worker-exit-code.js +++ b/test/parallel/test-worker-exit-code.js @@ -8,7 +8,8 @@ const assert = require('assert'); const worker = require('worker_threads'); const { Worker, parentPort } = worker; -const testCases = require('../fixtures/process-exit-code-cases'); +const { getTestCases } = require('../fixtures/process-exit-code-cases'); +const testCases = getTestCases(true); // Do not use isMainThread so that this test itself can be run inside a Worker. if (!process.env.HAS_STARTED_WORKER) { diff --git a/test/parallel/test-worker-message-port.js b/test/parallel/test-worker-message-port.js index 4f4863c45ed516..51618e4fab1850 100644 --- a/test/parallel/test-worker-message-port.js +++ b/test/parallel/test-worker-message-port.js @@ -16,7 +16,18 @@ const { MessageChannel, MessagePort } = require('worker_threads'); port2.close(common.mustCall()); })); } - +{ + // Test emitting non-message events on a port + const { port2 } = new MessageChannel(); + port2.addEventListener('foo', common.mustCall((received) => { + assert.strictEqual(received.type, 'foo'); + assert.strictEqual(received.detail, 'bar'); + })); + port2.on('foo', common.mustCall((received) => { + assert.strictEqual(received, 'bar'); + })); + port2.emit('foo', 'bar'); +} { const { port1, port2 } = new MessageChannel(); @@ -154,9 +165,7 @@ const { MessageChannel, MessagePort } = require('worker_threads'); assert.deepStrictEqual( Object.getOwnPropertyNames(MessagePort.prototype).sort(), [ - // TODO(addaleax): This should include onmessage (and eventually - // onmessageerror). - 'close', 'constructor', 'postMessage', 'ref', 'start', - 'unref' + 'close', 'constructor', 'onmessage', 'onmessageerror', 'postMessage', + 'ref', 'start', 'unref' ]); } diff --git a/test/parallel/test-worker-resource-limits.js b/test/parallel/test-worker-resource-limits.js index 0c168bbea1a488..ffda452f6c6335 100644 --- a/test/parallel/test-worker-resource-limits.js +++ b/test/parallel/test-worker-resource-limits.js @@ -35,9 +35,10 @@ if (!process.env.HAS_STARTED_WORKER) { assert.deepStrictEqual(resourceLimits, testResourceLimits); const array = []; while (true) { - // Leave 10 % wiggle room here. + // Leave 10% wiggle room here, and 20% on debug builds. + const wiggleRoom = common.buildType === 'Release' ? 1.1 : 1.2; const usedMB = v8.getHeapStatistics().used_heap_size / 1024 / 1024; - assert(usedMB < resourceLimits.maxOldGenerationSizeMb * 1.1); + assert(usedMB < resourceLimits.maxOldGenerationSizeMb * wiggleRoom); let seenSpaces = 0; for (const { space_name, space_size } of v8.getHeapSpaceStatistics()) { diff --git a/test/parallel/test-zlib-unused-weak.js b/test/parallel/test-zlib-unused-weak.js index e74384eccf05c7..2c1e2d729030dd 100644 --- a/test/parallel/test-zlib-unused-weak.js +++ b/test/parallel/test-zlib-unused-weak.js @@ -6,6 +6,7 @@ const zlib = require('zlib'); // Tests that native zlib handles start out their life as weak handles. +global.gc(); const before = process.memoryUsage().external; for (let i = 0; i < 100; ++i) zlib.createGzip(); diff --git a/test/pseudo-tty/test-tty-isatty.js b/test/pseudo-tty/test-tty-isatty.js index 3a7b2940311221..ad81a4c6eff92b 100644 --- a/test/pseudo-tty/test-tty-isatty.js +++ b/test/pseudo-tty/test-tty-isatty.js @@ -10,6 +10,7 @@ strictEqual(isatty(2), true, 'stderr reported to not be a tty, but it is'); strictEqual(isatty(-1), false, '-1 reported to be a tty, but it is not'); strictEqual(isatty(55555), false, '55555 reported to be a tty, but it is not'); +strictEqual(isatty(2 ** 31), false, '2^31 reported to be a tty, but it is not'); strictEqual(isatty(1.1), false, '1.1 reported to be a tty, but it is not'); strictEqual(isatty('1'), false, '\'1\' reported to be a tty, but it is not'); strictEqual(isatty({}), false, '{} reported to be a tty, but it is not'); diff --git a/test/pseudo-tty/testcfg.py b/test/pseudo-tty/testcfg.py index 8f09bef2e95599..ad9461f026366f 100644 --- a/test/pseudo-tty/testcfg.py +++ b/test/pseudo-tty/testcfg.py @@ -70,7 +70,7 @@ def IsFailureOutput(self, output): raw_lines = (output.stdout + output.stderr).split('\n') outlines = [ s.rstrip() for s in raw_lines if not self.IgnoreLine(s) ] if len(outlines) != len(patterns): - print("length differs.") + print(" length differs.") print("expect=%d" % len(patterns)) print("actual=%d" % len(outlines)) print("patterns:") @@ -82,7 +82,7 @@ def IsFailureOutput(self, output): return True for i in range(len(patterns)): if not re.match(patterns[i], outlines[i]): - print("match failed") + print(" match failed") print("line=%d" % i) print("expect=%s" % patterns[i]) print("actual=%s" % outlines[i]) diff --git a/test/pummel/test-crypto-dh-keys.js b/test/pummel/test-crypto-dh-keys.js index f8f990b5c5ee4a..37095135f85174 100644 --- a/test/pummel/test-crypto-dh-keys.js +++ b/test/pummel/test-crypto-dh-keys.js @@ -21,8 +21,14 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('node compiled without OpenSSL.'); +} + +if ((process.config.variables.arm_version === '6') || + (process.config.variables.arm_version === '7')) { + common.skip('Too slow for armv6 and armv7 bots'); +} const assert = require('assert'); const crypto = require('crypto'); diff --git a/test/pummel/test-fs-watch-non-recursive.js b/test/pummel/test-fs-watch-non-recursive.js index b9a6cd6dcfb1d0..49071a965f1737 100644 --- a/test/pummel/test-fs-watch-non-recursive.js +++ b/test/pummel/test-fs-watch-non-recursive.js @@ -21,6 +21,11 @@ 'use strict'; const common = require('../common'); + +if (common.isIBMi) { + common.skip('IBMi does not support fs.watch()'); +} + const path = require('path'); const fs = require('fs'); diff --git a/test/pummel/test-hash-seed.js b/test/pummel/test-hash-seed.js index 30edca32f6f852..d63754cb56ed02 100644 --- a/test/pummel/test-hash-seed.js +++ b/test/pummel/test-hash-seed.js @@ -2,6 +2,11 @@ // Check that spawn child doesn't create duplicated entries const common = require('../common'); + +if ((process.config.variables.arm_version === '6') || + (process.config.variables.arm_version === '7')) + common.skip('Too slow for armv6 and armv7 bots'); + const kRepetitions = 2; const assert = require('assert'); const fixtures = require('../common/fixtures'); diff --git a/test/pummel/test-heapdump-dns.js b/test/pummel/test-heapdump-dns.js index 6fe79f7dd4ec5a..fa9ec77561512e 100644 --- a/test/pummel/test-heapdump-dns.js +++ b/test/pummel/test-heapdump-dns.js @@ -10,7 +10,7 @@ dns.resolve('localhost', () => {}); validateSnapshotNodes('Node / ChannelWrap', [ { children: [ - { node_name: 'Node / node_ares_task_list', edge_name: 'task_list' }, + { node_name: 'Node / NodeAresTask::List', edge_name: 'task_list' }, // `Node / ChannelWrap` (C++) -> `ChannelWrap` (JS) { node_name: 'ChannelWrap', edge_name: 'wrapped' } ] diff --git a/test/pummel/test-net-timeout.js b/test/pummel/test-net-timeout.js index 5b9f2a01b3823e..f110254a50499d 100644 --- a/test/pummel/test-net-timeout.js +++ b/test/pummel/test-net-timeout.js @@ -45,7 +45,6 @@ const echo_server = net.createServer((socket) => { }); socket.on('data', (d) => { - console.log(d); socket.write(d); }); @@ -105,7 +104,4 @@ process.on('exit', () => { console.log(`diff = ${diff}`); assert.ok(timeout < diff); - - // Allow for 800 milliseconds more - assert.ok(diff < timeout + 800); }); diff --git a/test/pummel/test-vm-memleak.js b/test/pummel/test-vm-memleak.js index a433ab6565e147..4b97b3c5b57179 100644 --- a/test/pummel/test-vm-memleak.js +++ b/test/pummel/test-vm-memleak.js @@ -22,7 +22,12 @@ 'use strict'; // Flags: --max_old_space_size=32 -require('../common'); +const common = require('../common'); + +if (process.config.variables.asan) { + common.skip('ASAN messes with memory measurements'); +} + const assert = require('assert'); const vm = require('vm'); @@ -36,7 +41,7 @@ const interval = setInterval(function() { const rss = process.memoryUsage().rss; assert.ok(rss < 64 * 1024 * 1024, - `memory usage: ${Math.round(rss / (1024 * 1024))}Mb`); + `memory usage: ${rss} (${Math.round(rss / (1024 * 1024))} MB)`); // Stop after 5 seconds. if (Date.now() - start > 5 * 1000) { diff --git a/test/report/test-report-uv-handles.js b/test/report/test-report-uv-handles.js index 3a6a34a8573fe7..32140cf887f919 100644 --- a/test/report/test-report-uv-handles.js +++ b/test/report/test-report-uv-handles.js @@ -97,14 +97,14 @@ if (process.argv[2] === 'child') { // Test libuv handle key order { - const get_libuv = /"libuv":\s\[([\s\S]*?)\]/gm; - const get_handle_inner = /{([\s\S]*?),*?}/gm; + const get_libuv = /"libuv":\s\[([\s\S]*?)\]/g; + const get_handle_inner = /{([\s\S]*?),*?}/g; const libuv_handles_str = get_libuv.exec(stdout)[1]; const libuv_handles_array = libuv_handles_str.match(get_handle_inner); for (const i of libuv_handles_array) { // Exclude nested structure if (i.includes('type')) { - const handle_keys = i.match(/(".*"):/gm); + const handle_keys = i.match(/(".*"):/g); assert(handle_keys[0], 'type'); assert(handle_keys[1], 'is_active'); } @@ -128,6 +128,9 @@ if (process.argv[2] === 'child') { assert.strictEqual(handle.filename, expected_filename); assert(handle.is_referenced); }), + loop: common.mustCall(function loop_validator(handle) { + assert.strictEqual(typeof handle.loopIdleTimeSeconds, 'number'); + }), pipe: common.mustCallAtLeast(function pipe_validator(handle) { assert(handle.is_referenced); }), diff --git a/test/sequential/sequential.status b/test/sequential/sequential.status index fce8bd959f0326..a025cb0002655d 100644 --- a/test/sequential/sequential.status +++ b/test/sequential/sequential.status @@ -9,8 +9,6 @@ prefix sequential test-cpu-prof-dir-worker: PASS, FLAKY [$system==win32] -# https://github.com/nodejs/node/issues/22327 -test-http2-large-file: PASS, FLAKY # https://github.com/nodejs/node/issues/26401 test-worker-prof: PASS, FLAKY @@ -32,6 +30,6 @@ test-buffer-creation-regression: SKIP # https://github.com/nodejs/node/pull/30819 test-perf-hooks: SKIP -[$arch==arm] +[$arch==arm || $arch==arm64] # https://github.com/nodejs/node/issues/26401#issuecomment-613095719 test-worker-prof: PASS, FLAKY diff --git a/test/sequential/test-fs-watch.js b/test/sequential/test-fs-watch.js index 8c543a2a17290a..c5db585f3f4a59 100644 --- a/test/sequential/test-fs-watch.js +++ b/test/sequential/test-fs-watch.js @@ -42,6 +42,15 @@ const testDir = tmpdir.path; tmpdir.refresh(); +// Because macOS (and possibly other operating systems) can return a watcher +// before it is actually watching, we need to repeat the operation to avoid +// a race condition. +function repeat(fn) { + setImmediate(fn); + const interval = setInterval(fn, 5000); + return interval; +} + { const filepath = path.join(testDir, 'watch.txt'); @@ -54,12 +63,11 @@ tmpdir.refresh(); if (expectFilePath) { assert.strictEqual(filename, 'watch.txt'); } + clearInterval(interval); watcher.close(); })); - setImmediate(function() { - fs.writeFileSync(filepath, 'world'); - }); + const interval = repeat(() => { fs.writeFileSync(filepath, 'world'); }); } { @@ -76,12 +84,11 @@ tmpdir.refresh(); if (expectFilePath) { assert.strictEqual(filename, 'hasOwnProperty'); } + clearInterval(interval); watcher.close(); })); - setImmediate(function() { - fs.writeFileSync(filepathAbs, 'pardner'); - }); + const interval = repeat(() => { fs.writeFileSync(filepathAbs, 'pardner'); }); } { @@ -97,14 +104,15 @@ tmpdir.refresh(); } else { assert.strictEqual(filename, null); } + clearInterval(interval); watcher.close(); })); - setImmediate(function() { + const interval = repeat(() => { + fs.rmSync(filepath, { force: true }); const fd = fs.openSync(filepath, 'w'); fs.closeSync(fd); }); - } // https://github.com/joyent/node/issues/2293 - non-persistent watcher should diff --git a/test/sequential/test-inspector-port-zero.js b/test/sequential/test-inspector-port-zero.js index 59027b5e30769d..1683394a1dd4a3 100644 --- a/test/sequential/test-inspector-port-zero.js +++ b/test/sequential/test-inspector-port-zero.js @@ -4,7 +4,6 @@ const { mustCall, skipIfInspectorDisabled } = require('../common'); skipIfInspectorDisabled(); const assert = require('assert'); -const { URL } = require('url'); const { spawn } = require('child_process'); function test(arg, port = '') { diff --git a/test/parallel/test-vm-timeout-escape-promise-module-2.js b/test/sequential/test-vm-timeout-escape-promise-module-2.js similarity index 100% rename from test/parallel/test-vm-timeout-escape-promise-module-2.js rename to test/sequential/test-vm-timeout-escape-promise-module-2.js diff --git a/test/sequential/test-worker-eventlooputil.js b/test/sequential/test-worker-eventlooputil.js new file mode 100644 index 00000000000000..7e012cb2b02e7a --- /dev/null +++ b/test/sequential/test-worker-eventlooputil.js @@ -0,0 +1,114 @@ +'use strict'; + +const { mustCall, mustCallAtLeast } = require('../common'); + +const assert = require('assert'); +const { + Worker, + MessageChannel, + MessagePort, + parentPort, +} = require('worker_threads'); +const { eventLoopUtilization, now } = require('perf_hooks').performance; + +// Use argv to detect whether we're running as a Worker called by this test vs. +// this test also being called as a Worker. +if (process.argv[2] === 'iamalive') { + const iaElu = idleActive(eventLoopUtilization()); + // Checks that the worker bootstrap is running after the event loop started. + assert.ok(iaElu > 0, `${iaElu} <= 0`); + parentPort.once('message', mustCall((msg) => { + assert.ok(msg.metricsCh instanceof MessagePort); + msg.metricsCh.on('message', mustCallAtLeast(workerOnMetricsMsg, 1)); + })); + return; +} + +function workerOnMetricsMsg(msg) { + if (msg.cmd === 'close') { + return this.close(); + } + + if (msg.cmd === 'elu') { + return this.postMessage(eventLoopUtilization()); + } + + if (msg.cmd === 'spin') { + const elu = eventLoopUtilization(); + const t = now(); + while (now() - t < msg.dur); + return this.postMessage(eventLoopUtilization(elu)); + } +} + +let worker; +let metricsCh; +let mainElu; +let workerELU; + +(function r() { + // Force some idle time to accumulate before proceeding with test. + if (eventLoopUtilization().idle <= 0) + return setTimeout(mustCall(r), 5); + + mainElu = eventLoopUtilization(); + + worker = new Worker(__filename, { argv: [ 'iamalive' ] }); + metricsCh = new MessageChannel(); + worker.postMessage({ metricsCh: metricsCh.port1 }, [ metricsCh.port1 ]); + + workerELU = worker.performance.eventLoopUtilization; + metricsCh.port2.once('message', mustCall(checkWorkerIdle)); + metricsCh.port2.postMessage({ cmd: 'elu' }); + // Make sure it's still safe to call eventLoopUtilization() after the worker + // hass been closed. + worker.on('exit', mustCall(() => { + assert.deepStrictEqual(worker.performance.eventLoopUtilization(), + { idle: 0, active: 0, utilization: 0 }); + })); +})(); + +function checkWorkerIdle(wElu) { + const perfWorkerElu = workerELU(); + const tmpMainElu = eventLoopUtilization(mainElu); + + assert.ok(idleActive(wElu) > 0, `${idleActive(wElu)} <= 0`); + assert.ok(idleActive(workerELU(wElu)) > 0, + `${idleActive(workerELU(wElu))} <= 0`); + assert.ok(idleActive(perfWorkerElu) > idleActive(wElu), + `${idleActive(perfWorkerElu)} <= ${idleActive(wElu)}`); + assert.ok(idleActive(tmpMainElu) > idleActive(perfWorkerElu), + `${idleActive(tmpMainElu)} <= ${idleActive(perfWorkerElu)}`); + + wElu = workerELU(); + setTimeout(mustCall(() => { + wElu = workerELU(wElu); + // Some clocks fire early. Removing a few milliseconds to cover that. + assert.ok(idleActive(wElu) >= 45, `${idleActive(wElu)} < 45`); + // Cutting the idle time in half since it's possible that the call took a + // lot of resources to process? + assert.ok(wElu.idle >= 25, `${wElu.idle} < 25`); + + checkWorkerActive(); + }), 50); +} + +function checkWorkerActive() { + const w = workerELU(); + + metricsCh.port2.postMessage({ cmd: 'spin', dur: 50 }); + metricsCh.port2.once('message', (wElu) => { + const w2 = workerELU(w); + + assert.ok(w2.active >= 50, `${w2.active} < 50`); + assert.ok(wElu.active >= 50, `${wElu.active} < 50`); + assert.ok(idleActive(wElu) < idleActive(w2), + `${idleActive(wElu)} >= ${idleActive(w2)}`); + + metricsCh.port2.postMessage({ cmd: 'close' }); + }); +} + +function idleActive(elu) { + return elu.idle + elu.active; +} diff --git a/test/wasi/test-return-on-exit.js b/test/wasi/test-return-on-exit.js index 3f5d40c9ba7fb8..eef97996fbf744 100644 --- a/test/wasi/test-return-on-exit.js +++ b/test/wasi/test-return-on-exit.js @@ -21,7 +21,8 @@ const buffer = fs.readFileSync(modulePath); // Verify that if a WASI application throws an exception, Node rethrows it // properly. const wasi = new WASI({ returnOnExit: true }); - wasi.wasiImport.proc_exit = () => { throw new Error('test error'); }; + const patchedExit = () => { throw new Error('test error'); }; + wasi.wasiImport.proc_exit = patchedExit.bind(wasi.wasiImport); const importObject = { wasi_snapshot_preview1: wasi.wasiImport }; const { instance } = await WebAssembly.instantiate(buffer, importObject); diff --git a/test/wpt/status/dom/abort.json b/test/wpt/status/dom/abort.json new file mode 100644 index 00000000000000..0967ef424bce67 --- /dev/null +++ b/test/wpt/status/dom/abort.json @@ -0,0 +1 @@ +{} diff --git a/test/wpt/status/html/webappapis/atob.json b/test/wpt/status/html/webappapis/atob.json new file mode 100644 index 00000000000000..65deda7312d426 --- /dev/null +++ b/test/wpt/status/html/webappapis/atob.json @@ -0,0 +1,5 @@ +{ + "base64.any.js": { + "fail": "promise_test: Unhandled rejection with value: object \"Error: ENOENT: no such file or directory, open '/root/node/node/fetch/data-urls/resources/base64.json'\"" + } +} diff --git a/test/wpt/test-abort.js b/test/wpt/test-abort.js new file mode 100644 index 00000000000000..36946d721e9dc9 --- /dev/null +++ b/test/wpt/test-abort.js @@ -0,0 +1,9 @@ +'use strict'; +require('../common'); +const { WPTRunner } = require('../common/wpt'); + +const runner = new WPTRunner('dom/abort'); + +runner.setFlags(['--experimental-abortcontroller']); + +runner.runJsTests(); diff --git a/test/wpt/test-atob.js b/test/wpt/test-atob.js new file mode 100644 index 00000000000000..51fa27e36d3f2f --- /dev/null +++ b/test/wpt/test-atob.js @@ -0,0 +1,19 @@ +'use strict'; + +require('../common'); +const { WPTRunner } = require('../common/wpt'); + +const runner = new WPTRunner('html/webappapis/atob'); + +// Needed to access to DOMException. +runner.setFlags(['--expose-internals']); + +// Set a script that will be executed in the worker before running the tests. +runner.setInitScript(` + const { internalBinding } = require('internal/test/binding'); + const { atob, btoa } = require('buffer'); + const { DOMException } = internalBinding('messaging'); + global.DOMException = DOMException; +`); + +runner.runJsTests(); diff --git a/tools/actions/commit-queue.sh b/tools/actions/commit-queue.sh index 9a432237c18bda..e0dc01dd526048 100755 --- a/tools/actions/commit-queue.sh +++ b/tools/actions/commit-queue.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh set -xe @@ -14,19 +14,19 @@ API_URL=https://api.github.com COMMIT_QUEUE_LABEL='commit-queue' COMMIT_QUEUE_FAILED_LABEL='commit-queue-failed' -function issueUrl() { +issueUrl() { echo "$API_URL/repos/${OWNER}/${REPOSITORY}/issues/${1}" } -function labelsUrl() { +labelsUrl() { echo "$(issueUrl "${1}")/labels" } -function commentsUrl() { +commentsUrl() { echo "$(issueUrl "${1}")/comments" } -function gitHubCurl() { +gitHubCurl() { url=$1 method=$2 shift 2 @@ -67,6 +67,7 @@ for pr in "$@"; do if ! tail -n 10 output | grep '. Post "Landed in .*/pull/'"${pr}"; then gitHubCurl "$(labelsUrl "$pr")" POST --data '{"labels": ["'"${COMMIT_QUEUE_FAILED_LABEL}"'"]}' + # shellcheck disable=SC2154 cqurl="${GITHUB_SERVER_URL}/${OWNER}/${REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" jq -n --arg content "
Commit Queue failed
$(cat output)
$cqurl
" '{body: $content}' > output.json cat output.json diff --git a/tools/actions/start-ci.sh b/tools/actions/start-ci.sh index 8eb3dae3c5bdf0..72a04b6b321b1f 100755 --- a/tools/actions/start-ci.sh +++ b/tools/actions/start-ci.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh set -xe @@ -10,15 +10,15 @@ REQUEST_CI_LABEL='request-ci' REQUEST_CI_FAILED_LABEL='request-ci-failed' shift 3 -function issueUrl() { +issueUrl() { echo "$API_URL/repos/${OWNER}/${REPOSITORY}/issues/${1}" } -function labelsUrl() { +labelsUrl() { echo "$(issueUrl "${1}")/labels" } -function commentsUrl() { +commentsUrl() { echo "$(issueUrl "${1}")/comments" } @@ -33,7 +33,7 @@ for pr in "$@"; do ncu-ci run "$pr" >output 2>&1 || ci_started=no cat output - if [ "$ci_started" == "no" ]; then + if [ "$ci_started" = "no" ]; then # Do we need to reset? curl -sL --request PUT \ --url "$(labelsUrl "$pr")" \ diff --git a/tools/create_expfile.sh b/tools/create_expfile.sh index 1daed4b1984068..cac20da721b06a 100755 --- a/tools/create_expfile.sh +++ b/tools/create_expfile.sh @@ -37,15 +37,15 @@ echo "Searching $1 to write out expfile to $2" # This special sequence must be at the start of the exp file. -echo "#!." > $2.tmp +echo "#!." > "$2.tmp" # Pull the symbols from the .a files. -find $1 -name "*.a" | grep -v gtest \ +find "$1" -name "*.a" | grep -v gtest \ | xargs nm -Xany -BCpg \ | awk '{ if ((($2 == "T") || ($2 == "D") || ($2 == "B")) && (substr($3,1,1) != ".")) { print $3 } }' \ - | sort -u >> $2.tmp + | sort -u >> "$2.tmp" -mv -f $2.tmp $2 +mv -f "$2.tmp" "$2" diff --git a/tools/doc/allhtml.js b/tools/doc/allhtml.js index c038b1f57a9bb8..fad74e59d5cb2e 100644 --- a/tools/doc/allhtml.js +++ b/tools/doc/allhtml.js @@ -31,13 +31,13 @@ for (const link of toc.match(//g)) { const data = fs.readFileSync(source + '/' + href, 'utf8'); // Split the doc. - const match = /(<\/ul>\s*)?<\/div>\s*
/.exec(data); + const match = /(<\/ul>\s*)?<\/\w+>\s*<\w+ id="apicontent">/.exec(data); contents += data.slice(0, match.index) - .replace(/[\s\S]*?
\s*

.*?<\/h2>\s*(