%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /home2/vacivi36/vittasync.vacivitta.com.br/vittasync/node/src/
Upload File :
Create Path :
Current File : //home2/vacivi36/vittasync.vacivitta.com.br/vittasync/node/src/node_modules.cc

#include "node_modules.h"
#include <cstdio>
#include "base_object-inl.h"
#include "node_errors.h"
#include "node_external_reference.h"
#include "node_url.h"
#include "permission/permission.h"
#include "permission/permission_base.h"
#include "util-inl.h"
#include "v8-fast-api-calls.h"
#include "v8-function-callback.h"
#include "v8-primitive.h"
#include "v8-value.h"
#include "v8.h"

#include "simdjson.h"

namespace node {
namespace modules {

using v8::Array;
using v8::Context;
using v8::FunctionCallbackInfo;
using v8::HandleScope;
using v8::Isolate;
using v8::Local;
using v8::NewStringType;
using v8::Object;
using v8::ObjectTemplate;
using v8::Primitive;
using v8::String;
using v8::Undefined;
using v8::Value;

#ifdef __POSIX__
constexpr char kPathSeparator = '/';
constexpr std::string_view kNodeModules = "/node_modules";
#else
constexpr char kPathSeparator = '\\';
constexpr std::string_view kNodeModules = "\\node_modules";
#endif

void BindingData::MemoryInfo(MemoryTracker* tracker) const {
  // Do nothing
}

BindingData::BindingData(Realm* realm,
                         v8::Local<v8::Object> object,
                         InternalFieldInfo* info)
    : SnapshotableObject(realm, object, type_int) {}

bool BindingData::PrepareForSerialization(v8::Local<v8::Context> context,
                                          v8::SnapshotCreator* creator) {
  // Return true because we need to maintain the reference to the binding from
  // JS land.
  return true;
}

InternalFieldInfoBase* BindingData::Serialize(int index) {
  DCHECK_IS_SNAPSHOT_SLOT(index);
  InternalFieldInfo* info =
      InternalFieldInfoBase::New<InternalFieldInfo>(type());
  return info;
}

void BindingData::Deserialize(v8::Local<v8::Context> context,
                              v8::Local<v8::Object> holder,
                              int index,
                              InternalFieldInfoBase* info) {
  DCHECK_IS_SNAPSHOT_SLOT(index);
  HandleScope scope(context->GetIsolate());
  Realm* realm = Realm::GetCurrent(context);
  BindingData* binding = realm->AddBindingData<BindingData>(holder);
  CHECK_NOT_NULL(binding);
}

Local<Array> BindingData::PackageConfig::Serialize(Realm* realm) const {
  auto has_manifest = !realm->env()->options()->experimental_policy.empty();
  auto isolate = realm->isolate();
  const auto ToString = [isolate](std::string_view input) -> Local<Primitive> {
    return String::NewFromUtf8(
               isolate, input.data(), NewStringType::kNormal, input.size())
        .ToLocalChecked();
  };
  Local<Value> values[7] = {
      name.has_value() ? ToString(*name) : Undefined(isolate),
      main.has_value() ? ToString(*main) : Undefined(isolate),
      ToString(type),
      imports.has_value() ? ToString(*imports) : Undefined(isolate),
      exports.has_value() ? ToString(*exports) : Undefined(isolate),
      has_manifest ? ToString(raw_json) : Undefined(isolate),
      ToString(file_path),
  };
  return Array::New(isolate, values, 7);
}

const BindingData::PackageConfig* BindingData::GetPackageJSON(
    Realm* realm, std::string_view path, ErrorContext* error_context) {
  auto binding_data = realm->GetBindingData<BindingData>();

  auto cache_entry = binding_data->package_configs_.find(path.data());
  if (cache_entry != binding_data->package_configs_.end()) {
    return &cache_entry->second;
  }

  PackageConfig package_config{};
  package_config.file_path = path;
  // No need to exclude BOM since simdjson will skip it.
  if (ReadFileSync(&package_config.raw_json, path.data()) < 0) {
    return nullptr;
  }

  simdjson::ondemand::document document;
  simdjson::ondemand::object main_object;
  simdjson::error_code error =
      binding_data->json_parser.iterate(package_config.raw_json).get(document);

  const auto throw_invalid_package_config = [error_context, path, realm]() {
    if (error_context == nullptr) {
      THROW_ERR_INVALID_PACKAGE_CONFIG(
          realm->isolate(), "Invalid package config %s.", path.data());
    } else if (error_context->base.has_value()) {
      auto file_url = ada::parse(error_context->base.value());
      CHECK(file_url);
      auto file_path = url::FileURLToPath(realm->env(), *file_url);
      CHECK(file_path.has_value());
      THROW_ERR_INVALID_PACKAGE_CONFIG(
          realm->isolate(),
          "Invalid package config %s while importing \"%s\" from %s.",
          path.data(),
          error_context->specifier.c_str(),
          file_path->c_str());
    } else {
      THROW_ERR_INVALID_PACKAGE_CONFIG(
          realm->isolate(), "Invalid package config %s.", path.data());
    }

    return nullptr;
  };

  if (error || document.get_object().get(main_object)) {
    return throw_invalid_package_config();
  }

  simdjson::ondemand::raw_json_string key;
  simdjson::ondemand::value value;
  std::string_view field_value;
  simdjson::ondemand::json_type field_type;

  for (auto field : main_object) {
    // Throw error if getting key or value fails.
    if (field.key().get(key) || field.value().get(value)) {
      return throw_invalid_package_config();
    }

    // based on coverity using key with == derefs the raw value
    // avoid derefing if its null
    if (key.raw() == nullptr) continue;

    if (key == "name") {
      // Though there is a key "name" with a corresponding value,
      // the value may not be a string or could be an invalid JSON string
      if (value.get_string(package_config.name)) {
        return throw_invalid_package_config();
      }
    } else if (key == "main") {
      // Omit all non-string values
      USE(value.get_string(package_config.main));
    } else if (key == "exports") {
      if (value.type().get(field_type)) {
        return throw_invalid_package_config();
      }
      switch (field_type) {
        case simdjson::ondemand::json_type::object:
        case simdjson::ondemand::json_type::array: {
          if (value.raw_json().get(field_value)) {
            return throw_invalid_package_config();
          }
          package_config.exports = field_value;
          break;
        }
        case simdjson::ondemand::json_type::string: {
          if (value.get_string(package_config.exports)) {
            return throw_invalid_package_config();
          }
          break;
        }
        default:
          break;
      }
    } else if (key == "imports") {
      if (value.type().get(field_type)) {
        return throw_invalid_package_config();
      }
      switch (field_type) {
        case simdjson::ondemand::json_type::array:
        case simdjson::ondemand::json_type::object: {
          if (value.raw_json().get(field_value)) {
            return throw_invalid_package_config();
          }
          package_config.imports = field_value;
          break;
        }
        case simdjson::ondemand::json_type::string: {
          if (value.get_string(package_config.imports)) {
            return throw_invalid_package_config();
          }
          break;
        }
        default:
          break;
      }
    } else if (key == "type") {
      if (value.get_string().get(field_value)) {
        return throw_invalid_package_config();
      }
      // Only update type if it is "commonjs" or "module"
      // The default value is "none" for backward compatibility.
      if (field_value == "commonjs" || field_value == "module") {
        package_config.type = field_value;
      }
    }
  }
  // package_config could be quite large, so we should move it instead of
  // copying it.
  auto cached = binding_data->package_configs_.insert(
      {std::string(path), std::move(package_config)});

  return &cached.first->second;
}

void BindingData::ReadPackageJSON(const FunctionCallbackInfo<Value>& args) {
  CHECK_GE(args.Length(), 1);  // path, [is_esm, base, specifier]
  CHECK(args[0]->IsString());  // path

  Realm* realm = Realm::GetCurrent(args);
  auto isolate = realm->isolate();

  Utf8Value path(isolate, args[0]);
  bool is_esm = args[1]->IsTrue();
  auto error_context = ErrorContext();
  if (is_esm) {
    CHECK(args[2]->IsUndefined() || args[2]->IsString());  // base
    CHECK(args[3]->IsString());                            // specifier

    if (args[2]->IsString()) {
      Utf8Value base_value(isolate, args[2]);
      error_context.base = base_value.ToString();
    }
    Utf8Value specifier(isolate, args[3]);
    error_context.specifier = specifier.ToString();
  }

  THROW_IF_INSUFFICIENT_PERMISSIONS(
      realm->env(),
      permission::PermissionScope::kFileSystemRead,
      path.ToStringView());

  auto package_json =
      GetPackageJSON(realm, path.ToString(), is_esm ? &error_context : nullptr);
  if (package_json == nullptr) {
    return;
  }

  args.GetReturnValue().Set(package_json->Serialize(realm));
}

const BindingData::PackageConfig* BindingData::TraverseParent(
    Realm* realm, std::string_view check_path) {
  auto env = realm->env();
  auto root_separator_index = check_path.find_first_of(kPathSeparator);
  size_t separator_index = 0;
  const bool is_permissions_enabled = env->permission()->enabled();

  do {
    separator_index = check_path.find_last_of(kPathSeparator);
    check_path = check_path.substr(0, separator_index);

    // We don't need to try "/"
    if (check_path.empty()) {
      break;
    }

    // Stop the search when the process doesn't have permissions
    // to walk upwards
    if (UNLIKELY(is_permissions_enabled &&
                 !env->permission()->is_granted(
                     permission::PermissionScope::kFileSystemRead,
                     std::string(check_path) + kPathSeparator))) {
      return nullptr;
    }

    // Check if the path ends with `/node_modules`
    if (check_path.size() >= kNodeModules.size() &&
        std::equal(check_path.end() - kNodeModules.size(),
                   check_path.end(),
                   kNodeModules.begin())) {
      return nullptr;
    }

    auto package_json = GetPackageJSON(
        realm,
        std::string(check_path) + kPathSeparator + "package.json",
        nullptr);
    if (package_json != nullptr) {
      return package_json;
    }
  } while (separator_index > root_separator_index);

  return nullptr;
}

void BindingData::GetNearestParentPackageJSON(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  CHECK_GE(args.Length(), 1);
  CHECK(args[0]->IsString());

  Realm* realm = Realm::GetCurrent(args);
  Utf8Value path_value(realm->isolate(), args[0]);
  auto package_json = TraverseParent(realm, path_value.ToStringView());

  if (package_json != nullptr) {
    args.GetReturnValue().Set(package_json->Serialize(realm));
  }
}

void BindingData::GetNearestParentPackageJSONType(
    const FunctionCallbackInfo<Value>& args) {
  CHECK_GE(args.Length(), 1);
  CHECK(args[0]->IsString());

  Realm* realm = Realm::GetCurrent(args);
  Utf8Value path(realm->isolate(), args[0]);
  auto package_json = TraverseParent(realm, path.ToStringView());

  if (package_json == nullptr) {
    return;
  }

  Local<Value> values[3] = {
      ToV8Value(realm->context(), package_json->type).ToLocalChecked(),
      ToV8Value(realm->context(), package_json->file_path).ToLocalChecked(),
      ToV8Value(realm->context(), package_json->raw_json).ToLocalChecked()};
  args.GetReturnValue().Set(Array::New(realm->isolate(), values, 3));
}

void BindingData::GetPackageScopeConfig(
    const FunctionCallbackInfo<Value>& args) {
  CHECK_GE(args.Length(), 1);
  CHECK(args[0]->IsString());

  Realm* realm = Realm::GetCurrent(args);
  Utf8Value resolved(realm->isolate(), args[0]);
  auto package_json_url_base = ada::parse(resolved.ToStringView());
  if (!package_json_url_base) {
    url::ThrowInvalidURL(realm->env(), resolved.ToStringView(), std::nullopt);
    return;
  }
  auto package_json_url =
      ada::parse("./package.json", &package_json_url_base.value());
  if (!package_json_url) {
    url::ThrowInvalidURL(realm->env(), "./package.json", resolved.ToString());
    return;
  }

  std::string_view node_modules_package_path = "/node_modules/package.json";
  auto error_context = ErrorContext();
  error_context.is_esm = true;

  // TODO(@anonrig): Rewrite this function and avoid calling URL parser.
  while (true) {
    auto pathname = package_json_url->get_pathname();
    if (pathname.size() >= node_modules_package_path.size() &&
        pathname.compare(pathname.size() - node_modules_package_path.size(),
                         node_modules_package_path.size(),
                         node_modules_package_path) == 0) {
      break;
    }

    auto file_url = url::FileURLToPath(realm->env(), *package_json_url);
    CHECK(file_url);
    error_context.specifier = resolved.ToString();
    auto package_json = GetPackageJSON(realm, *file_url, &error_context);
    if (package_json != nullptr) {
      return args.GetReturnValue().Set(package_json->Serialize(realm));
    }

    auto last_href = std::string(package_json_url->get_href());
    auto last_pathname = std::string(package_json_url->get_pathname());
    package_json_url = ada::parse("../package.json", &package_json_url.value());
    if (!package_json_url) {
      url::ThrowInvalidURL(realm->env(), "../package.json", last_href);
      return;
    }

    // Terminates at root where ../package.json equals ../../package.json
    // (can't just check "/package.json" for Windows support).
    if (package_json_url->get_pathname() == last_pathname) {
      break;
    }
  }

  auto package_json_url_as_path =
      url::FileURLToPath(realm->env(), *package_json_url);
  CHECK(package_json_url_as_path);
  return args.GetReturnValue().Set(
      String::NewFromUtf8(realm->isolate(),
                          package_json_url_as_path->c_str(),
                          NewStringType::kNormal,
                          package_json_url_as_path->size())
          .ToLocalChecked());
}

void BindingData::CreatePerIsolateProperties(IsolateData* isolate_data,
                                             Local<ObjectTemplate> target) {
  Isolate* isolate = isolate_data->isolate();
  SetMethod(isolate, target, "readPackageJSON", ReadPackageJSON);
  SetMethod(isolate,
            target,
            "getNearestParentPackageJSONType",
            GetNearestParentPackageJSONType);
  SetMethod(isolate,
            target,
            "getNearestParentPackageJSON",
            GetNearestParentPackageJSON);
  SetMethod(isolate, target, "getPackageScopeConfig", GetPackageScopeConfig);
}

void BindingData::CreatePerContextProperties(Local<Object> target,
                                             Local<Value> unused,
                                             Local<Context> context,
                                             void* priv) {
  Realm* realm = Realm::GetCurrent(context);
  realm->AddBindingData<BindingData>(target);
}

void BindingData::RegisterExternalReferences(
    ExternalReferenceRegistry* registry) {
  registry->Register(ReadPackageJSON);
  registry->Register(GetNearestParentPackageJSONType);
  registry->Register(GetNearestParentPackageJSON);
  registry->Register(GetPackageScopeConfig);
}

}  // namespace modules
}  // namespace node

NODE_BINDING_CONTEXT_AWARE_INTERNAL(
    modules, node::modules::BindingData::CreatePerContextProperties)
NODE_BINDING_PER_ISOLATE_INIT(
    modules, node::modules::BindingData::CreatePerIsolateProperties)
NODE_BINDING_EXTERNAL_REFERENCE(
    modules, node::modules::BindingData::RegisterExternalReferences)

Zerion Mini Shell 1.0