From 40a37d088818fc2fbeba2ef850dbcaaf294befbf Mon Sep 17 00:00:00 2001 From: Apple Date: Fri, 6 Mar 2015 22:29:10 +0000 Subject: [PATCH] JavaScriptCore-7600.1.4.15.12.tar.gz --- API/JSObjectRef.cpp | 23 + API/JSWeakObjectMapRefPrivate.cpp | 4 +- API/JSWrapperMap.mm | 59 +- API/tests/CustomGlobalObjectClassTest.c | 45 ++ API/tests/CustomGlobalObjectClassTest.h | 2 + API/tests/Regress141809.h | 34 + API/tests/Regress141809.mm | 134 ++++ API/tests/testapi.c | 2 + API/tests/testapi.mm | 27 +- ChangeLog | 678 ++++++++++++++++++ Configurations/LLVMForJSC.xcconfig | 11 +- Configurations/Version.xcconfig | 4 +- JavaScriptCore.xcodeproj/project.pbxproj | 6 + assembler/ARMv7Assembler.h | 3 +- bytecompiler/NodesCodegen.cpp | 80 +-- dfg/DFGArrayMode.h | 9 +- dfg/DFGGraph.h | 8 + dfg/DFGPlan.cpp | 5 +- dfg/DFGStackLayoutPhase.cpp | 4 +- disassembler/ARMv7/ARMv7DOpcode.cpp | 23 +- disassembler/ARMv7/ARMv7DOpcode.h | 14 +- ftl/FTLLowerDFGToLLVM.cpp | 178 +++-- ftl/FTLLowerDFGToLLVM.h | 2 +- heap/Heap.cpp | 7 +- heap/Heap.h | 2 +- heap/MachineStackMarker.cpp | 87 ++- heap/MachineStackMarker.h | 27 +- inspector/InjectedScript.cpp | 3 +- inspector/InjectedScript.h | 2 +- inspector/InjectedScriptSource.js | 177 +++-- .../JSGlobalObjectInspectorController.cpp | 18 + inspector/JSGlobalObjectInspectorController.h | 2 +- inspector/agents/InspectorRuntimeAgent.cpp | 4 +- inspector/agents/InspectorRuntimeAgent.h | 2 +- inspector/protocol/Runtime.json | 3 +- interpreter/Interpreter.cpp | 8 +- jit/JITOpcodes.cpp | 5 + jit/RegisterSet.cpp | 1 - llint/LowLevelInterpreter.asm | 2 + llint/LowLevelInterpreter64.asm | 5 + llvm/LLVMAPIFunctions.h | 2 - llvm/library/LLVMExports.cpp | 1 - parser/ParserArena.h | 5 + runtime/MapData.cpp | 2 + runtime/VM.cpp | 1 + runtime/VM.h | 1 + ...ent-by-val-inlined-no-formal-parameters.js | 33 + tests/stress/regress-141489.js | 34 + .../throw-from-ftl-call-ic-slow-path-cells.js | 192 +++++ ...ow-from-ftl-call-ic-slow-path-undefined.js | 192 +++++ .../throw-from-ftl-call-ic-slow-path.js | 192 +++++ 51 files changed, 2090 insertions(+), 275 deletions(-) create mode 100644 API/tests/Regress141809.h create mode 100644 API/tests/Regress141809.mm create mode 100644 tests/stress/get-my-argument-by-val-inlined-no-formal-parameters.js create mode 100644 tests/stress/regress-141489.js create mode 100644 tests/stress/throw-from-ftl-call-ic-slow-path-cells.js create mode 100644 tests/stress/throw-from-ftl-call-ic-slow-path-undefined.js create mode 100644 tests/stress/throw-from-ftl-call-ic-slow-path.js diff --git a/API/JSObjectRef.cpp b/API/JSObjectRef.cpp index b67c40b..dfad3bd 100644 --- a/API/JSObjectRef.cpp +++ b/API/JSObjectRef.cpp @@ -302,6 +302,14 @@ void JSObjectSetPrototype(JSContextRef ctx, JSObjectRef object, JSValueRef value JSObject* jsObject = toJS(object); JSValue jsValue = toJS(exec, value); + if (JSProxy* proxy = jsDynamicCast(jsObject)) { + if (JSGlobalObject* globalObject = jsDynamicCast(proxy->target())) { + globalObject->resetPrototype(exec->vm(), jsValue.isObject() ? jsValue : jsNull()); + return; + } + // Someday we might use proxies for something other than JSGlobalObjects, but today is not that day. + RELEASE_ASSERT_NOT_REACHED(); + } jsObject->setPrototypeWithCycleCheck(exec, jsValue.isObject() ? jsValue : jsNull()); } @@ -501,6 +509,11 @@ JSValueRef JSObjectGetPrivateProperty(JSContextRef ctx, JSObjectRef object, JSSt JSObject* jsObject = toJS(object); JSValue result; Identifier name(propertyName->identifier(&exec->vm())); + + // Get wrapped object if proxied + if (jsObject->inherits(JSProxy::info())) + jsObject = jsCast(jsObject)->target(); + if (jsObject->inherits(JSCallbackObject::info())) result = jsCast*>(jsObject)->getPrivateProperty(name); else if (jsObject->inherits(JSCallbackObject::info())) @@ -519,6 +532,11 @@ bool JSObjectSetPrivateProperty(JSContextRef ctx, JSObjectRef object, JSStringRe JSObject* jsObject = toJS(object); JSValue jsValue = value ? toJS(exec, value) : JSValue(); Identifier name(propertyName->identifier(&exec->vm())); + + // Get wrapped object if proxied + if (jsObject->inherits(JSProxy::info())) + jsObject = jsCast(jsObject)->target(); + if (jsObject->inherits(JSCallbackObject::info())) { jsCast*>(jsObject)->setPrivateProperty(exec->vm(), name, jsValue); return true; @@ -542,6 +560,11 @@ bool JSObjectDeletePrivateProperty(JSContextRef ctx, JSObjectRef object, JSStrin JSLockHolder locker(exec); JSObject* jsObject = toJS(object); Identifier name(propertyName->identifier(&exec->vm())); + + // Get wrapped object if proxied + if (jsObject->inherits(JSProxy::info())) + jsObject = jsCast(jsObject)->target(); + if (jsObject->inherits(JSCallbackObject::info())) { jsCast*>(jsObject)->deletePrivateProperty(name); return true; diff --git a/API/JSWeakObjectMapRefPrivate.cpp b/API/JSWeakObjectMapRefPrivate.cpp index 5f38de2..446cf90 100644 --- a/API/JSWeakObjectMapRefPrivate.cpp +++ b/API/JSWeakObjectMapRefPrivate.cpp @@ -62,7 +62,9 @@ void JSWeakObjectMapSet(JSContextRef ctx, JSWeakObjectMapRef map, void* key, JSO JSObject* obj = toJS(object); if (!obj) return; - ASSERT(obj->inherits(JSCallbackObject::info()) || obj->inherits(JSCallbackObject::info())); + ASSERT(obj->inherits(JSProxy::info()) + || obj->inherits(JSCallbackObject::info()) + || obj->inherits(JSCallbackObject::info())); map->map().set(key, obj); } diff --git a/API/JSWrapperMap.mm b/API/JSWrapperMap.mm index f6717b9..069de82 100644 --- a/API/JSWrapperMap.mm +++ b/API/JSWrapperMap.mm @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Apple Inc. All rights reserved. + * Copyright (C) 2013-2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -363,15 +363,16 @@ static void copyPrototypeProperties(JSContext *context, Class objcClass, Protoco JSC::Weak m_constructor; } -- (id)initWithContext:(JSContext *)context forClass:(Class)cls superClassInfo:(JSObjCClassInfo*)superClassInfo; +- (id)initWithContext:(JSContext *)context forClass:(Class)cls; - (JSValue *)wrapperForObject:(id)object; - (JSValue *)constructor; +- (JSC::JSObject *)prototype; @end @implementation JSObjCClassInfo -- (id)initWithContext:(JSContext *)context forClass:(Class)cls superClassInfo:(JSObjCClassInfo*)superClassInfo +- (id)initWithContext:(JSContext *)context forClass:(Class)cls { self = [super init]; if (!self) @@ -386,8 +387,6 @@ static void copyPrototypeProperties(JSContext *context, Class objcClass, Protoco definition.className = className; m_classRef = JSClassCreate(&definition); - [self allocateConstructorAndPrototypeWithSuperClassInfo:superClassInfo]; - return self; } @@ -449,8 +448,12 @@ static JSValue *allocateConstructorForCustomClass(JSContext *context, const char return constructorWithCustomBrand(context, [NSString stringWithFormat:@"%sConstructor", className], cls); } -- (void)allocateConstructorAndPrototypeWithSuperClassInfo:(JSObjCClassInfo*)superClassInfo +typedef std::pair ConstructorPrototypePair; + +- (ConstructorPrototypePair)allocateConstructorAndPrototype { + JSObjCClassInfo* superClassInfo = [m_context.wrapperMap classInfoForClass:class_getSuperclass(m_class)]; + ASSERT(!m_constructor || !m_prototype); ASSERT((m_class == [NSObject class]) == !superClassInfo); if (!superClassInfo) { @@ -464,11 +467,6 @@ static JSValue *allocateConstructorForCustomClass(JSContext *context, const char m_prototype = toJS(JSValueToObject(cContext, valueInternalValue(prototype), 0)); } } else { - // We need to hold a reference to the superclass prototype here on the stack - // to that it won't get GC'ed while we do allocations between now and when we - // set it in this class' prototype below. - JSC::JSObject* superClassPrototype = superClassInfo->m_prototype.get(); - const char* className = class_getName(m_class); // Create or grab the prototype/constructor pair. @@ -498,15 +496,10 @@ static JSValue *allocateConstructorForCustomClass(JSContext *context, const char }); // Set [Prototype]. + JSC::JSObject* superClassPrototype = [superClassInfo prototype]; JSObjectSetPrototype([m_context JSGlobalContextRef], toRef(m_prototype.get()), toRef(superClassPrototype)); } -} - -- (void)reallocateConstructorAndOrPrototype -{ - [self allocateConstructorAndPrototypeWithSuperClassInfo:[m_context.wrapperMap classInfoForClass:class_getSuperclass(m_class)]]; - // We should not add any code here that can trigger a GC or the prototype and - // constructor that we just created may be collected before they can be used. + return ConstructorPrototypePair(m_constructor.get(), m_prototype.get()); } - (JSValue *)wrapperForObject:(id)object @@ -523,12 +516,7 @@ static JSValue *allocateConstructorForCustomClass(JSContext *context, const char } } - if (!m_prototype) - [self reallocateConstructorAndOrPrototype]; - ASSERT(!!m_prototype); - // We need to hold a reference to the prototype here on the stack to that it won't - // get GC'ed while we create the wrapper below. - JSC::JSObject* prototype = m_prototype.get(); + JSC::JSObject* prototype = [self prototype]; JSObjectRef wrapper = makeWrapper([m_context JSGlobalContextRef], m_classRef, object); JSObjectSetPrototype([m_context JSGlobalContextRef], wrapper, toRef(prototype)); @@ -537,13 +525,20 @@ static JSValue *allocateConstructorForCustomClass(JSContext *context, const char - (JSValue *)constructor { - if (!m_constructor) - [self reallocateConstructorAndOrPrototype]; - ASSERT(!!m_constructor); - // If we need to add any code here in the future that can trigger a GC, we should - // cache the constructor pointer in a stack local var first so that it is protected - // from the GC until it gets used below. - return [JSValue valueWithJSValueRef:toRef(m_constructor.get()) inContext:m_context]; + JSC::JSObject* constructor = m_constructor.get(); + if (!constructor) + constructor = [self allocateConstructorAndPrototype].first; + ASSERT(!!constructor); + return [JSValue valueWithJSValueRef:toRef(constructor) inContext:m_context]; +} + +- (JSC::JSObject*)prototype +{ + JSC::JSObject* prototype = m_prototype.get(); + if (!prototype) + prototype = [self allocateConstructorAndPrototype].second; + ASSERT(!!prototype); + return prototype; } @end @@ -590,7 +585,7 @@ static JSValue *allocateConstructorForCustomClass(JSContext *context, const char if ('_' == *class_getName(cls)) return m_classMap[cls] = [self classInfoForClass:class_getSuperclass(cls)]; - return m_classMap[cls] = [[[JSObjCClassInfo alloc] initWithContext:m_context forClass:cls superClassInfo:[self classInfoForClass:class_getSuperclass(cls)]] autorelease]; + return m_classMap[cls] = [[[JSObjCClassInfo alloc] initWithContext:m_context forClass:cls] autorelease]; } - (JSValue *)jsWrapperForObject:(id)object diff --git a/API/tests/CustomGlobalObjectClassTest.c b/API/tests/CustomGlobalObjectClassTest.c index ade3a0d..62e6397 100644 --- a/API/tests/CustomGlobalObjectClassTest.c +++ b/API/tests/CustomGlobalObjectClassTest.c @@ -25,6 +25,7 @@ #include "CustomGlobalObjectClassTest.h" +#include #include #include @@ -98,3 +99,47 @@ void customGlobalObjectClassTest() assertTrue(executedCallback, "Executed custom global object callback"); } + +void globalObjectSetPrototypeTest() +{ + JSClassDefinition definition = kJSClassDefinitionEmpty; + definition.className = "Global"; + JSClassRef global = JSClassCreate(&definition); + JSGlobalContextRef context = JSGlobalContextCreate(global); + JSObjectRef object = JSContextGetGlobalObject(context); + + JSObjectRef above = JSObjectMake(context, 0, 0); + JSStringRef test = JSStringCreateWithUTF8CString("test"); + JSValueRef value = JSValueMakeString(context, test); + JSObjectSetProperty(context, above, test, value, kJSPropertyAttributeDontEnum, 0); + + JSObjectSetPrototype(context, object, above); + JSStringRef script = JSStringCreateWithUTF8CString("test === \"test\""); + JSValueRef result = JSEvaluateScript(context, script, 0, 0, 0, 0); + + assertTrue(JSValueToBoolean(context, result), "test === \"test\""); + + JSStringRelease(test); + JSStringRelease(script); +} + +void globalObjectPrivatePropertyTest() +{ + JSClassDefinition definition = kJSClassDefinitionEmpty; + definition.className = "Global"; + JSClassRef global = JSClassCreate(&definition); + JSGlobalContextRef context = JSGlobalContextCreate(global); + JSObjectRef globalObject = JSContextGetGlobalObject(context); + + JSStringRef privateName = JSStringCreateWithUTF8CString("private"); + JSValueRef privateValue = JSValueMakeString(context, privateName); + assertTrue(JSObjectSetPrivateProperty(context, globalObject, privateName, privateValue), "JSObjectSetPrivateProperty succeeded"); + JSValueRef result = JSObjectGetPrivateProperty(context, globalObject, privateName); + assertTrue(JSValueIsStrictEqual(context, privateValue, result), "privateValue === \"private\""); + + assertTrue(JSObjectDeletePrivateProperty(context, globalObject, privateName), "JSObjectDeletePrivateProperty succeeded"); + result = JSObjectGetPrivateProperty(context, globalObject, privateName); + assertTrue(JSValueIsNull(context, result), "Deleted private property is indeed no longer present"); + + JSStringRelease(privateName); +} diff --git a/API/tests/CustomGlobalObjectClassTest.h b/API/tests/CustomGlobalObjectClassTest.h index 9f8630f..86914ca 100644 --- a/API/tests/CustomGlobalObjectClassTest.h +++ b/API/tests/CustomGlobalObjectClassTest.h @@ -27,5 +27,7 @@ #define CustomGlobalObjectClassTest_h void customGlobalObjectClassTest(void); +void globalObjectSetPrototypeTest(void); +void globalObjectPrivatePropertyTest(void); #endif // CustomGlobalObjectClassTest_h diff --git a/API/tests/Regress141809.h b/API/tests/Regress141809.h new file mode 100644 index 0000000..43b099c --- /dev/null +++ b/API/tests/Regress141809.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2015 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. AND ITS CONTRIBUTORS ``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 ITS 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 +#import + +#if JSC_OBJC_API_ENABLED + +void runRegress141809(); + +#endif // JSC_OBJC_API_ENABLED + diff --git a/API/tests/Regress141809.mm b/API/tests/Regress141809.mm new file mode 100644 index 0000000..16fd373 --- /dev/null +++ b/API/tests/Regress141809.mm @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2015 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. AND ITS CONTRIBUTORS ``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 ITS 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 "config.h" +#import "Regress141809.h" + +#import +#import + +#if JSC_OBJC_API_ENABLED + +extern "C" void checkResult(NSString *description, bool passed); +extern "C" void JSSynchronousGarbageCollectForDebugging(JSContextRef); + +@protocol TestClassAExports +@end + +@interface TestClassA : NSObject +@end + +@implementation TestClassA +@end + +@protocol TestClassBExports +- (NSString *)name; +@end + +@interface TestClassB : TestClassA +@end + +@implementation TestClassB +- (NSString *)name +{ + return @"B"; +} +@end + +@protocol TestClassCExports +- (NSString *)name; +@end + +@interface TestClassC : TestClassB +@end + +@implementation TestClassC +- (NSString *)name +{ + return @"C"; +} +@end + +void runRegress141809() +{ + // Test that the ObjC API can correctly re-construct the synthesized + // prototype and constructor of JS exported ObjC classes. + // See + @autoreleasepool { + JSContext *context = [[JSContext alloc] init]; + context[@"print"] = ^(NSString* str) { + NSLog(@"%@", str); + }; + + [context evaluateScript:@"function dumpPrototypes(obj) { \ + var objDepth = 0; \ + var currObj = obj; \ + var objChain = ''; \ + do { \ + var propIndex = 0; \ + var props = ''; \ + Object.getOwnPropertyNames(currObj).forEach(function(val, idx, array) { \ + props += ((propIndex > 0 ? ', ' : '') + val); \ + propIndex++; \ + }); \ + var str = ''; \ + if (!objDepth) \ + str += 'obj '; \ + else { \ + for (i = 0; i < objDepth; i++) \ + str += ' '; \ + str += '--> proto '; \ + } \ + str += currObj; \ + if (props) \ + str += (' with ' + propIndex + ' props: ' + props); \ + print(str); \ + objChain += (str + '\\n'); \ + objDepth++; \ + currObj = Object.getPrototypeOf(currObj); \ + } while (currObj); \ + return { objDepth: objDepth, objChain: objChain }; \ + }"]; + JSValue* dumpPrototypes = context[@"dumpPrototypes"]; + + JSValue* resultBeforeGC = nil; + @autoreleasepool { + TestClassC* obj = [[TestClassC alloc] init]; + resultBeforeGC = [dumpPrototypes callWithArguments:@[obj]]; + } + + JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]); + + @autoreleasepool { + TestClassC* obj = [[TestClassC alloc] init]; + JSValue* resultAfterGC = [dumpPrototypes callWithArguments:@[obj]]; + checkResult(@"object and prototype chain depth is 5 deep", [resultAfterGC[@"objDepth"] toInt32] == 5); + checkResult(@"object and prototype chain depth before and after GC matches", [resultAfterGC[@"objDepth"] toInt32] == [resultBeforeGC[@"objDepth"] toInt32]); + checkResult(@"object and prototype chain before and after GC matches", [[resultAfterGC[@"objChain"] toString] isEqualToString:[resultBeforeGC[@"objChain"] toString]]); + } + } +} + +#endif // JSC_OBJC_API_ENABLED diff --git a/API/tests/testapi.c b/API/tests/testapi.c index 9b29949..60d7dc0 100644 --- a/API/tests/testapi.c +++ b/API/tests/testapi.c @@ -2079,6 +2079,8 @@ int main(int argc, char* argv[]) printf("PASS: global context name behaves as expected.\n"); customGlobalObjectClassTest(); + globalObjectSetPrototypeTest(); + globalObjectPrivatePropertyTest(); if (failed) { printf("FAIL: Some tests failed.\n"); diff --git a/API/tests/testapi.mm b/API/tests/testapi.mm index c528e69..724867c 100644 --- a/API/tests/testapi.mm +++ b/API/tests/testapi.mm @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Apple Inc. All rights reserved. + * Copyright (C) 2013-2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,6 +28,9 @@ #import "CurrentThisInsideBlockGetterTest.h" #import "DateTests.h" #import "JSExportTests.h" +#import "Regress141809.h" + +#import extern "C" void JSSynchronousGarbageCollectForDebugging(JSContextRef); extern "C" void JSSynchronousEdenCollectForDebugging(JSContextRef); @@ -470,6 +473,16 @@ static bool blockSignatureContainsClass() return containsClass; } +static void* threadMain(void* contextPtr) +{ + JSContext *context = (__bridge JSContext*)contextPtr; + + // Do something to enter the VM. + TestObject *testObject = [TestObject testObject]; + context[@"testObject"] = testObject; + pthread_exit(nullptr); +} + void testObjectiveCAPI() { NSLog(@"Testing Objective-C API"); @@ -1359,9 +1372,21 @@ void testObjectiveCAPI() checkResult(@"EdenCollection doesn't reclaim new managed values", [managedJSObject value] != nil); } + @autoreleasepool { + JSContext *context = [[JSContext alloc] init]; + + pthread_t threadID; + pthread_create(&threadID, NULL, &threadMain, (__bridge void*)context); + pthread_join(threadID, nullptr); + JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]); + + checkResult(@"Did not crash after entering the VM from another thread", true); + } + currentThisInsideBlockGetterTest(); runDateTests(); runJSExportTests(); + runRegress141809(); } #else diff --git a/ChangeLog b/ChangeLog index bbda15d..097cfa1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,681 @@ +2015-03-06 Lucas Forschler + + Merge r180234 + + 2015-02-17 Filip Pizlo + + Throwing from an FTL call IC slow path may result in tag registers being clobbered on 64-bit CPUs + https://bugs.webkit.org/show_bug.cgi?id=141717 + rdar://problem/19863382 + + Reviewed by Geoffrey Garen. + + The best solution is to ensure that the engine catching an exception restores tag registers. + + Each of these new test cases reliably crashed prior to this patch and they don't crash at all now. + + * jit/JITOpcodes.cpp: + (JSC::JIT::emit_op_catch): + * llint/LowLevelInterpreter.asm: + * llint/LowLevelInterpreter64.asm: + * tests/stress/throw-from-ftl-call-ic-slow-path-cells.js: Added. + * tests/stress/throw-from-ftl-call-ic-slow-path-undefined.js: Added. + * tests/stress/throw-from-ftl-call-ic-slow-path.js: Added. + +2015-03-06 Lucas Forschler + + Merge r181030 + + 2015-03-04 Filip Pizlo + + [FTL] inlined GetMyArgumentByVal with no arguments passed causes instant crash + https://bugs.webkit.org/show_bug.cgi?id=141180 + rdar://problem/19677552 + + Reviewed by Benjamin Poulain. + + If we do a GetMyArgumentByVal on an inlined call frame that has no arguments, then the + bounds check already terminates execution. This means we can skip the part where we + previously did an out-of-bound array access on the inlined call frame arguments vector. + + * ftl/FTLLowerDFGToLLVM.cpp: + (JSC::FTL::LowerDFGToLLVM::safelyInvalidateAfterTermination): + (JSC::FTL::LowerDFGToLLVM::compileGetMyArgumentByVal): + (JSC::FTL::LowerDFGToLLVM::terminate): + (JSC::FTL::LowerDFGToLLVM::didAlreadyTerminate): + (JSC::FTL::LowerDFGToLLVM::crash): + * tests/stress/get-my-argument-by-val-inlined-no-formal-parameters.js: Added. + (foo): + (bar): + +2015-03-04 Matthew Hanson + + Merge r180101. rdar://problem/19913017 + + 2015-02-13 Joseph Pecoraro + + JSContext Inspector: Do not stash console messages for non-debuggable JSContext + https://bugs.webkit.org/show_bug.cgi?id=141589 + + Reviewed by Timothy Hatcher. + + Consider developer extras disabled for JSContext inspection if the + RemoteInspector server is not enabled (typically a non-debuggable + process rejected by webinspectord) or if remote debugging on the + JSContext was explicitly disabled via SPI. + + When developer extras are disabled, console message will not be stashed. + + * inspector/JSGlobalObjectInspectorController.cpp: + (Inspector::JSGlobalObjectInspectorController::developerExtrasEnabled): + * inspector/JSGlobalObjectInspectorController.h: + +2015-02-26 Lucas Forschler + + Merge r180452 + + 2015-02-20 Mark Lam + + [JSObjCClassInfo reallocateConstructorAndOrPrototype] should also reallocate super class prototype chain. + + + Reviewed by Geoffrey Garen. + + A ObjC class that implement the JSExport protocol will have a JS prototype + chain and constructor automatically synthesized for its JS wrapper object. + However, if there are no more instances of that ObjC class reachable by a + JS GC root scan, then its synthesized prototype chain and constructors may + be released by the GC. If a new instance of that ObjC class is subsequently + instantiated, then [JSObjCClassInfo reallocateConstructorAndOrPrototype] + should re-construct the prototype chain and constructor (if they were + previously released). However, the current implementation only + re-constructs the immediate prototype, but not every other prototype + object upstream in the prototype chain. + + To fix this, we do the following: + 1. We no longer allocate the JSObjCClassInfo's prototype and constructor + eagerly. Hence, -initWithContext:forClass: will no longer call + -allocateConstructorAndPrototypeWithSuperClassInfo:. + 2. Instead, we'll always access the prototype and constructor thru + accessor methods. The accessor methods will call + -allocateConstructorAndPrototype: if needed. + 3. -allocateConstructorAndPrototype: will fetch the needed superClassInfo + from the JSWrapperMap itself. This makes it so that we no longer + need to pass the superClassInfo all over. + 4. -allocateConstructorAndPrototype: will get the super class prototype + by invoking -prototype: on the superClassInfo, thereby allowing the + super class to allocate its prototype and constructor if needed and + fixing the issue in this bug. + + 5. Also removed the GC warning comments, and ensured that needed JS + objects are kept alive by having a local var pointing to it from the + stack (which makes a GC root). + + * API/JSWrapperMap.mm: + (-[JSObjCClassInfo initWithContext:forClass:]): + (-[JSObjCClassInfo allocateConstructorAndPrototype]): + (-[JSObjCClassInfo wrapperForObject:]): + (-[JSObjCClassInfo constructor]): + (-[JSObjCClassInfo prototype]): + (-[JSWrapperMap classInfoForClass:]): + (-[JSObjCClassInfo initWithContext:forClass:superClassInfo:]): Deleted. + (-[JSObjCClassInfo allocateConstructorAndPrototypeWithSuperClassInfo:]): Deleted. + (-[JSObjCClassInfo reallocateConstructorAndOrPrototype]): Deleted. + * API/tests/Regress141809.h: Added. + * API/tests/Regress141809.mm: Added. + (-[TestClassB name]): + (-[TestClassC name]): + (runRegress141809): + * API/tests/testapi.mm: + * JavaScriptCore.xcodeproj/project.pbxproj: + +2015-02-25 Babak Shafiei + + Merge patch for r180247 and r180249. + + 2015-02-20 Michael Saboff + + CrashTracer: DFG_CRASH beneath JSC::FTL::LowerDFGToLLVM::compileNode + https://bugs.webkit.org/show_bug.cgi?id=141730 + + Reviewed by Geoffrey Garen. + + Added a new failure handler, loweringFailed(), to LowerDFGToLLVM that reports failures + while processing DFG lowering. For debug builds, the failures are logged identical + to the way the DFG_CRASH() reports them. For release builds, the failures are reported + and that FTL compilation is terminated, but the process is allowed to continue. + Wrapped calls to loweringFailed() in a macro LOWERING_FAILED so the function and + line number are reported at the point of the inconsistancy. + + Converted instances of DFG_CRASH to LOWERING_FAILED. + + * dfg/DFGPlan.cpp: + (JSC::DFG::Plan::compileInThreadImpl): Added lowerDFGToLLVM() failure check that + will fail the FTL compile. + + * ftl/FTLLowerDFGToLLVM.cpp: + (JSC::FTL::LowerDFGToLLVM::LowerDFGToLLVM): + Added new member variable, m_loweringSucceeded, to stop compilation on the first + reported failure. + + * ftl/FTLLowerDFGToLLVM.cpp: + (JSC::FTL::LowerDFGToLLVM::lower): + * ftl/FTLLowerDFGToLLVM.h: + Added check for compilation failures and now report those failures via a boolean + return value. + + * ftl/FTLLowerDFGToLLVM.cpp: + (JSC::FTL::LowerDFGToLLVM::createPhiVariables): + (JSC::FTL::LowerDFGToLLVM::compileNode): + (JSC::FTL::LowerDFGToLLVM::compileUpsilon): + (JSC::FTL::LowerDFGToLLVM::compilePhi): + (JSC::FTL::LowerDFGToLLVM::compileDoubleRep): + (JSC::FTL::LowerDFGToLLVM::compileValueRep): + (JSC::FTL::LowerDFGToLLVM::compileValueToInt32): + (JSC::FTL::LowerDFGToLLVM::compilePutLocal): + (JSC::FTL::LowerDFGToLLVM::compileArithAddOrSub): + (JSC::FTL::LowerDFGToLLVM::compileArithMul): + (JSC::FTL::LowerDFGToLLVM::compileArithDiv): + (JSC::FTL::LowerDFGToLLVM::compileArithMod): + (JSC::FTL::LowerDFGToLLVM::compileArithMinOrMax): + (JSC::FTL::LowerDFGToLLVM::compileArithAbs): + (JSC::FTL::LowerDFGToLLVM::compileArithNegate): + (JSC::FTL::LowerDFGToLLVM::compileArrayifyToStructure): + (JSC::FTL::LowerDFGToLLVM::compileGetById): + (JSC::FTL::LowerDFGToLLVM::compileGetMyArgumentByVal): + (JSC::FTL::LowerDFGToLLVM::compileGetArrayLength): + (JSC::FTL::LowerDFGToLLVM::compileGetByVal): + (JSC::FTL::LowerDFGToLLVM::compilePutByVal): + (JSC::FTL::LowerDFGToLLVM::compileArrayPush): + (JSC::FTL::LowerDFGToLLVM::compileArrayPop): + (JSC::FTL::LowerDFGToLLVM::compileNewArray): + (JSC::FTL::LowerDFGToLLVM::compileToString): + (JSC::FTL::LowerDFGToLLVM::compileMakeRope): + (JSC::FTL::LowerDFGToLLVM::compileCompareEq): + (JSC::FTL::LowerDFGToLLVM::compileCompareStrictEq): + (JSC::FTL::LowerDFGToLLVM::compileSwitch): + (JSC::FTL::LowerDFGToLLVM::compare): + (JSC::FTL::LowerDFGToLLVM::boolify): + (JSC::FTL::LowerDFGToLLVM::opposite): + (JSC::FTL::LowerDFGToLLVM::lowJSValue): + (JSC::FTL::LowerDFGToLLVM::speculate): + (JSC::FTL::LowerDFGToLLVM::isArrayType): + (JSC::FTL::LowerDFGToLLVM::exitValueForAvailability): + (JSC::FTL::LowerDFGToLLVM::exitValueForNode): + (JSC::FTL::LowerDFGToLLVM::setInt52): + Changed DFG_CRASH() to LOWERING_FAILED(). Updated related control flow as appropriate. + + (JSC::FTL::LowerDFGToLLVM::loweringFailed): New error reporting member function. + +2015-02-25 Babak Shafiei + + Merge r180516. + + 2015-02-23 Matthew Mirman + + r9 is volatile on ARMv7 for iOS 3 and up. + https://bugs.webkit.org/show_bug.cgi?id=141489 + rdar://problem/19432916 + + Reviewed by Michael Saboff. + + * jit/RegisterSet.cpp: + (JSC::RegisterSet::calleeSaveRegisters): removed r9 from the list of ARMv7 callee save registers. + * tests/stress/regress-141489.js: Added. + (foo): + +2015-02-20 Lucas Forschler + + Merge r180237 + + 2015-02-17 Filip Pizlo + + StackLayoutPhase should use CodeBlock::usesArguments rather than FunctionExecutable::usesArguments + https://bugs.webkit.org/show_bug.cgi?id=141721 + rdar://problem/17198633 + + Reviewed by Michael Saboff. + + I've seen cases where the two are out of sync. We know we can trust the CodeBlock::usesArguments because + we use it everywhere else. + + No test because I could never reproduce the crash. + + * dfg/DFGGraph.h: + (JSC::DFG::Graph::usesArguments): + * dfg/DFGStackLayoutPhase.cpp: + (JSC::DFG::StackLayoutPhase::run): + +2015-02-20 Babak Shafiei + + Merge r178224. + + 2015-01-09 Joseph Pecoraro + + Web Inspector: Uncaught Exception in ProbeManager deleting breakpoint + https://bugs.webkit.org/show_bug.cgi?id=140279 + rdar://problem/19422299 + + Reviewed by Oliver Hunt. + + * runtime/MapData.cpp: + (JSC::MapData::replaceAndPackBackingStore): + The cell table also needs to have its values fixed. + +2015-02-20 Babak Shafiei + + Merge patch for rdar://problem/19828630. + + 2015-02-13 Filip Pizlo + + Effectful calls to length should only happen once on the varargs path. + rdar://problem/19828518 + + Reviewed by Michael Saboff. + + * interpreter/Interpreter.cpp: + (JSC::sizeFrameForVarargs): + (JSC::loadVarargs): + * runtime/VM.cpp: + (JSC::VM::VM): + * runtime/VM.h: + +2015-02-10 Babak Shafiei + + Merge r179576, r179648. + + 2015-02-04 Mark Lam + + r179576 introduce a deadlock potential during GC thread suspension. + + + Reviewed by Michael Saboff. + + http://trac.webkit.org/r179576 introduced a potential for deadlocking. + In the GC thread suspension loop, we currently delete + MachineThreads::Thread that we detect to be invalid. This is unsafe + because we may have already suspended some threads, and one of those + suspended threads may still be holding the C heap lock which we need + for deleting the invalid thread. + + The fix is to put the invalid threads in a separate toBeDeleted list, + and delete them only after GC has resumed all threads. + + * heap/MachineStackMarker.cpp: + (JSC::MachineThreads::removeCurrentThread): + - Undo refactoring removeThreadWithLockAlreadyAcquired() out of + removeCurrentThread() since it is no longer needed. + + (JSC::MachineThreads::tryCopyOtherThreadStacks): + - Put invalid Threads on a threadsToBeDeleted list, and delete those + Threads only after all threads have been resumed. + + (JSC::MachineThreads::removeThreadWithLockAlreadyAcquired): Deleted. + * heap/MachineStackMarker.h: + + 2015-02-03 Mark Lam + + Workaround a thread library bug where thread destructors may not get called. + + + Reviewed by Michael Saboff. + + There's a bug where thread destructors may not get called. As far as + we know, this only manifests on darwin ports. We will work around this + by checking at GC time if the platform thread is still valid. If not, + we'll purge it from the VM's registeredThreads list before proceeding + with thread scanning activity. + + Note: it is important that we do this invalid thread detection during + suspension, because the validity (and liveness) of the other thread is + only guaranteed while it is suspended. + + * API/tests/testapi.mm: + (threadMain): + - Added a test to enter the VM from another thread before we GC on + the main thread. + + * heap/MachineStackMarker.cpp: + (JSC::MachineThreads::removeThreadWithLockAlreadyAcquired): + (JSC::MachineThreads::removeCurrentThread): + - refactored removeThreadWithLockAlreadyAcquired() out from + removeCurrentThread() so that we can also call it for purging invalid + threads. + (JSC::suspendThread): + - Added a return status to tell if the suspension succeeded or not. + (JSC::MachineThreads::tryCopyOtherThreadStacks): + - Check if the suspension failed, and purge the thread if we can't + suspend it. Failure to suspend implies that the thread has + terminated without calling its destructor. + * heap/MachineStackMarker.h: + +2015-02-10 Babak Shafiei + + Merge r179187. + + 2015-01-27 Csaba Osztrogonác + + [ARM] Typo fix after r176083 + https://bugs.webkit.org/show_bug.cgi?id=140937 + + Reviewed by Anders Carlsson. + + * assembler/ARMv7Assembler.h: + (JSC::ARMv7Assembler::ldrh): + +2015-02-10 Babak Shafiei + + Merge r176083. + + 2014-11-13 Benjamin Poulain + + ARMv7(s) Assembler: LDRH with immediate offset is loading from the wrong offset + https://bugs.webkit.org/show_bug.cgi?id=136914 + + Reviewed by Michael Saboff. + + TLDR: the immediate offset of half-word load was divided by 2. + + Story time: So I started getting those weird reports of :nth-child() behaving bizarrely + on ARMv7 and ARMv7s. To make things worse, the behavior changes depending on style updates. + + I started looking the disassembly on the tests cases... + + The first thing I noticed was that the computation of An+B looked wrong. For example, + in the case of n+6, the instruction should have been: + subs r1, r1, #6 + but was + subs r1, r1, #2 + + After spending a lot of time trying to find the error in the assembler, I discovered + the problem was not real, but just a bug in the disassembler. + This is the first fix: ARMv7DOpcodeAddSubtractImmediate3's immediate3() was truncating + the value to 2 bits instead of 3 bits. + + The disassembler being fixed, I still have no lead on the weird bug. Some disassembly later, + I realize the LDRH instruction is not decoded at all. The reason is that both LDRH and STRH + were under the umbrella ARMv7DOpcodeLoadStoreRegisterImmediateHalfWord but the pattern + only matched SRTH. + + I fix that next, ARMv7DOpcodeLoadStoreRegisterImmediateHalfWord is split into + ARMv7DOpcodeStoreRegisterImmediateHalfWord and ARMv7DOpcodeLoadRegisterImmediateHalfWord, + each with their own pattern and their instruction group. + + Now that I can see the LDRHs correctly, there is something fishy about them, their offset + is way too small for the data I load. + + This time, looking at the binary, the generated code is indeed incorrect. It turns out that + the ARMv7 assembler shifted the offset of half-word load as if they were byte load: divided by 4. + As a result, all the load of half-words with more than zero offset were loading + values with a smaller offset than what they should have. + + That being fixed, I dump the assembly: still wrong. I am ready to throw my keyboard through + my screen at that point. + + Looking at the disassembler, there is yet again a bug. The computation of the scale() adjustment + of the offset was incorrect for anything but word loads. + I replaced it by a switch-case to make it explicit. + + STRH is likely incorrect too. I'll fix that in a follow up, I want to survey all the 16 bits cases + that are not directly used by the CSS JIT. + + * assembler/ARMv7Assembler.h: + (JSC::ARMv7Assembler::ldrh): + Fix the immediate scaling. Add an assertion to make sure the alignment of the input is correct. + + * disassembler/ARMv7/ARMv7DOpcode.cpp: + (JSC::ARMv7Disassembler::ARMv7DOpcodeLoadStoreRegisterImmediate::scale): + Fix the scaling code. Just hardcode instruction-to-scale table. + + * disassembler/ARMv7/ARMv7DOpcode.h: + (JSC::ARMv7Disassembler::ARMv7DOpcodeAddSubtractImmediate3::immediate3): + The mask for a 3 bits immediate is not 3 :) + + (JSC::ARMv7Disassembler::ARMv7DOpcodeLoadStoreRegisterImmediate::scale): Deleted. + +2015-02-05 Lucas Forschler + + Merge r178953 + + 2015-01-21 Joseph Pecoraro + + Web Inspector: ASSERT expanding objects in console PrimitiveBindingTraits::assertValueHasExpectedType + https://bugs.webkit.org/show_bug.cgi?id=140746 + + Reviewed by Timothy Hatcher. + + * inspector/InjectedScriptSource.js: + Do not add impure properties to the descriptor object that will + eventually be sent to the frontend. + +2015-02-05 Lucas Forschler + + Merge r178768 + + 2015-01-20 Joseph Pecoraro + + Web Inspector: Expanding event objects in console shows undefined for most values, it should have real values + https://bugs.webkit.org/show_bug.cgi?id=137306 + + Reviewed by Timothy Hatcher. + + Provide another optional parameter to getProperties, to gather a list + of all own and getter properties. + + * inspector/InjectedScript.cpp: + (Inspector::InjectedScript::getProperties): + * inspector/InjectedScript.h: + * inspector/InjectedScriptSource.js: + * inspector/agents/InspectorRuntimeAgent.cpp: + (Inspector::InspectorRuntimeAgent::getProperties): + * inspector/agents/InspectorRuntimeAgent.h: + * inspector/protocol/Runtime.json: + +2015-02-04 Lucas Forschler + + Merge r179329 + + 2015-01-13 Geoffrey Garen + + Out of bounds access in BytecodeGenerator::emitGetById under DotAccessorNode::emitBytecode + https://bugs.webkit.org/show_bug.cgi?id=140397 + + Reviewed by Geoffrey Garen. + + Patch by Alexey Proskuryakov. + + Reviewed, performance tested, and ChangeLogged by Geoffrey Garen. + + No performance change. + + No test, since this is a small past-the-end read, which is very + difficult to turn into a reproducible failing test -- and existing tests + crash reliably using ASan. + + * bytecompiler/NodesCodegen.cpp: + (JSC::BracketAccessorNode::emitBytecode): + (JSC::DotAccessorNode::emitBytecode): + (JSC::FunctionCallBracketNode::emitBytecode): + (JSC::PostfixNode::emitResolve): + (JSC::DeleteBracketNode::emitBytecode): + (JSC::DeleteDotNode::emitBytecode): + (JSC::PrefixNode::emitResolve): + (JSC::UnaryOpNode::emitBytecode): + (JSC::BitwiseNotNode::emitBytecode): + (JSC::BinaryOpNode::emitBytecode): + (JSC::EqualNode::emitBytecode): + (JSC::StrictEqualNode::emitBytecode): + (JSC::ThrowableBinaryOpNode::emitBytecode): + (JSC::AssignDotNode::emitBytecode): + (JSC::AssignBracketNode::emitBytecode): Use RefPtr in more places. Any + register used across a call to a function that might allocate a new + temporary register must be held in a RefPtr. + +2015-02-04 Lucas Forschler + + Merge r178311 + + 2015-01-12 Geoffrey Garen + + Out of bounds read in IdentifierArena::makeIdentifier + https://bugs.webkit.org/show_bug.cgi?id=140376 + + Patch by Alexey Proskuryakov. + + Reviewed and ChangeLogged by Geoffrey Garen. + + No test, since this is a small past-the-end read, which is very + difficult to turn into a reproducible failing test -- and existing tests + crash reliably using ASan. + + * parser/ParserArena.h: + (JSC::IdentifierArena::makeIdentifier): + (JSC::IdentifierArena::makeIdentifierLCharFromUChar): Check for a + zero-length string input, like we do in the literal parser, since it is + not valid to dereference characters in a zero-length string. + + A zero-length string is allowed in JavaScript -- for example, "". + +2015-01-28 Lucas Forschler + + Merge r178364 + + 2015-01-12 Michael Saboff + + Local JSArray* "keys" in objectConstructorKeys() is not marked during garbage collection + https://bugs.webkit.org/show_bug.cgi?id=140348 + + Reviewed by Mark Lam. + + We used to read registers in MachineThreads::gatherFromCurrentThread(), but that is too late + because those registers may have been spilled on the stack and replaced with other values by + the time we call down to gatherFromCurrentThread(). + + Now we get the register contents at the same place that we demarcate the current top of + stack using the address of a local variable, in Heap::markRoots(). The register contents + buffer is passed along with the demarcation pointer. These need to be done at this level + in the call tree and no lower, as markRoots() calls various functions that visit object + pointers that may be latter proven dead. Any of those pointers that are left on the + stack or in registers could be incorrectly marked as live if we scan the stack contents + from a called function or one of its callees. The stack demarcation pointer and register + saving need to be done in the same function so that we have a consistent stack, active + and spilled registers. + + Because we don't want to make unnecessary calls to get the register contents, we use + a macro to allocated, and possibly align, the register structure and get the actual + register contents. + + + * heap/Heap.cpp: + (JSC::Heap::markRoots): + (JSC::Heap::gatherStackRoots): + * heap/Heap.h: + * heap/MachineStackMarker.cpp: + (JSC::MachineThreads::gatherFromCurrentThread): + (JSC::MachineThreads::gatherConservativeRoots): + * heap/MachineStackMarker.h: + +2015-01-27 Lucas Forschler + + Merge r177455 + + 2014-12-17 Chris Dumez + + [iOS] Make it possible to toggle FeatureCounter support at runtime + https://bugs.webkit.org/show_bug.cgi?id=139688 + + + Reviewed by Andreas Kling. + + Stop linking against AppSupport framework as the functionality is no + longer in WTF (it was moved to WebCore). + + * Configurations/JavaScriptCore.xcconfig: + +2015-01-26 Lucas Forschler + + Merge r177328 + + 2014-12-15 Chris Dumez + + [iOS] Add feature counting support + https://bugs.webkit.org/show_bug.cgi?id=139652 + + + Reviewed by Gavin Barraclough. + + Link against AppSupport framework on iOS as we need it to implement + the new FeatureCounter API in WTF. + + * Configurations/JavaScriptCore.xcconfig: + +2015-01-21 Babak Shafiei + + Merge r176972. + + 2014-12-08 Mark Lam + + CFA wrongly assumes that a speculation for SlowPutArrayStorageShape disallows ArrayStorageShape arrays. + + + Reviewed by Michael Saboff. + + The code generator and runtime slow paths expects otherwise. This patch fixes + CFA to match the code generator's expectation. + + * dfg/DFGArrayMode.h: + (JSC::DFG::ArrayMode::arrayModesThatPassFiltering): + (JSC::DFG::ArrayMode::arrayModesWithIndexingShapes): + +2015-01-20 Babak Shafiei + + Merge r171691. + + 2014-07-28 Mark Hahnenberg + + REGRESSION: JSObjectSetPrototype() does not work on result of JSGetGlobalObject() + https://bugs.webkit.org/show_bug.cgi?id=135322 + + Reviewed by Oliver Hunt. + + The prototype chain of the JSProxy object should match that of the JSGlobalObject. + + This is a separate but related issue with JSObjectSetPrototype which doesn't correctly + account for JSProxies. I also audited the rest of the C API to check that we correctly + handle JSProxies in all other situations where we expect a JSCallbackObject of some sort + and found some SPI calls (JSObject*PrivateProperty) that didn't behave correctly when + passed a JSProxy. + + I also added some new tests for these cases. + + * API/JSObjectRef.cpp: + (JSObjectSetPrototype): + (JSObjectGetPrivateProperty): + (JSObjectSetPrivateProperty): + (JSObjectDeletePrivateProperty): + * API/JSWeakObjectMapRefPrivate.cpp: + * API/tests/CustomGlobalObjectClassTest.c: + (globalObjectSetPrototypeTest): + (globalObjectPrivatePropertyTest): + * API/tests/CustomGlobalObjectClassTest.h: + * API/tests/testapi.c: + (main): + +2015-01-11 Mark Lam + + Update WebKit branch to build with newer LLVM. + + + Reviewed by Filip Pizlo. + + * Configurations/LLVMForJSC.xcconfig: + - Add the ability to pick up LLVM_LIBS_iphoneos from AspenLLVM.xcconfig. + * llvm/LLVMAPIFunctions.h: + - Removed some erroneous and unused APIs. + * llvm/library/LLVMExports.cpp: + (initializeAndGetJSCLLVMAPI): + - Removed an unneeded option that is also not supported by the new LLVM. + 2014-12-10 Babak Shafiei Merge r176803. diff --git a/Configurations/LLVMForJSC.xcconfig b/Configurations/LLVMForJSC.xcconfig index 9357b81..8a66842 100644 --- a/Configurations/LLVMForJSC.xcconfig +++ b/Configurations/LLVMForJSC.xcconfig @@ -27,7 +27,16 @@ // Only export our hook for initializing LLVM and returning the API struct. OTHER_LDFLAGS_HIDE_SYMBOLS = -Wl,-exported_symbol -Wl,_initializeAndGetJSCLLVMAPI -Wl,-all_load; -LLVM_LIBS_iphoneos = -lLLVMLinker -lLLVMipo -lLLVMVectorize -lLLVMBitWriter -lLLVMTableGen -lLLVMInstrumentation -lLLVMIRReader -lLLVMBitReader -lLLVMAsmParser -lLLVMARM64Disassembler -lLLVMARM64CodeGen -lLLVMARM64AsmParser -lLLVMARM64Desc -lLLVMARM64Info -lLLVMARM64AsmPrinter -lLLVMSelectionDAG -lLLVMAsmPrinter -lLLVMMCParser -lLLVMDebugInfo -lLLVMOption -lLLVMInterpreter -lLLVMJIT -lLLVMCodeGen -lLLVMScalarOpts -lLLVMInstCombine -lLLVMTransformUtils -lLLVMipa -lLLVMAnalysis -lLLVMMCDisassembler -lLLVMMCJIT -lLLVMTarget -lLLVMRuntimeDyld -lLLVMExecutionEngine -lLLVMMC -lLLVMObject -lLLVMCore -lLLVMSupport -lprotobuf; +LLVM_LIBS_ios = -lLLVMLinker -lLLVMipo -lLLVMVectorize -lLLVMBitWriter -lLLVMTableGen -lLLVMInstrumentation -lLLVMIRReader -lLLVMBitReader -lLLVMAsmParser -lLLVMARM64Disassembler -lLLVMARM64CodeGen -lLLVMARM64AsmParser -lLLVMARM64Desc -lLLVMARM64Info -lLLVMARM64AsmPrinter -lLLVMSelectionDAG -lLLVMAsmPrinter -lLLVMMCParser -lLLVMDebugInfo -lLLVMOption -lLLVMInterpreter -lLLVMJIT -lLLVMCodeGen -lLLVMScalarOpts -lLLVMInstCombine -lLLVMTransformUtils -lLLVMipa -lLLVMAnalysis -lLLVMMCDisassembler -lLLVMMCJIT -lLLVMTarget -lLLVMRuntimeDyld -lLLVMExecutionEngine -lLLVMMC -lLLVMObject -lLLVMCore -lLLVMSupport -lprotobuf + +#include "/AppleInternal/XcodeConfig/AspenLLVM.xcconfig" + +// In general, we prefer to not append libraries this way because it may interfere with the required +// ordering of library linkage (as determined by their dependencies on other libraries). In this +// case, we'll make this a one time exception to work around the fact that there are pre-existing +// versions of AspenLLVM.xcconfig that overrides LLVM_LIBS_ios but is missing -lLLVMMCDisassembler. +LLVM_LIBS_iphoneos = $(LLVM_LIBS_ios) -lLLVMMCDisassembler + LLVM_LIBS_macosx = -lLLVMTableGen -lLLVMDebugInfo -lLLVMOption -lLLVMX86Disassembler -lLLVMX86AsmParser -lLLVMX86CodeGen -lLLVMSelectionDAG -lLLVMAsmPrinter -lLLVMX86Desc -lLLVMX86Info -lLLVMX86AsmPrinter -lLLVMX86Utils -lLLVMIRReader -lLLVMAsmParser -lLLVMMCDisassembler -lLLVMMCParser -lLLVMInstrumentation -lLLVMBitReader -lLLVMInterpreter -lLLVMipo -lLLVMVectorize -lLLVMLinker -lLLVMBitWriter -lLLVMMCJIT -lLLVMJIT -lLLVMCodeGen -lLLVMObjCARCOpts -lLLVMScalarOpts -lLLVMInstCombine -lLLVMTransformUtils -lLLVMipa -lLLVMAnalysis -lLLVMRuntimeDyld -lLLVMExecutionEngine -lLLVMTarget -lLLVMMC -lLLVMObject -lLLVMCore -lLLVMSupport; LLVM_LIBRARY_PATHS = $(LLVM_LIBRARY_PATHS_$(PLATFORM_NAME)) diff --git a/Configurations/Version.xcconfig b/Configurations/Version.xcconfig index 84fb2bf..128aac2 100644 --- a/Configurations/Version.xcconfig +++ b/Configurations/Version.xcconfig @@ -24,8 +24,8 @@ MAJOR_VERSION = 600; MINOR_VERSION = 1; TINY_VERSION = 4; -MICRO_VERSION = 13; -NANO_VERSION = 1; +MICRO_VERSION = 15; +NANO_VERSION = 12; FULL_VERSION = $(MAJOR_VERSION).$(MINOR_VERSION).$(TINY_VERSION).$(MICRO_VERSION).$(NANO_VERSION); // The bundle version and short version string are set based on the current build configuration, see below. diff --git a/JavaScriptCore.xcodeproj/project.pbxproj b/JavaScriptCore.xcodeproj/project.pbxproj index 8d02066..308d9df 100644 --- a/JavaScriptCore.xcodeproj/project.pbxproj +++ b/JavaScriptCore.xcodeproj/project.pbxproj @@ -1718,6 +1718,7 @@ FE5932A8183C5A2600A1ECCC /* VMEntryScope.h in Headers */ = {isa = PBXBuildFile; fileRef = FE5932A6183C5A2600A1ECCC /* VMEntryScope.h */; settings = {ATTRIBUTES = (Private, ); }; }; FEA08620182B7A0400F6D851 /* Breakpoint.h in Headers */ = {isa = PBXBuildFile; fileRef = FEA0861E182B7A0400F6D851 /* Breakpoint.h */; settings = {ATTRIBUTES = (Private, ); }; }; FEA08621182B7A0400F6D851 /* DebuggerPrimitives.h in Headers */ = {isa = PBXBuildFile; fileRef = FEA0861F182B7A0400F6D851 /* DebuggerPrimitives.h */; settings = {ATTRIBUTES = (Private, ); }; }; + FEB51F6C1A97B688001F921C /* Regress141809.mm in Sources */ = {isa = PBXBuildFile; fileRef = FEB51F6B1A97B688001F921C /* Regress141809.mm */; }; FEB58C14187B8B160098EF0B /* ErrorHandlingScope.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FEB58C12187B8B160098EF0B /* ErrorHandlingScope.cpp */; }; FEB58C15187B8B160098EF0B /* ErrorHandlingScope.h in Headers */ = {isa = PBXBuildFile; fileRef = FEB58C13187B8B160098EF0B /* ErrorHandlingScope.h */; settings = {ATTRIBUTES = (Private, ); }; }; FED287B215EC9A5700DA8161 /* LLIntOpcode.h in Headers */ = {isa = PBXBuildFile; fileRef = FED287B115EC9A5700DA8161 /* LLIntOpcode.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -3350,6 +3351,8 @@ FE5932A6183C5A2600A1ECCC /* VMEntryScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VMEntryScope.h; sourceTree = ""; }; FEA0861E182B7A0400F6D851 /* Breakpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Breakpoint.h; sourceTree = ""; }; FEA0861F182B7A0400F6D851 /* DebuggerPrimitives.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebuggerPrimitives.h; sourceTree = ""; }; + FEB51F6A1A97B688001F921C /* Regress141809.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Regress141809.h; path = API/tests/Regress141809.h; sourceTree = ""; }; + FEB51F6B1A97B688001F921C /* Regress141809.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Regress141809.mm; path = API/tests/Regress141809.mm; sourceTree = ""; }; FEB58C12187B8B160098EF0B /* ErrorHandlingScope.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ErrorHandlingScope.cpp; sourceTree = ""; }; FEB58C13187B8B160098EF0B /* ErrorHandlingScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ErrorHandlingScope.h; sourceTree = ""; }; FED287B115EC9A5700DA8161 /* LLIntOpcode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LLIntOpcode.h; path = llint/LLIntOpcode.h; sourceTree = ""; }; @@ -3712,6 +3715,8 @@ C288B2DD18A54D3E007BE40B /* DateTests.mm */, C2181FC018A948FB0025A235 /* JSExportTests.h */, C2181FC118A948FB0025A235 /* JSExportTests.mm */, + FEB51F6A1A97B688001F921C /* Regress141809.h */, + FEB51F6B1A97B688001F921C /* Regress141809.mm */, 144005170A531CB50005F061 /* minidom */, 14BD5A2D0A3E91F600BAF59C /* testapi.c */, 14D857740A4696C80032146C /* testapi.js */, @@ -6808,6 +6813,7 @@ buildActionMask = 2147483647; files = ( C29ECB031804D0ED00D2CBB4 /* CurrentThisInsideBlockGetterTest.mm in Sources */, + FEB51F6C1A97B688001F921C /* Regress141809.mm in Sources */, C20328201981979D0088B499 /* CustomGlobalObjectClassTest.c in Sources */, C288B2DE18A54D3E007BE40B /* DateTests.mm in Sources */, C2181FC218A948FB0025A235 /* JSExportTests.mm in Sources */, diff --git a/assembler/ARMv7Assembler.h b/assembler/ARMv7Assembler.h index ffc3090..ce079d9 100644 --- a/assembler/ARMv7Assembler.h +++ b/assembler/ARMv7Assembler.h @@ -1194,9 +1194,10 @@ public: { ASSERT(rn != ARMRegisters::pc); // LDR (literal) ASSERT(imm.isUInt12()); + ASSERT(!(imm.getUInt12() & 1)); if (!((rt | rn) & 8) && imm.isUInt6()) - m_formatter.oneWordOp5Imm5Reg3Reg3(OP_LDRH_imm_T1, imm.getUInt6() >> 2, rn, rt); + m_formatter.oneWordOp5Imm5Reg3Reg3(OP_LDRH_imm_T1, imm.getUInt6() >> 1, rn, rt); else m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDRH_imm_T2, rn, rt, imm.getUInt12()); } diff --git a/bytecompiler/NodesCodegen.cpp b/bytecompiler/NodesCodegen.cpp index c91e616..0e81cfa 100644 --- a/bytecompiler/NodesCodegen.cpp +++ b/bytecompiler/NodesCodegen.cpp @@ -366,9 +366,9 @@ RegisterID* BracketAccessorNode::emitBytecode(BytecodeGenerator& generator, Regi if (m_base->isResolveNode() && generator.willResolveToArguments(static_cast(m_base)->identifier()) && !generator.symbolTable().slowArguments()) { - RegisterID* property = generator.emitNode(m_subscript); + RefPtr property = generator.emitNode(m_subscript); generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); - return generator.emitGetArgumentByVal(generator.finalDestination(dst), generator.uncheckedRegisterForArguments(), property); + return generator.emitGetArgumentByVal(generator.finalDestination(dst), generator.uncheckedRegisterForArguments(), property.get()); } RefPtr base = generator.emitNodeForLeftHandSide(m_base, m_subscriptHasAssignments, m_subscript->isPure(generator)); @@ -392,9 +392,9 @@ RegisterID* DotAccessorNode::emitBytecode(BytecodeGenerator& generator, Register } nonArgumentsPath: - RegisterID* base = generator.emitNode(m_base); + RefPtr base = generator.emitNode(m_base); generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); - return generator.emitGetById(generator.finalDestination(dst), base, m_ident); + return generator.emitGetById(generator.finalDestination(dst), base.get(), m_ident); } // ------------------------------ ArgumentListNode ----------------------------- @@ -508,9 +508,9 @@ RegisterID* FunctionCallResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* FunctionCallBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { RefPtr base = generator.emitNode(m_base); - RegisterID* property = generator.emitNode(m_subscript); + RefPtr property = generator.emitNode(m_subscript); generator.emitExpressionInfo(subexpressionDivot(), subexpressionStart(), subexpressionEnd()); - RefPtr function = generator.emitGetByVal(generator.tempDestination(dst), base.get(), property); + RefPtr function = generator.emitGetByVal(generator.tempDestination(dst), base.get(), property.get()); RefPtr returnValue = generator.finalDestination(dst, function.get()); CallArguments callArguments(generator, m_args); generator.emitMove(callArguments.thisRegister(), base.get()); @@ -736,21 +736,21 @@ RegisterID* PostfixNode::emitResolve(BytecodeGenerator& generator, RegisterID* d const Identifier& ident = resolve->identifier(); if (Local local = generator.local(ident)) { - RegisterID* localReg = local.get(); + RefPtr localReg = local.get(); if (local.isReadOnly()) { generator.emitReadOnlyExceptionIfNeeded(); - localReg = generator.emitMove(generator.tempDestination(dst), localReg); + localReg = generator.emitMove(generator.tempDestination(dst), localReg.get()); } else if (local.isCaptured()) { RefPtr tempDst = generator.finalDestination(dst); ASSERT(dst != localReg); RefPtr tempDstSrc = generator.newTemporary(); - generator.emitToNumber(tempDst.get(), localReg); - generator.emitMove(tempDstSrc.get(), localReg); + generator.emitToNumber(tempDst.get(), localReg.get()); + generator.emitMove(tempDstSrc.get(), localReg.get()); emitIncOrDec(generator, tempDstSrc.get(), m_operator); - generator.emitMove(localReg, tempDstSrc.get()); + generator.emitMove(localReg.get(), tempDstSrc.get()); return tempDst.get(); } - return emitPostIncOrDec(generator, generator.finalDestination(dst), localReg, m_operator); + return emitPostIncOrDec(generator, generator.finalDestination(dst), localReg.get(), m_operator); } generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); @@ -835,20 +835,20 @@ RegisterID* DeleteResolveNode::emitBytecode(BytecodeGenerator& generator, Regist RegisterID* DeleteBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { RefPtr r0 = generator.emitNode(m_base); - RegisterID* r1 = generator.emitNode(m_subscript); + RefPtr r1 = generator.emitNode(m_subscript); generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); - return generator.emitDeleteByVal(generator.finalDestination(dst), r0.get(), r1); + return generator.emitDeleteByVal(generator.finalDestination(dst), r0.get(), r1.get()); } // ------------------------------ DeleteDotNode ----------------------------------- RegisterID* DeleteDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { - RegisterID* r0 = generator.emitNode(m_base); + RefPtr r0 = generator.emitNode(m_base); generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); - return generator.emitDeleteById(generator.finalDestination(dst), r0, m_ident); + return generator.emitDeleteById(generator.finalDestination(dst), r0.get(), m_ident); } // ------------------------------ DeleteValueNode ----------------------------------- @@ -911,19 +911,19 @@ RegisterID* PrefixNode::emitResolve(BytecodeGenerator& generator, RegisterID* ds const Identifier& ident = resolve->identifier(); if (Local local = generator.local(ident)) { - RegisterID* localReg = local.get(); + RefPtr localReg = local.get(); if (local.isReadOnly()) { generator.emitReadOnlyExceptionIfNeeded(); - localReg = generator.emitMove(generator.tempDestination(dst), localReg); + localReg = generator.emitMove(generator.tempDestination(dst), localReg.get()); } else if (local.isCaptured()) { RefPtr tempDst = generator.tempDestination(dst); - generator.emitMove(tempDst.get(), localReg); + generator.emitMove(tempDst.get(), localReg.get()); emitIncOrDec(generator, tempDst.get(), m_operator); - generator.emitMove(localReg, tempDst.get()); + generator.emitMove(localReg.get(), tempDst.get()); return generator.moveToDestinationIfNeeded(dst, tempDst.get()); } - emitIncOrDec(generator, localReg, m_operator); - return generator.moveToDestinationIfNeeded(dst, localReg); + emitIncOrDec(generator, localReg.get(), m_operator); + return generator.moveToDestinationIfNeeded(dst, localReg.get()); } generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); @@ -991,9 +991,9 @@ RegisterID* PrefixNode::emitBytecode(BytecodeGenerator& generator, RegisterID* d RegisterID* UnaryOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { - RegisterID* src = generator.emitNode(m_expr); + RefPtr src = generator.emitNode(m_expr); generator.emitExpressionInfo(position(), position(), position()); - return generator.emitUnaryOp(opcodeID(), generator.finalDestination(dst), src); + return generator.emitUnaryOp(opcodeID(), generator.finalDestination(dst), src.get()); } // ------------------------------ BitwiseNotNode ----------------------------------- @@ -1001,8 +1001,8 @@ RegisterID* UnaryOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* RegisterID* BitwiseNotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { RefPtr src2 = generator.emitLoad(generator.newTemporary(), jsNumber(-1)); - RegisterID* src1 = generator.emitNode(m_expr); - return generator.emitBinaryOp(op_bitxor, generator.finalDestination(dst, src1), src1, src2.get(), OperandTypes(m_expr->resultDescriptor(), ResultType::numberTypeIsInt32())); + RefPtr src1 = generator.emitNode(m_expr); + return generator.emitBinaryOp(op_bitxor, generator.finalDestination(dst, src1.get()), src1.get(), src2.get(), OperandTypes(m_expr->resultDescriptor(), ResultType::numberTypeIsInt32())); } // ------------------------------ LogicalNotNode ----------------------------------- @@ -1217,19 +1217,19 @@ RegisterID* BinaryOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* RefPtr src1 = generator.emitNodeForLeftHandSide(left, m_rightHasAssignments, right->isPure(generator)); bool wasTypeof = generator.m_lastOpcodeID == op_typeof; - RegisterID* src2 = generator.emitNode(right); + RefPtr src2 = generator.emitNode(right); generator.emitExpressionInfo(position(), position(), position()); if (wasTypeof && (opcodeID == op_neq || opcodeID == op_nstricteq)) { RefPtr tmp = generator.tempDestination(dst); if (opcodeID == op_neq) - generator.emitEqualityOp(op_eq, generator.finalDestination(tmp.get(), src1.get()), src1.get(), src2); + generator.emitEqualityOp(op_eq, generator.finalDestination(tmp.get(), src1.get()), src1.get(), src2.get()); else if (opcodeID == op_nstricteq) - generator.emitEqualityOp(op_stricteq, generator.finalDestination(tmp.get(), src1.get()), src1.get(), src2); + generator.emitEqualityOp(op_stricteq, generator.finalDestination(tmp.get(), src1.get()), src1.get(), src2.get()); else RELEASE_ASSERT_NOT_REACHED(); return generator.emitUnaryOp(op_not, generator.finalDestination(dst, tmp.get()), tmp.get()); } - RegisterID* result = generator.emitBinaryOp(opcodeID, generator.finalDestination(dst, src1.get()), src1.get(), src2, OperandTypes(left->resultDescriptor(), right->resultDescriptor())); + RegisterID* result = generator.emitBinaryOp(opcodeID, generator.finalDestination(dst, src1.get()), src1.get(), src2.get(), OperandTypes(left->resultDescriptor(), right->resultDescriptor())); if (opcodeID == op_urshift && dst != generator.ignoredResult()) return generator.emitUnaryOp(op_unsigned, result, result); return result; @@ -1249,8 +1249,8 @@ RegisterID* EqualNode::emitBytecode(BytecodeGenerator& generator, RegisterID* ds std::swap(left, right); RefPtr src1 = generator.emitNodeForLeftHandSide(left, m_rightHasAssignments, m_expr2->isPure(generator)); - RegisterID* src2 = generator.emitNode(right); - return generator.emitEqualityOp(op_eq, generator.finalDestination(dst, src1.get()), src1.get(), src2); + RefPtr src2 = generator.emitNode(right); + return generator.emitEqualityOp(op_eq, generator.finalDestination(dst, src1.get()), src1.get(), src2.get()); } RegisterID* StrictEqualNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) @@ -1261,16 +1261,16 @@ RegisterID* StrictEqualNode::emitBytecode(BytecodeGenerator& generator, Register std::swap(left, right); RefPtr src1 = generator.emitNodeForLeftHandSide(left, m_rightHasAssignments, m_expr2->isPure(generator)); - RegisterID* src2 = generator.emitNode(right); - return generator.emitEqualityOp(op_stricteq, generator.finalDestination(dst, src1.get()), src1.get(), src2); + RefPtr src2 = generator.emitNode(right); + return generator.emitEqualityOp(op_stricteq, generator.finalDestination(dst, src1.get()), src1.get(), src2.get()); } RegisterID* ThrowableBinaryOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { RefPtr src1 = generator.emitNodeForLeftHandSide(m_expr1, m_rightHasAssignments, m_expr2->isPure(generator)); - RegisterID* src2 = generator.emitNode(m_expr2); + RefPtr src2 = generator.emitNode(m_expr2); generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); - return generator.emitBinaryOp(opcodeID(), generator.finalDestination(dst, src1.get()), src1.get(), src2, OperandTypes(m_expr1->resultDescriptor(), m_expr2->resultDescriptor())); + return generator.emitBinaryOp(opcodeID(), generator.finalDestination(dst, src1.get()), src1.get(), src2.get(), OperandTypes(m_expr1->resultDescriptor(), m_expr2->resultDescriptor())); } RegisterID* InstanceOfNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) @@ -1469,9 +1469,9 @@ RegisterID* AssignDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID { RefPtr base = generator.emitNodeForLeftHandSide(m_base, m_rightHasAssignments, m_right->isPure(generator)); RefPtr value = generator.destinationForAssignResult(dst); - RegisterID* result = generator.emitNode(value.get(), m_right); + RefPtr result = generator.emitNode(value.get(), m_right); generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); - RegisterID* forwardResult = (dst == generator.ignoredResult()) ? result : generator.moveToDestinationIfNeeded(generator.tempDestination(result), result); + RegisterID* forwardResult = (dst == generator.ignoredResult()) ? result.get() : generator.moveToDestinationIfNeeded(generator.tempDestination(result.get()), result.get()); generator.emitPutById(base.get(), m_ident, forwardResult); return generator.moveToDestinationIfNeeded(dst, forwardResult); } @@ -1504,10 +1504,10 @@ RegisterID* AssignBracketNode::emitBytecode(BytecodeGenerator& generator, Regist RefPtr base = generator.emitNodeForLeftHandSide(m_base, m_subscriptHasAssignments || m_rightHasAssignments, m_subscript->isPure(generator) && m_right->isPure(generator)); RefPtr property = generator.emitNodeForLeftHandSide(m_subscript, m_rightHasAssignments, m_right->isPure(generator)); RefPtr value = generator.destinationForAssignResult(dst); - RegisterID* result = generator.emitNode(value.get(), m_right); + RefPtr result = generator.emitNode(value.get(), m_right); generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); - RegisterID* forwardResult = (dst == generator.ignoredResult()) ? result : generator.moveToDestinationIfNeeded(generator.tempDestination(result), result); + RegisterID* forwardResult = (dst == generator.ignoredResult()) ? result.get() : generator.moveToDestinationIfNeeded(generator.tempDestination(result.get()), result.get()); generator.emitPutByVal(base.get(), property.get(), forwardResult); return generator.moveToDestinationIfNeeded(dst, forwardResult); } diff --git a/dfg/DFGArrayMode.h b/dfg/DFGArrayMode.h index 9c67edb..084c985 100644 --- a/dfg/DFGArrayMode.h +++ b/dfg/DFGArrayMode.h @@ -406,7 +406,7 @@ public: case Array::ArrayStorage: return arrayModesWithIndexingShape(ArrayStorageShape); case Array::SlowPutArrayStorage: - return arrayModesWithIndexingShape(SlowPutArrayStorageShape); + return arrayModesWithIndexingShapes(SlowPutArrayStorageShape, ArrayStorageShape); default: return asArrayModes(NonArray); } @@ -462,6 +462,13 @@ private: } } + ArrayModes arrayModesWithIndexingShapes(IndexingType shape1, IndexingType shape2) const + { + ArrayModes arrayMode1 = arrayModesWithIndexingShape(shape1); + ArrayModes arrayMode2 = arrayModesWithIndexingShape(shape2); + return arrayMode1 | arrayMode2; + } + bool alreadyChecked(Graph&, Node*, AbstractValue&, IndexingType shape) const; union { diff --git a/dfg/DFGGraph.h b/dfg/DFGGraph.h index c3a308b..aa1a854 100644 --- a/dfg/DFGGraph.h +++ b/dfg/DFGGraph.h @@ -481,6 +481,14 @@ public: return hasExitSite(node->origin.semantic, exitKind); } + bool usesArguments(InlineCallFrame* inlineCallFrame) + { + if (!inlineCallFrame) + return m_profiledBlock->usesArguments(); + + return baselineCodeBlockForInlineCallFrame(inlineCallFrame)->usesArguments(); + } + VirtualRegister argumentsRegisterFor(InlineCallFrame* inlineCallFrame) { if (!inlineCallFrame) diff --git a/dfg/DFGPlan.cpp b/dfg/DFGPlan.cpp index c78a307..6de782c 100644 --- a/dfg/DFGPlan.cpp +++ b/dfg/DFGPlan.cpp @@ -348,7 +348,10 @@ Plan::CompilationPath Plan::compileInThreadImpl(LongLivedState& longLivedState) } FTL::State state(dfg); - FTL::lowerDFGToLLVM(state); + if (!FTL::lowerDFGToLLVM(state)) { + FTL::fail(state); + return FTLPath; + } if (reportCompileTimes()) beforeFTL = currentTimeMS(); diff --git a/dfg/DFGStackLayoutPhase.cpp b/dfg/DFGStackLayoutPhase.cpp index 0f869c0..0722bfa 100644 --- a/dfg/DFGStackLayoutPhase.cpp +++ b/dfg/DFGStackLayoutPhase.cpp @@ -105,7 +105,7 @@ public: usedLocals.set(codeBlock()->activationRegister().toLocal()); for (InlineCallFrameSet::iterator iter = m_graph.m_plan.inlineCallFrames->begin(); !!iter; ++iter) { InlineCallFrame* inlineCallFrame = *iter; - if (!inlineCallFrame->executable->usesArguments()) + if (!m_graph.usesArguments(inlineCallFrame)) continue; VirtualRegister argumentsRegister = m_graph.argumentsRegisterFor(inlineCallFrame); @@ -171,7 +171,7 @@ public: InlineVariableData data = m_graph.m_inlineVariableData[i]; InlineCallFrame* inlineCallFrame = data.inlineCallFrame; - if (inlineCallFrame->executable->usesArguments()) { + if (m_graph.usesArguments(inlineCallFrame)) { inlineCallFrame->argumentsRegister = virtualRegisterForLocal( allocation[m_graph.argumentsRegisterFor(inlineCallFrame).toLocal()]); diff --git a/disassembler/ARMv7/ARMv7DOpcode.cpp b/disassembler/ARMv7/ARMv7DOpcode.cpp index 43ed442..c11acf8 100644 --- a/disassembler/ARMv7/ARMv7DOpcode.cpp +++ b/disassembler/ARMv7/ARMv7DOpcode.cpp @@ -91,8 +91,8 @@ static Opcode16GroupInitializer opcode16BitGroupList[] = { OPCODE_GROUP_ENTRY(0xd, ARMv7DOpcodeLoadStoreRegisterImmediateWordAndByte), OPCODE_GROUP_ENTRY(0xe, ARMv7DOpcodeLoadStoreRegisterImmediateWordAndByte), OPCODE_GROUP_ENTRY(0xf, ARMv7DOpcodeLoadStoreRegisterImmediateWordAndByte), - OPCODE_GROUP_ENTRY(0x10, ARMv7DOpcodeLoadStoreRegisterImmediateHalfWord), - OPCODE_GROUP_ENTRY(0x11, ARMv7DOpcodeLoadStoreRegisterImmediateHalfWord), + OPCODE_GROUP_ENTRY(0x10, ARMv7DOpcodeStoreRegisterImmediateHalfWord), + OPCODE_GROUP_ENTRY(0x11, ARMv7DOpcodeLoadRegisterImmediateHalfWord), OPCODE_GROUP_ENTRY(0x12, ARMv7DOpcodeLoadStoreRegisterSPRelative), OPCODE_GROUP_ENTRY(0x13, ARMv7DOpcodeLoadStoreRegisterSPRelative), OPCODE_GROUP_ENTRY(0x14, ARMv7DOpcodeGeneratePCRelativeAddress), @@ -514,6 +514,25 @@ const char* ARMv7DOpcodeLoadStoreRegisterImmediate::format() return m_formatBuffer; } +unsigned ARMv7DOpcodeLoadStoreRegisterImmediate::scale() +{ + switch (op()) { + case 0: + case 1: + return 2; + case 2: + case 3: + return 0; + case 4: + case 5: + return 1; + default: + break; + } + ASSERT_NOT_REACHED(); + return 0; +} + const char* const ARMv7DOpcodeLoadStoreRegisterOffsetT1::s_opNames[8] = { "str", "strh", "strb", "ldrsb", "ldr", "ldrh", "ldrb", "ldrsh" }; diff --git a/disassembler/ARMv7/ARMv7DOpcode.h b/disassembler/ARMv7/ARMv7DOpcode.h index b415298..4273c31 100644 --- a/disassembler/ARMv7/ARMv7DOpcode.h +++ b/disassembler/ARMv7/ARMv7DOpcode.h @@ -275,7 +275,7 @@ protected: const char* opName() { return s_opNames[op()]; } unsigned op() { return (m_opcode >> 9) & 0x1; } - unsigned immediate3() { return (m_opcode >> 6) & 0x3; } + unsigned immediate3() { return (m_opcode >> 6) & 0x7; } unsigned rn() { return (m_opcode >> 3) & 0x7; } }; @@ -441,7 +441,7 @@ protected: unsigned immediate5() { return (m_opcode >> 6) & 0x01f; } unsigned rn() { return (m_opcode >> 3) & 0x7; } unsigned rt() { return m_opcode & 0x7; } - unsigned scale() { return 2 - (op() >> 1); } + unsigned scale(); }; class ARMv7DOpcodeLoadStoreRegisterImmediateWordAndByte : public ARMv7DOpcodeLoadStoreRegisterImmediate { @@ -452,7 +452,7 @@ public: DEFINE_STATIC_FORMAT16(ARMv7DOpcodeLoadStoreRegisterImmediate, thisObj); }; -class ARMv7DOpcodeLoadStoreRegisterImmediateHalfWord : public ARMv7DOpcodeLoadStoreRegisterImmediate { +class ARMv7DOpcodeStoreRegisterImmediateHalfWord : public ARMv7DOpcodeLoadStoreRegisterImmediate { public: static const uint16_t s_mask = 0xf800; static const uint16_t s_pattern = 0x8000; @@ -460,6 +460,14 @@ public: DEFINE_STATIC_FORMAT16(ARMv7DOpcodeLoadStoreRegisterImmediate, thisObj); }; +class ARMv7DOpcodeLoadRegisterImmediateHalfWord : public ARMv7DOpcodeLoadStoreRegisterImmediate { +public: + static const uint16_t s_mask = 0xf800; + static const uint16_t s_pattern = 0x8800; + + DEFINE_STATIC_FORMAT16(ARMv7DOpcodeLoadStoreRegisterImmediate, thisObj); +}; + class ARMv7DOpcodeLoadStoreRegisterOffsetT1 : public ARMv7D16BitOpcode { private: static const char* const s_opNames[8]; diff --git a/ftl/FTLLowerDFGToLLVM.cpp b/ftl/FTLLowerDFGToLLVM.cpp index 3f6050a..ecb1513 100644 --- a/ftl/FTLLowerDFGToLLVM.cpp +++ b/ftl/FTLLowerDFGToLLVM.cpp @@ -68,6 +68,7 @@ public: LowerDFGToLLVM(State& state) : m_graph(state.graph) , m_ftlState(state) + , m_loweringSucceeded(true) , m_heaps(state.context) , m_out(state.context) , m_availability(OperandsLike, state.graph.block(0)->variablesAtHead) @@ -76,8 +77,12 @@ public: , m_stackmapIDs(0) { } - - void lower() + + +#define LOWERING_FAILED(node, reason) \ + loweringFailed((node), __FILE__, __LINE__, WTF_PRETTY_FUNCTION, (reason)); + + bool lower() { CString name; if (verboseCompilationEnabled()) { @@ -157,10 +162,16 @@ public: m_out.constInt32(MacroAssembler::maxJumpReplacementSize())); m_out.unreachable(); + if (!m_loweringSucceeded) + return m_loweringSucceeded; + Vector depthFirst; m_graph.getBlocksInDepthFirstOrder(depthFirst); - for (unsigned i = 0; i < depthFirst.size(); ++i) + for (unsigned i = 0; i < depthFirst.size(); ++i) { compileBlock(depthFirst[i]); + if (!m_loweringSucceeded) + return m_loweringSucceeded; + } if (Options::dumpLLVMIR()) dumpModule(m_ftlState.module); @@ -169,6 +180,8 @@ public: m_ftlState.dumpState("after lowering"); if (validationEnabled()) verifyModule(m_ftlState.module); + + return m_loweringSucceeded; } private: @@ -201,8 +214,8 @@ private: type = m_out.int64; break; default: - RELEASE_ASSERT_NOT_REACHED(); - break; + LOWERING_FAILED(node, "Bad Phi node result type"); + return; } m_phis.add(node, buildAlloca(m_out.m_builder, type)); } @@ -631,15 +644,13 @@ private: case AllocationProfileWatchpoint: break; default: - dataLog("Unrecognized node in FTL backend:\n"); - m_graph.dump(WTF::dataFile(), " ", m_node); - dataLog("\n"); - dataLog("Full graph dump:\n"); - m_graph.dump(); - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Unrecognized node in FTL backend"); break; } + if (!m_loweringSucceeded) + return false; + if (shouldExecuteEffects) m_interpreter.executeEffects(nodeIndex); @@ -670,7 +681,7 @@ private: m_out.set(lowJSValue(m_node->child1()), destination); break; default: - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad use kind"); break; } } @@ -696,7 +707,7 @@ private: setJSValue(m_out.get(source)); break; default: - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad use kind"); break; } } @@ -739,7 +750,7 @@ private: } default: - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad use kind"); } } @@ -764,7 +775,7 @@ private: } default: - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad use kind"); } } @@ -788,7 +799,7 @@ private: return; default: - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad use kind"); } } @@ -827,7 +838,7 @@ private: } default: - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad use kind"); break; } } @@ -860,7 +871,7 @@ private: } default: - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad flush format"); return; } } @@ -890,7 +901,7 @@ private: setJSValue(jsValue); break; default: - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad use kind"); break; } } @@ -961,8 +972,8 @@ private: } default: - RELEASE_ASSERT_NOT_REACHED(); - break; + LOWERING_FAILED(m_node, "Bad flush format for argument"); + return; } m_availability.operand(variable->local()) = Availability(variable->flushedAt()); @@ -1137,7 +1148,7 @@ private: } default: - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad use kind"); break; } } @@ -1211,7 +1222,7 @@ private: } default: - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad use kind"); break; } } @@ -1314,7 +1325,7 @@ private: } default: - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad use kind"); break; } } @@ -1412,7 +1423,7 @@ private: } default: - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad use kind"); break; } } @@ -1463,7 +1474,7 @@ private: } default: - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad use kind"); break; } } @@ -1489,7 +1500,7 @@ private: } default: - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad use kind"); break; } } @@ -1556,7 +1567,7 @@ private: } default: - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad use kind"); break; } } @@ -1722,8 +1733,8 @@ private: vmCall(m_out.operation(operationEnsureArrayStorage), m_callFrame, cell); break; default: - RELEASE_ASSERT_NOT_REACHED(); - break; + LOWERING_FAILED(m_node, "Bad array type"); + return; } structureID = m_out.load32(cell, m_heaps.JSCell_structureID); @@ -1796,7 +1807,7 @@ private: } default: - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad use kind"); return; } } @@ -1948,13 +1959,19 @@ private: // FIXME: FTL should support activations. // https://bugs.webkit.org/show_bug.cgi?id=129576 - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Unimplemented"); + return; } TypedPointer base; - if (codeOrigin.inlineCallFrame) - base = addressFor(codeOrigin.inlineCallFrame->arguments[1].virtualRegister()); - else + if (codeOrigin.inlineCallFrame) { + VirtualRegister reg; + if (codeOrigin.inlineCallFrame->arguments.size() <= 1) + reg = virtualRegisterForLocal(0); // Doesn't matter what we do since we would have exited anyway. + else + reg = codeOrigin.inlineCallFrame->arguments[1].virtualRegister(); + base = addressFor(reg); + } else base = addressFor(virtualRegisterForArgument(1)); LValue pointer = m_out.baseIndex( @@ -1985,7 +2002,7 @@ private: return; } - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad array type"); return; } } @@ -2135,7 +2152,8 @@ private: result = m_out.load32(pointer); break; default: - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad element size"); + return; } if (elementSize(type) < 4) { @@ -2179,14 +2197,15 @@ private: result = m_out.loadDouble(pointer); break; default: - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad typed array type"); + return; } setDouble(result); return; } - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad array type"); return; } } } @@ -2292,7 +2311,8 @@ private: } default: - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad array type"); + return; } m_out.jump(continuation); @@ -2385,7 +2405,8 @@ private: } default: - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad use kind"); + return; } switch (elementSize(type)) { @@ -2402,7 +2423,8 @@ private: refType = m_out.ref32; break; default: - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad element size"); + return; } } else /* !isInt(type) */ { LValue value = lowDouble(child3); @@ -2416,7 +2438,8 @@ private: refType = m_out.refDouble; break; default: - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad typed array type"); + return; } } @@ -2440,8 +2463,8 @@ private: return; } - RELEASE_ASSERT_NOT_REACHED(); - break; + LOWERING_FAILED(m_node, "Bad array type"); + return; } } @@ -2512,7 +2535,7 @@ private: } default: - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad array type"); return; } } @@ -2570,7 +2593,7 @@ private: } default: - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad array type"); return; } } @@ -2929,8 +2952,8 @@ private: } default: - RELEASE_ASSERT_NOT_REACHED(); - break; + LOWERING_FAILED(m_node, "Bad use kind"); + return; } } @@ -3021,7 +3044,8 @@ private: m_out.operation(operationMakeRope3), m_callFrame, kids[0], kids[1], kids[2])); break; default: - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad number of children"); + return; break; } m_out.jump(continuation); @@ -3426,8 +3450,8 @@ private: nonSpeculativeCompare(LLVMIntEQ, operationCompareEq); return; } - - RELEASE_ASSERT_NOT_REACHED(); + + LOWERING_FAILED(m_node, "Bad use kinds"); } void compileCompareEqConstant() @@ -3520,7 +3544,7 @@ private: return; } - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad use kinds"); } void compileCompareStrictEqConstant() @@ -3656,8 +3680,8 @@ private: } default: - RELEASE_ASSERT_NOT_REACHED(); - break; + LOWERING_FAILED(m_node, "Bad use kind"); + return; } m_out.appendTo(switchOnInts, lastNext); @@ -3702,8 +3726,8 @@ private: } default: - RELEASE_ASSERT_NOT_REACHED(); - break; + LOWERING_FAILED(m_node, "Bad use kind"); + return; } LBasicBlock lengthIs1 = FTL_NEW_BLOCK(m_out, ("Switch/SwitchChar length is 1")); @@ -3755,11 +3779,11 @@ private: } case SwitchString: - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Unimplemented"); break; } - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad switch kind"); } void compileReturn() @@ -4216,7 +4240,7 @@ private: return; } - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad use kinds"); } void compareEqObjectOrOtherToObject(Edge leftChild, Edge rightChild) @@ -4495,7 +4519,7 @@ private: return m_out.phi(m_out.boolean, fastResult, slowResult); } default: - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad use kind"); return 0; } } @@ -4868,7 +4892,7 @@ private: case StrictInt52: return Int52; } - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Bad use kind"); return Int52; } @@ -5013,7 +5037,7 @@ private: return result; } - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Corrupt array class"); return 0; } @@ -5325,8 +5349,8 @@ private: speculateMisc(edge); break; default: - dataLog("Unsupported speculation use kind: ", edge.useKind(), "\n"); - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Unsupported speculation use kind"); + return; } } @@ -5387,7 +5411,7 @@ private: switch (arrayMode.arrayClass()) { case Array::OriginalArray: - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Unexpected original array"); return 0; case Array::Array: @@ -5407,7 +5431,8 @@ private: m_out.constInt8(arrayMode.shapeMask())); } - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Corrupt array class"); + return 0; } default: @@ -6043,7 +6068,7 @@ private: return; } - RELEASE_ASSERT_NOT_REACHED(); + LOWERING_FAILED(m_node, "Corrupt int52 kind"); } void setJSValue(Node* node, LValue value) { @@ -6170,11 +6195,24 @@ private: return addressFor(operand, TagOffset); } + NO_RETURN_DUE_TO_ASSERT void loweringFailed(Node* node, const char* file, int line, const char* function, const char* assertion) + { + if (!ASSERT_DISABLED) { + dataLog("FTL ASSERTION FAILED: ", assertion, "\n"); + dataLog(file, "(", line, ") : ", function, "\n"); + dataLog("While handling node ", node, "\n"); + RELEASE_ASSERT_NOT_REACHED(); + } + + m_loweringSucceeded = false; + } + VM& vm() { return m_graph.m_vm; } CodeBlock* codeBlock() { return m_graph.m_codeBlock; } Graph& m_graph; State& m_ftlState; + bool m_loweringSucceeded; AbstractHeapRepository m_heaps; Output m_out; @@ -6215,10 +6253,10 @@ private: uint32_t m_stackmapIDs; }; -void lowerDFGToLLVM(State& state) +bool lowerDFGToLLVM(State& state) { LowerDFGToLLVM lowering(state); - lowering.lower(); + return lowering.lower(); } } } // namespace JSC::FTL diff --git a/ftl/FTLLowerDFGToLLVM.h b/ftl/FTLLowerDFGToLLVM.h index 0e38d7b..141b625 100644 --- a/ftl/FTLLowerDFGToLLVM.h +++ b/ftl/FTLLowerDFGToLLVM.h @@ -33,7 +33,7 @@ namespace JSC { namespace FTL { -void lowerDFGToLLVM(State&); +bool lowerDFGToLLVM(State&); } } // namespace JSC::FTL diff --git a/heap/Heap.cpp b/heap/Heap.cpp index 08a09d1..447383b 100644 --- a/heap/Heap.cpp +++ b/heap/Heap.cpp @@ -497,8 +497,9 @@ void Heap::markRoots(double gcStartTime) // We gather conservative roots before clearing mark bits because conservative // gathering uses the mark bits to determine whether a reference is valid. void* dummy; + ALLOCATE_AND_GET_REGISTER_STATE(registers); ConservativeRoots conservativeRoots(&m_objectSpace.blocks(), &m_storageSpace); - gatherStackRoots(conservativeRoots, &dummy); + gatherStackRoots(conservativeRoots, &dummy, registers); gatherJSStackRoots(conservativeRoots); gatherScratchBufferRoots(conservativeRoots); @@ -558,11 +559,11 @@ void Heap::copyBackingStores() m_storageSpace.doneCopying(); } -void Heap::gatherStackRoots(ConservativeRoots& roots, void** dummy) +void Heap::gatherStackRoots(ConservativeRoots& roots, void** dummy, MachineThreads::RegisterState& registers) { GCPHASE(GatherStackRoots); m_jitStubRoutines.clearMarks(); - m_machineThreads.gatherConservativeRoots(roots, m_jitStubRoutines, m_codeBlocks, dummy); + m_machineThreads.gatherConservativeRoots(roots, m_jitStubRoutines, m_codeBlocks, dummy, registers); } void Heap::gatherJSStackRoots(ConservativeRoots& roots) diff --git a/heap/Heap.h b/heap/Heap.h index 1858f1b..2f2e3e9 100644 --- a/heap/Heap.h +++ b/heap/Heap.h @@ -271,7 +271,7 @@ private: void stopAllocation(); void markRoots(double gcStartTime); - void gatherStackRoots(ConservativeRoots&, void** dummy); + void gatherStackRoots(ConservativeRoots&, void** dummy, MachineThreads::RegisterState& registers); void gatherJSStackRoots(ConservativeRoots&); void gatherScratchBufferRoots(ConservativeRoots&); void clearLivenessData(); diff --git a/heap/MachineStackMarker.cpp b/heap/MachineStackMarker.cpp index 0654ffe..c354e77 100644 --- a/heap/MachineStackMarker.cpp +++ b/heap/MachineStackMarker.cpp @@ -227,25 +227,8 @@ void MachineThreads::removeCurrentThread() } } -#if COMPILER(GCC) -#define REGISTER_BUFFER_ALIGNMENT __attribute__ ((aligned (sizeof(void*)))) -#else -#define REGISTER_BUFFER_ALIGNMENT -#endif - -void MachineThreads::gatherFromCurrentThread(ConservativeRoots& conservativeRoots, JITStubRoutineSet& jitStubRoutines, CodeBlockSet& codeBlocks, void* stackCurrent) +void MachineThreads::gatherFromCurrentThread(ConservativeRoots& conservativeRoots, JITStubRoutineSet& jitStubRoutines, CodeBlockSet& codeBlocks, void* stackCurrent, RegisterState& registers) { - // setjmp forces volatile registers onto the stack - jmp_buf registers REGISTER_BUFFER_ALIGNMENT; -#if COMPILER(MSVC) -#pragma warning(push) -#pragma warning(disable: 4611) -#endif - setjmp(registers); -#if COMPILER(MSVC) -#pragma warning(pop) -#endif - void* registersBegin = ®isters; void* registersEnd = reinterpret_cast(roundUpToMultipleOf(reinterpret_cast(®isters + 1))); swapIfBackwards(registersBegin, registersEnd); @@ -257,14 +240,18 @@ void MachineThreads::gatherFromCurrentThread(ConservativeRoots& conservativeRoot conservativeRoots.add(stackBegin, stackEnd, jitStubRoutines, codeBlocks); } -static inline void suspendThread(const PlatformThread& platformThread) +static inline bool suspendThread(const PlatformThread& platformThread) { #if OS(DARWIN) - thread_suspend(platformThread); + kern_return_t result = thread_suspend(platformThread); + return result == KERN_SUCCESS; #elif OS(WINDOWS) - SuspendThread(platformThread); + bool threadIsSuspended = (SuspendThread(platformThread) != (DWORD)-1); + ASSERT(threadIsSuspended); + return threadIsSuspended; #elif USE(PTHREADS) pthread_kill(platformThread, SigThreadSuspendResume); + return true; #else #error Need a way to suspend threads on this platform #endif @@ -460,24 +447,67 @@ void MachineThreads::gatherFromOtherThread(ConservativeRoots& conservativeRoots, freePlatformThreadRegisters(regs); } -void MachineThreads::gatherConservativeRoots(ConservativeRoots& conservativeRoots, JITStubRoutineSet& jitStubRoutines, CodeBlockSet& codeBlocks, void* stackCurrent) +void MachineThreads::gatherConservativeRoots(ConservativeRoots& conservativeRoots, JITStubRoutineSet& jitStubRoutines, CodeBlockSet& codeBlocks, void* stackCurrent, RegisterState& registers) { - gatherFromCurrentThread(conservativeRoots, jitStubRoutines, codeBlocks, stackCurrent); + gatherFromCurrentThread(conservativeRoots, jitStubRoutines, codeBlocks, stackCurrent, registers); if (m_threadSpecific) { PlatformThread currentPlatformThread = getCurrentPlatformThread(); MutexLocker lock(m_registeredThreadsMutex); + Thread* threadsToBeDeleted = nullptr; + #ifndef NDEBUG // Forbid malloc during the gather phase. The gather phase suspends // threads, so a malloc during gather would risk a deadlock with a // thread that had been suspended while holding the malloc lock. fastMallocForbid(); #endif - for (Thread* thread = m_registeredThreads; thread; thread = thread->next) { - if (!equalThread(thread->platformThread, currentPlatformThread)) - suspendThread(thread->platformThread); + int numberOfThreads = 0; // Using 0 to denote that we haven't counted the number of threads yet. + int index = 1; + Thread* previousThread = nullptr; + for (Thread* thread = m_registeredThreads; thread; index++) { + if (!equalThread(thread->platformThread, currentPlatformThread)) { + bool success = suspendThread(thread->platformThread); +#if OS(DARWIN) + if (!success) { + if (!numberOfThreads) { + for (Thread* countedThread = m_registeredThreads; countedThread; countedThread = countedThread->next) + numberOfThreads++; + } + + // Re-do the suspension to get the actual failure result for logging. + kern_return_t error = thread_suspend(thread->platformThread); + ASSERT(error != KERN_SUCCESS); + + WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, + "JavaScript garbage collection encountered an invalid thread (err 0x%x): Thread [%d/%d: %p] platformThread %p.", + error, index, numberOfThreads, thread, reinterpret_cast(thread->platformThread)); + + // Put the invalid thread on the threadsToBeDeleted list. + // We can't just delete it here because we have suspended other + // threads, and they may still be holding the C heap lock which + // we need for deleting the invalid thread. Hence, we need to + // defer the deletion till after we have resumed all threads. + Thread* nextThread = thread->next; + thread->next = threadsToBeDeleted; + threadsToBeDeleted = thread; + + if (previousThread) + previousThread->next = nextThread; + else + m_registeredThreads = nextThread; + thread = nextThread; + continue; + } +#else + UNUSED_PARAM(numberOfThreads); + ASSERT_UNUSED(success, success); +#endif + } + previousThread = thread; + thread = thread->next; } // It is safe to access the registeredThreads list, because we earlier asserted that locks are being held, @@ -495,6 +525,11 @@ void MachineThreads::gatherConservativeRoots(ConservativeRoots& conservativeRoot #ifndef NDEBUG fastMallocAllow(); #endif + for (Thread* thread = threadsToBeDeleted; thread; ) { + Thread* nextThread = thread->next; + delete thread; + thread = nextThread; + } } } diff --git a/heap/MachineStackMarker.h b/heap/MachineStackMarker.h index 33b72c8..22b1d22 100644 --- a/heap/MachineStackMarker.h +++ b/heap/MachineStackMarker.h @@ -22,6 +22,7 @@ #ifndef MachineThreads_h #define MachineThreads_h +#include #include #include #include @@ -36,16 +37,18 @@ namespace JSC { class MachineThreads { WTF_MAKE_NONCOPYABLE(MachineThreads); public: + typedef jmp_buf RegisterState; + MachineThreads(Heap*); ~MachineThreads(); - void gatherConservativeRoots(ConservativeRoots&, JITStubRoutineSet&, CodeBlockSet&, void* stackCurrent); + void gatherConservativeRoots(ConservativeRoots&, JITStubRoutineSet&, CodeBlockSet&, void* stackCurrent, RegisterState& registers); JS_EXPORT_PRIVATE void makeUsableFromMultipleThreads(); JS_EXPORT_PRIVATE void addCurrentThread(); // Only needs to be called by clients that can use the same heap from multiple threads. private: - void gatherFromCurrentThread(ConservativeRoots&, JITStubRoutineSet&, CodeBlockSet&, void* stackCurrent); + void gatherFromCurrentThread(ConservativeRoots&, JITStubRoutineSet&, CodeBlockSet&, void* stackCurrent, RegisterState& registers); class Thread; @@ -64,4 +67,24 @@ namespace JSC { } // namespace JSC +#if COMPILER(GCC) +#define REGISTER_BUFFER_ALIGNMENT __attribute__ ((aligned (sizeof(void*)))) +#else +#define REGISTER_BUFFER_ALIGNMENT +#endif + +// ALLOCATE_AND_GET_REGISTER_STATE() is a macro so that it is always "inlined" even in debug builds. +#if COMPILER(MSVC) +#pragma warning(push) +#pragma warning(disable: 4611) +#define ALLOCATE_AND_GET_REGISTER_STATE(registers) \ + MachineThreads::RegisterState registers REGISTER_BUFFER_ALIGNMENT; \ + setjmp(registers) +#pragma warning(pop) +#else +#define ALLOCATE_AND_GET_REGISTER_STATE(registers) \ + MachineThreads::RegisterState registers REGISTER_BUFFER_ALIGNMENT; \ + setjmp(registers) +#endif + #endif // MachineThreads_h diff --git a/inspector/InjectedScript.cpp b/inspector/InjectedScript.cpp index 785dad0..1a2b930 100644 --- a/inspector/InjectedScript.cpp +++ b/inspector/InjectedScript.cpp @@ -109,11 +109,12 @@ void InjectedScript::getFunctionDetails(ErrorString* errorString, const String& *result = Inspector::TypeBuilder::Debugger::FunctionDetails::runtimeCast(resultValue); } -void InjectedScript::getProperties(ErrorString* errorString, const String& objectId, bool ownProperties, RefPtr>* properties) +void InjectedScript::getProperties(ErrorString* errorString, const String& objectId, bool ownProperties, bool ownAndGetterProperties, RefPtr>* properties) { Deprecated::ScriptFunctionCall function(injectedScriptObject(), ASCIILiteral("getProperties"), inspectorEnvironment()->functionCallHandler()); function.appendArgument(objectId); function.appendArgument(ownProperties); + function.appendArgument(ownAndGetterProperties); RefPtr result; makeCall(function, &result); diff --git a/inspector/InjectedScript.h b/inspector/InjectedScript.h index 4c58b19..fce17fc 100644 --- a/inspector/InjectedScript.h +++ b/inspector/InjectedScript.h @@ -60,7 +60,7 @@ public: void callFunctionOn(ErrorString*, const String& objectId, const String& expression, const String& arguments, bool returnByValue, bool generatePreview, RefPtr* result, TypeBuilder::OptOutput* wasThrown); void evaluateOnCallFrame(ErrorString*, const Deprecated::ScriptValue& callFrames, const String& callFrameId, const String& expression, const String& objectGroup, bool includeCommandLineAPI, bool returnByValue, bool generatePreview, RefPtr* result, TypeBuilder::OptOutput* wasThrown); void getFunctionDetails(ErrorString*, const String& functionId, RefPtr* result); - void getProperties(ErrorString*, const String& objectId, bool ownProperties, RefPtr>* result); + void getProperties(ErrorString*, const String& objectId, bool ownProperties, bool ownAndGetterProperties, RefPtr>* result); void getInternalProperties(ErrorString*, const String& objectId, RefPtr>* result); PassRefPtr> wrapCallFrames(const Deprecated::ScriptValue&); diff --git a/inspector/InjectedScriptSource.js b/inspector/InjectedScriptSource.js index db331cf..a7a1e5c 100644 --- a/inspector/InjectedScriptSource.js +++ b/inspector/InjectedScriptSource.js @@ -216,12 +216,7 @@ InjectedScript.prototype = { return result; }, - /** - * @param {string} objectId - * @param {boolean} ownProperties - * @return {Array.|boolean} - */ - getProperties: function(objectId, ownProperties) + getProperties: function(objectId, ownProperties, ownAndGetterProperties) { var parsedObjectId = this._parseObjectId(objectId); var object = this._objectForId(parsedObjectId); @@ -229,7 +224,8 @@ InjectedScript.prototype = { if (!this._isDefined(object)) return false; - var descriptors = this._propertyDescriptors(object, ownProperties); + + var descriptors = this._propertyDescriptors(object, ownProperties, ownAndGetterProperties); // Go over properties, wrap object values. for (var i = 0; i < descriptors.length; ++i) { @@ -245,6 +241,7 @@ InjectedScript.prototype = { if (!("enumerable" in descriptor)) descriptor.enumerable = false; } + return descriptors; }, @@ -317,70 +314,6 @@ InjectedScript.prototype = { delete this._idToObjectGroupName[id]; }, - /** - * @param {Object} object - * @param {boolean} ownProperties - * @return {Array.} - */ - _propertyDescriptors: function(object, ownProperties) - { - var descriptors = []; - var nameProcessed = {}; - nameProcessed["__proto__"] = null; - for (var o = object; this._isDefined(o); o = o.__proto__) { - var names = Object.getOwnPropertyNames(/** @type {!Object} */ (o)); - for (var i = 0; i < names.length; ++i) { - var name = names[i]; - if (nameProcessed[name]) - continue; - - try { - nameProcessed[name] = true; - var descriptor = Object.getOwnPropertyDescriptor(/** @type {!Object} */ (object), name); - if (!descriptor) { - // Not all bindings provide proper descriptors. Fall back to the writable, configurable property. - try { - descriptor = { name: name, value: object[name], writable: false, configurable: false, enumerable: false}; - if (o === object) - descriptor.isOwn = true; - descriptors.push(descriptor); - } catch (e) { - // Silent catch. - } - continue; - } - if (descriptor.hasOwnProperty("get") && descriptor.hasOwnProperty("set") && !descriptor.get && !descriptor.set) { - // Not all bindings provide proper descriptors. Fall back to the writable, configurable property. - try { - descriptor = { name: name, value: object[name], writable: false, configurable: false, enumerable: false}; - if (o === object) - descriptor.isOwn = true; - descriptors.push(descriptor); - } catch (e) { - // Silent catch. - } - continue; - } - } catch (e) { - var descriptor = {}; - descriptor.value = e; - descriptor.wasThrown = true; - } - - descriptor.name = name; - if (o === object) - descriptor.isOwn = true; - descriptors.push(descriptor); - } - if (ownProperties) { - if (object.__proto__) - descriptors.push({ name: "__proto__", value: object.__proto__, writable: true, configurable: true, enumerable: false, isOwn: true}); - break; - } - } - return descriptors; - }, - /** * @param {string} expression * @param {string} objectGroup @@ -679,6 +612,108 @@ InjectedScript.prototype = { return module; }, + _propertyDescriptors: function(object, ownProperties, ownAndGetterProperties) + { + // Modes: + // - ownProperties - only own properties and __proto__ + // - ownAndGetterProperties - own properties, __proto__, and getters in the prototype chain + // - neither - get all properties in the prototype chain, exclude __proto__ + + var descriptors = []; + var nameProcessed = {}; + nameProcessed["__proto__"] = null; + + function createFakeValueDescriptor(name, descriptor, isOwnProperty) + { + try { + return {name: name, value: object[name], writable: descriptor.writable || false, configurable: descriptor.configurable || false, enumerable: descriptor.enumerable || false}; + } catch (e) { + var errorDescriptor = {name: name, value: e, wasThrown: true}; + if (isOwnProperty) + errorDescriptor.isOwn = true; + return errorDescriptor; + } + } + + function processDescriptor(descriptor, isOwnProperty, possibleNativeBindingGetter) + { + // Own properties only. + if (ownProperties) { + if (isOwnProperty) + descriptors.push(descriptor); + return; + } + + // Own and getter properties. + if (ownAndGetterProperties) { + if (isOwnProperty) { + // Own property, include the descriptor as is. + descriptors.push(descriptor); + } else if (descriptor.hasOwnProperty("get") && descriptor.get) { + // Getter property in the prototype chain. Create a fake value descriptor. + descriptors.push(createFakeValueDescriptor(descriptor.name, descriptor, isOwnProperty)); + } else if (possibleNativeBindingGetter) { + // Possible getter property in the prototype chain. + descriptors.push(descriptor); + } + return; + } + + // All properties. + descriptors.push(descriptor); + } + + function processPropertyNames(o, names, isOwnProperty) + { + for (var i = 0; i < names.length; ++i) { + var name = names[i]; + if (nameProcessed[name] || name === "__proto__") + continue; + + nameProcessed[name] = true; + + var descriptor = Object.getOwnPropertyDescriptor(o, name); + if (!descriptor) { + // FIXME: Bad descriptor. Can we get here? + // Fall back to very restrictive settings. + var fakeDescriptor = createFakeValueDescriptor(name, {writable: false, configurable: false, enumerable: false}, isOwnProperty); + processDescriptor(fakeDescriptor, isOwnProperty); + continue; + } + + if (descriptor.hasOwnProperty("get") && descriptor.hasOwnProperty("set") && !descriptor.get && !descriptor.set) { + // FIXME: Web Inspector: Native Bindings Descriptors are Incomplete + // Developers may create such a descriptors, so we should be resilient: + // var x = {}; Object.defineProperty(x, "p", {get:undefined}); Object.getOwnPropertyDescriptor(x, "p") + var fakeDescriptor = createFakeValueDescriptor(name, descriptor, isOwnProperty); + processDescriptor(fakeDescriptor, isOwnProperty, true); + continue; + } + + descriptor.name = name; + if (isOwnProperty) + descriptor.isOwn = true; + processDescriptor(descriptor, isOwnProperty); + } + } + + // Iterate prototype chain. + for (var o = object; this._isDefined(o); o = o.__proto__) { + var isOwnProperty = o === object; + processPropertyNames(o, Object.getOwnPropertyNames(o), isOwnProperty); + if (ownProperties) + break; + } + + // Include __proto__ at the end. + try { + if (object.__proto__) + descriptors.push({name: "__proto__", value: object.__proto__, writable: true, configurable: true, enumerable: false, isOwn: true}); + } catch (e) {} + + return descriptors; + }, + /** * @param {*} object * @return {boolean} diff --git a/inspector/JSGlobalObjectInspectorController.cpp b/inspector/JSGlobalObjectInspectorController.cpp index 4b8301a..ebdd180 100644 --- a/inspector/JSGlobalObjectInspectorController.cpp +++ b/inspector/JSGlobalObjectInspectorController.cpp @@ -48,6 +48,11 @@ #include #include +#if ENABLE(REMOTE_INSPECTOR) +#include "JSGlobalObjectDebuggable.h" +#include "RemoteInspector.h" +#endif + using namespace JSC; namespace Inspector { @@ -175,6 +180,19 @@ ConsoleClient* JSGlobalObjectInspectorController::consoleClient() const return m_consoleClient.get(); } +bool JSGlobalObjectInspectorController::developerExtrasEnabled() const +{ +#if ENABLE(REMOTE_INSPECTOR) + if (!RemoteInspector::shared().enabled()) + return false; + + if (!m_globalObject.inspectorDebuggable().remoteDebuggingAllowed()) + return false; +#endif + + return true; +} + InspectorFunctionCallHandler JSGlobalObjectInspectorController::functionCallHandler() const { return JSC::call; diff --git a/inspector/JSGlobalObjectInspectorController.h b/inspector/JSGlobalObjectInspectorController.h index 9837717..61ab1c1 100644 --- a/inspector/JSGlobalObjectInspectorController.h +++ b/inspector/JSGlobalObjectInspectorController.h @@ -71,7 +71,7 @@ public: JSC::ConsoleClient* consoleClient() const; - virtual bool developerExtrasEnabled() const override { return true; } + virtual bool developerExtrasEnabled() const override; virtual bool canAccessInspectedScriptState(JSC::ExecState*) const override { return true; } virtual InspectorFunctionCallHandler functionCallHandler() const override; virtual InspectorEvaluateHandler evaluateHandler() const override; diff --git a/inspector/agents/InspectorRuntimeAgent.cpp b/inspector/agents/InspectorRuntimeAgent.cpp index d8d69a2..ac65979 100644 --- a/inspector/agents/InspectorRuntimeAgent.cpp +++ b/inspector/agents/InspectorRuntimeAgent.cpp @@ -157,7 +157,7 @@ void InspectorRuntimeAgent::callFunctionOn(ErrorString* errorString, const Strin } } -void InspectorRuntimeAgent::getProperties(ErrorString* errorString, const String& objectId, const bool* const ownProperties, RefPtr>& result, RefPtr>& internalProperties) +void InspectorRuntimeAgent::getProperties(ErrorString* errorString, const String& objectId, const bool* const ownProperties, const bool* const ownAndGetterProperties, RefPtr>& result, RefPtr>& internalProperties) { InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId); if (injectedScript.hasNoValue()) { @@ -168,7 +168,7 @@ void InspectorRuntimeAgent::getProperties(ErrorString* errorString, const String ScriptDebugServer::PauseOnExceptionsState previousPauseOnExceptionsState = setPauseOnExceptionsState(m_scriptDebugServer, ScriptDebugServer::DontPauseOnExceptions); muteConsole(); - injectedScript.getProperties(errorString, objectId, ownProperties ? *ownProperties : false, &result); + injectedScript.getProperties(errorString, objectId, ownProperties ? *ownProperties : false, ownAndGetterProperties ? *ownAndGetterProperties : false, &result); injectedScript.getInternalProperties(errorString, objectId, &internalProperties); unmuteConsole(); diff --git a/inspector/agents/InspectorRuntimeAgent.h b/inspector/agents/InspectorRuntimeAgent.h index d6c1eea..faf4378 100644 --- a/inspector/agents/InspectorRuntimeAgent.h +++ b/inspector/agents/InspectorRuntimeAgent.h @@ -63,7 +63,7 @@ public: virtual void evaluate(ErrorString*, const String& expression, const String* objectGroup, const bool* includeCommandLineAPI, const bool* doNotPauseOnExceptionsAndMuteConsole, const int* executionContextId, const bool* returnByValue, const bool* generatePreview, RefPtr& result, Inspector::TypeBuilder::OptOutput* wasThrown) override final; virtual void callFunctionOn(ErrorString*, const String& objectId, const String& expression, const RefPtr* optionalArguments, const bool* doNotPauseOnExceptionsAndMuteConsole, const bool* returnByValue, const bool* generatePreview, RefPtr& result, Inspector::TypeBuilder::OptOutput* wasThrown) override final; virtual void releaseObject(ErrorString*, const ErrorString& objectId) override final; - virtual void getProperties(ErrorString*, const String& objectId, const bool* ownProperties, RefPtr>& result, RefPtr>& internalProperties) override final; + virtual void getProperties(ErrorString*, const String& objectId, const bool* ownProperties, const bool* const ownAndGetterProperties, RefPtr>& result, RefPtr>& internalProperties) override final; virtual void releaseObjectGroup(ErrorString*, const String& objectGroup) override final; virtual void run(ErrorString*) override; diff --git a/inspector/protocol/Runtime.json b/inspector/protocol/Runtime.json index f7bf133..b888aa3 100644 --- a/inspector/protocol/Runtime.json +++ b/inspector/protocol/Runtime.json @@ -163,7 +163,8 @@ "name": "getProperties", "parameters": [ { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to return properties for." }, - { "name": "ownProperties", "optional": true, "type": "boolean", "description": "If true, returns properties belonging only to the element itself, not to its prototype chain." } + { "name": "ownProperties", "optional": true, "type": "boolean", "description": "If true, returns properties belonging only to the object itself, not to its prototype chain." }, + { "name": "ownAndGetterProperties", "optional": true, "type": "boolean", "description": "If true, returns properties belonging to the object itself, and getters in its prototype chain." } ], "returns": [ { "name": "result", "type": "array", "items": { "$ref": "PropertyDescriptor"}, "description": "Object properties." }, diff --git a/interpreter/Interpreter.cpp b/interpreter/Interpreter.cpp index f7c4dc8..fb07429 100644 --- a/interpreter/Interpreter.cpp +++ b/interpreter/Interpreter.cpp @@ -173,6 +173,7 @@ CallFrame* sizeFrameForVarargs(CallFrame* callFrame, JSStack* stack, JSValue arg if (asObject(arguments)->classInfo() == Arguments::info()) { Arguments* argsObject = asArguments(arguments); unsigned argCount = argsObject->length(callFrame); + callFrame->vm().varargsLength = argCount; if (argCount >= firstVarArgOffset) argCount -= firstVarArgOffset; else @@ -204,6 +205,7 @@ CallFrame* sizeFrameForVarargs(CallFrame* callFrame, JSStack* stack, JSValue arg JSObject* argObject = asObject(arguments); unsigned argCount = argObject->get(callFrame, callFrame->propertyNames().length).toUInt32(callFrame); + callFrame->vm().varargsLength = argCount; if (argCount >= firstVarArgOffset) argCount -= firstVarArgOffset; else @@ -240,7 +242,8 @@ void loadVarargs(CallFrame* callFrame, CallFrame* newCallFrame, JSValue thisValu if (asObject(arguments)->classInfo() == Arguments::info()) { Arguments* argsObject = asArguments(arguments); - unsigned argCount = argsObject->length(callFrame); + unsigned argCount = callFrame->vm().varargsLength; + callFrame->vm().varargsLength = 0; if (argCount >= firstVarArgOffset) { argCount -= firstVarArgOffset; newCallFrame->setArgumentCountIncludingThis(argCount + 1); @@ -264,8 +267,7 @@ void loadVarargs(CallFrame* callFrame, CallFrame* newCallFrame, JSValue thisValu return; } - JSObject* argObject = asObject(arguments); - unsigned argCount = argObject->get(callFrame, callFrame->propertyNames().length).toUInt32(callFrame); + unsigned argCount = callFrame->vm().varargsLength; if (argCount >= firstVarArgOffset) { argCount -= firstVarArgOffset; newCallFrame->setArgumentCountIncludingThis(argCount + 1); diff --git a/jit/JITOpcodes.cpp b/jit/JITOpcodes.cpp index de8adc4..25a843f 100644 --- a/jit/JITOpcodes.cpp +++ b/jit/JITOpcodes.cpp @@ -626,6 +626,11 @@ void JIT::emit_op_push_name_scope(Instruction* currentInstruction) void JIT::emit_op_catch(Instruction* currentInstruction) { + // Gotta restore the tag registers. We could be throwing from FTL, which may + // clobber them. + move(TrustedImm64(TagTypeNumber), tagTypeNumberRegister); + move(TrustedImm64(TagMask), tagMaskRegister); + move(TrustedImmPtr(m_vm), regT3); load64(Address(regT3, VM::callFrameForThrowOffset()), callFrameRegister); diff --git a/jit/RegisterSet.cpp b/jit/RegisterSet.cpp index 608ebd5..f93d99d 100644 --- a/jit/RegisterSet.cpp +++ b/jit/RegisterSet.cpp @@ -86,7 +86,6 @@ RegisterSet RegisterSet::calleeSaveRegisters() result.set(ARMRegisters::r5); result.set(ARMRegisters::r6); result.set(ARMRegisters::r8); - result.set(ARMRegisters::r9); result.set(ARMRegisters::r10); result.set(ARMRegisters::r11); #elif CPU(ARM64) diff --git a/llint/LowLevelInterpreter.asm b/llint/LowLevelInterpreter.asm index 45a604c..f0aa44e 100644 --- a/llint/LowLevelInterpreter.asm +++ b/llint/LowLevelInterpreter.asm @@ -63,6 +63,8 @@ const ValueFalse = TagBitTypeOther | TagBitBool const ValueTrue = TagBitTypeOther | TagBitBool | 1 const ValueUndefined = TagBitTypeOther | TagBitUndefined const ValueNull = TagBitTypeOther + const TagTypeNumber = 0xffff000000000000 + const TagMask = TagTypeNumber | TagBitTypeOther else const Int32Tag = -1 const BooleanTag = -2 diff --git a/llint/LowLevelInterpreter64.asm b/llint/LowLevelInterpreter64.asm index 486b7b3..67a8f1b 100644 --- a/llint/LowLevelInterpreter64.asm +++ b/llint/LowLevelInterpreter64.asm @@ -1978,6 +1978,11 @@ _llint_op_next_pname: _llint_op_catch: + # Gotta restore the tag registers. We could be throwing from FTL, which may + # clobber them. + move TagTypeNumber, tagTypeNumber + move TagMask, tagMask + # This is where we end up from the JIT's throw trampoline (because the # machine code return address will be set to _llint_op_catch), and from # the interpreter's throw trampoline (see _llint_throw_trampoline). diff --git a/llvm/LLVMAPIFunctions.h b/llvm/LLVMAPIFunctions.h index 9ff2a42..f9c11ce 100644 --- a/llvm/LLVMAPIFunctions.h +++ b/llvm/LLVMAPIFunctions.h @@ -543,9 +543,7 @@ macro(char *, GetTargetMachineFeatureString, (LLVMTargetMachineRef T)) \ macro(LLVMTargetDataRef, GetTargetMachineData, (LLVMTargetMachineRef T)) \ macro(LLVMBool, TargetMachineEmitToFile, (LLVMTargetMachineRef T, LLVMModuleRef M, char *Filename, LLVMCodeGenFileType codegen, char **ErrorMessage)) \ - macro(void, LinkInJIT, (void)) \ macro(void, LinkInMCJIT, (void)) \ - macro(void, LinkInInterpreter, (void)) \ macro(LLVMGenericValueRef, CreateGenericValueOfInt, (LLVMTypeRef Ty, unsigned long long N, LLVMBool IsSigned)) \ macro(LLVMGenericValueRef, CreateGenericValueOfPointer, (void *P)) \ macro(LLVMGenericValueRef, CreateGenericValueOfFloat, (LLVMTypeRef Ty, double N)) \ diff --git a/llvm/library/LLVMExports.cpp b/llvm/library/LLVMExports.cpp index 56cdfd0..b1979f2 100644 --- a/llvm/library/LLVMExports.cpp +++ b/llvm/library/LLVMExports.cpp @@ -92,7 +92,6 @@ extern "C" JSC::LLVMAPI* initializeAndGetJSCLLVMAPI(void (*callback)(const char* const char* args[] = { "llvmForJSC.dylib", - "-enable-stackmap-liveness=true", "-enable-patchpoint-liveness=true" }; llvm::cl::ParseCommandLineOptions(sizeof(args) / sizeof(const char*), args); diff --git a/parser/ParserArena.h b/parser/ParserArena.h index e806d45..d8bc2cf 100644 --- a/parser/ParserArena.h +++ b/parser/ParserArena.h @@ -26,6 +26,7 @@ #ifndef ParserArena_h #define ParserArena_h +#include "CommonIdentifiers.h" #include "Identifier.h" #include #include @@ -72,6 +73,8 @@ namespace JSC { template ALWAYS_INLINE const Identifier& IdentifierArena::makeIdentifier(VM* vm, const T* characters, size_t length) { + if (!length) + return vm->propertyNames->emptyIdentifier; if (characters[0] >= MaximumCachableCharacter) { m_identifiers.append(Identifier(vm, characters, length)); return m_identifiers.last(); @@ -93,6 +96,8 @@ namespace JSC { ALWAYS_INLINE const Identifier& IdentifierArena::makeIdentifierLCharFromUChar(VM* vm, const UChar* characters, size_t length) { + if (!length) + return vm->propertyNames->emptyIdentifier; if (characters[0] >= MaximumCachableCharacter) { m_identifiers.append(Identifier::createLCharFromUChar(vm, characters, length)); return m_identifiers.last(); diff --git a/runtime/MapData.cpp b/runtime/MapData.cpp index b9e73ae..f09e03a 100644 --- a/runtime/MapData.cpp +++ b/runtime/MapData.cpp @@ -169,6 +169,8 @@ void MapData::replaceAndPackBackingStore(Entry* destination, int32_t newCapacity // Fixup for the hashmaps for (auto ptr = m_valueKeyedTable.begin(); ptr != m_valueKeyedTable.end(); ++ptr) ptr->value = m_entries[ptr->value].value.get().asInt32(); + for (auto ptr = m_cellKeyedTable.begin(); ptr != m_cellKeyedTable.end(); ++ptr) + ptr->value = m_entries[ptr->value].value.get().asInt32(); for (auto ptr = m_stringKeyedTable.begin(); ptr != m_stringKeyedTable.end(); ++ptr) ptr->value = m_entries[ptr->value].value.get().asInt32(); diff --git a/runtime/VM.cpp b/runtime/VM.cpp index 3f7fb0e..fdfc06e 100644 --- a/runtime/VM.cpp +++ b/runtime/VM.cpp @@ -198,6 +198,7 @@ VM::VM(VMType vmType, HeapType heapType) , interpreter(0) , jsArrayClassInfo(JSArray::info()) , jsFinalObjectClassInfo(JSFinalObject::info()) + , varargsLength(0) , sizeOfLastScratchBuffer(0) , entryScope(0) , m_regExpCache(new RegExpCache(this)) diff --git a/runtime/VM.h b/runtime/VM.h index 1f4d172..8d55cb7 100644 --- a/runtime/VM.h +++ b/runtime/VM.h @@ -414,6 +414,7 @@ namespace JSC { JSValue hostCallReturnValue; ExecState* newCallFrameReturnValue; + unsigned varargsLength; ExecState* callFrameForThrow; void* targetMachinePCForThrow; Instruction* targetInterpreterPCForThrow; diff --git a/tests/stress/get-my-argument-by-val-inlined-no-formal-parameters.js b/tests/stress/get-my-argument-by-val-inlined-no-formal-parameters.js new file mode 100644 index 0000000..eef298f --- /dev/null +++ b/tests/stress/get-my-argument-by-val-inlined-no-formal-parameters.js @@ -0,0 +1,33 @@ +var index; + +function foo() { + if (index >= 0) + return arguments[index]; + else + return 13; +} + +function bar() { + return foo(); +} + +noInline(bar); + +for (var i = 0; i < 100; ++i) { + index = i & 1; + var result = foo(42, 53); + if (result != [42, 53][index]) + throw "Error: bad result in first loop: " + result; +} + +for (var i = 0; i < 100000; ++i) { + index = -(i & 1) - 1; + var result = bar(); + if (result !== 13) + throw "Error: bad result in second loop: " + result; +} + +index = 0; +var result = bar(); +if (result !== void 0) + throw "Error: bad result at end: " + result; diff --git a/tests/stress/regress-141489.js b/tests/stress/regress-141489.js new file mode 100644 index 0000000..bbca89c --- /dev/null +++ b/tests/stress/regress-141489.js @@ -0,0 +1,34 @@ +// this test checks that register r9 is not a callee save on ios armv7. +function ident(a) { + return a; +} + +function foo(array,obj) { + var a = array[0]; + var b = array[1]; + var c = array[2]; + obj.a = array; + obj.b = array; + obj.c = array; + obj.d = array; + obj.e = array; + obj.f = array; + obj.h = array; + return a(b(c(10))); +} +noInline(foo); + +var arr = [ident,ident,ident]; + +for (var i = 0; i < 100; i++) { + var obj = {}; + for (var j = 0; j < 200; j ++) { + obj["j"+j] = i; + } + foo(arr, obj); +} + +for (var i = 0; i < 100; i++) { + var obj = {}; + foo(arr, obj); +} \ No newline at end of file diff --git a/tests/stress/throw-from-ftl-call-ic-slow-path-cells.js b/tests/stress/throw-from-ftl-call-ic-slow-path-cells.js new file mode 100644 index 0000000..9e70485 --- /dev/null +++ b/tests/stress/throw-from-ftl-call-ic-slow-path-cells.js @@ -0,0 +1,192 @@ +// Attempts to induce a crash resulting from the FTL emitting code that clobbers the tag registers and then +// throwing an exception without restoring those tag registers' values. + +function ftlFunction(array, callee) { + // Gotta use lots of gprs. + var x0 = array[0]; + var x1 = array[1]; + var x2 = array[2]; + var x3 = array[3]; + var x4 = array[4]; + var x5 = array[5]; + var x6 = array[6]; + var x7 = array[7]; + var x8 = array[8]; + var x9 = array[9]; + var x10 = array[10]; + var x11 = array[11]; + var x12 = array[12]; + var x13 = array[13]; + var x14 = array[14]; + var x15 = array[15]; + var x16 = array[16]; + var x17 = array[17]; + var x18 = array[18]; + var x19 = array[19]; + var x20 = array[20]; + var x21 = array[21]; + var x22 = array[22]; + var x23 = array[23]; + var x24 = array[24]; + var x25 = array[25]; + var x26 = array[26]; + var x27 = array[27]; + var x28 = array[28]; + var x29 = array[29]; + var x30 = array[30]; + var x31 = array[31]; + var x32 = array[32]; + var x33 = array[33]; + var x34 = array[34]; + var x35 = array[35]; + var x36 = array[36]; + var x37 = array[37]; + var x38 = array[38]; + var x39 = array[39]; + var x40 = array[40]; + var x41 = array[41]; + var x42 = array[42]; + var x43 = array[43]; + var x44 = array[44]; + var x45 = array[45]; + var x46 = array[46]; + var x47 = array[47]; + var x48 = array[48]; + var x49 = array[49]; + var x50 = array[50]; + var x51 = array[51]; + var x52 = array[52]; + var x53 = array[53]; + var x54 = array[54]; + var x55 = array[55]; + var x56 = array[56]; + var x57 = array[57]; + var x58 = array[58]; + var x59 = array[59]; + var x60 = array[60]; + var x61 = array[61]; + var x62 = array[62]; + var x63 = array[63]; + + // Make a call that will throw, when we ask it to. + callee("hello"); + + // Use all of those crazy values. + return [x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, x30, x31, x32, x33, x34, x35, x36, x37, x38, x39, x40, x41, x42, x43, x44, x45, x46, x47, x48, x49, x50, x51, x52, x53, x54, x55, x56, x57, x58, x59, x60, x61, x62, x63] +} + +noInline(ftlFunction); + +// Create some callees that are too crazy to get inlined or devirtualized, but that don't have effects. + +function happyCallee0() { return 0 }; +function happyCallee1() { return 1 }; +function happyCallee2() { return 2 }; +function happyCallee3() { return 3 }; +function happyCallee4() { return 4 }; +function happyCallee5() { return 5 }; +function happyCallee6() { return 6 }; +function happyCallee7() { return 7 }; +function happyCallee8() { return 8 }; +function happyCallee9() { return 9 }; +function happyCallee10() { return 10 }; +function happyCallee11() { return 11 }; +function happyCallee12() { return 12 }; +function happyCallee13() { return 13 }; +function happyCallee14() { return 14 }; +function happyCallee15() { return 15 }; +function happyCallee16() { return 16 }; +function happyCallee17() { return 17 }; +function happyCallee18() { return 18 }; +function happyCallee19() { return 19 }; +function happyCallee20() { return 20 }; +function happyCallee21() { return 21 }; +function happyCallee22() { return 22 }; +function happyCallee23() { return 23 }; +function happyCallee24() { return 24 }; +function happyCallee25() { return 25 }; +function happyCallee26() { return 26 }; +function happyCallee27() { return 27 }; +function happyCallee28() { return 28 }; +function happyCallee29() { return 29 }; +function happyCallee30() { return 30 }; +function happyCallee31() { return 31 }; +function happyCallee32() { return 32 }; +function happyCallee33() { return 33 }; +function happyCallee34() { return 34 }; +function happyCallee35() { return 35 }; +function happyCallee36() { return 36 }; +function happyCallee37() { return 37 }; +function happyCallee38() { return 38 }; +function happyCallee39() { return 39 }; +function happyCallee40() { return 40 }; +function happyCallee41() { return 41 }; +function happyCallee42() { return 42 }; +function happyCallee43() { return 43 }; +function happyCallee44() { return 44 }; +function happyCallee45() { return 45 }; +function happyCallee46() { return 46 }; +function happyCallee47() { return 47 }; +function happyCallee48() { return 48 }; +function happyCallee49() { return 49 }; +function happyCallee50() { return 50 }; +function happyCallee51() { return 51 }; +function happyCallee52() { return 52 }; +function happyCallee53() { return 53 }; +function happyCallee54() { return 54 }; +function happyCallee55() { return 55 }; +function happyCallee56() { return 56 }; +function happyCallee57() { return 57 }; +function happyCallee58() { return 58 }; +function happyCallee59() { return 59 }; +function happyCallee60() { return 60 }; +function happyCallee61() { return 61 }; +function happyCallee62() { return 62 }; +function happyCallee63() { return 63 }; + +var happyCallees = [happyCallee0, happyCallee1, happyCallee2, happyCallee3, happyCallee4, happyCallee5, happyCallee6, happyCallee7, happyCallee8, happyCallee9, happyCallee10, happyCallee11, happyCallee12, happyCallee13, happyCallee14, happyCallee15, happyCallee16, happyCallee17, happyCallee18, happyCallee19, happyCallee20, happyCallee21, happyCallee22, happyCallee23, happyCallee24, happyCallee25, happyCallee26, happyCallee27, happyCallee28, happyCallee29, happyCallee30, happyCallee31, happyCallee32, happyCallee33, happyCallee34, happyCallee35, happyCallee36, happyCallee37, happyCallee38, happyCallee39, happyCallee40, happyCallee41, happyCallee42, happyCallee43, happyCallee44, happyCallee45, happyCallee46, happyCallee47, happyCallee48, happyCallee49, happyCallee50, happyCallee51, happyCallee52, happyCallee53, happyCallee54, happyCallee55, happyCallee56, happyCallee57, happyCallee58, happyCallee59, happyCallee60, happyCallee61, happyCallee62, happyCallee63]; + +for (var i = 0; i < happyCallees.length; ++i) + noInline(happyCallees[i]); + +// Unlike the other test (throw-from-ftl-call-ic-slow-path.js), we want to populate the registers with cells in +// this test. +var array = new Array(); +for (var i = 0; i < 64; ++i) + array[i] = new Object(); + +// Now, do some warming up. +for (var i = 0; i < 100000; ++i) { + var result = ftlFunction(array, happyCallees[i % happyCallees.length]); + if (result.length != array.length) + throw "Error: bad length: " + result; + for (var j = 0; j < result.length; ++j) { + if (result[j] != array[j]) + throw "Error: bad entry at j = " + j + ": " + result; + } +} + +// Finally, attempt to trigger the bug. +var notACell = 42; +for (var i = 0; i < 100; ++i) { + try { + ftlFunction(array, Int8Array); + } catch (e) { + if (e.message.indexOf("not a function") < 0) + throw "Error: bad exception message: " + e.message; + var result = notACell.f; + if (result !== void 0) { + print("Bad outcome of accessing f on notACell."); + print("Here's notACell:", notACell, describe(notACell)); + print("Here's the result:", result, describe(result)); + throw "Error: bad outcome of accessing f on " + notACell + ": " + result; + } + var result2 = result + 5; + var result3 = notACell + 5; + if ("" + result2 != "NaN") + throw "Error: bad outcome of adding 5 to result: " + result2; + if (result3 != 47) + throw "Error: bad outcome of adding 5 to 42: " + result3; + } +} + diff --git a/tests/stress/throw-from-ftl-call-ic-slow-path-undefined.js b/tests/stress/throw-from-ftl-call-ic-slow-path-undefined.js new file mode 100644 index 0000000..933a273 --- /dev/null +++ b/tests/stress/throw-from-ftl-call-ic-slow-path-undefined.js @@ -0,0 +1,192 @@ +// Attempts to induce a crash resulting from the FTL emitting code that clobbers the tag registers and then +// throwing an exception without restoring those tag registers' values. + +function ftlFunction(array, callee) { + // Gotta use lots of gprs. + var x0 = array[0]; + var x1 = array[1]; + var x2 = array[2]; + var x3 = array[3]; + var x4 = array[4]; + var x5 = array[5]; + var x6 = array[6]; + var x7 = array[7]; + var x8 = array[8]; + var x9 = array[9]; + var x10 = array[10]; + var x11 = array[11]; + var x12 = array[12]; + var x13 = array[13]; + var x14 = array[14]; + var x15 = array[15]; + var x16 = array[16]; + var x17 = array[17]; + var x18 = array[18]; + var x19 = array[19]; + var x20 = array[20]; + var x21 = array[21]; + var x22 = array[22]; + var x23 = array[23]; + var x24 = array[24]; + var x25 = array[25]; + var x26 = array[26]; + var x27 = array[27]; + var x28 = array[28]; + var x29 = array[29]; + var x30 = array[30]; + var x31 = array[31]; + var x32 = array[32]; + var x33 = array[33]; + var x34 = array[34]; + var x35 = array[35]; + var x36 = array[36]; + var x37 = array[37]; + var x38 = array[38]; + var x39 = array[39]; + var x40 = array[40]; + var x41 = array[41]; + var x42 = array[42]; + var x43 = array[43]; + var x44 = array[44]; + var x45 = array[45]; + var x46 = array[46]; + var x47 = array[47]; + var x48 = array[48]; + var x49 = array[49]; + var x50 = array[50]; + var x51 = array[51]; + var x52 = array[52]; + var x53 = array[53]; + var x54 = array[54]; + var x55 = array[55]; + var x56 = array[56]; + var x57 = array[57]; + var x58 = array[58]; + var x59 = array[59]; + var x60 = array[60]; + var x61 = array[61]; + var x62 = array[62]; + var x63 = array[63]; + + // Make a call that will throw, when we ask it to. + callee("hello"); + + // Use all of those crazy values. + return [x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, x30, x31, x32, x33, x34, x35, x36, x37, x38, x39, x40, x41, x42, x43, x44, x45, x46, x47, x48, x49, x50, x51, x52, x53, x54, x55, x56, x57, x58, x59, x60, x61, x62, x63] +} + +noInline(ftlFunction); + +// Create some callees that are too crazy to get inlined or devirtualized, but that don't have effects. + +function happyCallee0() { return 0 }; +function happyCallee1() { return 1 }; +function happyCallee2() { return 2 }; +function happyCallee3() { return 3 }; +function happyCallee4() { return 4 }; +function happyCallee5() { return 5 }; +function happyCallee6() { return 6 }; +function happyCallee7() { return 7 }; +function happyCallee8() { return 8 }; +function happyCallee9() { return 9 }; +function happyCallee10() { return 10 }; +function happyCallee11() { return 11 }; +function happyCallee12() { return 12 }; +function happyCallee13() { return 13 }; +function happyCallee14() { return 14 }; +function happyCallee15() { return 15 }; +function happyCallee16() { return 16 }; +function happyCallee17() { return 17 }; +function happyCallee18() { return 18 }; +function happyCallee19() { return 19 }; +function happyCallee20() { return 20 }; +function happyCallee21() { return 21 }; +function happyCallee22() { return 22 }; +function happyCallee23() { return 23 }; +function happyCallee24() { return 24 }; +function happyCallee25() { return 25 }; +function happyCallee26() { return 26 }; +function happyCallee27() { return 27 }; +function happyCallee28() { return 28 }; +function happyCallee29() { return 29 }; +function happyCallee30() { return 30 }; +function happyCallee31() { return 31 }; +function happyCallee32() { return 32 }; +function happyCallee33() { return 33 }; +function happyCallee34() { return 34 }; +function happyCallee35() { return 35 }; +function happyCallee36() { return 36 }; +function happyCallee37() { return 37 }; +function happyCallee38() { return 38 }; +function happyCallee39() { return 39 }; +function happyCallee40() { return 40 }; +function happyCallee41() { return 41 }; +function happyCallee42() { return 42 }; +function happyCallee43() { return 43 }; +function happyCallee44() { return 44 }; +function happyCallee45() { return 45 }; +function happyCallee46() { return 46 }; +function happyCallee47() { return 47 }; +function happyCallee48() { return 48 }; +function happyCallee49() { return 49 }; +function happyCallee50() { return 50 }; +function happyCallee51() { return 51 }; +function happyCallee52() { return 52 }; +function happyCallee53() { return 53 }; +function happyCallee54() { return 54 }; +function happyCallee55() { return 55 }; +function happyCallee56() { return 56 }; +function happyCallee57() { return 57 }; +function happyCallee58() { return 58 }; +function happyCallee59() { return 59 }; +function happyCallee60() { return 60 }; +function happyCallee61() { return 61 }; +function happyCallee62() { return 62 }; +function happyCallee63() { return 63 }; + +var happyCallees = [happyCallee0, happyCallee1, happyCallee2, happyCallee3, happyCallee4, happyCallee5, happyCallee6, happyCallee7, happyCallee8, happyCallee9, happyCallee10, happyCallee11, happyCallee12, happyCallee13, happyCallee14, happyCallee15, happyCallee16, happyCallee17, happyCallee18, happyCallee19, happyCallee20, happyCallee21, happyCallee22, happyCallee23, happyCallee24, happyCallee25, happyCallee26, happyCallee27, happyCallee28, happyCallee29, happyCallee30, happyCallee31, happyCallee32, happyCallee33, happyCallee34, happyCallee35, happyCallee36, happyCallee37, happyCallee38, happyCallee39, happyCallee40, happyCallee41, happyCallee42, happyCallee43, happyCallee44, happyCallee45, happyCallee46, happyCallee47, happyCallee48, happyCallee49, happyCallee50, happyCallee51, happyCallee52, happyCallee53, happyCallee54, happyCallee55, happyCallee56, happyCallee57, happyCallee58, happyCallee59, happyCallee60, happyCallee61, happyCallee62, happyCallee63]; + +for (var i = 0; i < happyCallees.length; ++i) + noInline(happyCallees[i]); + +// Unlike the other test (throw-from-ftl-call-ic-slow-path.js), we want to populate the registers with undefined +// in this test. +var array = new Array(); +for (var i = 0; i < 64; ++i) + array[i] = void 0; + +// Now, do some warming up. +for (var i = 0; i < 100000; ++i) { + var result = ftlFunction(array, happyCallees[i % happyCallees.length]); + if (result.length != array.length) + throw "Error: bad length: " + result; + for (var j = 0; j < result.length; ++j) { + if (result[j] != array[j]) + throw "Error: bad entry at j = " + j + ": " + result; + } +} + +// Finally, attempt to trigger the bug. +var notACell = 42; +for (var i = 0; i < 100; ++i) { + try { + ftlFunction(array, Int8Array); + } catch (e) { + if (e.message.indexOf("not a function") < 0) + throw "Error: bad exception message: " + e.message; + var result = notACell.f; + if (result !== void 0) { + print("Bad outcome of accessing f on notACell."); + print("Here's notACell:", notACell, describe(notACell)); + print("Here's the result:", result, describe(result)); + throw "Error: bad outcome of accessing f on " + notACell + ": " + result; + } + var result2 = result + 5; + var result3 = notACell + 5; + if ("" + result2 != "NaN") + throw "Error: bad outcome of adding 5 to result: " + result2; + if (result3 != 47) + throw "Error: bad outcome of adding 5 to 42: " + result3; + } +} + diff --git a/tests/stress/throw-from-ftl-call-ic-slow-path.js b/tests/stress/throw-from-ftl-call-ic-slow-path.js new file mode 100644 index 0000000..fb69204 --- /dev/null +++ b/tests/stress/throw-from-ftl-call-ic-slow-path.js @@ -0,0 +1,192 @@ +// Attempts to induce a crash resulting from the FTL emitting code that clobbers the tag registers and then +// throwing an exception without restoring those tag registers' values. + +function ftlFunction(array, callee) { + // Gotta use lots of gprs. + var x0 = array[0]; + var x1 = array[1]; + var x2 = array[2]; + var x3 = array[3]; + var x4 = array[4]; + var x5 = array[5]; + var x6 = array[6]; + var x7 = array[7]; + var x8 = array[8]; + var x9 = array[9]; + var x10 = array[10]; + var x11 = array[11]; + var x12 = array[12]; + var x13 = array[13]; + var x14 = array[14]; + var x15 = array[15]; + var x16 = array[16]; + var x17 = array[17]; + var x18 = array[18]; + var x19 = array[19]; + var x20 = array[20]; + var x21 = array[21]; + var x22 = array[22]; + var x23 = array[23]; + var x24 = array[24]; + var x25 = array[25]; + var x26 = array[26]; + var x27 = array[27]; + var x28 = array[28]; + var x29 = array[29]; + var x30 = array[30]; + var x31 = array[31]; + var x32 = array[32]; + var x33 = array[33]; + var x34 = array[34]; + var x35 = array[35]; + var x36 = array[36]; + var x37 = array[37]; + var x38 = array[38]; + var x39 = array[39]; + var x40 = array[40]; + var x41 = array[41]; + var x42 = array[42]; + var x43 = array[43]; + var x44 = array[44]; + var x45 = array[45]; + var x46 = array[46]; + var x47 = array[47]; + var x48 = array[48]; + var x49 = array[49]; + var x50 = array[50]; + var x51 = array[51]; + var x52 = array[52]; + var x53 = array[53]; + var x54 = array[54]; + var x55 = array[55]; + var x56 = array[56]; + var x57 = array[57]; + var x58 = array[58]; + var x59 = array[59]; + var x60 = array[60]; + var x61 = array[61]; + var x62 = array[62]; + var x63 = array[63]; + + // Make a call that will throw, when we ask it to. + callee("hello"); + + // Use all of those crazy values. + return [x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, x30, x31, x32, x33, x34, x35, x36, x37, x38, x39, x40, x41, x42, x43, x44, x45, x46, x47, x48, x49, x50, x51, x52, x53, x54, x55, x56, x57, x58, x59, x60, x61, x62, x63] +} + +noInline(ftlFunction); + +// Create some callees that are too crazy to get inlined or devirtualized, but that don't have effects. + +function happyCallee0() { return 0 }; +function happyCallee1() { return 1 }; +function happyCallee2() { return 2 }; +function happyCallee3() { return 3 }; +function happyCallee4() { return 4 }; +function happyCallee5() { return 5 }; +function happyCallee6() { return 6 }; +function happyCallee7() { return 7 }; +function happyCallee8() { return 8 }; +function happyCallee9() { return 9 }; +function happyCallee10() { return 10 }; +function happyCallee11() { return 11 }; +function happyCallee12() { return 12 }; +function happyCallee13() { return 13 }; +function happyCallee14() { return 14 }; +function happyCallee15() { return 15 }; +function happyCallee16() { return 16 }; +function happyCallee17() { return 17 }; +function happyCallee18() { return 18 }; +function happyCallee19() { return 19 }; +function happyCallee20() { return 20 }; +function happyCallee21() { return 21 }; +function happyCallee22() { return 22 }; +function happyCallee23() { return 23 }; +function happyCallee24() { return 24 }; +function happyCallee25() { return 25 }; +function happyCallee26() { return 26 }; +function happyCallee27() { return 27 }; +function happyCallee28() { return 28 }; +function happyCallee29() { return 29 }; +function happyCallee30() { return 30 }; +function happyCallee31() { return 31 }; +function happyCallee32() { return 32 }; +function happyCallee33() { return 33 }; +function happyCallee34() { return 34 }; +function happyCallee35() { return 35 }; +function happyCallee36() { return 36 }; +function happyCallee37() { return 37 }; +function happyCallee38() { return 38 }; +function happyCallee39() { return 39 }; +function happyCallee40() { return 40 }; +function happyCallee41() { return 41 }; +function happyCallee42() { return 42 }; +function happyCallee43() { return 43 }; +function happyCallee44() { return 44 }; +function happyCallee45() { return 45 }; +function happyCallee46() { return 46 }; +function happyCallee47() { return 47 }; +function happyCallee48() { return 48 }; +function happyCallee49() { return 49 }; +function happyCallee50() { return 50 }; +function happyCallee51() { return 51 }; +function happyCallee52() { return 52 }; +function happyCallee53() { return 53 }; +function happyCallee54() { return 54 }; +function happyCallee55() { return 55 }; +function happyCallee56() { return 56 }; +function happyCallee57() { return 57 }; +function happyCallee58() { return 58 }; +function happyCallee59() { return 59 }; +function happyCallee60() { return 60 }; +function happyCallee61() { return 61 }; +function happyCallee62() { return 62 }; +function happyCallee63() { return 63 }; + +var happyCallees = [happyCallee0, happyCallee1, happyCallee2, happyCallee3, happyCallee4, happyCallee5, happyCallee6, happyCallee7, happyCallee8, happyCallee9, happyCallee10, happyCallee11, happyCallee12, happyCallee13, happyCallee14, happyCallee15, happyCallee16, happyCallee17, happyCallee18, happyCallee19, happyCallee20, happyCallee21, happyCallee22, happyCallee23, happyCallee24, happyCallee25, happyCallee26, happyCallee27, happyCallee28, happyCallee29, happyCallee30, happyCallee31, happyCallee32, happyCallee33, happyCallee34, happyCallee35, happyCallee36, happyCallee37, happyCallee38, happyCallee39, happyCallee40, happyCallee41, happyCallee42, happyCallee43, happyCallee44, happyCallee45, happyCallee46, happyCallee47, happyCallee48, happyCallee49, happyCallee50, happyCallee51, happyCallee52, happyCallee53, happyCallee54, happyCallee55, happyCallee56, happyCallee57, happyCallee58, happyCallee59, happyCallee60, happyCallee61, happyCallee62, happyCallee63]; + +for (var i = 0; i < happyCallees.length; ++i) + noInline(happyCallees[i]); + +// We want the input array to have an easy-to-deal-with type that isn't exactly the same as the type that +// ftlFunction will return. +var array = new Int32Array(64); +for (var i = 0; i < array.length; ++i) + array[i] = i; + +// Now, do some warming up. +for (var i = 0; i < 100000; ++i) { + var result = ftlFunction(array, happyCallees[i % happyCallees.length]); + if (result.length != array.length) + throw "Error: bad length: " + result; + for (var j = 0; j < result.length; ++j) { + if (result[j] != array[j]) + throw "Error: bad entry at j = " + j + ": " + result; + } +} + +// Finally, attempt to trigger the bug. +var notACell = 42; +for (var i = 0; i < 100; ++i) { + try { + ftlFunction(array, Int8Array); + } catch (e) { + if (e.message.indexOf("not a function") < 0) + throw "Error: bad exception message: " + e.message; + var result = notACell.f; + if (result !== void 0) { + print("Bad outcome of accessing f on notACell."); + print("Here's notACell:", notACell, describe(notACell)); + print("Here's the result:", result, describe(result)); + throw "Error: bad outcome of accessing f on " + notACell + ": " + result; + } + var result2 = result + 5; + var result3 = notACell + 5; + if ("" + result2 != "NaN") + throw "Error: bad outcome of adding 5 to result: " + result2; + if (result3 != 47) + throw "Error: bad outcome of adding 5 to 42: " + result3; + } +} + -- 2.45.2