]> git.saurik.com Git - apple/javascriptcore.git/commitdiff
JavaScriptCore-1218.33.tar.gz ios-71 v1218.33
authorApple <opensource@apple.com>
Fri, 13 Feb 2015 03:14:50 +0000 (03:14 +0000)
committerApple <opensource@apple.com>
Fri, 13 Feb 2015 03:14:50 +0000 (03:14 +0000)
41 files changed:
API/APICallbackFunction.h [new file with mode: 0644]
API/JSCallbackConstructor.cpp
API/JSCallbackConstructor.h
API/JSCallbackFunction.cpp
API/JSCallbackFunction.h
API/JSManagedValue.mm
API/JSValue.mm
API/JSWrapperMap.mm
API/ObjCCallbackFunction.h
API/ObjCCallbackFunction.mm
API/tests/minidom.c
API/tests/testapi.c
API/tests/testapi.mm
Configurations/Version.xcconfig
GNUmakefile.list.am
JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters
JavaScriptCore.xcodeproj/project.pbxproj
assembler/MacroAssemblerARM64.h
dfg/DFGAbstractState.cpp
dfg/DFGArgumentsSimplificationPhase.cpp
dfg/DFGByteCodeParser.cpp
dfg/DFGCSEPhase.cpp
dfg/DFGConstantFoldingPhase.cpp
dfg/DFGFixupPhase.cpp
dfg/DFGNode.h
dfg/DFGNodeType.h
dfg/DFGPredictionPropagationPhase.cpp
dfg/DFGSpeculativeJIT32_64.cpp
dfg/DFGSpeculativeJIT64.cpp
heap/SlotVisitorInlines.h
interpreter/Interpreter.cpp
parser/NodeConstructors.h
parser/ResultType.h
runtime/ArrayConventions.h
runtime/JSArray.cpp
runtime/JSObject.cpp
runtime/JSObject.h
runtime/JSScope.cpp
runtime/RegExpMatchesArray.h
runtime/Structure.cpp

diff --git a/API/APICallbackFunction.h b/API/APICallbackFunction.h
new file mode 100644 (file)
index 0000000..0298ecb
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef APICallbackFunction_h
+#define APICallbackFunction_h
+
+#include "APICast.h"
+#include "APIShims.h"
+#include "Error.h"
+#include "JSCallbackConstructor.h"
+#include <wtf/Vector.h>
+
+namespace JSC {
+
+struct APICallbackFunction {
+
+template <typename T> static EncodedJSValue JSC_HOST_CALL call(ExecState*);
+template <typename T> static EncodedJSValue JSC_HOST_CALL construct(ExecState*);
+
+};
+
+template <typename T>
+EncodedJSValue APICallbackFunction::call(ExecState* exec)
+{
+    JSContextRef execRef = toRef(exec);
+    JSObjectRef functionRef = toRef(exec->callee());
+    JSObjectRef thisObjRef = toRef(exec->hostThisValue().toThisObject(exec));
+
+    int argumentCount = static_cast<int>(exec->argumentCount());
+    Vector<JSValueRef, 16> arguments;
+    arguments.reserveInitialCapacity(argumentCount);
+    for (int i = 0; i < argumentCount; i++)
+        arguments.uncheckedAppend(toRef(exec, exec->argument(i)));
+
+    JSValueRef exception = 0;
+    JSValueRef result;
+    {
+        APICallbackShim callbackShim(exec);
+        result = jsCast<T*>(toJS(functionRef))->functionCallback()(execRef, functionRef, thisObjRef, argumentCount, arguments.data(), &exception);
+    }
+    if (exception)
+        throwError(exec, toJS(exec, exception));
+
+    // result must be a valid JSValue.
+    if (!result)
+        return JSValue::encode(jsUndefined());
+
+    return JSValue::encode(toJS(exec, result));
+}
+
+template <typename T>
+EncodedJSValue JSC_HOST_CALL APICallbackFunction::construct(ExecState* exec)
+{
+    JSObject* constructor = exec->callee();
+    JSContextRef ctx = toRef(exec);
+    JSObjectRef constructorRef = toRef(constructor);
+
+    JSObjectCallAsConstructorCallback callback = jsCast<T*>(constructor)->constructCallback();
+    if (callback) {
+        size_t argumentCount = exec->argumentCount();
+        Vector<JSValueRef, 16> arguments;
+        arguments.reserveInitialCapacity(argumentCount);
+        for (size_t i = 0; i < argumentCount; ++i)
+            arguments.uncheckedAppend(toRef(exec, exec->argument(i)));
+
+        JSValueRef exception = 0;
+        JSObjectRef result;
+        {
+            APICallbackShim callbackShim(exec);
+            result = callback(ctx, constructorRef, argumentCount, arguments.data(), &exception);
+        }
+        if (exception)
+            throwError(exec, toJS(exec, exception));
+        // result must be a valid JSValue.
+        if (!result)
+            return throwVMTypeError(exec);
+        return JSValue::encode(toJS(result));
+    }
+    
+    return JSValue::encode(toJS(JSObjectMake(ctx, jsCast<JSCallbackConstructor*>(constructor)->classRef(), 0)));
+}
+
+} // namespace JSC
+
+#endif // APICallbackFunction_h
index 8340c10b4de3a6c343c556464702fac045197ce2..2fdea5f7556592447f87b6771c7d43f6329a671d 100644 (file)
@@ -26,8 +26,9 @@
 #include "config.h"
 #include "JSCallbackConstructor.h"
 
-#include "APIShims.h"
+#include "APICallbackFunction.h"
 #include "APICast.h"
+#include "APIShims.h"
 #include "Error.h"
 #include "JSGlobalObject.h"
 #include "JSLock.h"
@@ -65,40 +66,9 @@ void JSCallbackConstructor::destroy(JSCell* cell)
     static_cast<JSCallbackConstructor*>(cell)->JSCallbackConstructor::~JSCallbackConstructor();
 }
 
-static EncodedJSValue JSC_HOST_CALL constructJSCallback(ExecState* exec)
-{
-    JSObject* constructor = exec->callee();
-    JSContextRef ctx = toRef(exec);
-    JSObjectRef constructorRef = toRef(constructor);
-
-    JSObjectCallAsConstructorCallback callback = jsCast<JSCallbackConstructor*>(constructor)->callback();
-    if (callback) {
-        size_t argumentCount = exec->argumentCount();
-        Vector<JSValueRef, 16> arguments;
-        arguments.reserveInitialCapacity(argumentCount);
-        for (size_t i = 0; i < argumentCount; ++i)
-            arguments.uncheckedAppend(toRef(exec, exec->argument(i)));
-
-        JSValueRef exception = 0;
-        JSObjectRef result;
-        {
-            APICallbackShim callbackShim(exec);
-            result = callback(ctx, constructorRef, argumentCount, arguments.data(), &exception);
-        }
-        if (exception)
-            throwError(exec, toJS(exec, exception));
-        // result must be a valid JSValue.
-        if (!result)
-            return throwVMTypeError(exec);
-        return JSValue::encode(toJS(result));
-    }
-    
-    return JSValue::encode(toJS(JSObjectMake(ctx, jsCast<JSCallbackConstructor*>(constructor)->classRef(), 0)));
-}
-
 ConstructType JSCallbackConstructor::getConstructData(JSCell*, ConstructData& constructData)
 {
-    constructData.native.function = constructJSCallback;
+    constructData.native.function = APICallbackFunction::construct<JSCallbackConstructor>;
     return ConstructTypeHost;
 }
 
index 72100e672db3ae8719831af87b4bc513624235dd..b2eaa163cd662be591ba7b57371b5291f3da8271 100644 (file)
@@ -59,8 +59,12 @@ protected:
     static const unsigned StructureFlags = ImplementsHasInstance | JSObject::StructureFlags;
 
 private:
+    friend struct APICallbackFunction;
+
     static ConstructType getConstructData(JSCell*, ConstructData&);
 
+    JSObjectCallAsConstructorCallback constructCallback() { return m_callback; }
+
     JSClassRef m_class;
     JSObjectCallAsConstructorCallback m_callback;
 };
index c29b9077cf24025c71b5d7e80a14f99f5643f111..38fdc1cd9c87832ef048fc21ae3dc7430c8ad77e 100644 (file)
@@ -26,8 +26,9 @@
 #include "config.h"
 #include "JSCallbackFunction.h"
 
-#include "APIShims.h"
+#include "APICallbackFunction.h"
 #include "APICast.h"
+#include "APIShims.h"
 #include "CodeBlock.h"
 #include "Error.h"
 #include "ExceptionHelpers.h"
@@ -63,37 +64,9 @@ JSCallbackFunction* JSCallbackFunction::create(ExecState* exec, JSGlobalObject*
     return function;
 }
 
-EncodedJSValue JSCallbackFunction::call(ExecState* exec)
-{
-    JSContextRef execRef = toRef(exec);
-    JSObjectRef functionRef = toRef(exec->callee());
-    JSObjectRef thisObjRef = toRef(exec->hostThisValue().toThisObject(exec));
-
-    size_t argumentCount = exec->argumentCount();
-    Vector<JSValueRef, 16> arguments;
-    arguments.reserveInitialCapacity(argumentCount);
-    for (size_t i = 0; i < argumentCount; ++i)
-        arguments.uncheckedAppend(toRef(exec, exec->argument(i)));
-
-    JSValueRef exception = 0;
-    JSValueRef result;
-    {
-        APICallbackShim callbackShim(exec);
-        result = jsCast<JSCallbackFunction*>(toJS(functionRef))->m_callback(execRef, functionRef, thisObjRef, argumentCount, arguments.data(), &exception);
-    }
-    if (exception)
-        throwError(exec, toJS(exec, exception));
-
-    // result must be a valid JSValue.
-    if (!result)
-        return JSValue::encode(jsUndefined());
-
-    return JSValue::encode(toJS(exec, result));
-}
-
 CallType JSCallbackFunction::getCallData(JSCell*, CallData& callData)
 {
-    callData.native.function = call;
+    callData.native.function = APICallbackFunction::call<JSCallbackFunction>;
     return CallTypeHost;
 }
 
index 885ef949db41c1094201f28bc93b3af130bce15d..ea1d1abb48094470178198ff89e1e6c10a25e83a 100644 (file)
 namespace JSC {
 
 class JSCallbackFunction : public InternalFunction {
-protected:
-    JSCallbackFunction(JSGlobalObject*, Structure*, JSObjectCallAsFunctionCallback);
-    void finishCreation(VM&, const String& name);
-
+    friend struct APICallbackFunction;
 public:
     typedef InternalFunction Base;
 
@@ -50,11 +47,13 @@ public:
         return Structure::create(vm, globalObject, proto, TypeInfo(ObjectType, StructureFlags), &s_info); 
     }
 
-protected:
+private:
+    JSCallbackFunction(JSGlobalObject*, Structure*, JSObjectCallAsFunctionCallback);
+    void finishCreation(VM&, const String& name);
+
     static CallType getCallData(JSCell*, CallData&);
 
-private:
-    static EncodedJSValue JSC_HOST_CALL call(ExecState*);
+    JSObjectCallAsFunctionCallback functionCallback() { return m_callback; }
 
     JSObjectCallAsFunctionCallback m_callback;
 };
index f336ba662a78d598fced521cfde0944206909869..0a01f22ba3c53df3cbb492a53aa6acbc90d8b4ed 100644 (file)
@@ -50,8 +50,125 @@ static JSManagedValueHandleOwner* managedValueHandleOwner()
     return &jsManagedValueHandleOwner;
 }
 
+class WeakValueRef {
+public:
+    WeakValueRef()
+        : m_tag(NotSet)
+    {
+    }
+
+    ~WeakValueRef()
+    {
+        clear();
+    }
+
+    void clear()
+    {
+        switch (m_tag) {
+        case NotSet:
+            return;
+        case Primitive:
+            u.m_primitive = JSC::JSValue();
+            return;
+        case Object:
+            u.m_object.clear();
+            return;
+        case String:
+            u.m_string.clear();
+            return;
+        }
+        RELEASE_ASSERT_NOT_REACHED();
+    }
+
+    bool isClear() const
+    {
+        switch (m_tag) {
+        case NotSet:
+            return true;
+        case Primitive:
+            return !u.m_primitive;
+        case Object:
+            return !u.m_object;
+        case String:
+            return !u.m_string;
+        }
+        RELEASE_ASSERT_NOT_REACHED();
+    }
+
+    bool isSet() const { return m_tag != NotSet; }
+    bool isPrimitive() const { return m_tag == Primitive; }
+    bool isObject() const { return m_tag == Object; }
+    bool isString() const { return m_tag == String; }
+
+    void setPrimitive(JSC::JSValue primitive)
+    {
+        ASSERT(!isSet());
+        ASSERT(!u.m_primitive);
+        ASSERT(primitive.isPrimitive());
+        m_tag = Primitive;
+        u.m_primitive = primitive;
+    }
+
+    void setObject(JSC::JSObject* object, void* context)
+    {
+        ASSERT(!isSet());
+        ASSERT(!u.m_object);
+        m_tag = Object;
+        JSC::Weak<JSC::JSObject> weak(object, managedValueHandleOwner(), context);
+        u.m_object.swap(weak);
+    }
+
+    void setString(JSC::JSString* string, void* context)
+    {
+        ASSERT(!isSet());
+        ASSERT(!u.m_object);
+        m_tag = String;
+        JSC::Weak<JSC::JSString> weak(string, managedValueHandleOwner(), context);
+        u.m_string.swap(weak);
+    }
+
+    JSC::JSObject* object()
+    {
+        ASSERT(isObject());
+        return u.m_object.get();
+    }
+
+    JSC::JSValue primitive()
+    {
+        ASSERT(isPrimitive());
+        return u.m_primitive;
+    }
+
+    JSC::JSString* string()
+    {
+        ASSERT(isString());
+        return u.m_string.get();
+    }
+
+private:
+    enum WeakTypeTag { NotSet, Primitive, Object, String };
+    WeakTypeTag m_tag;
+    union WeakValueUnion {
+    public:
+        WeakValueUnion ()
+            : m_primitive(JSC::JSValue())
+        {
+        }
+
+        ~WeakValueUnion()
+        {
+            ASSERT(!m_primitive);
+        }
+
+        JSC::JSValue m_primitive;
+        JSC::Weak<JSC::JSObject> m_object;
+        JSC::Weak<JSC::JSString> m_string;
+    } u;
+};
+
 @implementation JSManagedValue {
-    JSC::Weak<JSC::JSObject> m_value;
+    JSC::Weak<JSC::JSGlobalObject> m_globalObject;
+    WeakValueRef m_weakValue;
 }
 
 + (JSManagedValue *)managedValueWithValue:(JSValue *)value
@@ -70,30 +187,46 @@ static JSManagedValueHandleOwner* managedValueHandleOwner()
     if (!self)
         return nil;
     
-    if (!value || !JSValueIsObject([value.context JSGlobalContextRef], [value JSValueRef])) {
-        JSC::Weak<JSC::JSObject> weak;
-        m_value.swap(weak);
-    } else {
-        JSC::JSObject* object = toJS(const_cast<OpaqueJSValue*>([value JSValueRef]));
-        JSC::Weak<JSC::JSObject> weak(object, managedValueHandleOwner(), self);
-        m_value.swap(weak);
-    }
-
+    if (!value)
+        return self;
+
+    JSC::ExecState* exec = toJS([value.context JSGlobalContextRef]);
+    JSC::JSGlobalObject* globalObject = exec->lexicalGlobalObject();
+    JSC::Weak<JSC::JSGlobalObject> weak(globalObject, managedValueHandleOwner(), self);
+    m_globalObject.swap(weak);
+
+    JSC::JSValue jsValue = toJS(exec, [value JSValueRef]);
+    if (jsValue.isObject())
+        m_weakValue.setObject(JSC::jsCast<JSC::JSObject*>(jsValue.asCell()), self);
+    else if (jsValue.isString())
+        m_weakValue.setString(JSC::jsCast<JSC::JSString*>(jsValue.asCell()), self);
+    else
+        m_weakValue.setPrimitive(jsValue);
     return self;
 }
 
 - (JSValue *)value
 {
-    if (!m_value)
+    if (!m_globalObject)
+        return nil;
+    if (m_weakValue.isClear())
         return nil;
-    JSC::JSObject* object = m_value.get();
-    JSContext *context = [JSContext contextWithJSGlobalContextRef:toGlobalRef(object->structure()->globalObject()->globalExec())];
-    return [JSValue valueWithJSValueRef:toRef(object) inContext:context];
+    JSC::ExecState* exec = m_globalObject->globalExec();
+    JSContext *context = [JSContext contextWithJSGlobalContextRef:toGlobalRef(exec)];
+    JSC::JSValue value;
+    if (m_weakValue.isPrimitive())
+        value = m_weakValue.primitive();
+    else if (m_weakValue.isString())
+        value = m_weakValue.string();
+    else
+        value = m_weakValue.object();
+    return [JSValue valueWithJSValueRef:toRef(exec, value) inContext:context];
 }
 
 - (void)disconnectValue
 {
-    m_value.clear();
+    m_globalObject.clear();
+    m_weakValue.clear();
 }
 
 @end
index a380964ad315587ee5635175ddb7850917eb14b5..f007573a25dc4638575a799062f1209987865b26 100644 (file)
 #import "ObjcRuntimeExtras.h"
 #import "Operations.h"
 #import "JSCJSValue.h"
+#import "StrongInlines.h"
 #import <wtf/HashMap.h>
 #import <wtf/HashSet.h>
+#import <wtf/ObjcRuntimeExtras.h>
 #import <wtf/Vector.h>
 #import <wtf/TCSpinLock.h>
 #import <wtf/text/WTFString.h>
@@ -594,6 +596,7 @@ private:
     JSGlobalContextRef m_context;
     HashMap<JSValueRef, id> m_objectMap;
     Vector<Task> m_worklist;
+    Vector<JSC::Strong<JSC::Unknown>> m_jsValues;
 };
 
 inline id JSContainerConvertor::convert(JSValueRef value)
@@ -610,6 +613,8 @@ inline id JSContainerConvertor::convert(JSValueRef value)
 
 void JSContainerConvertor::add(Task task)
 {
+    JSC::ExecState* exec = toJS(m_context);
+    m_jsValues.append(JSC::Strong<JSC::Unknown>(exec->vm(), toJSForGC(exec, task.js)));
     m_objectMap.add(task.js, task.objc);
     if (task.type != ContainerNone)
         m_worklist.append(task);
@@ -666,6 +671,7 @@ static JSContainerConvertor::Task valueToObjectWithoutCopy(JSGlobalContextRef co
 static id containerValueToObject(JSGlobalContextRef context, JSContainerConvertor::Task task)
 {
     ASSERT(task.type != ContainerNone);
+    JSC::APIEntryShim entryShim(toJS(context));
     JSContainerConvertor convertor(context);
     convertor.add(task);
     ASSERT(!convertor.isWorkListEmpty());
@@ -745,7 +751,7 @@ id valueToString(JSGlobalContextRef context, JSValueRef value, JSValueRef* excep
         return nil;
     }
 
-    NSString *stringNS = [(NSString *)JSStringCopyCFString(kCFAllocatorDefault, jsstring) autorelease];
+    NSString *stringNS = HardAutorelease(JSStringCopyCFString(kCFAllocatorDefault, jsstring));
     JSStringRelease(jsstring);
     return stringNS;
 }
@@ -816,6 +822,7 @@ private:
     JSContext *m_context;
     HashMap<id, JSValueRef> m_objectMap;
     Vector<Task> m_worklist;
+    Vector<JSC::Strong<JSC::Unknown>> m_jsValues;
 };
 
 JSValueRef ObjcContainerConvertor::convert(id object)
@@ -833,6 +840,8 @@ JSValueRef ObjcContainerConvertor::convert(id object)
 
 void ObjcContainerConvertor::add(ObjcContainerConvertor::Task task)
 {
+    JSC::ExecState* exec = toJS(m_context.JSGlobalContextRef);
+    m_jsValues.append(JSC::Strong<JSC::Unknown>(exec->vm(), toJSForGC(exec, task.js)));
     m_objectMap.add(task.objc, task.js);
     if (task.type != ContainerNone)
         m_worklist.append(task);
@@ -912,6 +921,7 @@ JSValueRef objectToValue(JSContext *context, id object)
     if (task.type == ContainerNone)
         return task.js;
 
+    JSC::APIEntryShim entryShim(toJS(contextRef));
     ObjcContainerConvertor convertor(context);
     convertor.add(task);
     ASSERT(!convertor.isWorkListEmpty());
index 4dde1a6597b670daa09ea7076d515e6793845302..5ec64d94c9dd3f36d3adac1538cc8a421f51c606 100644 (file)
 #import "WeakGCMap.h"
 #import <wtf/TCSpinLock.h>
 #import <wtf/Vector.h>
+#import <wtf/HashSet.h>
+
+#if OS(DARWIN)
+#include <mach-o/dyld.h>
+
+static const int32_t webkitFirstVersionWithInitConstructorSupport = 0x2193302; // 537.51.2
+#endif
 
 @class JSObjCClassInfo;
 
@@ -93,6 +100,16 @@ done:
     return result;
 }
 
+static bool constructorHasInstance(JSContextRef ctx, JSObjectRef constructorRef, JSValueRef possibleInstance, JSValueRef*)
+{
+    JSC::ExecState* exec = toJS(ctx);
+    JSC::APIEntryShim entryShim(exec);
+
+    JSC::JSObject* constructor = toJS(constructorRef);
+    JSC::JSValue instance = toJS(exec, possibleInstance);
+    return JSC::JSObject::defaultHasInstance(exec, instance, constructor->get(exec, exec->propertyNames().prototype));
+}
+
 static JSObjectRef makeWrapper(JSContextRef ctx, JSClassRef jsClass, id wrappedObject)
 {
     JSC::ExecState* exec = toJS(ctx);
@@ -121,6 +138,18 @@ static JSValue *objectWithCustomBrand(JSContext *context, NSString *brand, Class
     return [JSValue valueWithJSValueRef:result inContext:context];
 }
 
+static JSValue *constructorWithCustomBrand(JSContext *context, NSString *brand, Class cls)
+{
+    JSClassDefinition definition;
+    definition = kJSClassDefinitionEmpty;
+    definition.className = [brand UTF8String];
+    definition.hasInstance = constructorHasInstance;
+    JSClassRef classRef = JSClassCreate(&definition);
+    JSObjectRef result = makeWrapper([context JSGlobalContextRef], classRef, cls);
+    JSClassRelease(classRef);
+    return [JSValue valueWithJSValueRef:result inContext:context];
+}
+
 // Look for @optional properties in the prototype containing a selector to property
 // name mapping, separated by a __JS_EXPORT_AS__ delimiter.
 static NSMutableDictionary *createRenameMap(Protocol *protocol, BOOL isInstanceMethod)
@@ -163,6 +192,11 @@ static void copyMethodsToObject(JSContext *context, Class objcClass, Protocol *p
     forEachMethodInProtocol(protocol, YES, isInstanceMethod, ^(SEL sel, const char* types){
         const char* nameCStr = sel_getName(sel);
         NSString *name = @(nameCStr);
+        // Don't copy over init family methods because we handle those specially 
+        // for the purposes of hooking up the constructor correctly.
+        if ([name hasPrefix:@"init"])
+            return;
+
         if (accessorMethods && accessorMethods[name]) {
             JSObjectRef method = objCCallbackFunctionForMethod(context, objcClass, protocol, isInstanceMethod, sel, types);
             if (!method)
@@ -329,6 +363,59 @@ static void copyPrototypeProperties(JSContext *context, Class objcClass, Protoco
     [super dealloc];
 }
 
+static JSValue *allocateConstructorForCustomClass(JSContext *context, const char* className, Class cls)
+{
+#if OS(DARWIN)
+    if (NSVersionOfLinkTimeLibrary("JavaScriptCore") < webkitFirstVersionWithInitConstructorSupport)
+        return constructorWithCustomBrand(context, [NSString stringWithFormat:@"%sConstructor", className], cls);
+#endif
+    // For each protocol that the class implements, gather all of the init family methods into a hash table.
+    __block HashMap<String, Protocol *> initTable;
+    Protocol *exportProtocol = getJSExportProtocol();
+    for (Class currentClass = cls; currentClass; currentClass = class_getSuperclass(currentClass)) {
+        forEachProtocolImplementingProtocol(currentClass, exportProtocol, ^(Protocol *protocol) {
+            forEachMethodInProtocol(protocol, YES, YES, ^(SEL selector, const char*) {
+                const char* name = sel_getName(selector);
+                if (![@(name) hasPrefix:@"init"])
+                    return;
+                initTable.set(name, protocol);
+            });
+        });
+    }
+
+    for (Class currentClass = cls; currentClass; currentClass = class_getSuperclass(currentClass)) {
+        __block unsigned numberOfInitsFound = 0;
+        __block SEL initMethod = 0;
+        __block Protocol *initProtocol = 0;
+        __block const char* types = 0;
+        forEachMethodInClass(currentClass, ^(Method method) {
+            SEL selector = method_getName(method);
+            const char* name = sel_getName(selector);
+            auto iter = initTable.find(name);
+
+            if (iter == initTable.end())
+                return;
+
+            numberOfInitsFound++;
+            initMethod = selector;
+            initProtocol = iter->value;
+            types = method_getTypeEncoding(method);
+        });
+
+        if (!numberOfInitsFound)
+            continue;
+
+        if (numberOfInitsFound > 1) {
+            NSLog(@"ERROR: Class %@ exported more than one init family method via JSExport. Class %@ will not have a callable JavaScript constructor function.", cls, cls);
+            break;
+        }
+
+        JSObjectRef method = objCCallbackFunctionForInit(context, cls, initProtocol, initMethod, types);
+        return [JSValue valueWithJSValueRef:method inContext:context];
+    }
+    return constructorWithCustomBrand(context, [NSString stringWithFormat:@"%sConstructor", className], cls);
+}
+
 - (void)allocateConstructorAndPrototypeWithSuperClassInfo:(JSObjCClassInfo*)superClassInfo
 {
     ASSERT(!m_constructor || !m_prototype);
@@ -357,7 +444,7 @@ static void copyPrototypeProperties(JSContext *context, Class objcClass, Protoco
         if (m_constructor)
             constructor = [JSValue valueWithJSValueRef:toRef(m_constructor.get()) inContext:m_context];
         else
-            constructor = objectWithCustomBrand(m_context, [NSString stringWithFormat:@"%sConstructor", className], m_class);
+            constructor = allocateConstructorForCustomClass(m_context, className, m_class);
 
         JSContextRef cContext = [m_context JSGlobalContextRef];
         m_prototype = toJS(JSValueToObject(cContext, valueInternalValue(prototype), 0));
@@ -387,8 +474,13 @@ static void copyPrototypeProperties(JSContext *context, Class objcClass, Protoco
     ASSERT([object isKindOfClass:m_class]);
     ASSERT(m_block == [object isKindOfClass:getNSBlockClass()]);
     if (m_block) {
-        if (JSObjectRef method = objCCallbackFunctionForBlock(m_context, object))
-            return [JSValue valueWithJSValueRef:method inContext:m_context];
+        if (JSObjectRef method = objCCallbackFunctionForBlock(m_context, object)) {
+            JSValue *constructor = [JSValue valueWithJSValueRef:method inContext:m_context];
+            JSValue *prototype = [JSValue valueWithNewObjectInContext:m_context];
+            putNonEnumerable(constructor, @"prototype", prototype);
+            putNonEnumerable(prototype, @"constructor", constructor);
+            return constructor;
+        }
     }
 
     if (!m_prototype)
@@ -501,7 +593,7 @@ id tryUnwrapObjcObject(JSGlobalContextRef context, JSValueRef value)
     ASSERT(!exception);
     if (toJS(object)->inherits(&JSC::JSCallbackObject<JSC::JSAPIWrapperObject>::s_info))
         return (id)JSC::jsCast<JSC::JSAPIWrapperObject*>(toJS(object))->wrappedObject();
-    if (id target = tryUnwrapBlock(object))
+    if (id target = tryUnwrapConstructor(object))
         return target;
     return nil;
 }
index 0218cd8b465c9428c71429871cd4af70c6e50098..0b0b5432d657d57fb49031d42177ad0319142051 100644 (file)
 #if defined(__OBJC__)
 JSObjectRef objCCallbackFunctionForMethod(JSContext *, Class, Protocol *, BOOL isInstanceMethod, SEL, const char* types);
 JSObjectRef objCCallbackFunctionForBlock(JSContext *, id);
+JSObjectRef objCCallbackFunctionForInit(JSContext *, Class, Protocol *, SEL, const char* types);
 
-id tryUnwrapBlock(JSObjectRef);
+id tryUnwrapConstructor(JSObjectRef);
 #endif
 
 namespace JSC {
 
 class ObjCCallbackFunctionImpl;
 
-class ObjCCallbackFunction : public JSCallbackFunction {
+class ObjCCallbackFunction : public InternalFunction {
+    friend struct APICallbackFunction;
 public:
-    typedef JSCallbackFunction Base;
+    typedef InternalFunction Base;
 
-    static ObjCCallbackFunction* create(ExecState*, JSGlobalObject*, const String& name, PassOwnPtr<ObjCCallbackFunctionImpl>);
+    static ObjCCallbackFunction* create(VM&, JSGlobalObject*, const String& name, PassOwnPtr<ObjCCallbackFunctionImpl>);
     static void destroy(JSCell*);
 
     static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
@@ -57,12 +59,20 @@ public:
 
     static JS_EXPORTDATA const ClassInfo s_info;
 
-    ObjCCallbackFunctionImpl* impl() { return m_impl.get(); }
+    ObjCCallbackFunctionImpl* impl() const { return m_impl.get(); }
 
 protected:
-    ObjCCallbackFunction(JSGlobalObject*, JSObjectCallAsFunctionCallback, PassOwnPtr<ObjCCallbackFunctionImpl>);
+    ObjCCallbackFunction(VM&, JSGlobalObject*, JSObjectCallAsFunctionCallback, JSObjectCallAsConstructorCallback, PassOwnPtr<ObjCCallbackFunctionImpl>);
 
 private:
+    static CallType getCallData(JSCell*, CallData&);
+    static ConstructType getConstructData(JSCell*, ConstructData&);
+
+    JSObjectCallAsFunctionCallback functionCallback() { return m_functionCallback; }
+    JSObjectCallAsConstructorCallback constructCallback() { return m_constructCallback; }
+
+    JSObjectCallAsFunctionCallback m_functionCallback;
+    JSObjectCallAsConstructorCallback m_constructCallback;
     OwnPtr<ObjCCallbackFunctionImpl> m_impl;
 };
 
index cc342f59e3e31f0b73d011681a7d7d7b2d93b02c..e0bd2d7336636fd035b0da2deb0c0a6e3f3271d0 100644 (file)
@@ -28,6 +28,7 @@
 
 #if JSC_OBJC_API_ENABLED
 
+#import "APICallbackFunction.h"
 #import "APICast.h"
 #import "APIShims.h"
 #import "Error.h"
@@ -384,6 +385,7 @@ public:
 };
 
 enum CallbackType {
+    CallbackInitMethod,
     CallbackInstanceMethod,
     CallbackClassMethod,
     CallbackBlock
@@ -401,12 +403,14 @@ public:
         , m_arguments(arguments)
         , m_result(result)
     {
-        ASSERT(type != CallbackInstanceMethod || instanceClass);
+        ASSERT((type != CallbackInstanceMethod && type != CallbackInitMethod) || instanceClass);
     }
 
     ~ObjCCallbackFunctionImpl()
     {
-        if (m_type != CallbackInstanceMethod)
+        // We need to explicity release the target since we didn't call 
+        // -retainArguments on m_invocation (and we don't want to do so).
+        if (m_type == CallbackBlock || m_type == CallbackClassMethod)
             [[m_invocation.get() target] release];
         [m_instanceClass release];
     }
@@ -429,6 +433,25 @@ public:
         return m_type == CallbackBlock ? [m_invocation target] : nil;
     }
 
+    id wrappedConstructor()
+    {
+        switch (m_type) {
+        case CallbackBlock:
+            return [m_invocation target];
+        case CallbackInitMethod:
+            return m_instanceClass;
+        default:
+            return nil;
+        }
+    }
+
+    bool isConstructible()
+    {
+        return !!wrappedBlock() || m_type == CallbackInitMethod;
+    }
+
+    String name();
+
 private:
     WeakContextRef m_context;
     CallbackType m_type;
@@ -466,18 +489,49 @@ static JSValueRef objCCallbackFunctionCallAsFunction(JSContextRef callerContext,
     return result;
 }
 
+static JSObjectRef objCCallbackFunctionCallAsConstructor(JSContextRef callerContext, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+    JSC::APIEntryShim entryShim(toJS(callerContext));
+
+    ObjCCallbackFunction* callback = static_cast<ObjCCallbackFunction*>(toJS(constructor));
+    ObjCCallbackFunctionImpl* impl = callback->impl();
+    JSContext *context = [JSContext contextWithJSGlobalContextRef:toGlobalRef(toJS(callerContext)->lexicalGlobalObject()->globalExec())];
+
+    CallbackData callbackData;
+    JSValueRef result;
+    @autoreleasepool {
+        [context beginCallbackWithData:&callbackData thisValue:nil argumentCount:argumentCount arguments:arguments];
+        result = impl->call(context, NULL, argumentCount, arguments, exception);
+        if (context.exception)
+            *exception = valueInternalValue(context.exception);
+        [context endCallbackWithData:&callbackData];
+    }
+
+    JSGlobalContextRef contextRef = [context JSGlobalContextRef];
+    if (*exception)
+        return 0;
+
+    if (!JSValueIsObject(contextRef, result)) {
+        *exception = toRef(JSC::createTypeError(toJS(contextRef), "Objective-C blocks called as constructors must return an object."));
+        return 0;
+    }
+    return (JSObjectRef)result;
+}
+
 const JSC::ClassInfo ObjCCallbackFunction::s_info = { "CallbackFunction", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(ObjCCallbackFunction) };
 
-ObjCCallbackFunction::ObjCCallbackFunction(JSC::JSGlobalObject* globalObject, JSObjectCallAsFunctionCallback callback, PassOwnPtr<ObjCCallbackFunctionImpl> impl)
-    : Base(globalObject, globalObject->objcCallbackFunctionStructure(), callback)
+ObjCCallbackFunction::ObjCCallbackFunction(JSC::VM&, JSC::JSGlobalObject* globalObject, JSObjectCallAsFunctionCallback functionCallback, JSObjectCallAsConstructorCallback constructCallback, PassOwnPtr<ObjCCallbackFunctionImpl> impl)
+    : Base(globalObject, globalObject->objcCallbackFunctionStructure())
+    , m_functionCallback(functionCallback)
+    , m_constructCallback(constructCallback)
     , m_impl(impl)
 {
 }
 
-ObjCCallbackFunction* ObjCCallbackFunction::create(JSC::ExecState* exec, JSC::JSGlobalObject* globalObject, const String& name, PassOwnPtr<ObjCCallbackFunctionImpl> impl)
+ObjCCallbackFunction* ObjCCallbackFunction::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, const String& name, PassOwnPtr<ObjCCallbackFunctionImpl> impl)
 {
-    ObjCCallbackFunction* function = new (NotNull, allocateCell<ObjCCallbackFunction>(*exec->heap())) ObjCCallbackFunction(globalObject, objCCallbackFunctionCallAsFunction, impl);
-    function->finishCreation(exec->vm(), name);
+    ObjCCallbackFunction* function = new (NotNull, allocateCell<ObjCCallbackFunction>(vm.heap)) ObjCCallbackFunction(vm, globalObject, objCCallbackFunctionCallAsFunction, objCCallbackFunctionCallAsConstructor, impl);
+    function->finishCreation(vm, name);
     return function;
 }
 
@@ -486,21 +540,59 @@ void ObjCCallbackFunction::destroy(JSCell* cell)
     static_cast<ObjCCallbackFunction*>(cell)->ObjCCallbackFunction::~ObjCCallbackFunction();
 }
 
+
+CallType ObjCCallbackFunction::getCallData(JSCell*, CallData& callData)
+{
+    callData.native.function = APICallbackFunction::call<ObjCCallbackFunction>;
+    return CallTypeHost;
+}
+
+ConstructType ObjCCallbackFunction::getConstructData(JSCell* cell, ConstructData& constructData)
+{
+    ObjCCallbackFunction* callback = jsCast<ObjCCallbackFunction*>(cell);
+    if (!callback->impl()->isConstructible())
+        return Base::getConstructData(cell, constructData);
+    constructData.native.function = APICallbackFunction::construct<ObjCCallbackFunction>;
+    return ConstructTypeHost;
+}
+
+String ObjCCallbackFunctionImpl::name()
+{
+    if (m_type == CallbackInitMethod)
+        return class_getName(m_instanceClass);
+    // FIXME: Maybe we could support having the selector as the name of the non-init 
+    // functions to make it a bit more user-friendly from the JS side?
+    return "";
+}
+
 JSValueRef ObjCCallbackFunctionImpl::call(JSContext *context, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
 {
     JSGlobalContextRef contextRef = [context JSGlobalContextRef];
 
+    id target;
     size_t firstArgument;
     switch (m_type) {
+    case CallbackInitMethod: {
+        RELEASE_ASSERT(!thisObject);
+        target = [m_instanceClass alloc];
+        if (!target || ![target isKindOfClass:m_instanceClass]) {
+            *exception = toRef(JSC::createTypeError(toJS(contextRef), "self type check failed for Objective-C instance method"));
+            return JSValueMakeUndefined(contextRef);
+        }
+        [m_invocation setTarget:target];
+        firstArgument = 2;
+        break;
+    }
     case CallbackInstanceMethod: {
-        id target = tryUnwrapObjcObject(contextRef, thisObject);
+        target = tryUnwrapObjcObject(contextRef, thisObject);
         if (!target || ![target isKindOfClass:m_instanceClass]) {
             *exception = toRef(JSC::createTypeError(toJS(contextRef), "self type check failed for Objective-C instance method"));
             return JSValueMakeUndefined(contextRef);
         }
         [m_invocation setTarget:target];
+        firstArgument = 2;
+        break;
     }
-    // fallthrough - firstArgument for CallbackInstanceMethod is also 2!
     case CallbackClassMethod:
         firstArgument = 2;
         break;
@@ -519,7 +611,18 @@ JSValueRef ObjCCallbackFunctionImpl::call(JSContext *context, JSObjectRef thisOb
 
     [m_invocation invoke];
 
-    return m_result->get(m_invocation.get(), context, exception);
+    JSValueRef result = m_result->get(m_invocation.get(), context, exception);
+
+    // Balance our call to -alloc with a call to -autorelease. We have to do this after calling -init
+    // because init family methods are allowed to release the allocated object and return something 
+    // else in its place.
+    if (m_type == CallbackInitMethod) {
+        id objcResult = tryUnwrapObjcObject(contextRef, result);
+        if (objcResult)
+            [objcResult autorelease];
+    }
+
+    return result;
 }
 
 } // namespace JSC
@@ -550,6 +653,7 @@ static JSObjectRef objCCallbackFunctionForInvocation(JSContext *context, NSInvoc
         return nil;
 
     switch (type) {
+    case CallbackInitMethod:
     case CallbackInstanceMethod:
     case CallbackClassMethod:
         // Methods are passed two implicit arguments - (id)self, and the selector.
@@ -582,16 +686,24 @@ static JSObjectRef objCCallbackFunctionForInvocation(JSContext *context, NSInvoc
     JSC::ExecState* exec = toJS([context JSGlobalContextRef]);
     JSC::APIEntryShim shim(exec);
     OwnPtr<JSC::ObjCCallbackFunctionImpl> impl = adoptPtr(new JSC::ObjCCallbackFunctionImpl(context, invocation, type, instanceClass, arguments.release(), result.release()));
-    // FIXME: Maybe we could support having the selector as the name of the function to make it a bit more user-friendly from the JS side?
-    return toRef(JSC::ObjCCallbackFunction::create(exec, exec->lexicalGlobalObject(), "", impl.release()));
+    return toRef(JSC::ObjCCallbackFunction::create(exec->vm(), exec->lexicalGlobalObject(), impl->name(), impl.release()));
+}
+
+JSObjectRef objCCallbackFunctionForInit(JSContext *context, Class cls, Protocol *protocol, SEL sel, const char* types)
+{
+    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:types]];
+    [invocation setSelector:sel];
+    return objCCallbackFunctionForInvocation(context, invocation, CallbackInitMethod, cls, _protocol_getMethodTypeEncoding(protocol, sel, YES, YES));
 }
 
 JSObjectRef objCCallbackFunctionForMethod(JSContext *context, Class cls, Protocol *protocol, BOOL isInstanceMethod, SEL sel, const char* types)
 {
     NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:types]];
     [invocation setSelector:sel];
+    // We need to retain the target Class because m_invocation doesn't retain it
+    // by default (and we don't want it to).
     if (!isInstanceMethod)
-        [invocation setTarget:cls];
+        [invocation setTarget:[cls retain]];
     return objCCallbackFunctionForInvocation(context, invocation, isInstanceMethod ? CallbackInstanceMethod : CallbackClassMethod, isInstanceMethod ? cls : nil, _protocol_getMethodTypeEncoding(protocol, sel, YES, isInstanceMethod));
 }
 
@@ -601,15 +713,24 @@ JSObjectRef objCCallbackFunctionForBlock(JSContext *context, id target)
         return 0;
     const char* signature = _Block_signature(target);
     NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:signature]];
+
+    // We don't want to use -retainArguments because that leaks memory. Arguments 
+    // would be retained indefinitely between invocations of the callback.
+    // Additionally, we copy the target because we want the block to stick around
+    // until the ObjCCallbackFunctionImpl is destroyed.
     [invocation setTarget:[target copy]];
+
     return objCCallbackFunctionForInvocation(context, invocation, CallbackBlock, nil, signature);
 }
 
-id tryUnwrapBlock(JSObjectRef object)
+id tryUnwrapConstructor(JSObjectRef object)
 {
     if (!toJS(object)->inherits(&JSC::ObjCCallbackFunction::s_info))
         return nil;
-    return static_cast<JSC::ObjCCallbackFunction*>(toJS(object))->impl()->wrappedBlock();
+    JSC::ObjCCallbackFunctionImpl* impl = static_cast<JSC::ObjCCallbackFunction*>(toJS(object))->impl();
+    if (!impl->isConstructible())
+        return nil;
+    return impl->wrappedConstructor();
 }
 
 #endif
index 8614e51e9797fd53fcd25b4b8ba9ace16b47cbb1..f4ccf91e4ce1726f455ee636154af113d18f062c 100644 (file)
@@ -105,6 +105,7 @@ static char* createStringWithContentsOfFile(const char* fileName)
     FILE* f = fopen(fileName, "r");
     if (!f) {
         fprintf(stderr, "Could not open file: %s\n", fileName);
+        free(buffer);
         return 0;
     }
     
index a53a340976bc5c2995a425d6a074fa95a4c3db0d..83c605cf45804c6a77a3def1f93be345902aa908 100644 (file)
@@ -1980,6 +1980,7 @@ static char* createStringWithContentsOfFile(const char* fileName)
     FILE* f = fopen(fileName, "r");
     if (!f) {
         fprintf(stderr, "Could not open file: %s\n", fileName);
+        free(buffer);
         return 0;
     }
     
index 0ab82cd2808707f5551e47545d84b5d6ab54926d..d85d6e391e3fc4887ae36043f3cb6eb6c23c15fa 100644 (file)
@@ -35,6 +35,12 @@ extern "C" void testObjectiveCAPI(void);
 
 #if JSC_OBJC_API_ENABLED
 
+@interface UnexportedObject : NSObject
+@end
+
+@implementation UnexportedObject
+@end
+
 @protocol ParentObject <JSExport>
 @end
 
@@ -50,6 +56,7 @@ extern "C" void testObjectiveCAPI(void);
 @end
 
 @protocol TestObject <JSExport>
+- (id)init;
 @property int variable;
 @property (readonly) int six;
 @property CGPoint point;
@@ -101,6 +108,7 @@ JSExportAs(testArgumentTypes,
 bool testXYZTested = false;
 
 @protocol TextXYZ <JSExport>
+- (id)initWithString:(NSString*)string;
 @property int x;
 @property (readonly) int y;
 @property (assign) JSValue *onclick;
@@ -122,6 +130,16 @@ bool testXYZTested = false;
 @synthesize x;
 @synthesize y;
 @synthesize z;
+- (id)initWithString:(NSString*)string
+{
+    self = [super init];
+    if (!self)
+        return nil;
+
+    NSLog(@"%@", string);
+
+    return self;
+}
 - (void)test:(NSString *)message
 {
     testXYZTested = [message isEqual:@"test"] && x == 13 & y == 4 && z == 5;
@@ -241,6 +259,145 @@ static JSVirtualMachine *sharedInstance = nil;
 
 @end
 
+@interface JSCollection : NSObject
+- (void)setValue:(JSValue *)value forKey:(NSString *)key;
+- (JSValue *)valueForKey:(NSString *)key;
+@end
+
+@implementation JSCollection {
+    NSMutableDictionary *_dict;
+}
+- (id)init
+{
+    self = [super init];
+    if (!self)
+        return nil;
+
+    _dict = [[NSMutableDictionary alloc] init];
+
+    return self;
+}
+
+- (void)setValue:(JSValue *)value forKey:(NSString *)key
+{
+    JSManagedValue *oldManagedValue = [_dict objectForKey:key];
+    if (oldManagedValue) {
+        JSValue* oldValue = [oldManagedValue value];
+        if (oldValue)
+            [oldValue.context.virtualMachine removeManagedReference:oldManagedValue withOwner:self];
+    }
+    JSManagedValue *managedValue = [JSManagedValue managedValueWithValue:value];
+    [value.context.virtualMachine addManagedReference:managedValue withOwner:self];
+    [_dict setObject:managedValue forKey:key];
+}
+
+- (JSValue *)valueForKey:(NSString *)key
+{
+    JSManagedValue *managedValue = [_dict objectForKey:key];
+    if (!managedValue)
+        return nil;
+    return [managedValue value];
+}
+@end
+
+@protocol InitA <JSExport>
+- (id)initWithA:(int)a;
+@end
+
+@protocol InitB <JSExport>
+- (id)initWithA:(int)a b:(int)b;
+@end
+
+@interface ClassA : NSObject<InitA>
+@end
+
+@interface ClassB : ClassA<InitB>
+@end
+
+@interface ClassC : ClassB<InitA, InitB>
+@end
+
+@interface ClassD : NSObject<InitA>
+- (id)initWithA:(int)a;
+@end
+
+@interface ClassE : ClassD
+- (id)initWithA:(int)a;
+@end
+
+@implementation ClassA {
+    int _a;
+}
+- (id)initWithA:(int)a
+{
+    self = [super init];
+    if (!self)
+        return nil;
+
+    _a = a;
+
+    return self;
+}
+@end
+
+@implementation ClassB {
+    int _b;
+}
+- (id)initWithA:(int)a b:(int)b
+{
+    self = [super initWithA:a];
+    if (!self)
+        return nil;
+
+    _b = b;
+
+    return self;
+}
+@end
+
+@implementation ClassC {
+    int _c;
+}
+- (id)initWithA:(int)a
+{
+    return [self initWithA:a b:0];
+}
+- (id)initWithA:(int)a b:(int)b
+{
+    self = [super initWithA:a b:b];
+    if (!self)
+        return nil;
+
+    _c = a + b;
+
+    return self;
+}
+@end
+
+@implementation ClassD
+
+- (id)initWithA:(int)a
+{
+    self = nil;
+    return [[ClassE alloc] initWithA:a];
+}
+@end
+
+@implementation ClassE {
+    int _a;
+}
+
+- (id)initWithA:(int)a
+{
+    self = [super init];
+    if (!self)
+        return nil;
+
+    _a = a;
+
+    return self;
+}
+@end
 static void checkResult(NSString *description, bool passed)
 {
     NSLog(@"TEST: \"%@\": %@", description, passed ? @"PASSED" : @"FAILED");
@@ -290,6 +447,41 @@ void testObjectiveCAPI()
         checkResult(@"Check numeric literal", [num isKindOfClass:[NSNumber class]]);
     }
 
+    @autoreleasepool {
+        JSCollection* myPrivateProperties = [[JSCollection alloc] init];
+
+        @autoreleasepool {
+            JSContext* context = [[JSContext alloc] init];
+            TestObject* rootObject = [TestObject testObject];
+            context[@"root"] = rootObject;
+            [context.virtualMachine addManagedReference:myPrivateProperties withOwner:rootObject];
+            [myPrivateProperties setValue:[JSValue valueWithBool:true inContext:context] forKey:@"is_ham"];
+            [myPrivateProperties setValue:[JSValue valueWithObject:@"hello!" inContext:context] forKey:@"message"];
+            [myPrivateProperties setValue:[JSValue valueWithInt32:42 inContext:context] forKey:@"my_number"];
+            [myPrivateProperties setValue:[JSValue valueWithNullInContext:context] forKey:@"definitely_null"];
+            [myPrivateProperties setValue:[JSValue valueWithUndefinedInContext:context] forKey:@"not_sure_if_undefined"];
+
+            JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
+
+            JSValue *isHam = [myPrivateProperties valueForKey:@"is_ham"];
+            JSValue *message = [myPrivateProperties valueForKey:@"message"];
+            JSValue *myNumber = [myPrivateProperties valueForKey:@"my_number"];
+            JSValue *definitelyNull = [myPrivateProperties valueForKey:@"definitely_null"];
+            JSValue *notSureIfUndefined = [myPrivateProperties valueForKey:@"not_sure_if_undefined"];
+            checkResult(@"is_ham is true", [isHam isBoolean] && [isHam toBool]);
+            checkResult(@"message is hello!", [message isString] && [@"hello!" isEqualToString:[message toString]]);
+            checkResult(@"my_number is 42", [myNumber isNumber] && [myNumber toInt32] == 42);
+            checkResult(@"definitely_null is null", [definitelyNull isNull]);
+            checkResult(@"not_sure_if_undefined is undefined", [notSureIfUndefined isUndefined]);
+        }
+
+        checkResult(@"is_ham is nil", ![myPrivateProperties valueForKey:@"is_ham"]);
+        checkResult(@"message is nil", ![myPrivateProperties valueForKey:@"message"]);
+        checkResult(@"my_number is 42", ![myPrivateProperties valueForKey:@"my_number"]);
+        checkResult(@"definitely_null is null", ![myPrivateProperties valueForKey:@"definitely_null"]);
+        checkResult(@"not_sure_if_undefined is undefined", ![myPrivateProperties valueForKey:@"not_sure_if_undefined"]);
+    }
+
     @autoreleasepool {
         JSContext *context = [[JSContext alloc] init];
         __block int result;
@@ -522,7 +714,7 @@ void testObjectiveCAPI()
         JSContext *context = [[JSContext alloc] init];
         context[@"TestObject"] = [TestObject class];
         JSValue *result = [context evaluateScript:@"String(TestObject)"];
-        checkResult(@"String(TestObject)", [result isEqualToObject:@"[object TestObjectConstructor]"]);
+        checkResult(@"String(TestObject)", [result isEqualToObject:@"function TestObject() {\n    [native code]\n}"]);
     }
 
     @autoreleasepool {
@@ -857,6 +1049,109 @@ void testObjectiveCAPI()
             [context.virtualMachine addManagedReference:managedTestObject withOwner:testObject];
         }
     }
+
+    @autoreleasepool {
+        JSContext *context = [[JSContext alloc] init];
+        context[@"UnexportedObject"] = [UnexportedObject class];
+        context[@"makeObject"] = ^{
+            return [[UnexportedObject alloc] init];
+        };
+        JSValue *result = [context evaluateScript:@"(makeObject() instanceof UnexportedObject)"];
+        checkResult(@"makeObject() instanceof UnexportedObject", [result isBoolean] && [result toBool]);
+    }
+
+    @autoreleasepool {
+        JSContext *context = [[JSContext alloc] init];
+        context[@"MyClass"] = ^{
+            JSValue *newThis = [JSValue valueWithNewObjectInContext:[JSContext currentContext]];
+            JSGlobalContextRef contextRef = [[JSContext currentContext] JSGlobalContextRef];
+            JSObjectRef newThisRef = JSValueToObject(contextRef, [newThis JSValueRef], NULL);
+            JSObjectSetPrototype(contextRef, newThisRef, [[JSContext currentContext][@"MyClass"][@"prototype"] JSValueRef]);
+            return newThis;
+        };
+
+        context[@"MyOtherClass"] = ^{
+            JSValue *newThis = [JSValue valueWithNewObjectInContext:[JSContext currentContext]];
+            JSGlobalContextRef contextRef = [[JSContext currentContext] JSGlobalContextRef];
+            JSObjectRef newThisRef = JSValueToObject(contextRef, [newThis JSValueRef], NULL);
+            JSObjectSetPrototype(contextRef, newThisRef, [[JSContext currentContext][@"MyOtherClass"][@"prototype"] JSValueRef]);
+            return newThis;
+        };
+
+        context.exceptionHandler = ^(JSContext *context, JSValue *exception) {
+            NSLog(@"EXCEPTION: %@", [exception toString]);
+            context.exception = nil;
+        };
+
+        JSValue *constructor1 = context[@"MyClass"];
+        JSValue *constructor2 = context[@"MyOtherClass"];
+
+        JSValue *value1 = [context evaluateScript:@"new MyClass()"];
+        checkResult(@"value1 instanceof MyClass", [value1 isInstanceOf:constructor1]);
+        checkResult(@"!(value1 instanceof MyOtherClass)", ![value1 isInstanceOf:constructor2]);
+        checkResult(@"MyClass.prototype.constructor === MyClass", [[context evaluateScript:@"MyClass.prototype.constructor === MyClass"] toBool]);
+        checkResult(@"MyClass instanceof Function", [[context evaluateScript:@"MyClass instanceof Function"] toBool]);
+
+        JSValue *value2 = [context evaluateScript:@"new MyOtherClass()"];
+        checkResult(@"value2 instanceof MyOtherClass", [value2 isInstanceOf:constructor2]);
+        checkResult(@"!(value2 instanceof MyClass)", ![value2 isInstanceOf:constructor1]);
+        checkResult(@"MyOtherClass.prototype.constructor === MyOtherClass", [[context evaluateScript:@"MyOtherClass.prototype.constructor === MyOtherClass"] toBool]);
+        checkResult(@"MyOtherClass instanceof Function", [[context evaluateScript:@"MyOtherClass instanceof Function"] toBool]);
+    }
+
+    @autoreleasepool {
+        JSContext *context = [[JSContext alloc] init];
+        context[@"MyClass"] = ^{
+            NSLog(@"I'm intentionally not returning anything.");
+        };
+        JSValue *result = [context evaluateScript:@"new MyClass()"];
+    @autoreleasepool {
+        JSContext *context = [[JSContext alloc] init];
+        context[@"TestObject"] = [TestObject class];
+        JSValue *testObject = [context evaluateScript:@"(new TestObject())"];
+        checkResult(@"testObject instanceof TestObject", [testObject isInstanceOf:context[@"TestObject"]]);
+
+        context[@"TextXYZ"] = [TextXYZ class];
+        JSValue *textObject = [context evaluateScript:@"(new TextXYZ(\"Called TextXYZ constructor!\"))"];
+        checkResult(@"textObject instanceof TextXYZ", [textObject isInstanceOf:context[@"TextXYZ"]]);
+    }
+
+    @autoreleasepool {
+        JSContext *context = [[JSContext alloc] init];
+        context[@"ClassA"] = [ClassA class];
+        context[@"ClassB"] = [ClassB class];
+        context[@"ClassC"] = [ClassC class]; // Should print error message about too many inits found.
+
+        JSValue *a = [context evaluateScript:@"(new ClassA(42))"];
+        checkResult(@"a instanceof ClassA", [a isInstanceOf:context[@"ClassA"]]);
+
+        JSValue *b = [context evaluateScript:@"(new ClassB(42, 53))"];
+        checkResult(@"b instanceof ClassB", [b isInstanceOf:context[@"ClassB"]]);
+
+        JSValue *canConstructClassC = [context evaluateScript:@"(function() { \
+            try { \
+                (new ClassC(1, 2)); \
+                return true; \
+            } catch(e) { \
+                return false; \
+            } \
+        })()"];
+        checkResult(@"shouldn't be able to construct ClassC", ![canConstructClassC toBool]);
+    }
+
+    @autoreleasepool {
+        JSContext *context = [[JSContext alloc] init];
+        context[@"ClassD"] = [ClassD class];
+        context[@"ClassE"] = [ClassE class];
+       
+        JSValue *d = [context evaluateScript:@"(new ClassD())"];
+        checkResult(@"Returning instance of ClassE from ClassD's init has correct class", [d isInstanceOf:context[@"ClassE"]]);
+    }
+
+        checkResult(@"result === undefined", [result isUndefined]);
+        checkResult(@"exception.message is correct'", context.exception 
+            && [@"Objective-C blocks called as constructors must return an object." isEqualToString:[context.exception[@"message"] toString]]);
+    }
 }
 
 #else
index 0733d2782678a3869fef403510e5411c2bfcae3a..ec8645ea7f959aeca3a957a09c836415a2560874 100644 (file)
@@ -23,7 +23,7 @@
 
 MAJOR_VERSION = 537;
 MINOR_VERSION = 51;
-TINY_VERSION = 1;
+TINY_VERSION = 2;
 FULL_VERSION = $(MAJOR_VERSION).$(MINOR_VERSION).$(TINY_VERSION);
 
 // The bundle version and short version string are set based on the current build configuration, see below.
index 8b79c888caaac3f77cfe814d7529231c15729708..7056beab220af01b28b55e59663ff5479cdf8b3d 100644 (file)
@@ -32,6 +32,7 @@ javascriptcore_built_nosources += \
        DerivedSources/JavaScriptCore/LLIntAssembly.h
 
 javascriptcore_sources += \
+    Source/JavaScriptCore/API/APICallbackFunction.h \
        Source/JavaScriptCore/API/APICast.h \
        Source/JavaScriptCore/API/APIShims.h \
     Source/JavaScriptCore/API/JSAPIWrapperObject.h \
index d90ff2ed581c3c66d8db8db0e9ffd08cdb61ee78..e06fbcf43957a4568d9f09ee0ebeba0a94ada31b 100644 (file)
     <ClInclude Include="..\heap\MarkStack.h" />\r
     <ClInclude Include="..\heap\MarkStackInlines.h" />\r
     <ClInclude Include="..\heap\PassWeak.h" />\r
+    <ClInclude Include="..\API\APICallbackFunction.h" />
     <ClInclude Include="..\heap\Region.h" />\r
     <ClInclude Include="..\heap\SlotVisitor.h" />\r
     <ClInclude Include="..\heap\SlotVisitorInlines.h" />\r
index 902c4988bd9f70c97e5adb0629d1ede84ffe26a6..de7777f829259378c6cbb341e7f82b50eacdf849 100644 (file)
     <ClInclude Include="..\API\JavaScript.h">\r
       <Filter>API</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="..\API\APICallbackFunction.h">
+      <Filter>API</Filter>
+    </ClInclude>
     <ClInclude Include="..\API\JavaScriptCore.h">\r
       <Filter>API</Filter>\r
     </ClInclude>\r
index 75bdba5b3e5542bd37364f27438f71c5cbe047f7..a41a94279e2ac1d945d1074f4aa8bde03cc4b108 100644 (file)
                1ACF7377171CA6FB00C9BB1E /* Weak.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1ACF7376171CA6FB00C9BB1E /* Weak.cpp */; };
                2600B5A6152BAAA70091EE5F /* JSStringJoiner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2600B5A4152BAAA70091EE5F /* JSStringJoiner.cpp */; };
                2600B5A7152BAAA70091EE5F /* JSStringJoiner.h in Headers */ = {isa = PBXBuildFile; fileRef = 2600B5A5152BAAA70091EE5F /* JSStringJoiner.h */; };
+               2A3D60CF188DD95F00C9C528 /* APICallbackFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A3D60CE188DD95F00C9C528 /* APICallbackFunction.h */; };
                41359CF30FDD89AD00206180 /* DateConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = D21202290AD4310C00ED79B6 /* DateConversion.h */; };
                4443AE3316E188D90076F110 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51F0EB6105C86C6B00E6DF1B /* Foundation.framework */; };
                451539B912DC994500EF7AC4 /* Yarr.h in Headers */ = {isa = PBXBuildFile; fileRef = 451539B812DC994500EF7AC4 /* Yarr.h */; settings = {ATTRIBUTES = (Private, ); }; };
                1CAA8B4B0D32C39A0041BCFF /* JavaScriptCore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JavaScriptCore.h; sourceTree = "<group>"; };
                2600B5A4152BAAA70091EE5F /* JSStringJoiner.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSStringJoiner.cpp; sourceTree = "<group>"; };
                2600B5A5152BAAA70091EE5F /* JSStringJoiner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSStringJoiner.h; sourceTree = "<group>"; };
+               2A3D60CE188DD95F00C9C528 /* APICallbackFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APICallbackFunction.h; sourceTree = "<group>"; };
                449097EE0F8F81B50076A327 /* FeatureDefines.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = FeatureDefines.xcconfig; sourceTree = "<group>"; };
                451539B812DC994500EF7AC4 /* Yarr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Yarr.h; path = yarr/Yarr.h; sourceTree = "<group>"; };
                45E12D8806A49B0F00E9DF84 /* jsc.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = jsc.cpp; sourceTree = "<group>"; tabWidth = 4; };
                1432EBD70A34CAD400717B9F /* API */ = {
                        isa = PBXGroup;
                        children = (
+                               2A3D60CE188DD95F00C9C528 /* APICallbackFunction.h */,
                                1482B78A0A4305AB00517CFC /* APICast.h */,
                                865F408710E7D56300947361 /* APIShims.h */,
                                1CAA8B4A0D32C39A0041BCFF /* JavaScript.h */,
                                65C0285D1717966800351E35 /* ARMv7DOpcode.h in Headers */,
                                BC18C4260E16F5CD00B34460 /* JSRetainPtr.h in Headers */,
                                14874AE615EBDE4A002E3587 /* JSScope.h in Headers */,
+                               2A3D60CF188DD95F00C9C528 /* APICallbackFunction.h in Headers */,
                                A7C0C4AC168103020017011D /* JSScriptRefPrivate.h in Headers */,
                                0F919D11157F332C004A4E7D /* JSSegmentedVariableObject.h in Headers */,
                                BC18C45E0E16F5CD00B34460 /* JSStack.h in Headers */,
index 2033e05d3566e6c563c8c0cc194d024439563355..7c9e9228cda45d2a223a215c442cb0a5434436e3 100644 (file)
@@ -849,6 +849,8 @@ public:
     {
         moveToCachedReg(TrustedImmPtr(address), m_cachedMemoryTempRegister);
         m_assembler.ldrb(dest, memoryTempRegister, ARM64Registers::zr);
+        if (dest == memoryTempRegister)
+            m_cachedMemoryTempRegister.invalidate();
     }
 
     void load8Signed(BaseIndex address, RegisterID dest)
@@ -1516,8 +1518,8 @@ public:
 
     Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right)
     {
-        load32(left.m_ptr, getCachedMemoryTempRegisterIDAndInvalidate());
-        return branch32(cond, memoryTempRegister, right);
+        load32(left.m_ptr, getCachedDataTempRegisterIDAndInvalidate());
+        return branch32(cond, dataTempRegister, right);
     }
 
     Jump branch32(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right)
@@ -1554,8 +1556,8 @@ public:
 
     Jump branch64(RelationalCondition cond, AbsoluteAddress left, RegisterID right)
     {
-        load64(left.m_ptr, getCachedMemoryTempRegisterIDAndInvalidate());
-        return branch64(cond, memoryTempRegister, right);
+        load64(left.m_ptr, getCachedDataTempRegisterIDAndInvalidate());
+        return branch64(cond, dataTempRegister, right);
     }
 
     Jump branch64(RelationalCondition cond, Address left, RegisterID right)
@@ -2396,6 +2398,9 @@ private:
             intptr_t addressAsInt = reinterpret_cast<intptr_t>(address);
             intptr_t addressDelta = addressAsInt - currentRegisterContents;
 
+            if (dest == memoryTempRegister)
+                m_cachedMemoryTempRegister.invalidate();
+
             if (isInIntRange(addressDelta)) {
                 if (ARM64Assembler::canEncodeSImmOffset(addressDelta)) {
                     m_assembler.ldur<datasize>(dest,  memoryTempRegister, addressDelta);
@@ -2417,7 +2422,10 @@ private:
         }
 
         move(TrustedImmPtr(address), memoryTempRegister);
-        m_cachedMemoryTempRegister.setValue(reinterpret_cast<intptr_t>(address));
+        if (dest == memoryTempRegister)
+            m_cachedMemoryTempRegister.invalidate();
+        else
+            m_cachedMemoryTempRegister.setValue(reinterpret_cast<intptr_t>(address));
         m_assembler.ldr<datasize>(dest, memoryTempRegister, ARM64Registers::zr);
     }
 
index 200ed743c1184d9569da846f379dc6306cb22a38..be83f69c362afa796decc449bdbb116fd0dea81b 100644 (file)
@@ -1529,7 +1529,7 @@ bool AbstractState::executeEffects(unsigned indexInBlock, Node* node)
         // Again, sadly, we don't propagate the fact that we've done InstanceOf
         forNode(node).set(SpecBoolean);
         break;
-            
+
     case Phi:
     case Flush:
     case PhantomLocal:
@@ -1552,6 +1552,10 @@ bool AbstractState::executeEffects(unsigned indexInBlock, Node* node)
         forNode(node).makeTop();
         break;
 
+    case Unreachable:
+        RELEASE_ASSERT_NOT_REACHED();
+        break;
+
     case ForceOSRExit:
         node->setCanExit(true);
         m_isValid = false;
@@ -1777,8 +1781,7 @@ inline bool AbstractState::mergeToSuccessors(Graph& graph, BasicBlock* basicBloc
     }
         
     case Return:
-    case Throw:
-    case ThrowReferenceError:
+    case Unreachable:
         ASSERT(basicBlock->cfaBranchDirection == InvalidBranchDirection);
         return false;
         
index 06ce701fabb718a616fad5c6a77047766ea487dc..9b788566e3e48b6e288a818edcb4fd024cfa9168 100644 (file)
@@ -126,12 +126,9 @@ public:
         bool changed = false;
         
         // Record which arguments are known to escape no matter what.
-        for (unsigned i = codeBlock()->inlineCallFrames().size(); i--;) {
-            InlineCallFrame* inlineCallFrame = &codeBlock()->inlineCallFrames()[i];
-            if (m_graph.m_executablesWhoseArgumentsEscaped.contains(
-                    m_graph.executableFor(inlineCallFrame)))
-                m_createsArguments.add(inlineCallFrame);
-        }
+        for (unsigned i = codeBlock()->inlineCallFrames().size(); i--;)
+            pruneObviousArgumentCreations(&codeBlock()->inlineCallFrames()[i]);
+        pruneObviousArgumentCreations(0); // the machine call frame.
         
         // Create data for variable access datas that we will want to analyze.
         for (unsigned i = m_graph.m_variableAccessData.size(); i--;) {
@@ -703,6 +700,14 @@ private:
             NullableHashTraits<VariableAccessData*> > m_argumentsAliasing;
     HashSet<VariableAccessData*> m_isLive;
 
+    void pruneObviousArgumentCreations(InlineCallFrame* inlineCallFrame)
+    {
+        ScriptExecutable* executable = jsCast<ScriptExecutable*>(m_graph.executableFor(inlineCallFrame));
+        if (m_graph.m_executablesWhoseArgumentsEscaped.contains(executable)
+            || executable->isStrictMode())
+            m_createsArguments.add(inlineCallFrame);
+    }
+    
     void observeBadArgumentsUse(Node* node)
     {
         if (!node)
index a76d5f250135bb2ffe4c279dbe05fd731c0ea63d..c3e041c26c30cb3c6c189ce8dc32057ceefce575 100644 (file)
@@ -3031,13 +3031,15 @@ bool ByteCodeParser::parseBlock(unsigned limit)
             LAST_OPCODE(op_end);
 
         case op_throw:
-            flushAllArgumentsAndCapturedVariablesInInlineStack();
             addToGraph(Throw, get(currentInstruction[1].u.operand));
+            flushAllArgumentsAndCapturedVariablesInInlineStack();
+            addToGraph(Unreachable);
             LAST_OPCODE(op_throw);
             
         case op_throw_static_error:
-            flushAllArgumentsAndCapturedVariablesInInlineStack();
             addToGraph(ThrowReferenceError);
+            flushAllArgumentsAndCapturedVariablesInInlineStack();
+            addToGraph(Unreachable);
             LAST_OPCODE(op_throw_static_error);
             
         case op_call:
index 47af696a0c7d3debc12e7ad1657032182fd4e32d..0eb29fcafa8ede4d94aa7aac32d0b62f9bb9a94c 100644 (file)
@@ -254,9 +254,11 @@ private:
                 break;
             } 
             case PutScopedVar: {
-                if (node->child2() == registers && node->varNumber() == varNumber)
+                if (node->varNumber() != varNumber)
+                    break;
+                if (node->child2() == registers)
                     return node->child3().node();
-                break;
+                return 0;
             }
             case SetLocal: {
                 VariableAccessData* variableAccessData = node->variableAccessData();
@@ -327,9 +329,11 @@ private:
             Node* node = m_currentBlock->at(i);
             switch (node->op()) {
             case PutScopedVar: {
-                if (node->child1() == scope && node->child2() == registers && node->varNumber() == varNumber)
+                if (node->varNumber() != varNumber)
+                    break;
+                if (node->child1() == scope && node->child2() == registers)
                     return node;
-                break;
+                return 0;
             }
                 
             case GetScopedVar: {
index 39ac2ff7ac4b55a041c1086f277ec43e230fddea..ad699e7fbc1cb84575d9794f8eda49e78f4e31e3 100644 (file)
@@ -434,8 +434,7 @@ private:
             Node* node = block->at(indexInBlock);
             switch (node->op()) {
             case Return:
-            case Throw:
-            case ThrowReferenceError:
+            case Unreachable:
             case ForceOSRExit:
                 // Do nothing. These nodes will already do the right thing.
                 break;
index 0f280b153f0d861b258e9cc29d67cb95175fa0a2..f8b8fcb2ed81e2faa3dfd8fec35517f0ee3bbb44 100644 (file)
@@ -120,6 +120,7 @@ private:
         case ValueToInt32: {
             if (node->child1()->shouldSpeculateInteger()) {
                 setUseKindAndUnboxIfProfitable<Int32Use>(node->child1());
+                node->setOpAndDefaultFlags(Identity);
                 break;
             }
             
@@ -873,6 +874,7 @@ private:
         case CountExecution:
         case ForceOSRExit:
         case CheckWatchdogTimer:
+        case Unreachable:
             break;
 #else
         default:
index fdfa2941bb0e22bf4039997fdd3afe1ecf585805..03c9c2d5ab98f43ff9ac350eb3e2058335a7fc61 100644 (file)
@@ -676,8 +676,7 @@ struct Node {
         case Jump:
         case Branch:
         case Return:
-        case Throw:
-        case ThrowReferenceError:
+        case Unreachable:
             return true;
         default:
             return false;
index 9039e3f5fa03d5395e037dece3abe87844ce3c18..2a4707377b8205327aef7c9f887c82d9891ccef1 100644 (file)
@@ -250,12 +250,15 @@ namespace JSC { namespace DFG {
     macro(NewFunction, NodeResultJS) \
     macro(NewFunctionExpression, NodeResultJS) \
     \
+    /* These aren't terminals but always exit */ \
+    macro(Throw, NodeMustGenerate) \
+    macro(ThrowReferenceError, NodeMustGenerate) \
+    \
     /* Block terminals. */\
     macro(Jump, NodeMustGenerate) \
     macro(Branch, NodeMustGenerate) \
     macro(Return, NodeMustGenerate) \
-    macro(Throw, NodeMustGenerate) \
-    macro(ThrowReferenceError, NodeMustGenerate) \
+    macro(Unreachable, NodeMustGenerate) \
     \
     macro(GarbageValue, NodeResultJS | NodeClobbersWorld) \
     \
index 356adb5ec3e33ca270f732561b0cf90f06b165e1..ddcc313d6340c0cca8660ef67b8dcdc87215d9de 100644 (file)
@@ -539,6 +539,7 @@ private:
         case PutGlobalVar:
         case PutGlobalVarCheck:
         case CheckWatchdogTimer:
+        case Unreachable:
             break;
             
         // These gets ignored because it doesn't do anything.
index 0ab170d8ecddb2544eaef212016bc6a3100e2e4b..d31749506e1f403e023160dd88859a3a8a35604b 100644 (file)
@@ -1106,6 +1106,10 @@ GPRReg SpeculativeJIT::fillSpeculateCell(Edge edge)
 
     switch (info.registerFormat()) {
     case DataFormatNone: {
+        if (info.spillFormat() == DataFormatInteger || info.spillFormat() == DataFormatDouble) {
+            terminateSpeculativeExecution(Uncountable, JSValueRegs(), 0);
+            return allocate();
+        }
 
         if (edge->hasConstant()) {
             JSValue jsValue = valueOfJSConstant(edge.node());
@@ -4401,7 +4405,7 @@ void SpeculativeJIT::compile(Node* node)
 
         JITCompiler::Jump isNotCell = m_jit.branch32(JITCompiler::NotEqual, tagGPR, JITCompiler::TrustedImm32(JSValue::CellTag));
         if (node->child1().useKind() != UntypedUse)
-            speculationCheck(BadType, JSValueRegs(tagGPR, payloadGPR), node->child1(), isNotCell);
+            DFG_TYPE_CHECK(JSValueRegs(tagGPR, payloadGPR), node->child1(), SpecCell, isNotCell);
 
         if (!node->child1()->shouldSpeculateObject() || node->child1().useKind() == StringUse) {
             m_jit.loadPtr(JITCompiler::Address(payloadGPR, JSCell::structureOffset()), tempGPR);
@@ -4957,6 +4961,10 @@ void SpeculativeJIT::compile(Node* node)
         noResult(node);
         break;
 
+    case Unreachable:
+        RELEASE_ASSERT_NOT_REACHED();
+        break;
+
     case Nop:
     case LastNodeType:
         RELEASE_ASSERT_NOT_REACHED();
index bf5361e8db0421e2cc96d877b34dda7e0d6d7d3e..17852587caf5903623c72714a0984928c4737f93 100644 (file)
@@ -984,8 +984,6 @@ FPRReg SpeculativeJIT::fillSpeculateDouble(Edge edge)
                 m_jit.move64ToDouble(gpr, fpr);
                 unlock(gpr);
 
-                m_fprs.retain(fpr, virtualRegister, SpillOrderDouble);
-                info.fillDouble(*m_stream, fpr);
                 return fpr;
             }
             if (isNumberConstant(edge.node())) {
@@ -4798,6 +4796,10 @@ void SpeculativeJIT::compile(Node* node)
         // This is a no-op.
         noResult(node);
         break;
+
+    case Unreachable:
+        RELEASE_ASSERT_NOT_REACHED();
+        break;
         
     case Nop:
         RELEASE_ASSERT_NOT_REACHED();
index 4273a28d658bb75ffdbb7959f5acd5e593e4d594..da338ce11e66e284899731330f969f4cfb113f21 100644 (file)
@@ -174,6 +174,7 @@ inline void SlotVisitor::donateAndDrain()
 
 inline void SlotVisitor::copyLater(JSCell* owner, void* ptr, size_t bytes)
 {
+    ASSERT(bytes);
     CopiedBlock* block = CopiedSpace::blockFor(ptr);
     if (block->isOversize()) {
         m_shared.m_copiedSpace->pin(block);
index 87a9c2b56aa24d69f376858274db4a8a189bfc57..14e4cc63d16050472e8345c6e7207195eb975c3e 100644 (file)
@@ -765,7 +765,20 @@ NEVER_INLINE HandlerInfo* Interpreter::throwException(CallFrame*& callFrame, JSV
 
     if (Debugger* debugger = callFrame->dynamicGlobalObject()->debugger()) {
         DebuggerCallFrame debuggerCallFrame(callFrame, exceptionValue);
-        bool hasHandler = codeBlock->handlerForBytecodeOffset(bytecodeOffset);
+        bool hasHandler = false;
+        if (!isTermination) {
+            VM* vm = &callFrame->vm();
+            CallFrame* currentFrame = callFrame;
+            CodeBlock* currentCB = codeBlock;
+            unsigned currentOffset = bytecodeOffset;
+            while (currentFrame) {
+                if (currentCB && currentCB->handlerForBytecodeOffset(currentOffset)) {
+                    hasHandler = true;
+                    break;
+                }
+                currentFrame = getCallerInfo(vm, currentFrame, currentOffset, currentCB);
+            }
+        }
         debugger->exception(debuggerCallFrame, codeBlock->ownerExecutable()->sourceID(), codeBlock->lineNumberForBytecodeOffset(bytecodeOffset), 0, hasHandler);
     }
 
index 47f6eea0927aac2a04cff958a2770cb95af1e207..b10a0787cf09d608a9b5ed88fe24038994aa390c 100644 (file)
@@ -521,7 +521,7 @@ inline ResolveNode::ResolveNode(const JSTokenLocation& location, const Identifie
     }
 
     inline LogicalOpNode::LogicalOpNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, LogicalOperator oper)
-        : ExpressionNode(location, ResultType::booleanType())
+        : ExpressionNode(location, ResultType::forLogicalOp(expr1->resultDescriptor(), expr2->resultDescriptor()))
         , m_expr1(expr1)
         , m_expr2(expr2)
         , m_operator(oper)
index de4bde66ba52b0dc317a79d2f636b7fe9e465e55..ad86c98c7ff4ed134708270c00b369793c0952a3 100644 (file)
@@ -120,7 +120,20 @@ namespace JSC {
                 return stringType();
             return stringOrNumberType();
         }
-        
+
+        // Unlike in C, a logical op produces the value of the
+        // last expression evaluated (and not true or false).
+        static ResultType forLogicalOp(ResultType op1, ResultType op2)
+        {
+            if (op1.definitelyIsBoolean() && op2.definitelyIsBoolean())
+                return booleanType();
+            if (op1.definitelyIsNumber() && op2.definitelyIsNumber())
+                return numberType();
+            if (op1.definitelyIsString() && op2.definitelyIsString())
+                return stringType();
+            return unknownType();
+        }
+
         static ResultType forBitOp()
         {
             return numberTypeIsInt32();
index 3177c6c971eebdbd5f9d414ed914f34ea9ab074d..e5ef963360bca1bde1232226a6edea3810ae5a81 100644 (file)
@@ -71,6 +71,8 @@ namespace JSC {
 // is added.
 #define FIRST_VECTOR_GROW 4U
 
+#define MIN_BEYOND_LENGTH_SPARSE_INDEX 1000
+
 // Our policy for when to use a vector and when to use a sparse map.
 // For all array indices under MIN_SPARSE_ARRAY_INDEX, we always use a vector.
 // When indices greater than MIN_SPARSE_ARRAY_INDEX are involved, we use a vector
@@ -82,6 +84,11 @@ inline bool isDenseEnoughForVector(unsigned length, unsigned numValues)
     return length / minDensityMultiplier <= numValues;
 }
 
+inline bool indexIsSufficientlyBeyondLengthForSparseMap(unsigned i, unsigned length)
+{
+    return i >= MIN_BEYOND_LENGTH_SPARSE_INDEX && i > length;
+}
+
 inline IndexingHeader indexingHeaderForArray(unsigned length, unsigned vectorLength)
 {
     IndexingHeader result;
index f97ccedcd0d7a9153bce7801d606eab17169e412..2527b1466b136d269aa77d558fdd70f632aa1f58 100644 (file)
@@ -755,21 +755,21 @@ bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex
         // so only if it's not horribly slow.
         if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX)
             return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->vm()));
-        
+
+        // Storing to a hole is fine since we're still having a good time. But reading from a hole 
+        // is totally not fine, since we might have to read from the proto chain.
+        // We have to check for holes before we start moving things around so that we don't get halfway 
+        // through shifting and then realize we should have been in ArrayStorage mode.
         unsigned end = oldLength - count;
         for (unsigned i = startIndex; i < end; ++i) {
-            // Storing to a hole is fine since we're still having a good time. But reading
-            // from a hole is totally not fine, since we might have to read from the proto
-            // chain.
             JSValue v = m_butterfly->contiguous()[i + count].get();
-            if (UNLIKELY(!v)) {
-                // The purpose of this path is to ensure that we don't make the same
-                // mistake in the future: shiftCountWithArrayStorage() can't do anything
-                // about holes (at least for now), but it can detect them quickly. So
-                // we convert to array storage and then allow the array storage path to
-                // figure it out.
+            if (UNLIKELY(!v))
                 return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->vm()));
-            }
+        }
+
+        for (unsigned i = startIndex; i < end; ++i) {
+            JSValue v = m_butterfly->contiguous()[i + count].get();
+            ASSERT(v);
             // No need for a barrier since we're just moving data around in the same vector.
             // This is in line with our standing assumption that we won't have a deletion
             // barrier.
@@ -790,21 +790,21 @@ bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex
         // so only if it's not horribly slow.
         if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX)
             return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->vm()));
-        
+
+        // Storing to a hole is fine since we're still having a good time. But reading from a hole 
+        // is totally not fine, since we might have to read from the proto chain.
+        // We have to check for holes before we start moving things around so that we don't get halfway 
+        // through shifting and then realize we should have been in ArrayStorage mode.
         unsigned end = oldLength - count;
         for (unsigned i = startIndex; i < end; ++i) {
-            // Storing to a hole is fine since we're still having a good time. But reading
-            // from a hole is totally not fine, since we might have to read from the proto
-            // chain.
             double v = m_butterfly->contiguousDouble()[i + count];
-            if (UNLIKELY(v != v)) {
-                // The purpose of this path is to ensure that we don't make the same
-                // mistake in the future: shiftCountWithArrayStorage() can't do anything
-                // about holes (at least for now), but it can detect them quickly. So
-                // we convert to array storage and then allow the array storage path to
-                // figure it out.
+            if (UNLIKELY(v != v))
                 return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->vm()));
-            }
+        }
+            
+        for (unsigned i = startIndex; i < end; ++i) {
+            double v = m_butterfly->contiguousDouble()[i + count];
+            ASSERT(v == v);
             // No need for a barrier since we're just moving data around in the same vector.
             // This is in line with our standing assumption that we won't have a deletion
             // barrier.
@@ -889,11 +889,18 @@ bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startInd
             return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
         
         ensureLength(exec->vm(), oldLength + count);
-        
+
+        // We have to check for holes before we start moving things around so that we don't get halfway 
+        // through shifting and then realize we should have been in ArrayStorage mode.
         for (unsigned i = oldLength; i-- > startIndex;) {
             JSValue v = m_butterfly->contiguous()[i].get();
             if (UNLIKELY(!v))
                 return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
+        }
+
+        for (unsigned i = oldLength; i-- > startIndex;) {
+            JSValue v = m_butterfly->contiguous()[i].get();
+            ASSERT(v);
             m_butterfly->contiguous()[i + count].setWithoutWriteBarrier(v);
         }
         
@@ -915,10 +922,17 @@ bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startInd
         
         ensureLength(exec->vm(), oldLength + count);
         
+        // We have to check for holes before we start moving things around so that we don't get halfway 
+        // through shifting and then realize we should have been in ArrayStorage mode.
         for (unsigned i = oldLength; i-- > startIndex;) {
             double v = m_butterfly->contiguousDouble()[i];
             if (UNLIKELY(v != v))
                 return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
+        }
+
+        for (unsigned i = oldLength; i-- > startIndex;) {
+            double v = m_butterfly->contiguousDouble()[i];
+            ASSERT(v == v);
             m_butterfly->contiguousDouble()[i + count] = v;
         }
         
index a39daff80d795996cb79119aaf53b838ba549351..d18b4e244960586e4544f6c93c0979eedb185176 100644 (file)
@@ -595,7 +595,7 @@ void JSObject::notifyPresenceOfIndexedAccessors(VM& vm)
     if (mayInterceptIndexedAccesses())
         return;
     
-    setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AddIndexedAccessors));
+    setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AddIndexedAccessors), m_butterfly);
     
     if (!vm.prototypeMap.isPrototype(this))
         return;
@@ -681,7 +681,7 @@ ArrayStorage* JSObject::createInitialArrayStorage(VM& vm)
 ContiguousJSValues JSObject::convertUndecidedToInt32(VM& vm)
 {
     ASSERT(hasUndecided(structure()->indexingType()));
-    setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateInt32));
+    setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateInt32), m_butterfly);
     return m_butterfly->contiguousInt32();
 }
 
@@ -692,14 +692,14 @@ ContiguousDoubles JSObject::convertUndecidedToDouble(VM& vm)
     for (unsigned i = m_butterfly->vectorLength(); i--;)
         m_butterfly->contiguousDouble()[i] = QNaN;
     
-    setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateDouble));
+    setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateDouble), m_butterfly);
     return m_butterfly->contiguousDouble();
 }
 
 ContiguousJSValues JSObject::convertUndecidedToContiguous(VM& vm)
 {
     ASSERT(hasUndecided(structure()->indexingType()));
-    setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateContiguous));
+    setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateContiguous), m_butterfly);
     return m_butterfly->contiguous();
 }
 
@@ -765,7 +765,7 @@ ContiguousDoubles JSObject::convertInt32ToDouble(VM& vm)
         *currentAsDouble = v.asInt32();
     }
     
-    setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateDouble));
+    setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateDouble), m_butterfly);
     return m_butterfly->contiguousDouble();
 }
 
@@ -773,7 +773,7 @@ ContiguousJSValues JSObject::convertInt32ToContiguous(VM& vm)
 {
     ASSERT(hasInt32(structure()->indexingType()));
     
-    setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateContiguous));
+    setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateContiguous), m_butterfly);
     return m_butterfly->contiguous();
 }
 
@@ -831,7 +831,7 @@ ContiguousJSValues JSObject::genericConvertDoubleToContiguous(VM& vm)
         currentAsValue->setWithoutWriteBarrier(v);
     }
     
-    setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateContiguous));
+    setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateContiguous), m_butterfly);
     return m_butterfly->contiguous();
 }
 
@@ -1129,7 +1129,7 @@ void JSObject::switchToSlowPutArrayStorage(VM& vm)
     case NonArrayWithArrayStorage:
     case ArrayWithArrayStorage: {
         Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), SwitchToSlowPutArrayStorage);
-        setStructure(vm, newStructure);
+        setStructure(vm, newStructure, m_butterfly);
         break;
     }
         
@@ -1153,7 +1153,7 @@ void JSObject::setPrototype(VM& vm, JSValue prototype)
         vm.prototypeMap.addPrototype(asObject(prototype));
     
     Structure* newStructure = Structure::changePrototypeTransition(vm, structure(), prototype);
-    setStructure(vm, newStructure);
+    setStructure(vm, newStructure, m_butterfly);
     
     if (!newStructure->anyObjectInChainMayInterceptIndexedAccesses())
         return;
@@ -1213,7 +1213,7 @@ void JSObject::putDirectAccessor(ExecState* exec, PropertyName propertyName, JSV
     // getters and setters, though, we also need to change our Structure
     // if we override an existing non-getter or non-setter.
     if (slot.type() != PutPropertySlot::NewProperty)
-        setStructure(vm, Structure::attributeChangeTransition(vm, structure(), propertyName, attributes));
+        setStructure(vm, Structure::attributeChangeTransition(vm, structure(), propertyName, attributes), m_butterfly);
 
     if (attributes & ReadOnly)
         structure()->setContainsReadOnlyProperties();
@@ -1570,7 +1570,7 @@ void JSObject::seal(VM& vm)
     if (isSealed(vm))
         return;
     preventExtensions(vm);
-    setStructure(vm, Structure::sealTransition(vm, structure()));
+    setStructure(vm, Structure::sealTransition(vm, structure()), m_butterfly);
 }
 
 void JSObject::freeze(VM& vm)
@@ -1578,14 +1578,14 @@ void JSObject::freeze(VM& vm)
     if (isFrozen(vm))
         return;
     preventExtensions(vm);
-    setStructure(vm, Structure::freezeTransition(vm, structure()));
+    setStructure(vm, Structure::freezeTransition(vm, structure()), m_butterfly);
 }
 
 void JSObject::preventExtensions(VM& vm)
 {
     enterDictionaryIndexingMode(vm);
     if (isExtensible())
-        setStructure(vm, Structure::preventExtensionsTransition(vm, structure()));
+        setStructure(vm, Structure::preventExtensionsTransition(vm, structure()), m_butterfly);
 }
 
 // This presently will flatten to an uncachable dictionary; this is suitable
@@ -1603,7 +1603,7 @@ void JSObject::reifyStaticFunctionsForDelete(ExecState* exec)
     }
 
     if (!structure()->isUncacheableDictionary())
-        setStructure(vm, Structure::toUncacheableDictionaryTransition(vm, structure()));
+        setStructure(vm, Structure::toUncacheableDictionaryTransition(vm, structure()), m_butterfly);
 
     for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
         const HashTable* hashTable = info->propHashTable(globalObject()->globalExec());
@@ -1633,7 +1633,7 @@ bool JSObject::removeDirect(VM& vm, PropertyName propertyName)
         return true;
     }
 
-    setStructure(vm, Structure::removePropertyTransition(vm, structure(), propertyName, offset));
+    setStructure(vm, Structure::removePropertyTransition(vm, structure(), propertyName, offset), m_butterfly);
     if (offset == invalidOffset)
         return false;
     putDirectUndefined(offset);
@@ -1872,7 +1872,8 @@ void JSObject::putByIndexBeyondVectorLengthWithoutAttributes(ExecState* exec, un
     
     if (i >= MAX_ARRAY_INDEX - 1
         || (i >= MIN_SPARSE_ARRAY_INDEX
-            && !isDenseEnoughForVector(i, countElements<indexingShape>(m_butterfly)))) {
+            && !isDenseEnoughForVector(i, countElements<indexingShape>(m_butterfly)))
+        || indexIsSufficientlyBeyondLengthForSparseMap(i, m_butterfly->vectorLength())) {
         ASSERT(i <= MAX_ARRAY_INDEX);
         ensureArrayStorageSlow(vm);
         SparseArrayValueMap* map = allocateSparseIndexMap(vm);
@@ -1920,7 +1921,7 @@ void JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, uns
     
     // First, handle cases where we don't currently have a sparse map.
     if (LIKELY(!map)) {
-        // If the array is not extensible, we should have entered dictionary mode, and created the spare map.
+        // If the array is not extensible, we should have entered dictionary mode, and created the sparse map.
         ASSERT(isExtensible());
     
         // Update m_length if necessary.
@@ -1928,7 +1929,9 @@ void JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, uns
             storage->setLength(i + 1);
 
         // Check that it is sensible to still be using a vector, and then try to grow the vector.
-        if (LIKELY((isDenseEnoughForVector(i, storage->m_numValuesInVector)) && increaseVectorLength(vm, i + 1))) {
+        if (LIKELY(!indexIsSufficientlyBeyondLengthForSparseMap(i, storage->vectorLength()) 
+            && isDenseEnoughForVector(i, storage->m_numValuesInVector)
+            && increaseVectorLength(vm, i + 1))) {
             // success! - reread m_storage since it has likely been reallocated, and store to the vector.
             storage = arrayStorage();
             storage->m_vector[i].set(vm, this, value);
@@ -1995,7 +1998,7 @@ void JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue
                 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
             break;
         }
-        if (i >= MIN_SPARSE_ARRAY_INDEX) {
+        if (indexIsSufficientlyBeyondLengthForSparseMap(i, 0) || i >= MIN_SPARSE_ARRAY_INDEX) {
             putByIndexBeyondVectorLengthWithArrayStorage(
                 exec, i, value, shouldThrow, createArrayStorage(vm, 0, 0));
             break;
@@ -2075,7 +2078,8 @@ bool JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState* exec,
         if (LIKELY(
                 !attributes
                 && (isDenseEnoughForVector(i, storage->m_numValuesInVector))
-                && increaseVectorLength(vm, i + 1))) {
+                && !indexIsSufficientlyBeyondLengthForSparseMap(i, storage->vectorLength()))
+                && increaseVectorLength(vm, i + 1)) {
             // success! - reread m_storage since it has likely been reallocated, and store to the vector.
             storage = arrayStorage();
             storage->m_vector[i].set(vm, this, value);
index c62dc2aeca4d6fc3baf9004fe088a2cd4cc6d6af..7a78a46a6fd70040e53ee1c3c4c48fcba7aa1fd2 100644 (file)
@@ -595,6 +595,7 @@ public:
     void setButterfly(VM&, Butterfly*, Structure*);
     void setButterflyWithoutChangingStructure(Butterfly*); // You probably don't want to call this.
         
+    void setStructure(VM&, Structure*, Butterfly* = 0);
     void setStructureAndReallocateStorageIfNecessary(VM&, unsigned oldCapacity, Structure*);
     void setStructureAndReallocateStorageIfNecessary(VM&, Structure*);
 
@@ -1109,7 +1110,7 @@ inline void JSObject::setButterfly(VM& vm, Butterfly* butterfly, Structure* stru
 {
     ASSERT(structure);
     ASSERT(!butterfly == (!structure->outOfLineCapacity() && !hasIndexingHeader(structure->indexingType())));
-    setStructure(vm, structure);
+    setStructure(vm, structure, butterfly);
     m_butterfly = butterfly;
 }
 
@@ -1316,7 +1317,7 @@ inline bool JSObject::putDirectInternal(VM& vm, PropertyName propertyName, JSVal
                 return true;
             }
             // case (2) Despecify, fall through to (3).
-            setStructure(vm, Structure::despecifyFunctionTransition(vm, structure(), propertyName));
+            setStructure(vm, Structure::despecifyFunctionTransition(vm, structure(), propertyName), m_butterfly);
         }
 
         // case (3) set the slot, do the put, return.
@@ -1344,12 +1345,18 @@ inline bool JSObject::putDirectInternal(VM& vm, PropertyName propertyName, JSVal
     return true;
 }
 
+inline void JSObject::setStructure(VM& vm, Structure* structure, Butterfly* butterfly)
+{
+    JSCell::setStructure(vm, structure);
+    ASSERT_UNUSED(butterfly, !butterfly == !(structure->outOfLineCapacity() || hasIndexingHeader(structure->indexingType())));
+}
+
 inline void JSObject::setStructureAndReallocateStorageIfNecessary(VM& vm, unsigned oldCapacity, Structure* newStructure)
 {
     ASSERT(oldCapacity <= newStructure->outOfLineCapacity());
     
     if (oldCapacity == newStructure->outOfLineCapacity()) {
-        setStructure(vm, newStructure);
+        setStructure(vm, newStructure, m_butterfly);
         return;
     }
     
index 69ff1e47808b029d9f1f66d52c5ece508b9868e0..7ad566550b356bd1dcf64a908dab2786320c64f0 100644 (file)
@@ -301,6 +301,14 @@ template <JSScope::LookupMode mode, JSScope::ReturnValues returnValues> JSObject
                             operations->append(ResolveOperation::checkForDynamicEntriesBeforeGlobalScope());
 
                         if (putToBaseOperation) {
+                            unsigned currentAttributes;
+                            JSCell* currentSpecificFunction;
+                            PropertyOffset offset = globalObject->structure()->get(callFrame->vm(), identifier, currentAttributes, currentSpecificFunction);
+                            ASSERT_UNUSED(offset, offset != invalidOffset);
+                            ASSERT_UNUSED(offset, offset == slot.cachedOffset());
+                            // We just assume that we are clobbering the global specialisation
+                            if (currentSpecificFunction)
+                                globalObject->setStructure(callFrame->vm(), Structure::despecifyFunctionTransition(callFrame->vm(), globalObject->structure(), identifier));
                             putToBaseOperation->m_isDynamic = requiresDynamicChecks;
                             putToBaseOperation->m_kind = PutToBaseOperation::GlobalPropertyPut;
                             putToBaseOperation->m_structure.set(callFrame->vm(), callFrame->codeBlock()->ownerExecutable(), globalObject->structure());
index af747b6a99bc2024594987c809d47c3cbc8365c6..c7c9420529ee27936b249bd9cf29532a904b817f 100644 (file)
@@ -44,7 +44,7 @@ namespace JSC {
 
         static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
         {
-            return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info, ArrayWithArrayStorage);
+            return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info, ArrayWithSlowPutArrayStorage);
         }
 
         static void visitChildren(JSCell*, SlotVisitor&);
index f551eaeccc9ec4926aa1f71116fc41295669373e..950728cca637b3be41582820e6c7f023dcbb3dbe 100644 (file)
@@ -649,6 +649,12 @@ Structure* Structure::flattenDictionaryStructure(VM& vm, JSObject* object)
     }
 
     m_dictionaryKind = NoneDictionaryKind;
+
+    // If the object had a Butterfly but after flattening/compacting we no longer have need of it,
+    // we need to zero it out because the collector depends on the Structure to know the size for copying.
+    if (object->butterfly() && !this->outOfLineCapacity() && !hasIndexingHeader(this->indexingType()))
+        object->setButterfly(vm, 0, this);
+
     return this;
 }