Update JSScript SPI based on feedback
authorsbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 22 Feb 2019 04:21:54 +0000 (04:21 +0000)
committersbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 22 Feb 2019 04:21:54 +0000 (04:21 +0000)
https://bugs.webkit.org/show_bug.cgi?id=194517

Reviewed by Keith Miller.

This patch updates the JSScript SPI in the following ways:
- JSScript can now represent both modules and programs. This is a property
of the script determined during creation.
- JSScript now takes a sourceURL during construction. For modules, this acts
as the module identifier.
- JSScript now has SPI for writing the cache out to disk. We don't do this
automatically.
- JSScript will load the bytecode cache on creation if it exists.
- We retrofit these new requirements on the prior JSScript SPI that
we're going to remove as soon as we can: https://bugs.webkit.org/show_bug.cgi?id=194909.
Previous SPI assumes all JSScripts are modules. Previous SPI also assigns
a sourceURL to the JSScript based on what the module loader decided the
identifier should be. We'll remove this once we remove the old SPI.

This patch also adds SPI to JSContext to evaluate a JSScript. For modules,
this is like returning the result of doing dynamic import. For programs,
this does normal program evaluation.

This patch also fixes a bug in generateBytecode/generateModuleBytecode where
we would try to cache the bytecode even if recursivelyGenerateUnlinkedCodeBlock
returned null. E.g, if the script had a syntax error.

When writing tests, I also discovered that someone previously broke
testapi. This patch also fixes those failures. They were broken when
we switched to using a testapiScripts directory to hold our test .js
scripts.

* API/JSAPIGlobalObject.h:
* API/JSAPIGlobalObject.mm:
(JSC::JSAPIGlobalObject::moduleLoaderResolve):
(JSC::JSAPIGlobalObject::moduleLoaderFetch):
(JSC::JSAPIGlobalObject::loadAndEvaluateJSScriptModule):
* API/JSBase.cpp:
(JSEvaluateScriptInternal):
(JSEvaluateScript):
* API/JSBaseInternal.h: Added.
* API/JSContext.mm:
(-[JSContext evaluateScript:withSourceURL:]):
(-[JSContext evaluateJSScript:]):
* API/JSContextPrivate.h:
* API/JSScript.h:
* API/JSScript.mm:
(+[JSScript scriptWithSource:inVirtualMachine:]):
(+[JSScript scriptFromASCIIFile:inVirtualMachine:withCodeSigning:andBytecodeCache:]):
(createError):
(+[JSScript scriptOfType:inVirtualMachine:withSourceURL:andSource:andBytecodeCache:error:]):
(+[JSScript scriptOfType:inVirtualMachine:memoryMappedFromASCIIFile:withSourceURL:andBytecodeCache:error:]):
(-[JSScript cacheBytecodeWithError:]):
(-[JSScript sourceURL]):
(-[JSScript type]):
(-[JSScript jsSourceCode]):
(-[JSScript writeCache:]):
(-[JSScript setSourceURL:]):
(-[JSScript forceRecreateJSSourceCode]):
(-[JSScript writeCache]): Deleted.
(-[JSScript jsSourceCode:]): Deleted.
* API/JSScriptInternal.h:
* API/tests/FunctionOverridesTest.cpp:
(testFunctionOverrides):
* API/tests/testapi.c:
(main):
* API/tests/testapi.mm:
(tempFile):
(testModuleBytecodeCache):
(testProgramBytecodeCache):
(testBytecodeCacheWithSyntaxError):
(testProgramJSScriptException):
(testLoadBasicFileLegacySPI):
(+[JSContextMemoryMappedLoaderDelegate newContext]):
(-[JSContextMemoryMappedLoaderDelegate context:fetchModuleForIdentifier:withResolveHandler:andRejectHandler:]):
(testLoadBasicFile):
(+[JSContextAugmentedLoaderDelegate newContext]):
(-[JSContextAugmentedLoaderDelegate context:fetchModuleForIdentifier:withResolveHandler:andRejectHandler:]):
(testJSScriptURL):
(testObjectiveCAPI):
(testBytecodeCache): Deleted.
* API/tests/testapiScripts/foo.js: Added.
* JavaScriptCore.xcodeproj/project.pbxproj:
* runtime/Completion.cpp:
(JSC::generateBytecode):
(JSC::generateModuleBytecode):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@241929 268f45cc-cd09-0410-ab3c-d52691b4dbfc

18 files changed:
Source/JavaScriptCore/API/JSAPIGlobalObject.h
Source/JavaScriptCore/API/JSAPIGlobalObject.mm
Source/JavaScriptCore/API/JSBase.cpp
Source/JavaScriptCore/API/JSBaseInternal.h [new file with mode: 0644]
Source/JavaScriptCore/API/JSContext.mm
Source/JavaScriptCore/API/JSContextPrivate.h
Source/JavaScriptCore/API/JSScript.h
Source/JavaScriptCore/API/JSScript.mm
Source/JavaScriptCore/API/JSScriptInternal.h
Source/JavaScriptCore/API/tests/FunctionOverridesTest.cpp
Source/JavaScriptCore/API/tests/testIncludes.m
Source/JavaScriptCore/API/tests/testapi.c
Source/JavaScriptCore/API/tests/testapi.mm
Source/JavaScriptCore/API/tests/testapiScripts/foo.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/config.h
Source/JavaScriptCore/runtime/Completion.cpp

index 5d5f12c..d9bbcac 100644 (file)
@@ -27,6 +27,8 @@
 
 #include "JSGlobalObject.h"
 
+OBJC_CLASS JSScript;
+
 namespace JSC {
 
 class JSAPIGlobalObject : public JSGlobalObject {
@@ -55,6 +57,8 @@ public:
     static JSInternalPromise* moduleLoaderFetch(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSValue, JSValue);
     static JSObject* moduleLoaderCreateImportMetaProperties(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSModuleRecord*, JSValue);
 
+    JSValue loadAndEvaluateJSScriptModule(const JSLockHolder&, JSScript *);
+
 private:
     JSAPIGlobalObject(VM& vm, Structure* structure)
         : Base(vm, structure, &s_globalObjectMethodTable)
index 1fe872f..e41f56f 100644 (file)
 #import "Error.h"
 #import "Exception.h"
 #import "JSContextInternal.h"
+#import "JSInternalPromise.h"
 #import "JSInternalPromiseDeferred.h"
 #import "JSNativeStdFunction.h"
+#import "JSPromiseDeferred.h"
 #import "JSScriptInternal.h"
 #import "JSSourceCode.h"
 #import "JSValueInternal.h"
@@ -43,7 +45,6 @@
 #import "JavaScriptCore.h"
 #import "ObjectConstructor.h"
 #import "SourceOrigin.h"
-
 #import <wtf/URL.h>
 
 namespace JSC {
@@ -75,12 +76,20 @@ Identifier JSAPIGlobalObject::moduleLoaderResolve(JSGlobalObject* globalObject,
     ASSERT(key.isString() || key.isSymbol());
     String name =  key.toWTFString(exec);
 
-    URL referrerURL(URL(), jsCast<JSString*>(referrer)->tryGetValue());
-    RELEASE_ASSERT(referrerURL.isValid());
-
-    URL url = URL(referrerURL, name);
-    if (url.isValid())
-        return Identifier::fromString(exec, url);
+    if (JSString* referrerString = jsDynamicCast<JSString*>(vm, referrer)) {
+        String value = referrerString->value(exec);
+        URL referrerURL({ }, value);
+        RETURN_IF_EXCEPTION(scope, { });
+        RELEASE_ASSERT(referrerURL.isValid());
+
+        URL url = URL(referrerURL, name);
+        if (url.isValid())
+            return Identifier::fromString(exec, url);
+    } else {
+        URL url = URL({ }, name);
+        if (url.isValid())
+            return Identifier::fromString(exec, url);
+    }
 
     throwVMError(exec, scope, "Could not form valid URL from identifier and base"_s);
     return { };
@@ -162,20 +171,43 @@ JSInternalPromise* JSAPIGlobalObject::moduleLoaderFetch(JSGlobalObject* globalOb
         return deferred->reject(exec, createError(exec, "No module loader provided."));
 
     auto deferredPromise = Strong<JSInternalPromiseDeferred>(vm, deferred);
-    auto strongKey = Strong<JSString>(vm, jsSecureCast<JSString*>(vm, key));
     auto* resolve = JSNativeStdFunction::create(vm, globalObject, 1, "resolve", [=] (ExecState* exec) {
         // This captures the globalObject but that's ok because our structure keeps it alive anyway.
+        VM& vm = exec->vm();
         JSContext *context = [JSContext contextWithJSGlobalContextRef:toGlobalRef(globalObject->globalExec())];
         id script = valueToObject(context, toRef(exec, exec->argument(0)));
 
         MarkedArgumentBuffer args;
-        if (UNLIKELY(![script isKindOfClass:[JSScript class]])) {
-            args.append(createTypeError(exec, "First argument of resolution callback is not a JSScript"));
+
+        auto rejectPromise = [&] (String message) {
+            args.append(createTypeError(exec, message));
             call(exec, deferredPromise->JSPromiseDeferred::reject(), args, "This should never be seen...");
             return encodedJSUndefined();
+        };
+
+        if (UNLIKELY(![script isKindOfClass:[JSScript class]]))
+            return rejectPromise("First argument of resolution callback is not a JSScript"_s);
+
+        JSScript* jsScript = static_cast<JSScript *>(script);
+
+        JSSourceCode* source = [jsScript jsSourceCode];
+        if (UNLIKELY([jsScript type] != kJSScriptTypeModule))
+            return rejectPromise("The JSScript that was provided did not have expected type of kJSScriptTypeModule."_s);
+
+        // FIXME: The SPI we're deprecating did not require sourceURL, so we just
+        // ignore this check for such use cases until we can remove that SPI. Once
+        // we do that, we can remove the null check for sourceURL:
+        // https://bugs.webkit.org/show_bug.cgi?id=194909
+        if (NSURL *sourceURL = [jsScript sourceURL]) {
+            String oldModuleKey { [sourceURL absoluteString] };
+            if (UNLIKELY(Identifier::fromString(&vm, oldModuleKey) != moduleKey))
+                return rejectPromise(makeString("The same JSScript was provided for two different identifiers, previously: ", oldModuleKey, " and now: ", moduleKey.string()));
+        } else {
+            [jsScript setSourceURL:[NSURL URLWithString:static_cast<NSString *>(moduleKey.string())]];
+            source = [jsScript forceRecreateJSSourceCode];
         }
 
-        args.append([static_cast<JSScript *>(script) jsSourceCode:moduleKey]);
+        args.append(source);
         call(exec, deferredPromise->JSPromiseDeferred::resolve(), args, "This should never be seen...");
         return encodedJSUndefined();
     });
@@ -210,6 +242,22 @@ JSObject* JSAPIGlobalObject::moduleLoaderCreateImportMetaProperties(JSGlobalObje
     return metaProperties;
 }
 
+JSValue JSAPIGlobalObject::loadAndEvaluateJSScriptModule(const JSLockHolder&, JSScript *script)
+{
+    ASSERT(script.type == kJSScriptTypeModule);
+    VM& vm = this->vm();
+    ExecState* exec = globalExec();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    Identifier key = Identifier::fromString(exec, String { [[script sourceURL] absoluteString] });
+    JSInternalPromise* promise = importModule(exec, key, jsUndefined(), jsUndefined());
+    RETURN_IF_EXCEPTION(scope, { });
+    auto result = JSPromiseDeferred::tryCreate(exec, this);
+    RETURN_IF_EXCEPTION(scope, { });
+    result->resolve(exec, promise);
+    return result->promise();
+}
+
 }
 
 #endif // JSC_OBJC_API_ENABLED
index 734b2ec..44ed498 100644 (file)
@@ -25,6 +25,7 @@
 
 #include "config.h"
 #include "JSBase.h"
+#include "JSBaseInternal.h"
 #include "JSBasePrivate.h"
 
 #include "APICast.h"
 
 using namespace JSC;
 
-JSValueRef JSEvaluateScript(JSContextRef ctx, JSStringRef script, JSObjectRef thisObject, JSStringRef sourceURL, int startingLineNumber, JSValueRef* exception)
+JSValueRef JSEvaluateScriptInternal(const JSLockHolder&, ExecState* exec, JSContextRef ctx, JSObjectRef thisObject, const SourceCode& source, JSValueRef* exception)
 {
-    if (!ctx) {
-        ASSERT_NOT_REACHED();
-        return 0;
-    }
-    ExecState* exec = toJS(ctx);
-    VM& vm = exec->vm();
-    JSLockHolder locker(vm);
+    UNUSED_PARAM(ctx);
 
     JSObject* jsThisObject = toJS(thisObject);
 
-    startingLineNumber = std::max(1, startingLineNumber);
-
     // evaluate sets "this" to the global object if it is NULL
+    VM& vm = exec->vm();
     JSGlobalObject* globalObject = vm.vmEntryGlobalObject(exec);
-    auto sourceURLString = sourceURL ? sourceURL->string() : String();
-    SourceCode source = makeSource(script->string(), SourceOrigin { sourceURLString }, URL({ }, sourceURLString), TextPosition(OrdinalNumber::fromOneBasedInt(startingLineNumber), OrdinalNumber()));
-
     NakedPtr<Exception> evaluationException;
     JSValue returnValue = profiledEvaluate(globalObject->globalExec(), ProfilingReason::API, source, jsThisObject, evaluationException);
 
@@ -80,7 +71,7 @@ JSValueRef JSEvaluateScript(JSContextRef ctx, JSStringRef script, JSObjectRef th
         // We could stash it in the inspector in case an inspector is ever opened.
         globalObject->inspectorController().reportAPIException(exec, evaluationException);
 #endif
-        return 0;
+        return nullptr;
     }
 
     if (returnValue)
@@ -90,6 +81,24 @@ JSValueRef JSEvaluateScript(JSContextRef ctx, JSStringRef script, JSObjectRef th
     return toRef(exec, jsUndefined());
 }
 
+JSValueRef JSEvaluateScript(JSContextRef ctx, JSStringRef script, JSObjectRef thisObject, JSStringRef sourceURL, int startingLineNumber, JSValueRef* exception)
+{
+    if (!ctx) {
+        ASSERT_NOT_REACHED();
+        return nullptr;
+    }
+    ExecState* exec = toJS(ctx);
+    VM& vm = exec->vm();
+    JSLockHolder locker(vm);
+
+    startingLineNumber = std::max(1, startingLineNumber);
+
+    auto sourceURLString = sourceURL ? sourceURL->string() : String();
+    SourceCode source = makeSource(script->string(), SourceOrigin { sourceURLString }, URL({ }, sourceURLString), TextPosition(OrdinalNumber::fromOneBasedInt(startingLineNumber), OrdinalNumber()));
+
+    return JSEvaluateScriptInternal(locker, exec, ctx, thisObject, source, exception);
+}
+
 bool JSCheckScriptSyntax(JSContextRef ctx, JSStringRef script, JSStringRef sourceURL, int startingLineNumber, JSValueRef* exception)
 {
     if (!ctx) {
diff --git a/Source/JavaScriptCore/API/JSBaseInternal.h b/Source/JavaScriptCore/API/JSBaseInternal.h
new file mode 100644 (file)
index 0000000..423fb30
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 Apple Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#pragma once
+
+#include <JavaScriptCore/JSBase.h>
+#include <JavaScriptCore/WebKitAvailability.h>
+
+namespace JSC {
+class JSLockHolder;
+class ExecState;
+}
+
+extern "C" JSValueRef JSEvaluateScriptInternal(const JSC::JSLockHolder&, JSC::ExecState*, JSContextRef, JSObjectRef thisObject, const JSC::SourceCode&, JSValueRef* exception);
index 518fb87..6608237 100644 (file)
@@ -27,6 +27,7 @@
 
 #import "APICast.h"
 #import "Completion.h"
+#import "JSBaseInternal.h"
 #import "JSCInlines.h"
 #import "JSContextInternal.h"
 #import "JSContextPrivate.h"
 
     if (exceptionValue)
         return [self valueFromNotifyException:exceptionValue];
-
     return [JSValue valueWithJSValueRef:result inContext:self];
 }
 
+- (JSValue *)evaluateJSScript:(JSScript *)script
+{
+    JSC::ExecState* exec = toJS(m_context);
+    JSC::VM& vm = exec->vm();
+    JSC::JSLockHolder locker(vm);
+
+    if (script.type == kJSScriptTypeProgram) {
+        JSValueRef exceptionValue = nullptr;
+        JSValueRef result = JSEvaluateScriptInternal(locker, exec, m_context, nullptr, [script jsSourceCode]->sourceCode(), &exceptionValue);
+
+        if (exceptionValue)
+            return [self valueFromNotifyException:exceptionValue];
+        return [JSValue valueWithJSValueRef:result inContext:self];
+    }
+
+    auto* globalObject = JSC::jsDynamicCast<JSC::JSAPIGlobalObject*>(vm, exec->lexicalGlobalObject());
+    if (!globalObject)
+        return [JSValue valueWithNewPromiseRejectedWithReason:[JSValue valueWithNewErrorFromMessage:@"Context does not support module loading" inContext:self] inContext:self];
+
+    auto scope = DECLARE_CATCH_SCOPE(vm);
+    JSC::JSValue result = globalObject->loadAndEvaluateJSScriptModule(locker, script);
+    if (scope.exception()) {
+        JSValueRef exceptionValue = toRef(exec, scope.exception()->value());
+        scope.clearException();
+        return [JSValue valueWithNewPromiseRejectedWithReason:[JSValue valueWithJSValueRef:exceptionValue inContext:self] inContext:self];
+    }
+    return [JSValue valueWithJSValueRef:toRef(vm, result) inContext:self];
+}
+
 - (void)setException:(JSValue *)value
 {
     JSC::ExecState* exec = toJS(m_context);
index d4e206b..3fb3d2b 100644 (file)
 /*! @abstract The delegate the context will use when trying to load a module. Note, this delegate will be ignored for contexts returned by UIWebView. */
 @property (nonatomic, weak) id <JSModuleLoaderDelegate> moduleLoaderDelegate JSC_API_AVAILABLE(macosx(JSC_MAC_TBA), ios(JSC_IOS_TBA));
 
+/*!
+ @method
+ @abstract Run a JSScript.
+ @param script the JSScript to evaluate.
+ @discussion If the provided JSScript was created with kJSScriptTypeProgram, the script will run synchronously and return the result of evaluation.
+
+ Otherwise, if the script was created with kJSScriptTypeModule, the module will be run asynchronously and will return a promise resolved when the module and any transitive dependencies are loaded. The module loader will treat the script as if it had been returned from a delegate call to moduleLoaderDelegate. This mirrors the JavaScript dynamic import operation.
+ */
+- (JSValue *)evaluateJSScript:(JSScript *)script;
+
 @end
 
 #endif
index 4aaa899..adc5646 100644 (file)
@@ -31,37 +31,84 @@ NS_ASSUME_NONNULL_BEGIN
 
 @class JSVirtualMachine;
 
+/*!
+ @enum JSScriptType
+ @abstract     A constant identifying the execution type of a JSScript.
+ @constant     kJSScriptTypeProgram  The type of a normal JavaScript program.
+ @constant     kJSScriptTypeModule   The type of a module JavaScript program.
+ */
+typedef NS_ENUM(NSInteger, JSScriptType) {
+    kJSScriptTypeProgram,
+    kJSScriptTypeModule,
+};
+
+
 JSC_CLASS_AVAILABLE(macosx(JSC_MAC_TBA), ios(JSC_IOS_TBA))
 @interface JSScript : NSObject
 
 /*!
+ This SPI is deprecated and should not be used. Use "scriptOfType:withSource:andSourceURL:andBytecodeCache:inVirtualMachine:error:" instead.
+ */
++ (nullable instancetype)scriptWithSource:(NSString *)source inVirtualMachine:(JSVirtualMachine *)vm JSC_API_DEPRECATED("Use +scriptOfType:withSource:andSourceURL:andBytecodeCache:inVirtualMachine:error: instead.", macosx(JSC_MAC_TBA, JSC_MAC_TBA), ios(JSC_IOS_TBA, JSC_IOS_TBA));
+
+/*!
+ This SPI is deprecated and should not be used. Use "scriptOfType:memoryMappedFromASCIIFile:withSourceURL:andBytecodeCache:inVirtualMachine:error:" instead.
+ */
++ (nullable instancetype)scriptFromASCIIFile:(NSURL *)filePath inVirtualMachine:(JSVirtualMachine *)vm withCodeSigning:(nullable NSURL *)codeSigningPath andBytecodeCache:(nullable NSURL *)cachePath JSC_API_DEPRECATED("Use +scriptOfType:memoryMappedFromASCIIFile:withSourceURL:andBytecodeCache:inVirtualMachine:error: instead.", macosx(JSC_MAC_TBA, JSC_MAC_TBA), ios(JSC_IOS_TBA, JSC_IOS_TBA));
+
+/*!
+ This API is deprecated and should not be used.
+ */
++ (nullable instancetype)scriptFromUTF8File:(NSURL *)filePath inVirtualMachine:(JSVirtualMachine *)vm withCodeSigning:(nullable NSURL *)codeSigningPath andBytecodeCache:(nullable NSURL *)cachePath JSC_API_DEPRECATED("Do not use this. Use +scriptOfType:memoryMappedFromASCIIFile:withSourceURL:andBytecodeCache:inVirtualMachine:error: or +scriptOfType:withSource:andSourceURL:andBytecodeCache:inVirtualMachine:error: instead", macosx(JSC_MAC_TBA, JSC_MAC_TBA), ios(JSC_IOS_TBA, JSC_IOS_TBA));
+
+/*!
  @method
  @abstract Create a JSScript for the specified virtual machine.
+ @param type The type of JavaScript source.
  @param source The source code to use when the script is evaluated by the JS vm.
+ @param sourceURL The source URL to associate with this script. For modules, this is the module identifier.
+ @param cachePath A URL containing the path where the VM should cache for future execution. On creation, we use this path to load the cached bytecode off disk. If the cached bytecode at this location is stale, you should delete that file before calling this constructor.
  @param vm The JSVirtualMachine the script can be evaluated in.
+ @param error A description of why the script could not be created if the result is nil.
  @result The new script.
+ @discussion The file at cachePath should not be externally modified for the lifecycle of vm.
  */
-+ (nullable instancetype)scriptWithSource:(NSString *)source inVirtualMachine:(JSVirtualMachine *)vm;
++ (nullable instancetype)scriptOfType:(JSScriptType)type withSource:(NSString *)source andSourceURL:(NSURL *)sourceURL andBytecodeCache:(nullable NSURL *)cachePath inVirtualMachine:(JSVirtualMachine *)vm error:(out NSError * _Nullable * _Nullable)error;
 
 /*!
  @method
  @abstract Create a JSScript for the specified virtual machine with a path to a codesigning and bytecode caching.
+ @param type The type of JavaScript source.
  @param filePath A URL containing the path to a JS source code file on disk.
+ @param sourceURL The source URL to associate with this script. For modules, this is the module identifier.
+ @param cachePath A URL containing the path where the VM should cache for future execution. On creation, we use this path to load the cached bytecode off disk. If the cached bytecode at this location is stale, you should delete that file before calling this constructor.
  @param vm The JSVirtualMachine the script can be evaluated in.
- @param codeSigningPath A URL containing the path to the codeSigning file for filePath on disk.
- @param cachePath A URL containing the path where the VM should cache for future execution.
+ @param error A description of why the script could not be created if the result is nil.
  @result The new script.
- @discussion the files at filePath, codeSigningPath, and cachePath should not be externally modified  for the lifecycle of vm. Note that codeSigningPath and cachePath are not used currently, but that will change in the near future.
+ @discussion The files at filePath and cachePath should not be externally modified for the lifecycle of vm. This method will file back the memory for the source.
 
  If the file at filePath is not ascii this method will return nil.
  */
-+ (nullable instancetype)scriptFromASCIIFile:(NSURL *)filePath inVirtualMachine:(JSVirtualMachine *)vm withCodeSigning:(nullable NSURL *)codeSigningPath andBytecodeCache:(nullable NSURL *)cachePath;
++ (nullable instancetype)scriptOfType:(JSScriptType)type memoryMappedFromASCIIFile:(NSURL *)filePath withSourceURL:(NSURL *)sourceURL andBytecodeCache:(nullable NSURL *)cachePath inVirtualMachine:(JSVirtualMachine *)vm error:(out NSError * _Nullable * _Nullable)error;
+
+/*!
+ @method
+ @abstract Cache the bytecode for this JSScript to disk at the path passed in during creation.
+ @param error A description of why the script could not be cached if the result is FALSE.
+ */
+- (BOOL)cacheBytecodeWithError:(out NSError * _Nullable * _Nullable)error;
 
+/*!
+ @method
+ @abstract Returns the JSScriptType of this JSScript.
+ */
+- (JSScriptType)type;
 
 /*!
- This is deprecated and is equivalent to scriptFromASCIIFile:inVirtualMachine:withCodeSigning:andBytecodeCache:.
+ @method
+ @abstract Returns the sourceURL of this JSScript.
  */
-+ (nullable instancetype)scriptFromUTF8File:(NSURL *)filePath inVirtualMachine:(JSVirtualMachine *)vm withCodeSigning:(nullable NSURL *)codeSigningPath andBytecodeCache:(nullable NSURL *)cachePath;
+- (NSURL *)sourceURL;
 
 @end
 
index 25fc1c1..434b02f 100644 (file)
 #import "ParserError.h"
 #import "Symbol.h"
 #include <sys/stat.h>
+#include <wtf/FileSystem.h>
 
 #if JSC_OBJC_API_ENABLED
 
 @implementation JSScript {
     __weak JSVirtualMachine* m_virtualMachine;
+    JSScriptType m_type;
+    FileSystem::MappedFileData m_mappedSource;
     String m_source;
+    RetainPtr<NSURL> m_sourceURL;
     RetainPtr<NSURL> m_cachePath;
     JSC::CachedBytecode m_cachedBytecode;
     JSC::Strong<JSC::JSSourceCode> m_jsSourceCode;
@@ -53,6 +57,7 @@
     JSScript *result = [[[JSScript alloc] init] autorelease];
     result->m_source = source;
     result->m_virtualMachine = vm;
+    result->m_type = kJSScriptTypeModule;
     return result;
 }
 
@@ -90,12 +95,13 @@ static bool fillBufferWithContentsOfFile(const String& fileName, Vector<LChar>&
 
 + (instancetype)scriptFromASCIIFile:(NSURL *)filePath inVirtualMachine:(JSVirtualMachine *)vm withCodeSigning:(NSURL *)codeSigningPath andBytecodeCache:(NSURL *)cachePath
 {
-    // FIXME: This should check codeSigning.
     UNUSED_PARAM(codeSigningPath);
+    UNUSED_PARAM(cachePath);
+
     URL filePathURL([filePath absoluteURL]);
     if (!filePathURL.isLocalFile())
         return nil;
-    // FIXME: This should mmap the contents of the file instead of copying it into dirty memory.
+
     Vector<LChar> buffer;
     if (!fillBufferWithContentsOfFile(filePathURL.fileSystemPath(), buffer))
         return nil;
@@ -106,8 +112,7 @@ static bool fillBufferWithContentsOfFile(const String& fileName, Vector<LChar>&
     JSScript *result = [[[JSScript alloc] init] autorelease];
     result->m_virtualMachine = vm;
     result->m_source = String::fromUTF8WithLatin1Fallback(buffer.data(), buffer.size());
-    result->m_cachePath = cachePath;
-    [result readCache];
+    result->m_type = kJSScriptTypeModule;
     return result;
 }
 
@@ -116,6 +121,53 @@ static bool fillBufferWithContentsOfFile(const String& fileName, Vector<LChar>&
     return [JSScript scriptFromASCIIFile:filePath inVirtualMachine:vm withCodeSigning:codeSigningPath andBytecodeCache:cachePath];
 }
 
+static JSScript *createError(NSString *message, NSError** error)
+{
+    if (error)
+        *error = [NSError errorWithDomain:@"JSScriptErrorDomain" code:1 userInfo:@{ @"message": message }];
+    return nil;
+}
+
++ (instancetype)scriptOfType:(JSScriptType)type withSource:(NSString *)source andSourceURL:(NSURL *)sourceURL andBytecodeCache:(NSURL *)cachePath inVirtualMachine:(JSVirtualMachine *)vm error:(out NSError **)error
+{
+    UNUSED_PARAM(error);
+    JSScript *result = [[[JSScript alloc] init] autorelease];
+    result->m_virtualMachine = vm;
+    result->m_type = type;
+    result->m_source = source;
+    result->m_sourceURL = sourceURL;
+    result->m_cachePath = cachePath;
+    [result readCache];
+    return result;
+}
+
++ (instancetype)scriptOfType:(JSScriptType)type memoryMappedFromASCIIFile:(NSURL *)filePath withSourceURL:(NSURL *)sourceURL andBytecodeCache:(NSURL *)cachePath inVirtualMachine:(JSVirtualMachine *)vm error:(out NSError **)error
+{
+    UNUSED_PARAM(error);
+    URL filePathURL([filePath absoluteURL]);
+    if (!filePathURL.isLocalFile())
+        return createError([NSString stringWithFormat:@"File path %@ is not a local file", static_cast<NSString *>(filePathURL)], error);
+
+    bool success = false;
+    String systemPath = filePathURL.fileSystemPath();
+    FileSystem::MappedFileData fileData(systemPath, success);
+    if (!success)
+        return createError([NSString stringWithFormat:@"File at path %@ could not be mapped.", static_cast<NSString *>(systemPath)], error);
+
+    if (!charactersAreAllASCII(reinterpret_cast<const LChar*>(fileData.data()), fileData.size()))
+        return createError([NSString stringWithFormat:@"Not all characters in file at %@ are ASCII.", static_cast<NSString *>(systemPath)], error);
+
+    JSScript *result = [[[JSScript alloc] init] autorelease];
+    result->m_virtualMachine = vm;
+    result->m_type = type;
+    result->m_source = String(StringImpl::createWithoutCopying(bitwise_cast<const LChar*>(fileData.data()), fileData.size()));
+    result->m_mappedSource = WTFMove(fileData);
+    result->m_sourceURL = sourceURL;
+    result->m_cachePath = cachePath;
+    [result readCache];
+    return result;
+}
+
 - (void)dealloc
 {
     if (m_cachedBytecode.size() && !m_cachedBytecode.owned())
@@ -152,22 +204,26 @@ static bool fillBufferWithContentsOfFile(const String& fileName, Vector<LChar>&
     m_cachedBytecode = JSC::CachedBytecode { buffer, size };
 }
 
-- (void)writeCache
+- (BOOL)cacheBytecodeWithError:(NSError **)error
 {
-    if (m_cachedBytecode.size() || !m_cachePath)
-        return;
+    String errorString { };
+    [self writeCache:errorString];
+    if (!errorString.isNull()) {
+        createError(errorString, error);
+        return NO;
+    }
 
-    JSC::ParserError error;
-    m_cachedBytecode = JSC::generateModuleBytecode(m_virtualMachine.vm, m_jsSourceCode->sourceCode(), error);
-    if (error.isValid())
-        return;
-    int fd = open([m_cachePath path].UTF8String, O_CREAT | O_WRONLY, 0666);
-    if (fd == -1)
-        return;
-    int rc = flock(fd, LOCK_EX | LOCK_NB);
-    if (!rc)
-        write(fd, m_cachedBytecode.data(), m_cachedBytecode.size());
-    close(fd);
+    return YES;
+}
+
+- (NSURL *)sourceURL
+{
+    return m_sourceURL.get();
+}
+
+- (JSScriptType)type
+{
+    return m_type;
 }
 
 @end
@@ -189,24 +245,75 @@ static bool fillBufferWithContentsOfFile(const String& fileName, Vector<LChar>&
     return &m_cachedBytecode;
 }
 
-- (JSC::JSSourceCode*)jsSourceCode:(const JSC::Identifier&)moduleKey
+- (JSC::JSSourceCode*)jsSourceCode
 {
-    if (m_jsSourceCode) {
-        ASSERT(moduleKey.impl() == m_moduleKey);
+    if (m_jsSourceCode)
         return m_jsSourceCode.get();
+
+    return [self forceRecreateJSSourceCode];
+}
+
+- (BOOL)writeCache:(String&)error
+{
+    if (m_cachedBytecode.size())
+        return YES;
+
+    if (!m_cachePath) {
+        error = "No cache was path provided during construction of this JSScript."_s;
+        return NO;
+    }
+
+    JSC::ParserError parserError;
+    switch (m_type) {
+    case kJSScriptTypeModule:
+        m_cachedBytecode = JSC::generateModuleBytecode(m_virtualMachine.vm, [self jsSourceCode]->sourceCode(), parserError);
+        break;
+    case kJSScriptTypeProgram:
+        m_cachedBytecode = JSC::generateBytecode(m_virtualMachine.vm, [self jsSourceCode]->sourceCode(), parserError);
+        break;
+    }
+
+    if (parserError.isValid()) {
+        m_cachedBytecode = { };
+        error = makeString("Unable to generate bytecode for this JSScript because of a parser error: ", parserError.message());
+        return NO;
+    }
+
+    int fd = open([m_cachePath path].UTF8String, O_CREAT | O_WRONLY, 0666);
+    if (fd == -1) {
+        error = makeString("Unable to open file: ", [m_cachePath path].UTF8String, " due to error: ", strerror(errno));
+        return NO;
     }
+    int returnCode = flock(fd, LOCK_EX | LOCK_NB);
+    if (returnCode)
+        error = "Unable to lock the cache file; it may already be in use."_s;
+    else
+        write(fd, m_cachedBytecode.data(), m_cachedBytecode.size());
+    close(fd);
+    return !returnCode;
+}
+
+- (void)setSourceURL:(NSURL *)url
+{
+    m_sourceURL = url;
+}
 
+- (JSC::JSSourceCode*)forceRecreateJSSourceCode
+{
     JSC::VM& vm = m_virtualMachine.vm;
+    JSC::JSLockHolder locker(vm);
+
     TextPosition startPosition { };
-    Ref<JSScriptSourceProvider> sourceProvider = JSScriptSourceProvider::create(self, JSC::SourceOrigin(moduleKey.string()), URL({ }, moduleKey.string()), TextPosition(), JSC::SourceProviderSourceType::Module);
+
+    String url = String { [[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);
-    [self writeCache];
     return jsSourceCode;
 }
 
 @end
 
-
 #endif
index 66a75e2..d56f95c 100644 (file)
@@ -46,8 +46,12 @@ class String;
 
 - (unsigned)hash;
 - (const WTF::String&)source;
-- (const JSC::CachedBytecode*)cachedBytecode;
-- (JSC::JSSourceCode*)jsSourceCode:(const JSC::Identifier&)moduleKey;
+- (nullable const JSC::CachedBytecode*)cachedBytecode;
+- (JSC::JSSourceCode*)jsSourceCode;
+// FIXME: Remove this once we require sourceURL upon creation: https://bugs.webkit.org/show_bug.cgi?id=194909
+- (JSC::JSSourceCode*)forceRecreateJSSourceCode;
+- (BOOL)writeCache:(String&)error;
+- (void)setSourceURL:(NSURL *)url;
 
 @end
 
index 8389615..4629342 100644 (file)
@@ -44,7 +44,7 @@ int testFunctionOverrides()
 
     const char* oldFunctionOverrides = Options::functionOverrides();
     
-    Options::functionOverrides() = "testapi-function-overrides.js";
+    Options::functionOverrides() = "./testapiScripts/testapi-function-overrides.js";
     JSC::FunctionOverrides::reinstallOverrides();
 
     JSGlobalContextRef context = JSGlobalContextCreateInGroup(nullptr, nullptr);
index 61f05da..bfe119c 100644 (file)
@@ -26,6 +26,7 @@
 
 // Since we include files that haven't passed through the rewriter we need to handle the non-rewritten values...
 #define JSC_API_AVAILABLE(...)
+#define JSC_API_DEPRECATED(...)
 #define JSC_CLASS_AVAILABLE(...)
 #define JSC_MAC_VERSION_TBA 0
 #define JSC_IOS_VERSION_TBA 0
index 01f94cd..fa24534 100644 (file)
 #include "JSScriptRefPrivate.h"
 #include "JSStringRefPrivate.h"
 #include "JSWeakPrivate.h"
+#if !OS(WINDOWS)
+#include <libgen.h>
+#endif
+#include <limits.h>
 #include <math.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
+#if !OS(WINDOWS)
+#include <unistd.h>
+#endif
 #include <wtf/Assertions.h>
 
 #if OS(WINDOWS)
@@ -67,7 +74,7 @@
 #endif
 
 #if JSC_OBJC_API_ENABLED
-void testObjectiveCAPI(void);
+void testObjectiveCAPI(const char*);
 #endif
 
 int testCAPIViaCpp(const char* filter);
@@ -1380,11 +1387,19 @@ int main(int argc, char* argv[])
     SetErrorMode(0);
 #endif
 
-#if JSC_OBJC_API_ENABLED
-    testObjectiveCAPI();
+#if !OS(WINDOWS)
+    char resolvedPath[PATH_MAX];
+    realpath(argv[0], resolvedPath); 
+    char* newCWD = dirname(resolvedPath);
+    if (chdir(newCWD))
+        fprintf(stdout, "Could not chdir to: %s\n", newCWD);
 #endif
 
     const char* filter = argc > 1 ? argv[1] : NULL;
+#if JSC_OBJC_API_ENABLED
+    testObjectiveCAPI(filter);
+#endif
+
     RELEASE_ASSERT(!testCAPIViaCpp(filter));
     if (filter)
         return 0;
@@ -1979,7 +1994,7 @@ int main(int argc, char* argv[])
     JSObjectMakeConstructor(context, nullClass, 0);
     JSClassRelease(nullClass);
 
-    const char* scriptPath = "testapi.js";
+    const char* scriptPath = "./testapiScripts/testapi.js";
     char* scriptUTF8 = createStringWithContentsOfFile(scriptPath);
     if (!scriptUTF8) {
         printf("FAIL: Test script could not be loaded.\n");
@@ -2082,13 +2097,13 @@ int main(int argc, char* argv[])
         }
         JSGlobalContextRelease(context);
     }
-    failed = testTypedArrayCAPI() || failed;
-    failed = testExecutionTimeLimit() || failed;
-    failed = testFunctionOverrides() || failed;
-    failed = testGlobalContextWithFinalizer() || failed;
-    failed = testPingPongStackOverflow() || failed;
-    failed = testJSONParse() || failed;
-    failed = testJSObjectGetProxyTarget() || failed;
+    failed |= testTypedArrayCAPI();
+    failed |= testExecutionTimeLimit();
+    failed |= testFunctionOverrides();
+    failed |= testGlobalContextWithFinalizer();
+    failed |= testPingPongStackOverflow();
+    failed |= testJSONParse();
+    failed |= testJSObjectGetProxyTarget();
 
     // Clear out local variables pointing at JSObjectRefs to allow their values to be collected
     function = NULL;
index 3b0526e..fea65ce 100644 (file)
 #import "Regress141275.h"
 #import "Regress141809.h"
 
+#if __has_include(<libproc.h>)
+#define HAS_LIBPROC 1
+#import <libproc.h>
+#else
+#define HAS_LIBPROC 0
+#endif
 #import <pthread.h>
 #import <vector>
 #import <wtf/MemoryFootprint.h>
@@ -54,7 +60,7 @@ extern "C" bool _Block_has_signature(id);
 extern "C" const char * _Block_signature(id);
 
 extern int failed;
-extern "C" void testObjectiveCAPI(void);
+extern "C" void testObjectiveCAPI(const char*);
 extern "C" void checkResult(NSString *, bool);
 
 #if JSC_OBJC_API_ENABLED
@@ -1980,22 +1986,30 @@ static void testImportModuleTwice()
     }
 }
 
-static void testBytecodeCache()
+static NSURL *tempFile(NSString *string)
+{
+    NSURL* tempDirectory = [NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES];
+    return [tempDirectory URLByAppendingPathComponent:string];
+}
+
+static void testModuleBytecodeCache()
 {
     @autoreleasepool {
-        NSURL* tempDirectory = [NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES];
+        NSString *fooSource = @"import 'otherDirectory/baz.js'; export let n = null;";
+        NSString *barSource = @"import { n } from '../foo.js'; export let foo = () => n;";
+        NSString *bazSource = @"import { foo } from '../directory/bar.js'; globalThis.ran = null; export let exp = foo();";
 
-        NSString* fooSource = @"import { n } from \"../foo.js\"; export let foo = n;";
-        NSString* barSource = @"import \"otherDirectory/baz.js\"; export let n = null;";
-        NSString* bazSource = @"import { foo } from \"../directory/bar.js\"; globalThis.ran = null; export let exp = foo;";
+        NSURL *fooPath = tempFile(@"foo.js");
+        NSURL *barPath = tempFile(@"bar.js");
+        NSURL *bazPath = tempFile(@"baz.js");
 
-        NSURL* fooPath = [tempDirectory URLByAppendingPathComponent:@"foo.js"];
-        NSURL* barPath = [tempDirectory URLByAppendingPathComponent:@"bar.js"];
-        NSURL* bazPath = [tempDirectory URLByAppendingPathComponent:@"baz.js"];
+        NSURL *fooCachePath = tempFile(@"foo.js.cache");
+        NSURL *barCachePath = tempFile(@"bar.js.cache");
+        NSURL *bazCachePath = tempFile(@"baz.js.cache");
 
-        NSURL* fooCachePath = [tempDirectory URLByAppendingPathComponent:@"foo.js.cache"];
-        NSURL* barCachePath = [tempDirectory URLByAppendingPathComponent:@"bar.js.cache"];
-        NSURL* bazCachePath = [tempDirectory URLByAppendingPathComponent:@"baz.js.cache"];
+        NSURL *fooFakePath = [NSURL fileURLWithPath:@"/foo.js"];
+        NSURL *barFakePath = [NSURL fileURLWithPath:@"/directory/bar.js"];
+        NSURL *bazFakePath = [NSURL fileURLWithPath:@"/otherDirectory/baz.js"];
 
         [fooSource writeToURL:fooPath atomically:NO encoding:NSASCIIStringEncoding error:nil];
         [barSource writeToURL:barPath atomically:NO encoding:NSASCIIStringEncoding error:nil];
@@ -2003,13 +2017,19 @@ static void testBytecodeCache()
 
         auto block = ^(JSContext *context, JSValue *identifier, JSValue *resolve, JSValue *reject) {
             JSC::Options::forceDiskCache() = true;
-            if ([identifier isEqualToObject:@"file:///directory/bar.js"])
-                [resolve callWithArguments:@[[JSScript scriptFromASCIIFile:fooPath inVirtualMachine:context.virtualMachine withCodeSigning:nil andBytecodeCache:fooCachePath]]];
-            else if ([identifier isEqualToObject:@"file:///foo.js"])
-                [resolve callWithArguments:@[[JSScript scriptFromASCIIFile:barPath inVirtualMachine:context.virtualMachine withCodeSigning:nil andBytecodeCache:barCachePath]]];
-            else if ([identifier isEqualToObject:@"file:///otherDirectory/baz.js"])
-                [resolve callWithArguments:@[[JSScript scriptFromASCIIFile:bazPath inVirtualMachine:context.virtualMachine withCodeSigning:nil andBytecodeCache:bazCachePath]]];
-            else
+            JSScript *script = nil;
+            if ([identifier isEqualToObject:[fooFakePath absoluteString]])
+                script = [JSScript scriptOfType:kJSScriptTypeModule memoryMappedFromASCIIFile:fooPath withSourceURL:fooFakePath andBytecodeCache:fooCachePath inVirtualMachine:context.virtualMachine error:nil];
+            else if ([identifier isEqualToObject:[barFakePath absoluteString]])
+                script = [JSScript scriptOfType:kJSScriptTypeModule memoryMappedFromASCIIFile:barPath withSourceURL:barFakePath andBytecodeCache:barCachePath inVirtualMachine:context.virtualMachine error:nil];
+            else if ([identifier isEqualToObject:[bazFakePath absoluteString]])
+                script = [JSScript scriptOfType:kJSScriptTypeModule memoryMappedFromASCIIFile:bazPath withSourceURL:bazFakePath andBytecodeCache:bazCachePath inVirtualMachine:context.virtualMachine error:nil];
+
+            if (script) {
+                if (![script cacheBytecodeWithError:nil])
+                    CRASH();
+                [resolve callWithArguments:@[script]];
+            } else
                 [reject callWithArguments:@[[JSValue valueWithNewErrorFromMessage:@"Weird path" inContext:context]]];
         };
 
@@ -2023,12 +2043,75 @@ static void testBytecodeCache()
         }
 
         NSFileManager* fileManager = [NSFileManager defaultManager];
-        [fileManager removeItemAtURL:fooPath error:nil];
-        [fileManager removeItemAtURL:barPath error:nil];
-        [fileManager removeItemAtURL:bazPath error:nil];
-        [fileManager removeItemAtURL:fooCachePath error:nil];
-        [fileManager removeItemAtURL:barCachePath error:nil];
-        [fileManager removeItemAtURL:bazCachePath error:nil];
+        BOOL removedAll = true;
+        removedAll &= [fileManager removeItemAtURL:fooPath error:nil];
+        removedAll &= [fileManager removeItemAtURL:barPath error:nil];
+        removedAll &= [fileManager removeItemAtURL:bazPath error:nil];
+        removedAll &= [fileManager removeItemAtURL:fooCachePath error:nil];
+        removedAll &= [fileManager removeItemAtURL:barCachePath error:nil];
+        removedAll &= [fileManager removeItemAtURL:bazCachePath error:nil];
+        checkResult(@"Removed all temp files created", removedAll);
+    }
+}
+
+static void testProgramBytecodeCache()
+{
+    @autoreleasepool {
+        NSString *fooSource = @"function foo() { return 42; }; function bar() { return 40; }; foo() + bar();";
+        NSURL *fooCachePath = tempFile(@"foo.js.cache");
+        JSContext *context = [[JSContext alloc] init];
+        JSScript *script = [JSScript scriptOfType:kJSScriptTypeProgram withSource:fooSource andSourceURL:[NSURL URLWithString:@"my-path"] andBytecodeCache:fooCachePath inVirtualMachine:context.virtualMachine error:nil];
+        RELEASE_ASSERT(script);
+        if (![script cacheBytecodeWithError:nil])
+            CRASH();
+
+        JSC::Options::forceDiskCache() = true;
+        JSValue *result = [context evaluateJSScript:script];
+        RELEASE_ASSERT(result);
+        RELEASE_ASSERT([result isNumber]);
+        checkResult(@"result of cached program is 40+42", [[result toNumber] intValue] == 40 + 42);
+        JSC::Options::forceDiskCache() = false;
+
+        NSFileManager* fileManager = [NSFileManager defaultManager];
+        BOOL removedAll = [fileManager removeItemAtURL:fooCachePath error:nil];
+        checkResult(@"Removed all temp files created", removedAll);
+    }
+}
+
+static void testBytecodeCacheWithSyntaxError(JSScriptType type)
+{
+    @autoreleasepool {
+        NSString *fooSource = @"this is a syntax error";
+        NSURL *fooCachePath = tempFile(@"foo.js.cache");
+        JSContext *context = [[JSContext alloc] init];
+        JSScript *script = [JSScript scriptOfType:type withSource:fooSource andSourceURL:[NSURL URLWithString:@"my-path"] andBytecodeCache:fooCachePath inVirtualMachine:context.virtualMachine error:nil];
+        RELEASE_ASSERT(script);
+        NSError *error = nil;
+        if ([script cacheBytecodeWithError:&error])
+            CRASH();
+        RELEASE_ASSERT(error);
+        checkResult(@"Got error when trying to cache bytecode for a script with a syntax error.", [[error description] containsString:@"Unable to generate bytecode for this JSScript because of a parser error"]);
+    }
+}
+
+static void testProgramJSScriptException()
+{
+    @autoreleasepool {
+        NSString *source = @"throw 42;";
+        JSContext *context = [[JSContext alloc] init];
+        JSScript *script = [JSScript scriptOfType:kJSScriptTypeProgram withSource:source andSourceURL:[NSURL URLWithString:@"my-path"] andBytecodeCache:nil inVirtualMachine:context.virtualMachine error:nil];
+        RELEASE_ASSERT(script);
+        __block bool handledException = false;
+        context.exceptionHandler = ^(JSContext *, JSValue *exception) {
+            handledException = true;
+            RELEASE_ASSERT([exception isNumber]);
+            checkResult(@"Program JSScript with exception should have the exception value be 42.", [[exception toNumber] intValue] == 42);
+        };
+
+        JSValue *result = [context evaluateJSScript:script];
+        RELEASE_ASSERT(result);
+        checkResult(@"Program JSScript with exception should return undefined.", [result isUndefined]);
+        checkResult(@"Program JSScript with exception should call exception handler.", handledException);
     }
 }
 
@@ -2078,7 +2161,7 @@ static NSURL *resolvePathToScripts()
 
 @end
 
-static void testLoadBasicFile()
+static void testLoadBasicFileLegacySPI()
 {
     @autoreleasepool {
         auto *context = [JSContextFileLoaderDelegate newContext];
@@ -2089,41 +2172,179 @@ static void testLoadBasicFile()
     }
 }
 
-void testObjectiveCAPI()
+
+@interface JSContextMemoryMappedLoaderDelegate : JSContext <JSModuleLoaderDelegate>
+
++ (instancetype)newContext;
+
+@end
+
+@implementation JSContextMemoryMappedLoaderDelegate {
+}
+
++ (instancetype)newContext
+{
+    auto *result = [[JSContextMemoryMappedLoaderDelegate alloc] init];
+    return result;
+}
+
+- (void)context:(JSContext *)context fetchModuleForIdentifier:(JSValue *)identifier withResolveHandler:(JSValue *)resolve andRejectHandler:(JSValue *)reject
+{
+    NSURL *filePath = [NSURL URLWithString:[identifier toString]];
+    auto *script = [JSScript scriptOfType:kJSScriptTypeModule memoryMappedFromASCIIFile:filePath withSourceURL:filePath andBytecodeCache:nil inVirtualMachine:context.virtualMachine error:nil];
+    if (script)
+        [resolve callWithArguments:@[script]];
+    else
+        [reject callWithArguments:@[[JSValue valueWithNewErrorFromMessage:@"Unable to create Script" inContext:context]]];
+}
+
+@end
+
+static void testLoadBasicFile()
+{
+#if HAS_LIBPROC
+    size_t count = proc_pidinfo(getpid(), PROC_PIDLISTFDS, 0, 0, 0);
+#endif
+    @autoreleasepool {
+        auto *context = [JSContextMemoryMappedLoaderDelegate newContext];
+        context.moduleLoaderDelegate = context;
+        JSValue *promise = [context evaluateScript:@"import('./basic.js');" withSourceURL:resolvePathToScripts()];
+        JSValue *null = [JSValue valueWithNullInContext:context];
+#if HAS_LIBPROC
+        size_t afterCount = proc_pidinfo(getpid(), PROC_PIDLISTFDS, 0, 0, 0);
+        checkResult(@"JSScript should not hold a file descriptor", count == afterCount);
+#endif
+        checkModuleCodeRan(context, promise, null);
+    }
+#if HAS_LIBPROC
+    size_t after = proc_pidinfo(getpid(), PROC_PIDLISTFDS, 0, 0, 0);
+    checkResult(@"File descriptor count sholudn't change after context is dealloced", count == after);
+#endif
+}
+
+@interface JSContextAugmentedLoaderDelegate : JSContext <JSModuleLoaderDelegate>
+
++ (instancetype)newContext;
+
+@end
+
+@implementation JSContextAugmentedLoaderDelegate {
+}
+
++ (instancetype)newContext
+{
+    auto *result = [[JSContextAugmentedLoaderDelegate alloc] init];
+    return result;
+}
+
+- (void)context:(JSContext *)context fetchModuleForIdentifier:(JSValue *)identifier withResolveHandler:(JSValue *)resolve andRejectHandler:(JSValue *)reject
+{
+    UNUSED_PARAM(reject);
+
+    NSURL *filePath = [NSURL URLWithString:[identifier toString]];
+    NSString *pathString = [filePath absoluteString];
+    if ([pathString containsString:@"basic.js"] || [pathString containsString:@"foo.js"]) {
+        auto *script = [JSScript scriptOfType:kJSScriptTypeModule memoryMappedFromASCIIFile:filePath withSourceURL:filePath andBytecodeCache:nil inVirtualMachine:context.virtualMachine error:nil];
+        RELEASE_ASSERT(script);
+        [resolve callWithArguments:@[script]];
+        return;
+    }
+
+    if ([pathString containsString:@"bar.js"]) {
+        auto *script = [JSScript scriptOfType:kJSScriptTypeModule withSource:@"" andSourceURL:[NSURL fileURLWithPath:@"/not/path/to/bar.js"] andBytecodeCache:nil inVirtualMachine:context.virtualMachine error:nil];
+        RELEASE_ASSERT(script);
+        [resolve callWithArguments:@[script]];
+        return;
+    }
+
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+@end
+
+static void testJSScriptURL()
+{
+    @autoreleasepool {
+        auto *context = [JSContextAugmentedLoaderDelegate newContext];
+        context.moduleLoaderDelegate = context;
+        NSURL *basic = [NSURL URLWithString:@"./basic.js" relativeToURL:resolvePathToScripts()];
+        JSScript *script1 = [JSScript scriptOfType:kJSScriptTypeModule memoryMappedFromASCIIFile:basic withSourceURL:basic andBytecodeCache:nil inVirtualMachine:context.virtualMachine error:nil];
+
+        JSValue *result1 = [context evaluateJSScript:script1];
+        JSValue *null = [JSValue valueWithNullInContext:context];
+        checkModuleCodeRan(context, result1, null);
+
+        NSURL *foo = [NSURL URLWithString:@"./foo.js" relativeToURL:resolvePathToScripts()];
+        JSScript *script2 = [JSScript scriptOfType:kJSScriptTypeModule memoryMappedFromASCIIFile:foo withSourceURL:foo andBytecodeCache:nil inVirtualMachine:context.virtualMachine error:nil];
+        RELEASE_ASSERT(script2);
+        JSValue *result2 = [context evaluateJSScript:script2];
+
+        __block bool wasRejected = false;
+        [result2 invokeMethod:@"catch" withArguments:@[^(JSValue *reason) {
+            wasRejected = [reason isObject];
+            RELEASE_ASSERT([[reason toString] containsString:@"The same JSScript was provided for two different identifiers"]);
+        }]];
+
+        checkResult(@"Module JSScript imported with different identifiers is rejected", wasRejected);
+    }
+}
+
+#define RUN(test) do { \
+        if (!shouldRun(#test)) \
+            break; \
+        NSLog(@"%s...\n", #test); \
+        test; \
+        NSLog(@"%s: done.\n", #test); \
+    } while (false)
+
+void testObjectiveCAPI(const char* filter)
 {
     NSLog(@"Testing Objective-C API");
 
-    checkNegativeNSIntegers();
-    runJITThreadLimitTests();
+    auto shouldRun = [&] (const char* test) -> bool {
+        if (filter)
+            return strcasestr(test, filter);
+        return true;
+    };
 
-    testLoaderResolvesAbsoluteScriptURL();
-    testFetch();
-    testFetchWithTwoCycle();
-    testFetchWithThreeCycle();
-    testImportModuleTwice();
-    testBytecodeCache();
+    RUN(checkNegativeNSIntegers());
+    RUN(runJITThreadLimitTests());
 
-    testLoaderRejectsNilScriptURL();
-    testLoaderRejectsFailedFetch();
+    RUN(testLoaderResolvesAbsoluteScriptURL());
+    RUN(testFetch());
+    RUN(testFetchWithTwoCycle());
+    RUN(testFetchWithThreeCycle());
+    RUN(testImportModuleTwice());
+    RUN(testModuleBytecodeCache());
+    RUN(testProgramBytecodeCache());
+    RUN(testBytecodeCacheWithSyntaxError(kJSScriptTypeProgram));
+    RUN(testBytecodeCacheWithSyntaxError(kJSScriptTypeModule));
+    RUN(testProgramJSScriptException());
 
-    // File loading
-    testLoadBasicFile();
+    RUN(testLoaderRejectsNilScriptURL());
+    RUN(testLoaderRejectsFailedFetch());
+
+    RUN(testJSScriptURL());
 
-    promiseWithExecutor(Resolution::ResolveEager);
-    promiseWithExecutor(Resolution::RejectEager);
-    promiseWithExecutor(Resolution::ResolveLate);
-    promiseWithExecutor(Resolution::RejectLate);
-    promiseRejectOnJSException();
-    promiseCreateResolved();
-    promiseCreateRejected();
-    parallelPromiseResolveTest();
+    // File loading
+    RUN(testLoadBasicFileLegacySPI());
+    RUN(testLoadBasicFile());
+
+    RUN(promiseWithExecutor(Resolution::ResolveEager));
+    RUN(promiseWithExecutor(Resolution::RejectEager));
+    RUN(promiseWithExecutor(Resolution::ResolveLate));
+    RUN(promiseWithExecutor(Resolution::RejectLate));
+    RUN(promiseRejectOnJSException());
+    RUN(promiseCreateResolved());
+    RUN(promiseCreateRejected());
+    RUN(parallelPromiseResolveTest());
 
     testObjectiveCAPIMain();
 }
 
 #else
 
-void testObjectiveCAPI()
+void testObjectiveCAPI(const char*)
 {
 }
 
diff --git a/Source/JavaScriptCore/API/tests/testapiScripts/foo.js b/Source/JavaScriptCore/API/tests/testapiScripts/foo.js
new file mode 100644 (file)
index 0000000..7eafd98
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 Apple Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import "./bar.js";
index ac83082..d72d851 100644 (file)
@@ -1,3 +1,92 @@
+2019-02-21  Saam Barati  <sbarati@apple.com>
+
+        Update JSScript SPI based on feedback
+        https://bugs.webkit.org/show_bug.cgi?id=194517
+
+        Reviewed by Keith Miller.
+
+        This patch updates the JSScript SPI in the following ways:
+        - JSScript can now represent both modules and programs. This is a property
+        of the script determined during creation.
+        - JSScript now takes a sourceURL during construction. For modules, this acts
+        as the module identifier.
+        - JSScript now has SPI for writing the cache out to disk. We don't do this
+        automatically.
+        - JSScript will load the bytecode cache on creation if it exists.
+        - We retrofit these new requirements on the prior JSScript SPI that
+        we're going to remove as soon as we can: https://bugs.webkit.org/show_bug.cgi?id=194909.
+        Previous SPI assumes all JSScripts are modules. Previous SPI also assigns
+        a sourceURL to the JSScript based on what the module loader decided the
+        identifier should be. We'll remove this once we remove the old SPI.
+        
+        This patch also adds SPI to JSContext to evaluate a JSScript. For modules,
+        this is like returning the result of doing dynamic import. For programs,
+        this does normal program evaluation.
+        
+        This patch also fixes a bug in generateBytecode/generateModuleBytecode where
+        we would try to cache the bytecode even if recursivelyGenerateUnlinkedCodeBlock
+        returned null. E.g, if the script had a syntax error.
+        
+        When writing tests, I also discovered that someone previously broke
+        testapi. This patch also fixes those failures. They were broken when
+        we switched to using a testapiScripts directory to hold our test .js
+        scripts. 
+
+        * API/JSAPIGlobalObject.h:
+        * API/JSAPIGlobalObject.mm:
+        (JSC::JSAPIGlobalObject::moduleLoaderResolve):
+        (JSC::JSAPIGlobalObject::moduleLoaderFetch):
+        (JSC::JSAPIGlobalObject::loadAndEvaluateJSScriptModule):
+        * API/JSBase.cpp:
+        (JSEvaluateScriptInternal):
+        (JSEvaluateScript):
+        * API/JSBaseInternal.h: Added.
+        * API/JSContext.mm:
+        (-[JSContext evaluateScript:withSourceURL:]):
+        (-[JSContext evaluateJSScript:]):
+        * API/JSContextPrivate.h:
+        * API/JSScript.h:
+        * API/JSScript.mm:
+        (+[JSScript scriptWithSource:inVirtualMachine:]):
+        (+[JSScript scriptFromASCIIFile:inVirtualMachine:withCodeSigning:andBytecodeCache:]):
+        (createError):
+        (+[JSScript scriptOfType:inVirtualMachine:withSourceURL:andSource:andBytecodeCache:error:]):
+        (+[JSScript scriptOfType:inVirtualMachine:memoryMappedFromASCIIFile:withSourceURL:andBytecodeCache:error:]):
+        (-[JSScript cacheBytecodeWithError:]):
+        (-[JSScript sourceURL]):
+        (-[JSScript type]):
+        (-[JSScript jsSourceCode]):
+        (-[JSScript writeCache:]):
+        (-[JSScript setSourceURL:]):
+        (-[JSScript forceRecreateJSSourceCode]):
+        (-[JSScript writeCache]): Deleted.
+        (-[JSScript jsSourceCode:]): Deleted.
+        * API/JSScriptInternal.h:
+        * API/tests/FunctionOverridesTest.cpp:
+        (testFunctionOverrides):
+        * API/tests/testapi.c:
+        (main):
+        * API/tests/testapi.mm:
+        (tempFile):
+        (testModuleBytecodeCache):
+        (testProgramBytecodeCache):
+        (testBytecodeCacheWithSyntaxError):
+        (testProgramJSScriptException):
+        (testLoadBasicFileLegacySPI):
+        (+[JSContextMemoryMappedLoaderDelegate newContext]):
+        (-[JSContextMemoryMappedLoaderDelegate context:fetchModuleForIdentifier:withResolveHandler:andRejectHandler:]):
+        (testLoadBasicFile):
+        (+[JSContextAugmentedLoaderDelegate newContext]):
+        (-[JSContextAugmentedLoaderDelegate context:fetchModuleForIdentifier:withResolveHandler:andRejectHandler:]):
+        (testJSScriptURL):
+        (testObjectiveCAPI):
+        (testBytecodeCache): Deleted.
+        * API/tests/testapiScripts/foo.js: Added.
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * runtime/Completion.cpp:
+        (JSC::generateBytecode):
+        (JSC::generateModuleBytecode):
+
 2019-02-21  Mark Lam  <mark.lam@apple.com>
 
         Add more doesGC() assertions.
index d08b0b3..7136290 100644 (file)
                52B311011975B4670080857C /* TypeLocationCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 52B311001975B4670080857C /* TypeLocationCache.h */; settings = {ATTRIBUTES = (Private, ); }; };
                52C0611F1AA51E1C00B4ADBA /* RuntimeType.h in Headers */ = {isa = PBXBuildFile; fileRef = 52C0611D1AA51E1B00B4ADBA /* RuntimeType.h */; settings = {ATTRIBUTES = (Private, ); }; };
                52C952B719A289850069B386 /* TypeProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 52C952B619A289850069B386 /* TypeProfiler.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               52D13091221CE176009C836C /* foo.js in Copy Support Script */ = {isa = PBXBuildFile; fileRef = 52D1308F221CE03A009C836C /* foo.js */; };
                52F6C35E1E71EB080081F4CC /* WebAssemblyWrapperFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 52F6C35C1E71EB080081F4CC /* WebAssemblyWrapperFunction.h */; };
                530A66B91FA3E78B0026A545 /* UnifiedSource3-mm.mm in Sources */ = {isa = PBXBuildFile; fileRef = 530A66B11FA3E77A0026A545 /* UnifiedSource3-mm.mm */; };
                530A66BA1FA3E78B0026A545 /* UnifiedSource4-mm.mm in Sources */ = {isa = PBXBuildFile; fileRef = 530A66B81FA3E77E0026A545 /* UnifiedSource4-mm.mm */; };
                797E07AA1B8FCFB9008400BA /* JSGlobalLexicalEnvironment.h in Headers */ = {isa = PBXBuildFile; fileRef = 797E07A81B8FCFB9008400BA /* JSGlobalLexicalEnvironment.h */; settings = {ATTRIBUTES = (Private, ); }; };
                7980C16D1E3A940E00B71615 /* DFGRegisteredStructureSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 7980C16B1E3A940E00B71615 /* DFGRegisteredStructureSet.h */; settings = {ATTRIBUTES = (Private, ); }; };
                7986943B1F8C0ACC009232AE /* StructureCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 7986943A1F8C0AC8009232AE /* StructureCache.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               79872C48221BBAF3008C6969 /* JSBaseInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 79872C47221BBAED008C6969 /* JSBaseInternal.h */; };
                798937791DCAB57300F8D4FB /* JSFixedArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 798937771DCAB57300F8D4FB /* JSFixedArray.h */; settings = {ATTRIBUTES = (Private, ); }; };
                799EF7C41C56ED96002B0534 /* B3PCToOriginMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 799EF7C31C56ED96002B0534 /* B3PCToOriginMap.h */; settings = {ATTRIBUTES = (Private, ); }; };
                79A090801D768465008B889B /* HashMapImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 79A0907E1D768465008B889B /* HashMapImpl.h */; settings = {ATTRIBUTES = (Private, ); }; };
                        dstPath = testapiScripts;
                        dstSubfolderSpec = 16;
                        files = (
+                               52D13091221CE176009C836C /* foo.js in Copy Support Script */,
                                53C3D5E521ECE7720087FDFC /* basic.js in Copy Support Script */,
                                FECB8B2A1D25CB5A006F2463 /* testapi-function-overrides.js in Copy Support Script */,
                                5DBB151B131D0B310056AD36 /* testapi.js in Copy Support Script */,
                52C0611D1AA51E1B00B4ADBA /* RuntimeType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RuntimeType.h; sourceTree = "<group>"; };
                52C952B619A289850069B386 /* TypeProfiler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TypeProfiler.h; sourceTree = "<group>"; };
                52C952B819A28A1C0069B386 /* TypeProfiler.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TypeProfiler.cpp; sourceTree = "<group>"; };
+               52D1308F221CE03A009C836C /* foo.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = foo.js; sourceTree = "<group>"; };
                52F6C35B1E71EB080081F4CC /* WebAssemblyWrapperFunction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = WebAssemblyWrapperFunction.cpp; path = js/WebAssemblyWrapperFunction.cpp; sourceTree = "<group>"; };
                52F6C35C1E71EB080081F4CC /* WebAssemblyWrapperFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WebAssemblyWrapperFunction.h; path = js/WebAssemblyWrapperFunction.h; sourceTree = "<group>"; };
                530A63401FA3E31C0026A545 /* SourcesCocoa.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = SourcesCocoa.txt; sourceTree = "<group>"; };
                7980C16B1E3A940E00B71615 /* DFGRegisteredStructureSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGRegisteredStructureSet.h; path = dfg/DFGRegisteredStructureSet.h; sourceTree = "<group>"; };
                798694391F8C0AC7009232AE /* StructureCache.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = StructureCache.cpp; sourceTree = "<group>"; };
                7986943A1F8C0AC8009232AE /* StructureCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StructureCache.h; sourceTree = "<group>"; };
+               79872C47221BBAED008C6969 /* JSBaseInternal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSBaseInternal.h; sourceTree = "<group>"; };
                798937761DCAB57300F8D4FB /* JSFixedArray.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSFixedArray.cpp; sourceTree = "<group>"; };
                798937771DCAB57300F8D4FB /* JSFixedArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSFixedArray.h; sourceTree = "<group>"; };
                799EF7C31C56ED96002B0534 /* B3PCToOriginMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = B3PCToOriginMap.h; path = b3/B3PCToOriginMap.h; sourceTree = "<group>"; };
                                C2CF39BF16E15A8100DD69BE /* JSAPIWrapperObject.mm */,
                                1421359A0A677F4F00A8195E /* JSBase.cpp */,
                                142711380A460BBB0080EEEA /* JSBase.h */,
+                               79872C47221BBAED008C6969 /* JSBaseInternal.h */,
                                140D17D60E8AD4A9000CD17D /* JSBasePrivate.h */,
                                1440F8AD0A508D200005F061 /* JSCallbackConstructor.cpp */,
                                1440F8AC0A508D200005F061 /* JSCallbackConstructor.h */,
                        isa = PBXGroup;
                        children = (
                                53C3D5E421ECE6CE0087FDFC /* basic.js */,
+                               52D1308F221CE03A009C836C /* foo.js */,
                        );
                        name = testapiScripts;
                        path = API/tests/testapiScripts;
                                0FF42748158EBE91004CB9FF /* udis86_syn.h in Headers */,
                                0FF42749158EBE91004CB9FF /* udis86_types.h in Headers */,
                                A7E5AB391799E4B200D2833D /* UDis86Disassembler.h in Headers */,
+                               79872C48221BBAF3008C6969 /* JSBaseInternal.h in Headers */,
                                A7A8AF4117ADB5F3005AB174 /* Uint16Array.h in Headers */,
                                866739D313BFDE710023D87C /* Uint16WithFraction.h in Headers */,
                                A7A8AF4217ADB5F3005AB174 /* Uint32Array.h in Headers */,
index e364390..fa1b8a9 100644 (file)
@@ -25,6 +25,7 @@
 
 #define JSC_API_AVAILABLE(...)
 #define JSC_CLASS_AVAILABLE(...) JS_EXPORT
+#define JSC_API_DEPRECATED(...)
 // Use zero since it will be less than any possible version number.
 #define JSC_MAC_VERSION_TBA 0
 #define JSC_IOS_VERSION_TBA 0
index 8a875cd..38a9b9f 100644 (file)
@@ -103,6 +103,8 @@ CachedBytecode generateBytecode(VM& vm, const SourceCode& source, ParserError& e
     EvalContextType evalContextType = EvalContextType::None;
 
     UnlinkedCodeBlock* unlinkedCodeBlock = recursivelyGenerateUnlinkedCodeBlock<UnlinkedProgramCodeBlock>(vm, source, strictMode, scriptMode, debuggerMode, error, evalContextType, &variablesUnderTDZ);
+    if (!unlinkedCodeBlock)
+        return { };
     return serializeBytecode(vm, unlinkedCodeBlock, source, SourceCodeType::ProgramType, strictMode, scriptMode, debuggerMode);
 }
 
@@ -118,6 +120,8 @@ CachedBytecode generateModuleBytecode(VM& vm, const SourceCode& source, ParserEr
     EvalContextType evalContextType = EvalContextType::None;
 
     UnlinkedCodeBlock* unlinkedCodeBlock = recursivelyGenerateUnlinkedCodeBlock<UnlinkedModuleProgramCodeBlock>(vm, source, strictMode, scriptMode, debuggerMode, error, evalContextType, &variablesUnderTDZ);
+    if (!unlinkedCodeBlock)
+        return { };
     return serializeBytecode(vm, unlinkedCodeBlock, source, SourceCodeType::ModuleType, strictMode, scriptMode, debuggerMode);
 }