aboutsummaryrefslogtreecommitdiff
path: root/src/javascript/jsc/bindings/ZigSourceProvider.cpp
blob: 63bf1dcfd84e9e69061bbc736b7de61486a15afb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#include "ZigSourceProvider.h"
#include "helpers.h"
#include <JavaScriptCore/BytecodeCacheError.h>
#include <JavaScriptCore/CodeCache.h>

#include <JavaScriptCore/Completion.h>
#include <sys/stat.h>
#include <wtf/FileSystem.h>
#include <wtf/Scope.h>
#include <wtf/text/StringHash.h>

namespace Zig {

using Base = JSC::SourceProvider;
using BytecodeCacheGenerator = JSC::BytecodeCacheGenerator;
using UnlinkedFunctionExecutable = JSC::UnlinkedFunctionExecutable;
using CachedBytecode = JSC::CachedBytecode;
using UnlinkedFunctionCodeBlock = JSC::UnlinkedFunctionCodeBlock;
using SourceCode = JSC::SourceCode;
using CodeSpecializationKind = JSC::CodeSpecializationKind;
using SourceOrigin = JSC::SourceOrigin;
using String = WTF::String;
using SourceProviderSourceType = JSC::SourceProviderSourceType;

Ref<SourceProvider> SourceProvider::create(ResolvedSource resolvedSource) {
  return adoptRef(*new SourceProvider(
    resolvedSource,
    JSC::SourceOrigin(WTF::URL::fileURLWithFileSystemPath(toString(resolvedSource.source_url))),
    toStringNotConst(resolvedSource.source_url), TextPosition(),
    JSC::SourceProviderSourceType::Module));
}

unsigned SourceProvider::getHash() {
  if (m_hash) { return m_hash; }

  m_hash = WTF::StringHash::hash(m_source.get());
  return m_hash;
}

void SourceProvider::updateCache(const UnlinkedFunctionExecutable *executable, const SourceCode &,
                                 CodeSpecializationKind kind,
                                 const UnlinkedFunctionCodeBlock *codeBlock) {
  if (!m_resolvedSource.bytecodecache_fd || !m_cachedBytecode) return;

  JSC::BytecodeCacheError error;
  RefPtr<JSC::CachedBytecode> cachedBytecode =
    JSC::encodeFunctionCodeBlock(executable->vm(), codeBlock, error);
  if (cachedBytecode && !error.isValid())
    m_cachedBytecode->addFunctionUpdate(executable, kind, *cachedBytecode);
}

void SourceProvider::cacheBytecode(const BytecodeCacheGenerator &generator) {
  if (!m_resolvedSource.bytecodecache_fd) return;

  if (!m_cachedBytecode) m_cachedBytecode = JSC::CachedBytecode::create();
  auto update = generator();
  if (update) m_cachedBytecode->addGlobalUpdate(*update);
}
void SourceProvider::commitCachedBytecode() {
  if (!m_resolvedSource.bytecodecache_fd || !m_cachedBytecode || !m_cachedBytecode->hasUpdates())
    return;

  auto clearBytecode = WTF::makeScopeExit([&] { m_cachedBytecode = nullptr; });
  const auto fd = m_resolvedSource.bytecodecache_fd;

  auto fileSize = FileSystem::fileSize(fd);
  if (!fileSize) return;

  size_t cacheFileSize;
  if (!WTF::convertSafely(*fileSize, cacheFileSize) || cacheFileSize != m_cachedBytecode->size()) {
    // The bytecode cache has already been updated
    return;
  }

  if (!FileSystem::truncateFile(fd, m_cachedBytecode->sizeForUpdate())) return;

  m_cachedBytecode->commitUpdates([&](off_t offset, const void *data, size_t size) {
    long long result = FileSystem::seekFile(fd, offset, FileSystem::FileSeekOrigin::Beginning);
    ASSERT_UNUSED(result, result != -1);
    size_t bytesWritten = static_cast<size_t>(FileSystem::writeToFile(fd, data, size));
    ASSERT_UNUSED(bytesWritten, bytesWritten == size);
  });
}

bool SourceProvider::isBytecodeCacheEnabled() const {
  return m_resolvedSource.bytecodecache_fd > 0;
}

void SourceProvider::readOrGenerateByteCodeCache(JSC::VM &vm, const JSC::SourceCode &sourceCode) {
  auto status = this->readCache(vm, sourceCode);
  switch (status) {
    case -1: {
      m_resolvedSource.bytecodecache_fd = 0;
      break;
    }
    case 0: {
      JSC::BytecodeCacheError err;
      m_cachedBytecode =
        JSC::generateModuleBytecode(vm, sourceCode, m_resolvedSource.bytecodecache_fd, err);

      if (err.isValid()) {
        m_resolvedSource.bytecodecache_fd = 0;
        m_cachedBytecode = JSC::CachedBytecode::create();
      }
    }
    // TODO: read the bytecode into a JSC::SourceCode object here
    case 1: {
    }
  }
}
int SourceProvider::readCache(JSC::VM &vm, const JSC::SourceCode &sourceCode) {
  if (m_resolvedSource.bytecodecache_fd == 0) return -1;
  if (!FileSystem::isHandleValid(m_resolvedSource.bytecodecache_fd)) return -1;
  const auto fd = m_resolvedSource.bytecodecache_fd;

  bool success;
  FileSystem::MappedFileData mappedFile(fd, FileSystem::MappedFileMode::Shared, success);
  if (!success) return -1;

  const uint8_t *fileData = reinterpret_cast<const uint8_t *>(mappedFile.data());
  unsigned fileTotalSize = mappedFile.size();
  if (fileTotalSize == 0) return 0;

  Ref<JSC::CachedBytecode> cachedBytecode = JSC::CachedBytecode::create(WTFMove(mappedFile));
  auto key = JSC::sourceCodeKeyForSerializedModule(vm, sourceCode);
  if (isCachedBytecodeStillValid(vm, cachedBytecode.copyRef(), key,
                                 JSC::SourceCodeType::ModuleType)) {
    m_cachedBytecode = WTFMove(cachedBytecode);
    return 1;
  } else {
    FileSystem::truncateFile(fd, 0);
    return 0;
  }
}

}; // namespace Zig