Modified: trunk/Source/_javascript_Core/API/JSContext.mm (242979 => 242980)
--- trunk/Source/_javascript_Core/API/JSContext.mm 2019-03-15 01:10:09 UTC (rev 242979)
+++ trunk/Source/_javascript_Core/API/JSContext.mm 2019-03-15 01:10:41 UTC (rev 242980)
@@ -124,7 +124,8 @@
if (script.type == kJSScriptTypeProgram) {
JSValueRef exceptionValue = nullptr;
- JSValueRef result = JSEvaluateScriptInternal(locker, exec, m_context, nullptr, [script jsSourceCode]->sourceCode(), &exceptionValue);
+ JSC::SourceCode sourceCode = [script sourceCode];
+ JSValueRef result = JSEvaluateScriptInternal(locker, exec, m_context, nullptr, sourceCode, &exceptionValue);
if (exceptionValue)
return [self valueFromNotifyException:exceptionValue];
Modified: trunk/Source/_javascript_Core/API/JSScript.mm (242979 => 242980)
--- trunk/Source/_javascript_Core/API/JSScript.mm 2019-03-15 01:10:09 UTC (rev 242979)
+++ trunk/Source/_javascript_Core/API/JSScript.mm 2019-03-15 01:10:41 UTC (rev 242980)
@@ -39,6 +39,7 @@
#import "Symbol.h"
#include <sys/stat.h>
#include <wtf/FileSystem.h>
+#include <wtf/Scope.h>
#if JSC_OBJC_API_ENABLED
@@ -50,8 +51,6 @@
RetainPtr<NSURL> m_sourceURL;
RetainPtr<NSURL> m_cachePath;
JSC::CachedBytecode m_cachedBytecode;
- JSC::Strong<JSC::JSSourceCode> m_jsSourceCode;
- int m_cacheFileDescriptor;
}
+ (instancetype)scriptWithSource:(NSString *)source inVirtualMachine:(JSVirtualMachine *)vm
@@ -175,9 +174,6 @@
if (m_cachedBytecode.size() && !m_cachedBytecode.owned())
munmap(const_cast<void*>(m_cachedBytecode.data()), m_cachedBytecode.size());
- if (m_cacheFileDescriptor != -1)
- close(m_cacheFileDescriptor);
-
[super dealloc];
}
@@ -186,27 +182,30 @@
if (!m_cachePath)
return;
- m_cacheFileDescriptor = open([m_cachePath path].UTF8String, O_CREAT | O_RDWR | O_EXLOCK | O_NONBLOCK, 0666);
- if (m_cacheFileDescriptor == -1)
+ int fd = open([m_cachePath path].UTF8String, O_RDONLY | O_EXLOCK | O_NONBLOCK, 0666);
+ if (fd == -1)
return;
+ auto closeFD = makeScopeExit([&] {
+ close(fd);
+ });
struct stat sb;
- int res = fstat(m_cacheFileDescriptor, &sb);
+ int res = fstat(fd, &sb);
size_t size = static_cast<size_t>(sb.st_size);
if (res || !size)
return;
- void* buffer = mmap(nullptr, size, PROT_READ, MAP_PRIVATE, m_cacheFileDescriptor, 0);
+ void* buffer = mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
JSC::CachedBytecode cachedBytecode { buffer, size };
JSC::VM& vm = m_virtualMachine.vm;
- const JSC::SourceCode& sourceCode = [self jsSourceCode]->sourceCode();
+ JSC::SourceCode sourceCode = [self sourceCode];
JSC::SourceCodeKey key = m_type == kJSScriptTypeProgram ? sourceCodeKeyForSerializedProgram(vm, sourceCode) : sourceCodeKeyForSerializedModule(vm, sourceCode);
if (isCachedBytecodeStillValid(vm, cachedBytecode, key, m_type == kJSScriptTypeProgram ? JSC::SourceCodeType::ProgramType : JSC::SourceCodeType::ModuleType))
m_cachedBytecode = WTFMove(cachedBytecode);
else
- ftruncate(m_cacheFileDescriptor, 0);
+ ftruncate(fd, 0);
}
- (BOOL)cacheBytecodeWithError:(NSError **)error
@@ -241,7 +240,6 @@
if (!self)
return nil;
- m_cacheFileDescriptor = -1;
return self;
}
@@ -260,14 +258,27 @@
return &m_cachedBytecode;
}
-- (JSC::JSSourceCode*)jsSourceCode
+- (JSC::SourceCode)sourceCode
{
- if (m_jsSourceCode)
- return m_jsSourceCode.get();
+ JSC::VM& vm = m_virtualMachine.vm;
+ JSC::JSLockHolder locker(vm);
- return [self forceRecreateJSSourceCode];
+ TextPosition startPosition { };
+ String url = "" { [[self sourceURL] absoluteString] };
+ auto type = m_type == kJSScriptTypeModule ? JSC::SourceProviderSourceType::Module : JSC::SourceProviderSourceType::Program;
+ Ref<JSScriptSourceProvider> sourceProvider = JSScriptSourceProvider::create(self, JSC::SourceOrigin(url), URL({ }, url), startPosition, type);
+ JSC::SourceCode sourceCode(WTFMove(sourceProvider), startPosition.m_line.oneBasedInt(), startPosition.m_column.oneBasedInt());
+ return sourceCode;
}
+- (JSC::JSSourceCode*)jsSourceCode
+{
+ JSC::VM& vm = m_virtualMachine.vm;
+ JSC::JSLockHolder locker(vm);
+ JSC::JSSourceCode* jsSourceCode = JSC::JSSourceCode::create(vm, [self sourceCode]);
+ return jsSourceCode;
+}
+
- (BOOL)writeCache:(String&)error
{
if (m_cachedBytecode.size()) {
@@ -275,21 +286,28 @@
return NO;
}
- if (m_cacheFileDescriptor == -1) {
- if (!m_cachePath)
- error = "No cache was path provided during construction of this JSScript."_s;
- else
- error = "Could not lock the bytecode cache file. It's likely another VM or process is already using it."_s;
+ if (!m_cachePath) {
+ error = "No cache path was provided during construction of this JSScript."_s;
return NO;
}
+ int fd = open([m_cachePath path].UTF8String, O_CREAT | O_RDWR | O_EXLOCK | O_NONBLOCK, 0666);
+ if (fd == -1) {
+ error = makeString("Could not open or lock the bytecode cache file. It's likely another VM or process is already using it. Error: ", strerror(errno));
+ return NO;
+ }
+ auto closeFD = makeScopeExit([&] {
+ close(fd);
+ });
+
JSC::ParserError parserError;
+ JSC::SourceCode sourceCode = [self sourceCode];
switch (m_type) {
case kJSScriptTypeModule:
- m_cachedBytecode = JSC::generateModuleBytecode(m_virtualMachine.vm, [self jsSourceCode]->sourceCode(), parserError);
+ m_cachedBytecode = JSC::generateModuleBytecode(m_virtualMachine.vm, sourceCode, parserError);
break;
case kJSScriptTypeProgram:
- m_cachedBytecode = JSC::generateProgramBytecode(m_virtualMachine.vm, [self jsSourceCode]->sourceCode(), parserError);
+ m_cachedBytecode = JSC::generateProgramBytecode(m_virtualMachine.vm, sourceCode, parserError);
break;
}
@@ -299,7 +317,7 @@
return NO;
}
- ssize_t bytesWritten = write(m_cacheFileDescriptor, m_cachedBytecode.data(), m_cachedBytecode.size());
+ ssize_t bytesWritten = write(fd, m_cachedBytecode.data(), m_cachedBytecode.size());
if (bytesWritten == -1) {
error = makeString("Could not write cache file to disk: ", strerror(errno));
return NO;
@@ -306,7 +324,7 @@
}
if (static_cast<size_t>(bytesWritten) != m_cachedBytecode.size()) {
- ftruncate(m_cacheFileDescriptor, 0);
+ ftruncate(fd, 0);
error = makeString("Could not write the full cache file to disk. Only wrote ", String::number(bytesWritten), " of the expected ", String::number(m_cachedBytecode.size()), " bytes.");
return NO;
}
@@ -319,22 +337,6 @@
m_sourceURL = url;
}
-- (JSC::JSSourceCode*)forceRecreateJSSourceCode
-{
- JSC::VM& vm = m_virtualMachine.vm;
- JSC::JSLockHolder locker(vm);
-
- TextPosition startPosition { };
-
- String url = "" { [[self sourceURL] absoluteString] };
- auto type = m_type == kJSScriptTypeModule ? JSC::SourceProviderSourceType::Module : JSC::SourceProviderSourceType::Program;
- Ref<JSScriptSourceProvider> sourceProvider = JSScriptSourceProvider::create(self, JSC::SourceOrigin(url), URL({ }, url), startPosition, type);
- JSC::SourceCode sourceCode(WTFMove(sourceProvider), startPosition.m_line.oneBasedInt(), startPosition.m_column.oneBasedInt());
- JSC::JSSourceCode* jsSourceCode = JSC::JSSourceCode::create(vm, WTFMove(sourceCode));
- m_jsSourceCode.set(vm, jsSourceCode);
- return jsSourceCode;
-}
-
@end
#endif
Modified: trunk/Source/_javascript_Core/API/tests/testapi.mm (242979 => 242980)
--- trunk/Source/_javascript_Core/API/tests/testapi.mm 2019-03-15 01:10:09 UTC (rev 242979)
+++ trunk/Source/_javascript_Core/API/tests/testapi.mm 2019-03-15 01:10:41 UTC (rev 242980)
@@ -2159,31 +2159,6 @@
}
}
-static void testCacheFileIsExclusive()
-{
- NSURL* cachePath = tempFile(@"foo.program.cache");
-
- @autoreleasepool {
- NSString *source = @"function foo() { return 42; } foo();";
- NSURL* sourceURL = [NSURL URLWithString:@"my-path"];
- JSVirtualMachine *vm = [[JSVirtualMachine alloc] init];
-
- JSScript *script1 = [JSScript scriptOfType:kJSScriptTypeProgram withSource:source andSourceURL:sourceURL andBytecodeCache:cachePath inVirtualMachine:vm error:nil];
- RELEASE_ASSERT(script1);
- checkResult(@"Should be able to cache the first file", [script1 cacheBytecodeWithError:nil]);
-
- JSScript *script2 = [JSScript scriptOfType:kJSScriptTypeProgram withSource:source andSourceURL:sourceURL andBytecodeCache:cachePath inVirtualMachine:vm error:nil];
- RELEASE_ASSERT(script2);
- NSError* error = nil;
- checkResult(@"Should NOT be able to cache the second file", ![script2 cacheBytecodeWithError:&error]);
- checkResult(@"Should NOT be able to cache the second file has the correct error message", [[error description] containsString:@"Could not lock the bytecode cache file. It's likely another VM or process is already using it"]);
- }
-
- NSFileManager* fileManager = [NSFileManager defaultManager];
- BOOL removedAll = [fileManager removeItemAtURL:cachePath error:nil];
- checkResult(@"Successfully removed cache file", removedAll);
-}
-
static void testCacheFileFailsWhenItsAlreadyCached()
{
NSURL* cachePath = tempFile(@"foo.program.cache");
@@ -2220,6 +2195,48 @@
checkResult(@"Successfully removed cache file", removedAll);
}
+static void testCanCacheManyFilesWithTheSameVM()
+{
+ NSMutableArray *cachePaths = [[NSMutableArray alloc] init];
+ NSMutableArray *scripts = [[NSMutableArray alloc] init];
+
+ for (unsigned i = 0; i < 10000; ++i)
+ [cachePaths addObject:tempFile([NSString stringWithFormat:@"cache-%d.cache", i])];
+
+ JSVirtualMachine *vm = [[JSVirtualMachine alloc] init];
+ bool cachedAll = true;
+ for (NSURL *path : cachePaths) {
+ @autoreleasepool {
+ NSURL *sourceURL = [NSURL URLWithString:@"id"];
+ NSString *source = @"function foo() { return 42; } foo();";
+ JSScript *script = [JSScript scriptOfType:kJSScriptTypeProgram withSource:source andSourceURL:sourceURL andBytecodeCache:path inVirtualMachine:vm error:nil];
+ RELEASE_ASSERT(script);
+
+ [scripts addObject:script];
+ cachedAll &= [script cacheBytecodeWithError:nil];
+ }
+ }
+ checkResult(@"Cached all 10000 scripts", cachedAll);
+
+ JSContext *context = [[JSContext alloc] init];
+ bool all42 = true;
+ for (JSScript *script : scripts) {
+ @autoreleasepool {
+ JSValue *result = [context evaluateJSScript:script];
+ RELEASE_ASSERT(result);
+ all42 &= [result isNumber] && [result toInt32] == 42;
+ }
+ }
+ checkResult(@"All scripts returned 42", all42);
+
+ NSFileManager* fileManager = [NSFileManager defaultManager];
+ bool removedAll = true;
+ for (NSURL *path : cachePaths)
+ removedAll &= [fileManager removeItemAtURL:path error:nil];
+
+ checkResult(@"Removed all cache files", removedAll);
+}
+
@interface JSContextFileLoaderDelegate : JSContext <JSModuleLoaderDelegate>
+ (instancetype)newContext;
@@ -2427,8 +2444,8 @@
RUN(testBytecodeCacheWithSameCacheFileAndDifferentScript(false));
RUN(testBytecodeCacheWithSameCacheFileAndDifferentScript(true));
RUN(testProgramJSScriptException());
- RUN(testCacheFileIsExclusive());
RUN(testCacheFileFailsWhenItsAlreadyCached());
+ RUN(testCanCacheManyFilesWithTheSameVM());
RUN(testLoaderRejectsNilScriptURL());
RUN(testLoaderRejectsFailedFetch());