Fix require/define detection (#2168)

Summary:
Release Notes: None

It is okay for a define call to happen within a require call so long as the define call has happened by the time the require call has returned (since all define calls should have the same effect and only happen once). Previously if we encountered a module required before it was defined (or defined in a nested partial evaluation scope), we would not record it in ModuleTracer logic.
Closes https://github.com/facebook/prepack/pull/2168

Differential Revision: D8688788

Pulled By: cblappert

fbshipit-source-id: 6696ea54ed9ae7a3d7e8a944c45e14feccf3139b
This commit is contained in:
Chris Blappert 2018-06-28 18:37:17 -07:00 committed by Facebook Github Bot
parent 9905f613d6
commit bb49439adf

View File

@ -107,14 +107,11 @@ export class ModuleTracer extends Tracer {
// If we don't delay unsupported requires, we simply want to record here
// when a module gets initialized, and then we return.
_callRequireAndRecord(moduleIdValue: number | string, performCall: () => Value) {
if (
(this.requireStack.length === 0 || this.requireStack[this.requireStack.length - 1] !== moduleIdValue) &&
this.modules.moduleIds.has(moduleIdValue)
) {
if (this.requireStack.length === 0 || this.requireStack[this.requireStack.length - 1] !== moduleIdValue) {
this.requireStack.push(moduleIdValue);
try {
let value = performCall();
this.modules.recordModuleInitialized(moduleIdValue, value);
if (this.modules.moduleIds.has(moduleIdValue)) this.modules.recordModuleInitialized(moduleIdValue, value);
return value;
} finally {
invariant(this.requireStack.pop() === moduleIdValue);
@ -337,26 +334,45 @@ export class ModuleTracer extends Tracer {
// Here, we handle calls of the form
// __d(factoryFunction, moduleId, dependencyArray)
if (this.evaluateForEffectsNesting !== 0)
this.modules.logger.logWarning(F, "Defining a module in nested partial evaluation is not supported.");
else {
let factoryFunction = argumentsList[0];
if (factoryFunction instanceof FunctionValue) {
let dependencies = this._tryExtractDependencies(argumentsList[2]);
if (dependencies !== undefined) this.modules.factoryFunctionDependencies.set(factoryFunction, dependencies);
else
this.modules.logger.logError(
argumentsList[2],
"Third argument to define function is present but not a concrete array."
);
let factoryFunction = argumentsList[0];
if (factoryFunction instanceof FunctionValue) {
let dependencies = this._tryExtractDependencies(argumentsList[2]);
if (dependencies !== undefined) {
let previousDependencies = this.modules.factoryFunctionDependencies.get(factoryFunction);
if (previousDependencies) {
// Verify that they are the same
let logError = () => {
let moduleId = argumentsList[1];
let moduleString =
moduleId instanceof StringValue || moduleId instanceof NumberValue ? moduleId.value : "unknown";
this.modules.logger.logError(
factoryFunction,
`Called define on the same module ${moduleString} twice with different dependencies each time.`
);
};
if (previousDependencies.length !== dependencies.length) {
logError();
} else {
let previousDependenciesSet = new Set(previousDependencies);
dependencies.forEach(dependency => {
if (!previousDependenciesSet.has(dependency)) logError();
});
}
} else {
this.modules.factoryFunctionDependencies.set(factoryFunction, dependencies);
}
} else
this.modules.logger.logError(factoryFunction, "First argument to define function is not a function value.");
let moduleId = argumentsList[1];
if (moduleId instanceof NumberValue || moduleId instanceof StringValue)
this.modules.moduleIds.add(moduleId.value);
else
this.modules.logger.logError(moduleId, "Second argument to define function is not a number or string value.");
}
this.modules.logger.logError(
argumentsList[2],
"Third argument to define function is present but not a concrete array."
);
} else
this.modules.logger.logError(factoryFunction, "First argument to define function is not a function value.");
let moduleId = argumentsList[1];
if (moduleId instanceof NumberValue || moduleId instanceof StringValue)
this.modules.moduleIds.add(moduleId.value);
else
this.modules.logger.logError(moduleId, "Second argument to define function is not a number or string value.");
}
return undefined;
}