-
-
Notifications
You must be signed in to change notification settings - Fork 9.1k
Description
Feature request
What is the expected behavior?
If a tree falls in a forest and no one is around to hear it, does it make a sound?
The basic idea is to preserve live binding semantics only when necessary and/or it could be noticed.
Typical generated harmony export code looks like this:
__webpack_require__.d = function(exports, definition) {
for (var key in definition ){
if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
Object.defineProperty(exports, key, {
enumerable: true,
get: definition[key]
});
}
}
};
...,
123: ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ K: () => (/* binding */ setValue),
/* harmony export */ U: () => (/* binding */ value)
/* harmony export */ });
let value = 0;
function setValue(v) {
value = v;
}
})
It would be more performant if it was this:
...
Object.defineProperty(exports, key, {
enumerable: true,
value: definition[key] // CHANGED
});
...
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ K: setValue, // CHANGED
/* harmony export */ U: value // CHANGED
/* harmony export */ });
...
Now the imported code is just referencing a variable directly:
/* harmony import */ var _shared_1__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(123);
console.log(_shared_1__WEBPACK_IMPORTED_MODULE_0__/* .value */ .U); // U is a number, not a getter
The problem with this is the export setup is making a copy o
738B
f value
. setValue
doesn't update the exported copy so liveness is lost.
The indirection needs to be preserved for value
, but it isn't required for setValue
because there's conceptually no setSetValue
. In fact, the vast majority of all exports don't require the indirection because they are not modified in the exporting module.
Once liveness requirements are determined for each export, we now emit something like this:
__webpack_require__.d2 = function(exports, definition, liveDefinition) {
for (var key in definition || {}) {
if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
Object.defineProperty(exports, key, {
enumerable: true,
value: definition[key]
});
}
}
for (var key in liveDefinition || {}) {
if (__webpack_require__.o(liveDefinition, key) && !__webpack_require__.o(exports, key)) {
Object.defineProperty(exports, key, {
enumerable: true,
get: liveDefinition[key]
});
}
}
};
...,
123: ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
/* harmony export */ __webpack_require__.d2(__webpack_exports__, {
/* harmony export */ K: setValue,
/* harmony export */ }, {
/* harmony export */ U: () => (/* binding */ value)
/* harmony export */ });
let value = 0;
function setValue(v) {
value = v;
}
})
What is motivation or use case for adding/changing the behavior?
There is a performance improvement both in file size and runtime execution as an additional level of indirection (getter function call) is removed in most cases.
How should this be implemented in your opinion?
Hook assign
on the javascriptparser and look for assignments to exported variables. Any that have an assignment would retain the getter level of indirection. Also look for circular references (e.g. module A imports function1 from module B, module B imports function2 from module A, function1 and function2 call each other). These also require a bailout to retain the getter. Everything else can be a value copy because the export is never changed and immediate evaluation is not a problem.
Generated export declaration code in modules will also need to be moved to the bottom of the module scope (from the top of the scope where it is now) to prevent references to variables that haven't been declared yet. Previously the getter indirection had prevented immediate access to exported variables.
Are you willing to work on this yourself?
No
Metadata
Metadata
Assignees
Labels
Type
Projects
Status