美文网首页
NodeJS执行js文件流程

NodeJS执行js文件流程

作者: orgcheng | 来源:发表于2020-03-27 15:31 被阅读0次

    在终端执行node test.js来运行test.js文件,通过抛出的异常日志,来分析nodejs的执行流程

    D:\AliOS\HelloWorldTS>node test.js
    internal/modules/cjs/loader.js:800
        throw err;
        ^
    
    Error: Cannot find module 'D:\AliOS\HelloWorldTS\test.js'
    [90m    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:797:15)[
    [90m    at Function.Module._load (internal/modules/cjs/loader.js:690:27)[39m
    [90m    at Function.Module.runMain (internal/modules/cjs/loader.js:1047:10)[39m
    [90m    at internal/main/run_main_module.js:17:11[39m {
      code: [32m'MODULE_NOT_FOUND'[39m,
      requireStack: []
    }
    

    先看 internal/main/run_main_module.js

    const {
      prepareMainThreadExecution
    } = require('internal/bootstrap/pre_execution');
    
    prepareMainThreadExecution(true);
    
    require('internal/modules/cjs/loader').Module.runMain(process.argv[1]);
    

    最后一句,加载internal/modules/cjs/loader并运行Module.runMain方法,传递的参数就是test.js

    但是它首先加载了internal/bootstrap/pre_execution的prepareMainThreadExecution方法,并执行。

    接下来就看看internal/bootstrap/pre_execution

    function prepareMainThreadExecution(expandArgv1 = false) {
        ...
        initializeCJSLoader();
        ...
    }
    
    function initializeCJSLoader() {
      const CJSLoader = require('internal/modules/cjs/loader');
      CJSLoader.Module._initPaths();
      // TODO(joyeecheung): deprecate this in favor of a proper hook?
      CJSLoader.Module.runMain =
        require('internal/modules/run_main').executeUserEntryPoint;
    }
        
    module.exports = {
    
      ...
    
      prepareMainThreadExecut
    };
    

    到此我们知道,internal/modules/cjs/loader中的Module.runMain方法,是internal/modules/run_main模块的executeUserEntryPoint方法

    看下internal/modules/run_main

    function executeUserEntryPoint(main = process.argv[1]) {
      const resolvedMain = resolveMainPath(main);
      const useESMLoader = shouldUseESMLoader(resolvedMain);
      if (useESMLoader) {
        runMainESM(resolvedMain || main);
      } else {
        // Module._load is the monkey-patchable CJS module loader.
        Module._load(main, null, true); // 只需要知道,最终走这里就可以了
      }
    }
    
    module.exports = {
      executeUserEntryPoint
    };
    

    接下来就是执行internal/modules/cjs/loader中的Module._load方法

    Module._extensions = ObjectCreate(null);
    
    // Check the cache for the requested file.
    // 1. If a module already exists in the cache: return its exports object.
    // 2. If the module is native: call
    //    `NativeModule.prototype.compileForPublicLoader()` and return the exports.
    // 3. Otherwise, create a new module for the file and save it to the cache.
    //    Then have it load  the file contents before returning its exports
    //    object.
    Module._load = function(request, parent, isMain) {
      let relResolveCacheIdentifier;
      if (parent) {
        ... // 这里parent=null,不执行此处代码
      }
    
      // 开头报错就是这里抛出的
      const filename = Module._resolveFilename(request, parent, isMain);
    
      // 查找缓存
      const cachedModule = Module._cache[filename];
      if (cachedModule !== undefined) {
        updateChildren(parent, cachedModule, true);
        return cachedModule.exports;
      }
       
      // 加载Native模块
      const mod = loadNativeModule(filename, request, experimentalModules);
      if (mod && mod.canBeRequiredByUsers) return mod.exports;
    
      // Don't call updateChildren(), Module constructor already does.
      const module = new Module(filename, parent);
    
      if (isMain) {
        process.mainModule = module;
        module.id = '.';
      }
    
      Module._cache[filename] = module;
      if (parent !== undefined) {
        relativeResolveCache[relResolveCacheIdentifier] = filename;
      }
    
      let threw = true;
      try {
        // 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) {
          try {
            module.load(filename);
          } catch (err) {
            rekeySourceMap(Module._cache[filename], err);
            throw err; /* node-do-not-add-exception-line */
          }
        } else {
          module.load(filename);
        }
        threw = false;
      } finally {
        if (threw) {
          delete Module._cache[filename];
          if (parent !== undefined) {
            delete relativeResolveCache[relResolveCacheIdentifier];
          }
        }
      }
    
      return module.exports;
    };
    
    // Given a file name, pass it to the proper extension handler.
    Module.prototype.load = function(filename) {
      this.filename = filename;
      this.paths = Module._nodeModulePaths(path.dirname(filename));
    
      const extension = findLongestRegisteredExtension(filename);
      // allow .mjs to be overridden
      if (filename.endsWith('.mjs') && !Module._extensions['.mjs']) {
        throw new ERR_REQUIRE_ESM(filename);
      }
      // 这里是调用对应的扩展名方法
      Module._extensions[extension](this, filename);
      this.loaded = true;
      ...
    }  
        
    // Native extension for .js
    Module._extensions['.js'] = function(module, filename) {
      if (filename.endsWith('.js')) {
        const pkg = readPackageScope(filename);
        // Function require shouldn't be used in ES modules.
        if (pkg && pkg.data && pkg.data.type === 'module') {
          const parentPath = module.parent && module.parent.filename;
          const packageJsonPath = path.resolve(pkg.path, 'package.json');
          throw new ERR_REQUIRE_ESM(filename, parentPath, packageJsonPath);
        }
      }
      const content = fs.readFileSync(filename, 'utf8');
      module._compile(content, filename);
    };
        
    // Run the file contents in the correct scope or sandbox. Expose
    // the correct helper variables (require, module, exports) to
    // the file.
    // Returns exception, if any.
    Module.prototype._compile = function(content, filename) {
      ...
    
      maybeCacheSourceMap(filename, content, this);
      const compiledWrapper = wrapSafe(filename, content, this);
    
      ...
      
      const dirname = path.dirname(filename);
      const require = makeRequireFunction(this, redirects);
      let result;
      const exports = this.exports;
      const thisValue = exports;
      const module = this;
      ...
        result = compiledWrapper.call(thisValue, exports, require, module,
                                      filename, dirname);
      ...
      hasLoadedAnyUserCJSModule = true;
      
      return result;
    }; 
    
        
    function wrapSafe(filename, content, cjsModuleInstance) {
      if (patched) {
        ...//这里patched为false,表示没有修改过wrapper数组
      }
    
      let compiled;
      try {
        compiled = compileFunction(
          content,
          filename,
          0,
          0,
          undefined,
          false,
          undefined,
          [],
          [
            'exports',
            'require',
            'module',
            '__filename',
            '__dirname',
          ]
        );
      } catch (err) {
        if (experimentalModules && process.mainModule === cjsModuleInstance)
          enrichCJSError(err);
        throw err;
      }
      return compiled.function;
    }    
        
    const { compileFunction } = internalBinding('contextify');    
    

    internalBinding就是nodejs的C++代码层暴露到js层的方法,用来加载内置的C++模块

    接着看下internal/main/run_main_module方法是在哪里调用的,搜索文件名后发现在node.cc代码中使用到了

    // node.cc文件
    
    MaybeLocal<Value> StartMainThreadExecution(Environment* env) {
      // To allow people to extend Node in different ways, this hook allows
      // one to drop a file lib/_third_party_main.js into the build
      // directory which will be executed instead of Node's normal loading.
      if (NativeModuleEnv::Exists("_third_party_main")) {
        return StartExecution(env, "internal/main/run_third_party_main");
      }
    
      std::string first_argv;
      if (env->argv().size() > 1) {
        first_argv = env->argv()[1];
      }
      ...
      if (!first_argv.empty() && first_argv != "-") {
        return StartExecution(env, "internal/main/run_main_module");
      }
      ...
      return StartExecution(env, "internal/main/eval_stdin");
    }
    
    
    void LoadEnvironment(Environment* env) {
      CHECK(env->is_main_thread());
      // TODO(joyeecheung): Not all of the execution modes in
      // StartMainThreadExecution() make sense for embedders. Pick the
      // useful ones out, and allow embedders to customize the entry
      // point more directly without using _third_party_main.js
      USE(StartMainThreadExecution(env));
    }
    
    

    LoadEnvironment方法是在node_main_instance.cc方法中执行的

    int NodeMainInstance::Run() {
      Locker locker(isolate_);
      Isolate::Scope isolate_scope(isolate_);
      HandleScope handle_scope(isolate_);
    
      int exit_code = 0;
      // TODO 
      std::unique_ptr<Environment> env = CreateMainEnvironment(&exit_code);
    
      CHECK_NOT_NULL(env);
      Context::Scope context_scope(env->context());
    
      if (exit_code == 0) {
        {
          InternalCallbackScope callback_scope(
              env.get(),
              Local<Object>(),
              { 1, 0 },
              InternalCallbackScope::kAllowEmptyResource |
                  InternalCallbackScope::kSkipAsyncHooks);
          // TODO             
          LoadEnvironment(env.get());
        }
    
        env->set_trace_sync_io(env->options()->trace_sync_io);
    
        {
          SealHandleScope seal(isolate_);
          bool more;
          env->performance_state()->Mark(
              node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_START);
          // TODO chengzhen libuv库的试下Nodejs主循环
          do {
            uv_run(env->event_loop(), UV_RUN_DEFAULT);
    
            per_process::v8_platform.DrainVMTasks(isolate_);
    
            more = uv_loop_alive(env->event_loop());
            if (more && !env->is_stopping()) continue;
    
            if (!uv_loop_alive(env->event_loop())) {
              EmitBeforeExit(env.get());
            }
    
            // Emit `beforeExit` if the loop became alive either after emitting
            // event, or after running some callbacks.
            more = uv_loop_alive(env->event_loop());
          } while (more == true && !env->is_stopping());
          env->performance_state()->Mark(
              node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_EXIT);
        }
    
        env->set_trace_sync_io(false);
        exit_code = EmitExit(env.get());
      }
    
      env->set_can_call_into_js(false);
      env->stop_sub_worker_contexts();
      ResetStdio();
      env->RunCleanup();
    
      // TODO(addaleax): Neither NODE_SHARED_MODE nor HAVE_INSPECTOR really
      // make sense here.
    #if HAVE_INSPECTOR && defined(__POSIX__) && !defined(NODE_SHARED_MODE)
      struct sigaction act;
      memset(&act, 0, sizeof(act));
      for (unsigned nr = 1; nr < kMaxSignal; nr += 1) {
        if (nr == SIGKILL || nr == SIGSTOP || nr == SIGPROF)
          continue;
        act.sa_handler = (nr == SIGPIPE) ? SIG_IGN : SIG_DFL;
        CHECK_EQ(0, sigaction(nr, &act, nullptr));
      }
    #endif
    
      RunAtExit(env.get());
    
      per_process::v8_platform.DrainVMTasks(isolate_);
    
    #if defined(LEAK_SANITIZER)
      __lsan_do_leak_check();
    #endif
    
      return exit_code;
    }
    

    相关文章

      网友评论

          本文标题:NodeJS执行js文件流程

          本文链接:https://www.haomeiwen.com/subject/hkwluhtx.html