Changeset 241929 in webkit


Ignore:
Timestamp:
Feb 21, 2019 8:21:54 PM (5 years ago)
Author:
sbarati@apple.com
Message:

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):

Location:
trunk/Source/JavaScriptCore
Files:
16 edited
2 copied

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/API/JSAPIGlobalObject.h

    r239933 r241929  
    2828#include "JSGlobalObject.h"
    2929
     30OBJC_CLASS JSScript;
     31
    3032namespace JSC {
    3133
     
    5658    static JSObject* moduleLoaderCreateImportMetaProperties(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSModuleRecord*, JSValue);
    5759
     60    JSValue loadAndEvaluateJSScriptModule(const JSLockHolder&, JSScript *);
     61
    5862private:
    5963    JSAPIGlobalObject(VM& vm, Structure* structure)
  • trunk/Source/JavaScriptCore/API/JSAPIGlobalObject.mm

    r241172 r241929  
    3535#import "Exception.h"
    3636#import "JSContextInternal.h"
     37#import "JSInternalPromise.h"
    3738#import "JSInternalPromiseDeferred.h"
    3839#import "JSNativeStdFunction.h"
     40#import "JSPromiseDeferred.h"
    3941#import "JSScriptInternal.h"
    4042#import "JSSourceCode.h"
     
    4446#import "ObjectConstructor.h"
    4547#import "SourceOrigin.h"
    46 
    4748#import <wtf/URL.h>
    4849
     
    7677    String name =  key.toWTFString(exec);
    7778
    78     URL referrerURL(URL(), jsCast<JSString*>(referrer)->tryGetValue());
    79     RELEASE_ASSERT(referrerURL.isValid());
    80 
    81     URL url = URL(referrerURL, name);
    82     if (url.isValid())
    83         return Identifier::fromString(exec, url);
     79    if (JSString* referrerString = jsDynamicCast<JSString*>(vm, referrer)) {
     80        String value = referrerString->value(exec);
     81        URL referrerURL({ }, value);
     82        RETURN_IF_EXCEPTION(scope, { });
     83        RELEASE_ASSERT(referrerURL.isValid());
     84
     85        URL url = URL(referrerURL, name);
     86        if (url.isValid())
     87            return Identifier::fromString(exec, url);
     88    } else {
     89        URL url = URL({ }, name);
     90        if (url.isValid())
     91            return Identifier::fromString(exec, url);
     92    }
    8493
    8594    throwVMError(exec, scope, "Could not form valid URL from identifier and base"_s);
     
    163172
    164173    auto deferredPromise = Strong<JSInternalPromiseDeferred>(vm, deferred);
    165     auto strongKey = Strong<JSString>(vm, jsSecureCast<JSString*>(vm, key));
    166174    auto* resolve = JSNativeStdFunction::create(vm, globalObject, 1, "resolve", [=] (ExecState* exec) {
    167175        // This captures the globalObject but that's ok because our structure keeps it alive anyway.
     176        VM& vm = exec->vm();
    168177        JSContext *context = [JSContext contextWithJSGlobalContextRef:toGlobalRef(globalObject->globalExec())];
    169178        id script = valueToObject(context, toRef(exec, exec->argument(0)));
    170179
    171180        MarkedArgumentBuffer args;
    172         if (UNLIKELY(![script isKindOfClass:[JSScript class]])) {
    173             args.append(createTypeError(exec, "First argument of resolution callback is not a JSScript"));
     181
     182        auto rejectPromise = [&] (String message) {
     183            args.append(createTypeError(exec, message));
    174184            call(exec, deferredPromise->JSPromiseDeferred::reject(), args, "This should never be seen...");
    175185            return encodedJSUndefined();
     186        };
     187
     188        if (UNLIKELY(![script isKindOfClass:[JSScript class]]))
     189            return rejectPromise("First argument of resolution callback is not a JSScript"_s);
     190
     191        JSScript* jsScript = static_cast<JSScript *>(script);
     192
     193        JSSourceCode* source = [jsScript jsSourceCode];
     194        if (UNLIKELY([jsScript type] != kJSScriptTypeModule))
     195            return rejectPromise("The JSScript that was provided did not have expected type of kJSScriptTypeModule."_s);
     196
     197        // FIXME: The SPI we're deprecating did not require sourceURL, so we just
     198        // ignore this check for such use cases until we can remove that SPI. Once
     199        // we do that, we can remove the null check for sourceURL:
     200        // https://bugs.webkit.org/show_bug.cgi?id=194909
     201        if (NSURL *sourceURL = [jsScript sourceURL]) {
     202            String oldModuleKey { [sourceURL absoluteString] };
     203            if (UNLIKELY(Identifier::fromString(&vm, oldModuleKey) != moduleKey))
     204                return rejectPromise(makeString("The same JSScript was provided for two different identifiers, previously: ", oldModuleKey, " and now: ", moduleKey.string()));
     205        } else {
     206            [jsScript setSourceURL:[NSURL URLWithString:static_cast<NSString *>(moduleKey.string())]];
     207            source = [jsScript forceRecreateJSSourceCode];
    176208        }
    177209
    178         args.append([static_cast<JSScript *>(script) jsSourceCode:moduleKey]);
     210        args.append(source);
    179211        call(exec, deferredPromise->JSPromiseDeferred::resolve(), args, "This should never be seen...");
    180212        return encodedJSUndefined();
     
    211243}
    212244
     245JSValue JSAPIGlobalObject::loadAndEvaluateJSScriptModule(const JSLockHolder&, JSScript *script)
     246{
     247    ASSERT(script.type == kJSScriptTypeModule);
     248    VM& vm = this->vm();
     249    ExecState* exec = globalExec();
     250    auto scope = DECLARE_THROW_SCOPE(vm);
     251
     252    Identifier key = Identifier::fromString(exec, String { [[script sourceURL] absoluteString] });
     253    JSInternalPromise* promise = importModule(exec, key, jsUndefined(), jsUndefined());
     254    RETURN_IF_EXCEPTION(scope, { });
     255    auto result = JSPromiseDeferred::tryCreate(exec, this);
     256    RETURN_IF_EXCEPTION(scope, { });
     257    result->resolve(exec, promise);
     258    return result->promise();
     259}
     260
    213261}
    214262
  • trunk/Source/JavaScriptCore/API/JSBase.cpp

    r239569 r241929  
    2626#include "config.h"
    2727#include "JSBase.h"
     28#include "JSBaseInternal.h"
    2829#include "JSBasePrivate.h"
    2930
     
    4849using namespace JSC;
    4950
    50 JSValueRef JSEvaluateScript(JSContextRef ctx, JSStringRef script, JSObjectRef thisObject, JSStringRef sourceURL, int startingLineNumber, JSValueRef* exception)
    51 {
    52     if (!ctx) {
    53         ASSERT_NOT_REACHED();
    54         return 0;
    55     }
    56     ExecState* exec = toJS(ctx);
    57     VM& vm = exec->vm();
    58     JSLockHolder locker(vm);
     51JSValueRef JSEvaluateScriptInternal(const JSLockHolder&, ExecState* exec, JSContextRef ctx, JSObjectRef thisObject, const SourceCode& source, JSValueRef* exception)
     52{
     53    UNUSED_PARAM(ctx);
    5954
    6055    JSObject* jsThisObject = toJS(thisObject);
    6156
    62     startingLineNumber = std::max(1, startingLineNumber);
    63 
    6457    // evaluate sets "this" to the global object if it is NULL
     58    VM& vm = exec->vm();
    6559    JSGlobalObject* globalObject = vm.vmEntryGlobalObject(exec);
    66     auto sourceURLString = sourceURL ? sourceURL->string() : String();
    67     SourceCode source = makeSource(script->string(), SourceOrigin { sourceURLString }, URL({ }, sourceURLString), TextPosition(OrdinalNumber::fromOneBasedInt(startingLineNumber), OrdinalNumber()));
    68 
    6960    NakedPtr<Exception> evaluationException;
    7061    JSValue returnValue = profiledEvaluate(globalObject->globalExec(), ProfilingReason::API, source, jsThisObject, evaluationException);
     
    8172        globalObject->inspectorController().reportAPIException(exec, evaluationException);
    8273#endif
    83         return 0;
     74        return nullptr;
    8475    }
    8576
     
    8980    // happens, for example, when the only statement is an empty (';') statement
    9081    return toRef(exec, jsUndefined());
     82}
     83
     84JSValueRef JSEvaluateScript(JSContextRef ctx, JSStringRef script, JSObjectRef thisObject, JSStringRef sourceURL, int startingLineNumber, JSValueRef* exception)
     85{
     86    if (!ctx) {
     87        ASSERT_NOT_REACHED();
     88        return nullptr;
     89    }
     90    ExecState* exec = toJS(ctx);
     91    VM& vm = exec->vm();
     92    JSLockHolder locker(vm);
     93
     94    startingLineNumber = std::max(1, startingLineNumber);
     95
     96    auto sourceURLString = sourceURL ? sourceURL->string() : String();
     97    SourceCode source = makeSource(script->string(), SourceOrigin { sourceURLString }, URL({ }, sourceURLString), TextPosition(OrdinalNumber::fromOneBasedInt(startingLineNumber), OrdinalNumber()));
     98
     99    return JSEvaluateScriptInternal(locker, exec, ctx, thisObject, source, exception);
    91100}
    92101
  • trunk/Source/JavaScriptCore/API/JSBaseInternal.h

    r241928 r241929  
    2121 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    2222 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
    2424 */
    2525
     26#pragma once
    2627
    27 // Since we include files that haven't passed through the rewriter we need to handle the non-rewritten values...
    28 #define JSC_API_AVAILABLE(...)
    29 #define JSC_CLASS_AVAILABLE(...)
    30 #define JSC_MAC_VERSION_TBA 0
    31 #define JSC_IOS_VERSION_TBA 0
     28#include <JavaScriptCore/JSBase.h>
     29#include <JavaScriptCore/WebKitAvailability.h>
    3230
    33 // umbrella header
    34 #import <JavaScriptCore/JavaScriptCore.h>
     31namespace JSC {
     32class JSLockHolder;
     33class ExecState;
     34}
    3535
    36 // private headers
    37 #import <JavaScriptCore/JSContextPrivate.h>
    38 #import <JavaScriptCore/JSScript.h>
    39 #import <JavaScriptCore/JSValuePrivate.h>
    40 #import <JavaScriptCore/JSVirtualMachinePrivate.h>
    41 
     36extern "C" JSValueRef JSEvaluateScriptInternal(const JSC::JSLockHolder&, JSC::ExecState*, JSContextRef, JSObjectRef thisObject, const JSC::SourceCode&, JSValueRef* exception);
  • trunk/Source/JavaScriptCore/API/JSContext.mm

    r240511 r241929  
    2828#import "APICast.h"
    2929#import "Completion.h"
     30#import "JSBaseInternal.h"
    3031#import "JSCInlines.h"
    3132#import "JSContextInternal.h"
     
    113114    if (exceptionValue)
    114115        return [self valueFromNotifyException:exceptionValue];
    115 
    116116    return [JSValue valueWithJSValueRef:result inContext:self];
     117}
     118
     119- (JSValue *)evaluateJSScript:(JSScript *)script
     120{
     121    JSC::ExecState* exec = toJS(m_context);
     122    JSC::VM& vm = exec->vm();
     123    JSC::JSLockHolder locker(vm);
     124
     125    if (script.type == kJSScriptTypeProgram) {
     126        JSValueRef exceptionValue = nullptr;
     127        JSValueRef result = JSEvaluateScriptInternal(locker, exec, m_context, nullptr, [script jsSourceCode]->sourceCode(), &exceptionValue);
     128
     129        if (exceptionValue)
     130            return [self valueFromNotifyException:exceptionValue];
     131        return [JSValue valueWithJSValueRef:result inContext:self];
     132    }
     133
     134    auto* globalObject = JSC::jsDynamicCast<JSC::JSAPIGlobalObject*>(vm, exec->lexicalGlobalObject());
     135    if (!globalObject)
     136        return [JSValue valueWithNewPromiseRejectedWithReason:[JSValue valueWithNewErrorFromMessage:@"Context does not support module loading" inContext:self] inContext:self];
     137
     138    auto scope = DECLARE_CATCH_SCOPE(vm);
     139    JSC::JSValue result = globalObject->loadAndEvaluateJSScriptModule(locker, script);
     140    if (scope.exception()) {
     141        JSValueRef exceptionValue = toRef(exec, scope.exception()->value());
     142        scope.clearException();
     143        return [JSValue valueWithNewPromiseRejectedWithReason:[JSValue valueWithJSValueRef:exceptionValue inContext:self] inContext:self];
     144    }
     145    return [JSValue valueWithJSValueRef:toRef(vm, result) inContext:self];
    117146}
    118147
  • trunk/Source/JavaScriptCore/API/JSContextPrivate.h

    r239933 r241929  
    7575@property (nonatomic, weak) id <JSModuleLoaderDelegate> moduleLoaderDelegate JSC_API_AVAILABLE(macosx(JSC_MAC_TBA), ios(JSC_IOS_TBA));
    7676
     77/*!
     78 @method
     79 @abstract Run a JSScript.
     80 @param script the JSScript to evaluate.
     81 @discussion If the provided JSScript was created with kJSScriptTypeProgram, the script will run synchronously and return the result of evaluation.
     82
     83 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.
     84 */
     85- (JSValue *)evaluateJSScript:(JSScript *)script;
     86
    7787@end
    7888
  • trunk/Source/JavaScriptCore/API/JSScript.h

    r240468 r241929  
    3232@class JSVirtualMachine;
    3333
     34/*!
     35 @enum JSScriptType
     36 @abstract     A constant identifying the execution type of a JSScript.
     37 @constant     kJSScriptTypeProgram  The type of a normal JavaScript program.
     38 @constant     kJSScriptTypeModule   The type of a module JavaScript program.
     39 */
     40typedef NS_ENUM(NSInteger, JSScriptType) {
     41    kJSScriptTypeProgram,
     42    kJSScriptTypeModule,
     43};
     44
     45
    3446JSC_CLASS_AVAILABLE(macosx(JSC_MAC_TBA), ios(JSC_IOS_TBA))
    3547@interface JSScript : NSObject
    3648
    3749/*!
     50 This SPI is deprecated and should not be used. Use "scriptOfType:withSource:andSourceURL:andBytecodeCache:inVirtualMachine:error:" instead.
     51 */
     52+ (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));
     53
     54/*!
     55 This SPI is deprecated and should not be used. Use "scriptOfType:memoryMappedFromASCIIFile:withSourceURL:andBytecodeCache:inVirtualMachine:error:" instead.
     56 */
     57+ (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));
     58
     59/*!
     60 This API is deprecated and should not be used.
     61 */
     62+ (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));
     63
     64/*!
    3865 @method
    3966 @abstract Create a JSScript for the specified virtual machine.
     67 @param type The type of JavaScript source.
    4068 @param source The source code to use when the script is evaluated by the JS vm.
     69 @param sourceURL The source URL to associate with this script. For modules, this is the module identifier.
     70 @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.
    4171 @param vm The JSVirtualMachine the script can be evaluated in.
     72 @param error A description of why the script could not be created if the result is nil.
    4273 @result The new script.
     74 @discussion The file at cachePath should not be externally modified for the lifecycle of vm.
    4375 */
    44 + (nullable instancetype)scriptWithSource:(NSString *)source inVirtualMachine:(JSVirtualMachine *)vm;
     76+ (nullable instancetype)scriptOfType:(JSScriptType)type withSource:(NSString *)source andSourceURL:(NSURL *)sourceURL andBytecodeCache:(nullable NSURL *)cachePath inVirtualMachine:(JSVirtualMachine *)vm error:(out NSError * _Nullable * _Nullable)error;
    4577
    4678/*!
    4779 @method
    4880 @abstract Create a JSScript for the specified virtual machine with a path to a codesigning and bytecode caching.
     81 @param type The type of JavaScript source.
    4982 @param filePath A URL containing the path to a JS source code file on disk.
     83 @param sourceURL The source URL to associate with this script. For modules, this is the module identifier.
     84 @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.
    5085 @param vm The JSVirtualMachine the script can be evaluated in.
    51  @param codeSigningPath A URL containing the path to the codeSigning file for filePath on disk.
    52  @param cachePath A URL containing the path where the VM should cache for future execution.
     86 @param error A description of why the script could not be created if the result is nil.
    5387 @result The new script.
    54  @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.
     88 @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.
    5589
    5690 If the file at filePath is not ascii this method will return nil.
    5791 */
    58 + (nullable instancetype)scriptFromASCIIFile:(NSURL *)filePath inVirtualMachine:(JSVirtualMachine *)vm withCodeSigning:(nullable NSURL *)codeSigningPath andBytecodeCache:(nullable NSURL *)cachePath;
    59 
     92+ (nullable instancetype)scriptOfType:(JSScriptType)type memoryMappedFromASCIIFile:(NSURL *)filePath withSourceURL:(NSURL *)sourceURL andBytecodeCache:(nullable NSURL *)cachePath inVirtualMachine:(JSVirtualMachine *)vm error:(out NSError * _Nullable * _Nullable)error;
    6093
    6194/*!
    62  This is deprecated and is equivalent to scriptFromASCIIFile:inVirtualMachine:withCodeSigning:andBytecodeCache:.
     95 @method
     96 @abstract Cache the bytecode for this JSScript to disk at the path passed in during creation.
     97 @param error A description of why the script could not be cached if the result is FALSE.
    6398 */
    64 + (nullable instancetype)scriptFromUTF8File:(NSURL *)filePath inVirtualMachine:(JSVirtualMachine *)vm withCodeSigning:(nullable NSURL *)codeSigningPath andBytecodeCache:(nullable NSURL *)cachePath;
     99- (BOOL)cacheBytecodeWithError:(out NSError * _Nullable * _Nullable)error;
     100
     101/*!
     102 @method
     103 @abstract Returns the JSScriptType of this JSScript.
     104 */
     105- (JSScriptType)type;
     106
     107/*!
     108 @method
     109 @abstract Returns the sourceURL of this JSScript.
     110 */
     111- (NSURL *)sourceURL;
    65112
    66113@end
  • trunk/Source/JavaScriptCore/API/JSScript.mm

    r241340 r241929  
    3737#import "Symbol.h"
    3838#include <sys/stat.h>
     39#include <wtf/FileSystem.h>
    3940
    4041#if JSC_OBJC_API_ENABLED
     
    4243@implementation JSScript {
    4344    __weak JSVirtualMachine* m_virtualMachine;
     45    JSScriptType m_type;
     46    FileSystem::MappedFileData m_mappedSource;
    4447    String m_source;
     48    RetainPtr<NSURL> m_sourceURL;
    4549    RetainPtr<NSURL> m_cachePath;
    4650    JSC::CachedBytecode m_cachedBytecode;
     
    5458    result->m_source = source;
    5559    result->m_virtualMachine = vm;
     60    result->m_type = kJSScriptTypeModule;
    5661    return result;
    5762}
     
    9196+ (instancetype)scriptFromASCIIFile:(NSURL *)filePath inVirtualMachine:(JSVirtualMachine *)vm withCodeSigning:(NSURL *)codeSigningPath andBytecodeCache:(NSURL *)cachePath
    9297{
    93     // FIXME: This should check codeSigning.
    9498    UNUSED_PARAM(codeSigningPath);
     99    UNUSED_PARAM(cachePath);
     100
    95101    URL filePathURL([filePath absoluteURL]);
    96102    if (!filePathURL.isLocalFile())
    97103        return nil;
    98     // FIXME: This should mmap the contents of the file instead of copying it into dirty memory.
     104
    99105    Vector<LChar> buffer;
    100106    if (!fillBufferWithContentsOfFile(filePathURL.fileSystemPath(), buffer))
     
    107113    result->m_virtualMachine = vm;
    108114    result->m_source = String::fromUTF8WithLatin1Fallback(buffer.data(), buffer.size());
     115    result->m_type = kJSScriptTypeModule;
     116    return result;
     117}
     118
     119+ (instancetype)scriptFromUTF8File:(NSURL *)filePath inVirtualMachine:(JSVirtualMachine *)vm withCodeSigning:(NSURL *)codeSigningPath andBytecodeCache:(NSURL *)cachePath
     120{
     121    return [JSScript scriptFromASCIIFile:filePath inVirtualMachine:vm withCodeSigning:codeSigningPath andBytecodeCache:cachePath];
     122}
     123
     124static JSScript *createError(NSString *message, NSError** error)
     125{
     126    if (error)
     127        *error = [NSError errorWithDomain:@"JSScriptErrorDomain" code:1 userInfo:@{ @"message": message }];
     128    return nil;
     129}
     130
     131+ (instancetype)scriptOfType:(JSScriptType)type withSource:(NSString *)source andSourceURL:(NSURL *)sourceURL andBytecodeCache:(NSURL *)cachePath inVirtualMachine:(JSVirtualMachine *)vm error:(out NSError **)error
     132{
     133    UNUSED_PARAM(error);
     134    JSScript *result = [[[JSScript alloc] init] autorelease];
     135    result->m_virtualMachine = vm;
     136    result->m_type = type;
     137    result->m_source = source;
     138    result->m_sourceURL = sourceURL;
    109139    result->m_cachePath = cachePath;
    110140    [result readCache];
     
    112142}
    113143
    114 + (instancetype)scriptFromUTF8File:(NSURL *)filePath inVirtualMachine:(JSVirtualMachine *)vm withCodeSigning:(NSURL *)codeSigningPath andBytecodeCache:(NSURL *)cachePath
    115 {
    116     return [JSScript scriptFromASCIIFile:filePath inVirtualMachine:vm withCodeSigning:codeSigningPath andBytecodeCache:cachePath];
     144+ (instancetype)scriptOfType:(JSScriptType)type memoryMappedFromASCIIFile:(NSURL *)filePath withSourceURL:(NSURL *)sourceURL andBytecodeCache:(NSURL *)cachePath inVirtualMachine:(JSVirtualMachine *)vm error:(out NSError **)error
     145{
     146    UNUSED_PARAM(error);
     147    URL filePathURL([filePath absoluteURL]);
     148    if (!filePathURL.isLocalFile())
     149        return createError([NSString stringWithFormat:@"File path %@ is not a local file", static_cast<NSString *>(filePathURL)], error);
     150
     151    bool success = false;
     152    String systemPath = filePathURL.fileSystemPath();
     153    FileSystem::MappedFileData fileData(systemPath, success);
     154    if (!success)
     155        return createError([NSString stringWithFormat:@"File at path %@ could not be mapped.", static_cast<NSString *>(systemPath)], error);
     156
     157    if (!charactersAreAllASCII(reinterpret_cast<const LChar*>(fileData.data()), fileData.size()))
     158        return createError([NSString stringWithFormat:@"Not all characters in file at %@ are ASCII.", static_cast<NSString *>(systemPath)], error);
     159
     160    JSScript *result = [[[JSScript alloc] init] autorelease];
     161    result->m_virtualMachine = vm;
     162    result->m_type = type;
     163    result->m_source = String(StringImpl::createWithoutCopying(bitwise_cast<const LChar*>(fileData.data()), fileData.size()));
     164    result->m_mappedSource = WTFMove(fileData);
     165    result->m_sourceURL = sourceURL;
     166    result->m_cachePath = cachePath;
     167    [result readCache];
     168    return result;
    117169}
    118170
     
    153205}
    154206
    155 - (void)writeCache
    156 {
    157     if (m_cachedBytecode.size() || !m_cachePath)
    158         return;
    159 
    160     JSC::ParserError error;
    161     m_cachedBytecode = JSC::generateModuleBytecode(m_virtualMachine.vm, m_jsSourceCode->sourceCode(), error);
    162     if (error.isValid())
    163         return;
     207- (BOOL)cacheBytecodeWithError:(NSError **)error
     208{
     209    String errorString { };
     210    [self writeCache:errorString];
     211    if (!errorString.isNull()) {
     212        createError(errorString, error);
     213        return NO;
     214    }
     215
     216    return YES;
     217}
     218
     219- (NSURL *)sourceURL
     220{
     221    return m_sourceURL.get();
     222}
     223
     224- (JSScriptType)type
     225{
     226    return m_type;
     227}
     228
     229@end
     230
     231@implementation JSScript(Internal)
     232
     233- (unsigned)hash
     234{
     235    return m_source.hash();
     236}
     237
     238- (const String&)source
     239{
     240    return m_source;
     241}
     242
     243- (const JSC::CachedBytecode*)cachedBytecode
     244{
     245    return &m_cachedBytecode;
     246}
     247
     248- (JSC::JSSourceCode*)jsSourceCode
     249{
     250    if (m_jsSourceCode)
     251        return m_jsSourceCode.get();
     252
     253    return [self forceRecreateJSSourceCode];
     254}
     255
     256- (BOOL)writeCache:(String&)error
     257{
     258    if (m_cachedBytecode.size())
     259        return YES;
     260
     261    if (!m_cachePath) {
     262        error = "No cache was path provided during construction of this JSScript."_s;
     263        return NO;
     264    }
     265
     266    JSC::ParserError parserError;
     267    switch (m_type) {
     268    case kJSScriptTypeModule:
     269        m_cachedBytecode = JSC::generateModuleBytecode(m_virtualMachine.vm, [self jsSourceCode]->sourceCode(), parserError);
     270        break;
     271    case kJSScriptTypeProgram:
     272        m_cachedBytecode = JSC::generateBytecode(m_virtualMachine.vm, [self jsSourceCode]->sourceCode(), parserError);
     273        break;
     274    }
     275
     276    if (parserError.isValid()) {
     277        m_cachedBytecode = { };
     278        error = makeString("Unable to generate bytecode for this JSScript because of a parser error: ", parserError.message());
     279        return NO;
     280    }
     281
    164282    int fd = open([m_cachePath path].UTF8String, O_CREAT | O_WRONLY, 0666);
    165     if (fd == -1)
    166         return;
    167     int rc = flock(fd, LOCK_EX | LOCK_NB);
    168     if (!rc)
     283    if (fd == -1) {
     284        error = makeString("Unable to open file: ", [m_cachePath path].UTF8String, " due to error: ", strerror(errno));
     285        return NO;
     286    }
     287    int returnCode = flock(fd, LOCK_EX | LOCK_NB);
     288    if (returnCode)
     289        error = "Unable to lock the cache file; it may already be in use."_s;
     290    else
    169291        write(fd, m_cachedBytecode.data(), m_cachedBytecode.size());
    170292    close(fd);
    171 }
    172 
    173 @end
    174 
    175 @implementation JSScript(Internal)
    176 
    177 - (unsigned)hash
    178 {
    179     return m_source.hash();
    180 }
    181 
    182 - (const String&)source
    183 {
    184     return m_source;
    185 }
    186 
    187 - (const JSC::CachedBytecode*)cachedBytecode
    188 {
    189     return &m_cachedBytecode;
    190 }
    191 
    192 - (JSC::JSSourceCode*)jsSourceCode:(const JSC::Identifier&)moduleKey
    193 {
    194     if (m_jsSourceCode) {
    195         ASSERT(moduleKey.impl() == m_moduleKey);
    196         return m_jsSourceCode.get();
    197     }
    198 
     293    return !returnCode;
     294}
     295
     296- (void)setSourceURL:(NSURL *)url
     297{
     298    m_sourceURL = url;
     299}
     300
     301- (JSC::JSSourceCode*)forceRecreateJSSourceCode
     302{
    199303    JSC::VM& vm = m_virtualMachine.vm;
     304    JSC::JSLockHolder locker(vm);
     305
    200306    TextPosition startPosition { };
    201     Ref<JSScriptSourceProvider> sourceProvider = JSScriptSourceProvider::create(self, JSC::SourceOrigin(moduleKey.string()), URL({ }, moduleKey.string()), TextPosition(), JSC::SourceProviderSourceType::Module);
     307
     308    String url = String { [[self sourceURL] absoluteString] };
     309    auto type = m_type == kJSScriptTypeModule ? JSC::SourceProviderSourceType::Module : JSC::SourceProviderSourceType::Program;
     310    Ref<JSScriptSourceProvider> sourceProvider = JSScriptSourceProvider::create(self, JSC::SourceOrigin(url), URL({ }, url), startPosition, type);
    202311    JSC::SourceCode sourceCode(WTFMove(sourceProvider), startPosition.m_line.oneBasedInt(), startPosition.m_column.oneBasedInt());
    203312    JSC::JSSourceCode* jsSourceCode = JSC::JSSourceCode::create(vm, WTFMove(sourceCode));
    204313    m_jsSourceCode.set(vm, jsSourceCode);
    205     [self writeCache];
    206314    return jsSourceCode;
    207315}
     
    209317@end
    210318
    211 
    212319#endif
  • trunk/Source/JavaScriptCore/API/JSScriptInternal.h

    r240511 r241929  
    4747- (unsigned)hash;
    4848- (const WTF::String&)source;
    49 - (const JSC::CachedBytecode*)cachedBytecode;
    50 - (JSC::JSSourceCode*)jsSourceCode:(const JSC::Identifier&)moduleKey;
     49- (nullable const JSC::CachedBytecode*)cachedBytecode;
     50- (JSC::JSSourceCode*)jsSourceCode;
     51// FIXME: Remove this once we require sourceURL upon creation: https://bugs.webkit.org/show_bug.cgi?id=194909
     52- (JSC::JSSourceCode*)forceRecreateJSSourceCode;
     53- (BOOL)writeCache:(String&)error;
     54- (void)setSourceURL:(NSURL *)url;
    5155
    5256@end
  • trunk/Source/JavaScriptCore/API/tests/FunctionOverridesTest.cpp

    r236032 r241929  
    4545    const char* oldFunctionOverrides = Options::functionOverrides();
    4646   
    47     Options::functionOverrides() = "testapi-function-overrides.js";
     47    Options::functionOverrides() = "./testapiScripts/testapi-function-overrides.js";
    4848    JSC::FunctionOverrides::reinstallOverrides();
    4949
  • trunk/Source/JavaScriptCore/API/tests/testIncludes.m

    r240468 r241929  
    2727// Since we include files that haven't passed through the rewriter we need to handle the non-rewritten values...
    2828#define JSC_API_AVAILABLE(...)
     29#define JSC_API_DEPRECATED(...)
    2930#define JSC_CLASS_AVAILABLE(...)
    3031#define JSC_MAC_VERSION_TBA 0
  • trunk/Source/JavaScriptCore/API/tests/testapi.c

    r237440 r241929  
    4141#include "JSStringRefPrivate.h"
    4242#include "JSWeakPrivate.h"
     43#if !OS(WINDOWS)
     44#include <libgen.h>
     45#endif
     46#include <limits.h>
    4347#include <math.h>
    4448#include <stdio.h>
     
    4650#include <string.h>
    4751#include <time.h>
     52#if !OS(WINDOWS)
     53#include <unistd.h>
     54#endif
    4855#include <wtf/Assertions.h>
    4956
     
    6875
    6976#if JSC_OBJC_API_ENABLED
    70 void testObjectiveCAPI(void);
     77void testObjectiveCAPI(const char*);
    7178#endif
    7279
     
    13811388#endif
    13821389
     1390#if !OS(WINDOWS)
     1391    char resolvedPath[PATH_MAX];
     1392    realpath(argv[0], resolvedPath);
     1393    char* newCWD = dirname(resolvedPath);
     1394    if (chdir(newCWD))
     1395        fprintf(stdout, "Could not chdir to: %s\n", newCWD);
     1396#endif
     1397
     1398    const char* filter = argc > 1 ? argv[1] : NULL;
    13831399#if JSC_OBJC_API_ENABLED
    1384     testObjectiveCAPI();
     1400    testObjectiveCAPI(filter);
    13851401#endif
    13861402
    1387     const char* filter = argc > 1 ? argv[1] : NULL;
    13881403    RELEASE_ASSERT(!testCAPIViaCpp(filter));
    13891404    if (filter)
     
    19801995    JSClassRelease(nullClass);
    19811996
    1982     const char* scriptPath = "testapi.js";
     1997    const char* scriptPath = "./testapiScripts/testapi.js";
    19831998    char* scriptUTF8 = createStringWithContentsOfFile(scriptPath);
    19841999    if (!scriptUTF8) {
     
    20832098        JSGlobalContextRelease(context);
    20842099    }
    2085     failed = testTypedArrayCAPI() || failed;
    2086     failed = testExecutionTimeLimit() || failed;
    2087     failed = testFunctionOverrides() || failed;
    2088     failed = testGlobalContextWithFinalizer() || failed;
    2089     failed = testPingPongStackOverflow() || failed;
    2090     failed = testJSONParse() || failed;
    2091     failed = testJSObjectGetProxyTarget() || failed;
     2100    failed |= testTypedArrayCAPI();
     2101    failed |= testExecutionTimeLimit();
     2102    failed |= testFunctionOverrides();
     2103    failed |= testGlobalContextWithFinalizer();
     2104    failed |= testPingPongStackOverflow();
     2105    failed |= testJSONParse();
     2106    failed |= testJSObjectGetProxyTarget();
    20922107
    20932108    // Clear out local variables pointing at JSObjectRefs to allow their values to be collected
  • trunk/Source/JavaScriptCore/API/tests/testapi.mm

    r240821 r241929  
    4242#import "Regress141809.h"
    4343
     44#if __has_include(<libproc.h>)
     45#define HAS_LIBPROC 1
     46#import <libproc.h>
     47#else
     48#define HAS_LIBPROC 0
     49#endif
    4450#import <pthread.h>
    4551#import <vector>
     
    5561
    5662extern int failed;
    57 extern "C" void testObjectiveCAPI(void);
     63extern "C" void testObjectiveCAPI(const char*);
    5864extern "C" void checkResult(NSString *, bool);
    5965
     
    19811987}
    19821988
    1983 static void testBytecodeCache()
    1984 {
    1985     @autoreleasepool {
    1986         NSURL* tempDirectory = [NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES];
    1987 
    1988         NSString* fooSource = @"import { n } from \"../foo.js\"; export let foo = n;";
    1989         NSString* barSource = @"import \"otherDirectory/baz.js\"; export let n = null;";
    1990         NSString* bazSource = @"import { foo } from \"../directory/bar.js\"; globalThis.ran = null; export let exp = foo;";
    1991 
    1992         NSURL* fooPath = [tempDirectory URLByAppendingPathComponent:@"foo.js"];
    1993         NSURL* barPath = [tempDirectory URLByAppendingPathComponent:@"bar.js"];
    1994         NSURL* bazPath = [tempDirectory URLByAppendingPathComponent:@"baz.js"];
    1995 
    1996         NSURL* fooCachePath = [tempDirectory URLByAppendingPathComponent:@"foo.js.cache"];
    1997         NSURL* barCachePath = [tempDirectory URLByAppendingPathComponent:@"bar.js.cache"];
    1998         NSURL* bazCachePath = [tempDirectory URLByAppendingPathComponent:@"baz.js.cache"];
     1989static NSURL *tempFile(NSString *string)
     1990{
     1991    NSURL* tempDirectory = [NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES];
     1992    return [tempDirectory URLByAppendingPathComponent:string];
     1993}
     1994
     1995static void testModuleBytecodeCache()
     1996{
     1997    @autoreleasepool {
     1998        NSString *fooSource = @"import 'otherDirectory/baz.js'; export let n = null;";
     1999        NSString *barSource = @"import { n } from '../foo.js'; export let foo = () => n;";
     2000        NSString *bazSource = @"import { foo } from '../directory/bar.js'; globalThis.ran = null; export let exp = foo();";
     2001
     2002        NSURL *fooPath = tempFile(@"foo.js");
     2003        NSURL *barPath = tempFile(@"bar.js");
     2004        NSURL *bazPath = tempFile(@"baz.js");
     2005
     2006        NSURL *fooCachePath = tempFile(@"foo.js.cache");
     2007        NSURL *barCachePath = tempFile(@"bar.js.cache");
     2008        NSURL *bazCachePath = tempFile(@"baz.js.cache");
     2009
     2010        NSURL *fooFakePath = [NSURL fileURLWithPath:@"/foo.js"];
     2011        NSURL *barFakePath = [NSURL fileURLWithPath:@"/directory/bar.js"];
     2012        NSURL *bazFakePath = [NSURL fileURLWithPath:@"/otherDirectory/baz.js"];
    19992013
    20002014        [fooSource writeToURL:fooPath atomically:NO encoding:NSASCIIStringEncoding error:nil];
     
    20042018        auto block = ^(JSContext *context, JSValue *identifier, JSValue *resolve, JSValue *reject) {
    20052019            JSC::Options::forceDiskCache() = true;
    2006             if ([identifier isEqualToObject:@"file:///directory/bar.js"])
    2007                 [resolve callWithArguments:@[[JSScript scriptFromASCIIFile:fooPath inVirtualMachine:context.virtualMachine withCodeSigning:nil andBytecodeCache:fooCachePath]]];
    2008             else if ([identifier isEqualToObject:@"file:///foo.js"])
    2009                 [resolve callWithArguments:@[[JSScript scriptFromASCIIFile:barPath inVirtualMachine:context.virtualMachine withCodeSigning:nil andBytecodeCache:barCachePath]]];
    2010             else if ([identifier isEqualToObject:@"file:///otherDirectory/baz.js"])
    2011                 [resolve callWithArguments:@[[JSScript scriptFromASCIIFile:bazPath inVirtualMachine:context.virtualMachine withCodeSigning:nil andBytecodeCache:bazCachePath]]];
    2012             else
     2020            JSScript *script = nil;
     2021            if ([identifier isEqualToObject:[fooFakePath absoluteString]])
     2022                script = [JSScript scriptOfType:kJSScriptTypeModule memoryMappedFromASCIIFile:fooPath withSourceURL:fooFakePath andBytecodeCache:fooCachePath inVirtualMachine:context.virtualMachine error:nil];
     2023            else if ([identifier isEqualToObject:[barFakePath absoluteString]])
     2024                script = [JSScript scriptOfType:kJSScriptTypeModule memoryMappedFromASCIIFile:barPath withSourceURL:barFakePath andBytecodeCache:barCachePath inVirtualMachine:context.virtualMachine error:nil];
     2025            else if ([identifier isEqualToObject:[bazFakePath absoluteString]])
     2026                script = [JSScript scriptOfType:kJSScriptTypeModule memoryMappedFromASCIIFile:bazPath withSourceURL:bazFakePath andBytecodeCache:bazCachePath inVirtualMachine:context.virtualMachine error:nil];
     2027
     2028            if (script) {
     2029                if (![script cacheBytecodeWithError:nil])
     2030                    CRASH();
     2031                [resolve callWithArguments:@[script]];
     2032            } else
    20132033                [reject callWithArguments:@[[JSValue valueWithNewErrorFromMessage:@"Weird path" inContext:context]]];
    20142034        };
     
    20242044
    20252045        NSFileManager* fileManager = [NSFileManager defaultManager];
    2026         [fileManager removeItemAtURL:fooPath error:nil];
    2027         [fileManager removeItemAtURL:barPath error:nil];
    2028         [fileManager removeItemAtURL:bazPath error:nil];
    2029         [fileManager removeItemAtURL:fooCachePath error:nil];
    2030         [fileManager removeItemAtURL:barCachePath error:nil];
    2031         [fileManager removeItemAtURL:bazCachePath error:nil];
     2046        BOOL removedAll = true;
     2047        removedAll &= [fileManager removeItemAtURL:fooPath error:nil];
     2048        removedAll &= [fileManager removeItemAtURL:barPath error:nil];
     2049        removedAll &= [fileManager removeItemAtURL:bazPath error:nil];
     2050        removedAll &= [fileManager removeItemAtURL:fooCachePath error:nil];
     2051        removedAll &= [fileManager removeItemAtURL:barCachePath error:nil];
     2052        removedAll &= [fileManager removeItemAtURL:bazCachePath error:nil];
     2053        checkResult(@"Removed all temp files created", removedAll);
     2054    }
     2055}
     2056
     2057static void testProgramBytecodeCache()
     2058{
     2059    @autoreleasepool {
     2060        NSString *fooSource = @"function foo() { return 42; }; function bar() { return 40; }; foo() + bar();";
     2061        NSURL *fooCachePath = tempFile(@"foo.js.cache");
     2062        JSContext *context = [[JSContext alloc] init];
     2063        JSScript *script = [JSScript scriptOfType:kJSScriptTypeProgram withSource:fooSource andSourceURL:[NSURL URLWithString:@"my-path"] andBytecodeCache:fooCachePath inVirtualMachine:context.virtualMachine error:nil];
     2064        RELEASE_ASSERT(script);
     2065        if (![script cacheBytecodeWithError:nil])
     2066            CRASH();
     2067
     2068        JSC::Options::forceDiskCache() = true;
     2069        JSValue *result = [context evaluateJSScript:script];
     2070        RELEASE_ASSERT(result);
     2071        RELEASE_ASSERT([result isNumber]);
     2072        checkResult(@"result of cached program is 40+42", [[result toNumber] intValue] == 40 + 42);
     2073        JSC::Options::forceDiskCache() = false;
     2074
     2075        NSFileManager* fileManager = [NSFileManager defaultManager];
     2076        BOOL removedAll = [fileManager removeItemAtURL:fooCachePath error:nil];
     2077        checkResult(@"Removed all temp files created", removedAll);
     2078    }
     2079}
     2080
     2081static void testBytecodeCacheWithSyntaxError(JSScriptType type)
     2082{
     2083    @autoreleasepool {
     2084        NSString *fooSource = @"this is a syntax error";
     2085        NSURL *fooCachePath = tempFile(@"foo.js.cache");
     2086        JSContext *context = [[JSContext alloc] init];
     2087        JSScript *script = [JSScript scriptOfType:type withSource:fooSource andSourceURL:[NSURL URLWithString:@"my-path"] andBytecodeCache:fooCachePath inVirtualMachine:context.virtualMachine error:nil];
     2088        RELEASE_ASSERT(script);
     2089        NSError *error = nil;
     2090        if ([script cacheBytecodeWithError:&error])
     2091            CRASH();
     2092        RELEASE_ASSERT(error);
     2093        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"]);
     2094    }
     2095}
     2096
     2097static void testProgramJSScriptException()
     2098{
     2099    @autoreleasepool {
     2100        NSString *source = @"throw 42;";
     2101        JSContext *context = [[JSContext alloc] init];
     2102        JSScript *script = [JSScript scriptOfType:kJSScriptTypeProgram withSource:source andSourceURL:[NSURL URLWithString:@"my-path"] andBytecodeCache:nil inVirtualMachine:context.virtualMachine error:nil];
     2103        RELEASE_ASSERT(script);
     2104        __block bool handledException = false;
     2105        context.exceptionHandler = ^(JSContext *, JSValue *exception) {
     2106            handledException = true;
     2107            RELEASE_ASSERT([exception isNumber]);
     2108            checkResult(@"Program JSScript with exception should have the exception value be 42.", [[exception toNumber] intValue] == 42);
     2109        };
     2110
     2111        JSValue *result = [context evaluateJSScript:script];
     2112        RELEASE_ASSERT(result);
     2113        checkResult(@"Program JSScript with exception should return undefined.", [result isUndefined]);
     2114        checkResult(@"Program JSScript with exception should call exception handler.", handledException);
    20322115    }
    20332116}
     
    20792162@end
    20802163
    2081 static void testLoadBasicFile()
     2164static void testLoadBasicFileLegacySPI()
    20822165{
    20832166    @autoreleasepool {
     
    20902173}
    20912174
    2092 void testObjectiveCAPI()
     2175
     2176@interface JSContextMemoryMappedLoaderDelegate : JSContext <JSModuleLoaderDelegate>
     2177
     2178+ (instancetype)newContext;
     2179
     2180@end
     2181
     2182@implementation JSContextMemoryMappedLoaderDelegate {
     2183}
     2184
     2185+ (instancetype)newContext
     2186{
     2187    auto *result = [[JSContextMemoryMappedLoaderDelegate alloc] init];
     2188    return result;
     2189}
     2190
     2191- (void)context:(JSContext *)context fetchModuleForIdentifier:(JSValue *)identifier withResolveHandler:(JSValue *)resolve andRejectHandler:(JSValue *)reject
     2192{
     2193    NSURL *filePath = [NSURL URLWithString:[identifier toString]];
     2194    auto *script = [JSScript scriptOfType:kJSScriptTypeModule memoryMappedFromASCIIFile:filePath withSourceURL:filePath andBytecodeCache:nil inVirtualMachine:context.virtualMachine error:nil];
     2195    if (script)
     2196        [resolve callWithArguments:@[script]];
     2197    else
     2198        [reject callWithArguments:@[[JSValue valueWithNewErrorFromMessage:@"Unable to create Script" inContext:context]]];
     2199}
     2200
     2201@end
     2202
     2203static void testLoadBasicFile()
     2204{
     2205#if HAS_LIBPROC
     2206    size_t count = proc_pidinfo(getpid(), PROC_PIDLISTFDS, 0, 0, 0);
     2207#endif
     2208    @autoreleasepool {
     2209        auto *context = [JSContextMemoryMappedLoaderDelegate newContext];
     2210        context.moduleLoaderDelegate = context;
     2211        JSValue *promise = [context evaluateScript:@"import('./basic.js');" withSourceURL:resolvePathToScripts()];
     2212        JSValue *null = [JSValue valueWithNullInContext:context];
     2213#if HAS_LIBPROC
     2214        size_t afterCount = proc_pidinfo(getpid(), PROC_PIDLISTFDS, 0, 0, 0);
     2215        checkResult(@"JSScript should not hold a file descriptor", count == afterCount);
     2216#endif
     2217        checkModuleCodeRan(context, promise, null);
     2218    }
     2219#if HAS_LIBPROC
     2220    size_t after = proc_pidinfo(getpid(), PROC_PIDLISTFDS, 0, 0, 0);
     2221    checkResult(@"File descriptor count sholudn't change after context is dealloced", count == after);
     2222#endif
     2223}
     2224
     2225@interface JSContextAugmentedLoaderDelegate : JSContext <JSModuleLoaderDelegate>
     2226
     2227+ (instancetype)newContext;
     2228
     2229@end
     2230
     2231@implementation JSContextAugmentedLoaderDelegate {
     2232}
     2233
     2234+ (instancetype)newContext
     2235{
     2236    auto *result = [[JSContextAugmentedLoaderDelegate alloc] init];
     2237    return result;
     2238}
     2239
     2240- (void)context:(JSContext *)context fetchModuleForIdentifier:(JSValue *)identifier withResolveHandler:(JSValue *)resolve andRejectHandler:(JSValue *)reject
     2241{
     2242    UNUSED_PARAM(reject);
     2243
     2244    NSURL *filePath = [NSURL URLWithString:[identifier toString]];
     2245    NSString *pathString = [filePath absoluteString];
     2246    if ([pathString containsString:@"basic.js"] || [pathString containsString:@"foo.js"]) {
     2247        auto *script = [JSScript scriptOfType:kJSScriptTypeModule memoryMappedFromASCIIFile:filePath withSourceURL:filePath andBytecodeCache:nil inVirtualMachine:context.virtualMachine error:nil];
     2248        RELEASE_ASSERT(script);
     2249        [resolve callWithArguments:@[script]];
     2250        return;
     2251    }
     2252
     2253    if ([pathString containsString:@"bar.js"]) {
     2254        auto *script = [JSScript scriptOfType:kJSScriptTypeModule withSource:@"" andSourceURL:[NSURL fileURLWithPath:@"/not/path/to/bar.js"] andBytecodeCache:nil inVirtualMachine:context.virtualMachine error:nil];
     2255        RELEASE_ASSERT(script);
     2256        [resolve callWithArguments:@[script]];
     2257        return;
     2258    }
     2259
     2260    RELEASE_ASSERT_NOT_REACHED();
     2261}
     2262
     2263@end
     2264
     2265static void testJSScriptURL()
     2266{
     2267    @autoreleasepool {
     2268        auto *context = [JSContextAugmentedLoaderDelegate newContext];
     2269        context.moduleLoaderDelegate = context;
     2270        NSURL *basic = [NSURL URLWithString:@"./basic.js" relativeToURL:resolvePathToScripts()];
     2271        JSScript *script1 = [JSScript scriptOfType:kJSScriptTypeModule memoryMappedFromASCIIFile:basic withSourceURL:basic andBytecodeCache:nil inVirtualMachine:context.virtualMachine error:nil];
     2272
     2273        JSValue *result1 = [context evaluateJSScript:script1];
     2274        JSValue *null = [JSValue valueWithNullInContext:context];
     2275        checkModuleCodeRan(context, result1, null);
     2276
     2277        NSURL *foo = [NSURL URLWithString:@"./foo.js" relativeToURL:resolvePathToScripts()];
     2278        JSScript *script2 = [JSScript scriptOfType:kJSScriptTypeModule memoryMappedFromASCIIFile:foo withSourceURL:foo andBytecodeCache:nil inVirtualMachine:context.virtualMachine error:nil];
     2279        RELEASE_ASSERT(script2);
     2280        JSValue *result2 = [context evaluateJSScript:script2];
     2281
     2282        __block bool wasRejected = false;
     2283        [result2 invokeMethod:@"catch" withArguments:@[^(JSValue *reason) {
     2284            wasRejected = [reason isObject];
     2285            RELEASE_ASSERT([[reason toString] containsString:@"The same JSScript was provided for two different identifiers"]);
     2286        }]];
     2287
     2288        checkResult(@"Module JSScript imported with different identifiers is rejected", wasRejected);
     2289    }
     2290}
     2291
     2292#define RUN(test) do { \
     2293        if (!shouldRun(#test)) \
     2294            break; \
     2295        NSLog(@"%s...\n", #test); \
     2296        test; \
     2297        NSLog(@"%s: done.\n", #test); \
     2298    } while (false)
     2299
     2300void testObjectiveCAPI(const char* filter)
    20932301{
    20942302    NSLog(@"Testing Objective-C API");
    20952303
    2096     checkNegativeNSIntegers();
    2097     runJITThreadLimitTests();
    2098 
    2099     testLoaderResolvesAbsoluteScriptURL();
    2100     testFetch();
    2101     testFetchWithTwoCycle();
    2102     testFetchWithThreeCycle();
    2103     testImportModuleTwice();
    2104     testBytecodeCache();
    2105 
    2106     testLoaderRejectsNilScriptURL();
    2107     testLoaderRejectsFailedFetch();
     2304    auto shouldRun = [&] (const char* test) -> bool {
     2305        if (filter)
     2306            return strcasestr(test, filter);
     2307        return true;
     2308    };
     2309
     2310    RUN(checkNegativeNSIntegers());
     2311    RUN(runJITThreadLimitTests());
     2312
     2313    RUN(testLoaderResolvesAbsoluteScriptURL());
     2314    RUN(testFetch());
     2315    RUN(testFetchWithTwoCycle());
     2316    RUN(testFetchWithThreeCycle());
     2317    RUN(testImportModuleTwice());
     2318    RUN(testModuleBytecodeCache());
     2319    RUN(testProgramBytecodeCache());
     2320    RUN(testBytecodeCacheWithSyntaxError(kJSScriptTypeProgram));
     2321    RUN(testBytecodeCacheWithSyntaxError(kJSScriptTypeModule));
     2322    RUN(testProgramJSScriptException());
     2323
     2324    RUN(testLoaderRejectsNilScriptURL());
     2325    RUN(testLoaderRejectsFailedFetch());
     2326
     2327    RUN(testJSScriptURL());
    21082328
    21092329    // File loading
    2110     testLoadBasicFile();
    2111 
    2112     promiseWithExecutor(Resolution::ResolveEager);
    2113     promiseWithExecutor(Resolution::RejectEager);
    2114     promiseWithExecutor(Resolution::ResolveLate);
    2115     promiseWithExecutor(Resolution::RejectLate);
    2116     promiseRejectOnJSException();
    2117     promiseCreateResolved();
    2118     promiseCreateRejected();
    2119     parallelPromiseResolveTest();
     2330    RUN(testLoadBasicFileLegacySPI());
     2331    RUN(testLoadBasicFile());
     2332
     2333    RUN(promiseWithExecutor(Resolution::ResolveEager));
     2334    RUN(promiseWithExecutor(Resolution::RejectEager));
     2335    RUN(promiseWithExecutor(Resolution::ResolveLate));
     2336    RUN(promiseWithExecutor(Resolution::RejectLate));
     2337    RUN(promiseRejectOnJSException());
     2338    RUN(promiseCreateResolved());
     2339    RUN(promiseCreateRejected());
     2340    RUN(parallelPromiseResolveTest());
    21202341
    21212342    testObjectiveCAPIMain();
     
    21242345#else
    21252346
    2126 void testObjectiveCAPI()
     2347void testObjectiveCAPI(const char*)
    21272348{
    21282349}
  • trunk/Source/JavaScriptCore/API/tests/testapiScripts/foo.js

    r241928 r241929  
    2424 */
    2525
    26 
    27 // Since we include files that haven't passed through the rewriter we need to handle the non-rewritten values...
    28 #define JSC_API_AVAILABLE(...)
    29 #define JSC_CLASS_AVAILABLE(...)
    30 #define JSC_MAC_VERSION_TBA 0
    31 #define JSC_IOS_VERSION_TBA 0
    32 
    33 // umbrella header
    34 #import <JavaScriptCore/JavaScriptCore.h>
    35 
    36 // private headers
    37 #import <JavaScriptCore/JSContextPrivate.h>
    38 #import <JavaScriptCore/JSScript.h>
    39 #import <JavaScriptCore/JSValuePrivate.h>
    40 #import <JavaScriptCore/JSVirtualMachinePrivate.h>
    41 
     26import "./bar.js";
  • trunk/Source/JavaScriptCore/ChangeLog

    r241927 r241929  
     12019-02-21  Saam Barati  <sbarati@apple.com>
     2
     3        Update JSScript SPI based on feedback
     4        https://bugs.webkit.org/show_bug.cgi?id=194517
     5
     6        Reviewed by Keith Miller.
     7
     8        This patch updates the JSScript SPI in the following ways:
     9        - JSScript can now represent both modules and programs. This is a property
     10        of the script determined during creation.
     11        - JSScript now takes a sourceURL during construction. For modules, this acts
     12        as the module identifier.
     13        - JSScript now has SPI for writing the cache out to disk. We don't do this
     14        automatically.
     15        - JSScript will load the bytecode cache on creation if it exists.
     16        - We retrofit these new requirements on the prior JSScript SPI that
     17        we're going to remove as soon as we can: https://bugs.webkit.org/show_bug.cgi?id=194909.
     18        Previous SPI assumes all JSScripts are modules. Previous SPI also assigns
     19        a sourceURL to the JSScript based on what the module loader decided the
     20        identifier should be. We'll remove this once we remove the old SPI.
     21       
     22        This patch also adds SPI to JSContext to evaluate a JSScript. For modules,
     23        this is like returning the result of doing dynamic import. For programs,
     24        this does normal program evaluation.
     25       
     26        This patch also fixes a bug in generateBytecode/generateModuleBytecode where
     27        we would try to cache the bytecode even if recursivelyGenerateUnlinkedCodeBlock
     28        returned null. E.g, if the script had a syntax error.
     29       
     30        When writing tests, I also discovered that someone previously broke
     31        testapi. This patch also fixes those failures. They were broken when
     32        we switched to using a testapiScripts directory to hold our test .js
     33        scripts.
     34
     35        * API/JSAPIGlobalObject.h:
     36        * API/JSAPIGlobalObject.mm:
     37        (JSC::JSAPIGlobalObject::moduleLoaderResolve):
     38        (JSC::JSAPIGlobalObject::moduleLoaderFetch):
     39        (JSC::JSAPIGlobalObject::loadAndEvaluateJSScriptModule):
     40        * API/JSBase.cpp:
     41        (JSEvaluateScriptInternal):
     42        (JSEvaluateScript):
     43        * API/JSBaseInternal.h: Added.
     44        * API/JSContext.mm:
     45        (-[JSContext evaluateScript:withSourceURL:]):
     46        (-[JSContext evaluateJSScript:]):
     47        * API/JSContextPrivate.h:
     48        * API/JSScript.h:
     49        * API/JSScript.mm:
     50        (+[JSScript scriptWithSource:inVirtualMachine:]):
     51        (+[JSScript scriptFromASCIIFile:inVirtualMachine:withCodeSigning:andBytecodeCache:]):
     52        (createError):
     53        (+[JSScript scriptOfType:inVirtualMachine:withSourceURL:andSource:andBytecodeCache:error:]):
     54        (+[JSScript scriptOfType:inVirtualMachine:memoryMappedFromASCIIFile:withSourceURL:andBytecodeCache:error:]):
     55        (-[JSScript cacheBytecodeWithError:]):
     56        (-[JSScript sourceURL]):
     57        (-[JSScript type]):
     58        (-[JSScript jsSourceCode]):
     59        (-[JSScript writeCache:]):
     60        (-[JSScript setSourceURL:]):
     61        (-[JSScript forceRecreateJSSourceCode]):
     62        (-[JSScript writeCache]): Deleted.
     63        (-[JSScript jsSourceCode:]): Deleted.
     64        * API/JSScriptInternal.h:
     65        * API/tests/FunctionOverridesTest.cpp:
     66        (testFunctionOverrides):
     67        * API/tests/testapi.c:
     68        (main):
     69        * API/tests/testapi.mm:
     70        (tempFile):
     71        (testModuleBytecodeCache):
     72        (testProgramBytecodeCache):
     73        (testBytecodeCacheWithSyntaxError):
     74        (testProgramJSScriptException):
     75        (testLoadBasicFileLegacySPI):
     76        (+[JSContextMemoryMappedLoaderDelegate newContext]):
     77        (-[JSContextMemoryMappedLoaderDelegate context:fetchModuleForIdentifier:withResolveHandler:andRejectHandler:]):
     78        (testLoadBasicFile):
     79        (+[JSContextAugmentedLoaderDelegate newContext]):
     80        (-[JSContextAugmentedLoaderDelegate context:fetchModuleForIdentifier:withResolveHandler:andRejectHandler:]):
     81        (testJSScriptURL):
     82        (testObjectiveCAPI):
     83        (testBytecodeCache): Deleted.
     84        * API/tests/testapiScripts/foo.js: Added.
     85        * JavaScriptCore.xcodeproj/project.pbxproj:
     86        * runtime/Completion.cpp:
     87        (JSC::generateBytecode):
     88        (JSC::generateModuleBytecode):
     89
    1902019-02-21  Mark Lam  <mark.lam@apple.com>
    291
  • trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj

    r241841 r241929  
    885885                52C0611F1AA51E1C00B4ADBA /* RuntimeType.h in Headers */ = {isa = PBXBuildFile; fileRef = 52C0611D1AA51E1B00B4ADBA /* RuntimeType.h */; settings = {ATTRIBUTES = (Private, ); }; };
    886886                52C952B719A289850069B386 /* TypeProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 52C952B619A289850069B386 /* TypeProfiler.h */; settings = {ATTRIBUTES = (Private, ); }; };
     887                52D13091221CE176009C836C /* foo.js in Copy Support Script */ = {isa = PBXBuildFile; fileRef = 52D1308F221CE03A009C836C /* foo.js */; };
    887888                52F6C35E1E71EB080081F4CC /* WebAssemblyWrapperFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 52F6C35C1E71EB080081F4CC /* WebAssemblyWrapperFunction.h */; };
    888889                530A66B91FA3E78B0026A545 /* UnifiedSource3-mm.mm in Sources */ = {isa = PBXBuildFile; fileRef = 530A66B11FA3E77A0026A545 /* UnifiedSource3-mm.mm */; };
     
    11611162                7980C16D1E3A940E00B71615 /* DFGRegisteredStructureSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 7980C16B1E3A940E00B71615 /* DFGRegisteredStructureSet.h */; settings = {ATTRIBUTES = (Private, ); }; };
    11621163                7986943B1F8C0ACC009232AE /* StructureCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 7986943A1F8C0AC8009232AE /* StructureCache.h */; settings = {ATTRIBUTES = (Private, ); }; };
     1164                79872C48221BBAF3008C6969 /* JSBaseInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 79872C47221BBAED008C6969 /* JSBaseInternal.h */; };
    11631165                798937791DCAB57300F8D4FB /* JSFixedArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 798937771DCAB57300F8D4FB /* JSFixedArray.h */; settings = {ATTRIBUTES = (Private, ); }; };
    11641166                799EF7C41C56ED96002B0534 /* B3PCToOriginMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 799EF7C31C56ED96002B0534 /* B3PCToOriginMap.h */; settings = {ATTRIBUTES = (Private, ); }; };
     
    20302032                        dstSubfolderSpec = 16;
    20312033                        files = (
     2034                                52D13091221CE176009C836C /* foo.js in Copy Support Script */,
    20322035                                53C3D5E521ECE7720087FDFC /* basic.js in Copy Support Script */,
    20332036                                FECB8B2A1D25CB5A006F2463 /* testapi-function-overrides.js in Copy Support Script */,
     
    33783381                52C952B619A289850069B386 /* TypeProfiler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TypeProfiler.h; sourceTree = "<group>"; };
    33793382                52C952B819A28A1C0069B386 /* TypeProfiler.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TypeProfiler.cpp; sourceTree = "<group>"; };
     3383                52D1308F221CE03A009C836C /* foo.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = foo.js; sourceTree = "<group>"; };
    33803384                52F6C35B1E71EB080081F4CC /* WebAssemblyWrapperFunction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = WebAssemblyWrapperFunction.cpp; path = js/WebAssemblyWrapperFunction.cpp; sourceTree = "<group>"; };
    33813385                52F6C35C1E71EB080081F4CC /* WebAssemblyWrapperFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WebAssemblyWrapperFunction.h; path = js/WebAssemblyWrapperFunction.h; sourceTree = "<group>"; };
     
    37733777                798694391F8C0AC7009232AE /* StructureCache.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = StructureCache.cpp; sourceTree = "<group>"; };
    37743778                7986943A1F8C0AC8009232AE /* StructureCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StructureCache.h; sourceTree = "<group>"; };
     3779                79872C47221BBAED008C6969 /* JSBaseInternal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSBaseInternal.h; sourceTree = "<group>"; };
    37753780                798937761DCAB57300F8D4FB /* JSFixedArray.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSFixedArray.cpp; sourceTree = "<group>"; };
    37763781                798937771DCAB57300F8D4FB /* JSFixedArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSFixedArray.h; sourceTree = "<group>"; };
     
    59966001                                1421359A0A677F4F00A8195E /* JSBase.cpp */,
    59976002                                142711380A460BBB0080EEEA /* JSBase.h */,
     6003                                79872C47221BBAED008C6969 /* JSBaseInternal.h */,
    59986004                                140D17D60E8AD4A9000CD17D /* JSBasePrivate.h */,
    59996005                                1440F8AD0A508D200005F061 /* JSCallbackConstructor.cpp */,
     
    63416347                        children = (
    63426348                                53C3D5E421ECE6CE0087FDFC /* basic.js */,
     6349                                52D1308F221CE03A009C836C /* foo.js */,
    63436350                        );
    63446351                        name = testapiScripts;
     
    97599766                                0FF42749158EBE91004CB9FF /* udis86_types.h in Headers */,
    97609767                                A7E5AB391799E4B200D2833D /* UDis86Disassembler.h in Headers */,
     9768                                79872C48221BBAF3008C6969 /* JSBaseInternal.h in Headers */,
    97619769                                A7A8AF4117ADB5F3005AB174 /* Uint16Array.h in Headers */,
    97629770                                866739D313BFDE710023D87C /* Uint16WithFraction.h in Headers */,
  • trunk/Source/JavaScriptCore/config.h

    r239933 r241929  
    2626#define JSC_API_AVAILABLE(...)
    2727#define JSC_CLASS_AVAILABLE(...) JS_EXPORT
     28#define JSC_API_DEPRECATED(...)
    2829// Use zero since it will be less than any possible version number.
    2930#define JSC_MAC_VERSION_TBA 0
  • trunk/Source/JavaScriptCore/runtime/Completion.cpp

    r240511 r241929  
    104104
    105105    UnlinkedCodeBlock* unlinkedCodeBlock = recursivelyGenerateUnlinkedCodeBlock<UnlinkedProgramCodeBlock>(vm, source, strictMode, scriptMode, debuggerMode, error, evalContextType, &variablesUnderTDZ);
     106    if (!unlinkedCodeBlock)
     107        return { };
    106108    return serializeBytecode(vm, unlinkedCodeBlock, source, SourceCodeType::ProgramType, strictMode, scriptMode, debuggerMode);
    107109}
     
    119121
    120122    UnlinkedCodeBlock* unlinkedCodeBlock = recursivelyGenerateUnlinkedCodeBlock<UnlinkedModuleProgramCodeBlock>(vm, source, strictMode, scriptMode, debuggerMode, error, evalContextType, &variablesUnderTDZ);
     123    if (!unlinkedCodeBlock)
     124        return { };
    121125    return serializeBytecode(vm, unlinkedCodeBlock, source, SourceCodeType::ModuleType, strictMode, scriptMode, debuggerMode);
    122126}
Note: See TracChangeset for help on using the changeset viewer.