]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - API/tests/testapi.c
JavaScriptCore-1218.35.tar.gz
[apple/javascriptcore.git] / API / tests / testapi.c
index 48c8583b60f8e2582f4e144318ad907d419c33ed..83c605cf45804c6a77a3def1f93be345902aa908 100644 (file)
 
 #include "JavaScriptCore.h"
 #include "JSBasePrivate.h"
+#include "JSContextRefPrivate.h"
+#include "JSObjectRefPrivate.h"
+#include "JSScriptRefPrivate.h"
+#include "JSStringRefPrivate.h"
 #include <math.h>
+#define ASSERT_DISABLED 0
 #include <wtf/Assertions.h>
-#include <wtf/UnusedParam.h>
+
+#if PLATFORM(MAC) || PLATFORM(IOS)
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <sys/time.h>
+#endif
+
+#if OS(WINDOWS)
+#include <windows.h>
+#endif
 
 #if COMPILER(MSVC)
 
@@ -38,14 +52,25 @@ static double nan(const char*)
     return std::numeric_limits<double>::quiet_NaN();
 }
 
+using std::isinf;
+using std::isnan;
+
+#endif
+
+#if JSC_OBJC_API_ENABLED
+void testObjectiveCAPI(void);
 #endif
 
-static JSGlobalContextRef context = 0;
+extern void JSSynchronousGarbageCollectForDebugging(JSContextRef);
 
+static JSGlobalContextRef context;
+int failed;
 static void assertEqualsAsBoolean(JSValueRef value, bool expectedValue)
 {
-    if (JSValueToBoolean(context, value) != expectedValue)
+    if (JSValueToBoolean(context, value) != expectedValue) {
         fprintf(stderr, "assertEqualsAsBoolean failed: %p, %d\n", value, expectedValue);
+        failed = 1;
+    }
 }
 
 static void assertEqualsAsNumber(JSValueRef value, double expectedValue)
@@ -55,8 +80,10 @@ static void assertEqualsAsNumber(JSValueRef value, double expectedValue)
     // FIXME <rdar://4668451> - On i386 the isnan(double) macro tries to map to the isnan(float) function,
     // causing a build break with -Wshorten-64-to-32 enabled.  The issue is known by the appropriate team.
     // After that's resolved, we can remove these casts
-    if (number != expectedValue && !(isnan((float)number) && isnan((float)expectedValue)))
+    if (number != expectedValue && !(isnan((float)number) && isnan((float)expectedValue))) {
         fprintf(stderr, "assertEqualsAsNumber failed: %p, %lf\n", value, expectedValue);
+        failed = 1;
+    }
 }
 
 static void assertEqualsAsUTF8String(JSValueRef value, const char* expectedValue)
@@ -68,12 +95,17 @@ static void assertEqualsAsUTF8String(JSValueRef value, const char* expectedValue
     JSStringGetUTF8CString(valueAsString, jsBuffer, jsSize);
     
     unsigned i;
-    for (i = 0; jsBuffer[i]; i++)
-        if (jsBuffer[i] != expectedValue[i])
+    for (i = 0; jsBuffer[i]; i++) {
+        if (jsBuffer[i] != expectedValue[i]) {
             fprintf(stderr, "assertEqualsAsUTF8String failed at character %d: %c(%d) != %c(%d)\n", i, jsBuffer[i], jsBuffer[i], expectedValue[i], expectedValue[i]);
-        
-    if (jsSize < strlen(jsBuffer) + 1)
+            failed = 1;
+        }
+    }
+
+    if (jsSize < strlen(jsBuffer) + 1) {
         fprintf(stderr, "assertEqualsAsUTF8String failed: jsSize was too small\n");
+        failed = 1;
+    }
 
     free(jsBuffer);
     JSStringRelease(valueAsString);
@@ -94,16 +126,30 @@ static void assertEqualsAsCharactersPtr(JSValueRef value, const char* expectedVa
     CFStringGetCharacters(expectedValueAsCFString, CFRangeMake(0, cfLength), cfBuffer);
     CFRelease(expectedValueAsCFString);
 
-    if (memcmp(jsBuffer, cfBuffer, cfLength * sizeof(UniChar)) != 0)
+    if (memcmp(jsBuffer, cfBuffer, cfLength * sizeof(UniChar)) != 0) {
         fprintf(stderr, "assertEqualsAsCharactersPtr failed: jsBuffer != cfBuffer\n");
+        failed = 1;
+    }
     
-    if (jsLength != (size_t)cfLength)
+    if (jsLength != (size_t)cfLength) {
         fprintf(stderr, "assertEqualsAsCharactersPtr failed: jsLength(%ld) != cfLength(%ld)\n", jsLength, cfLength);
-    
+        failed = 1;
+    }
+
     free(cfBuffer);
     JSStringRelease(valueAsString);
 }
 
+static bool timeZoneIsPST()
+{
+    char timeZoneName[70];
+    struct tm gtm;
+    memset(&gtm, 0, sizeof(gtm));
+    strftime(timeZoneName, sizeof(timeZoneName), "%Z", &gtm);
+
+    return 0 == strcmp("PST", timeZoneName);
+}
+
 static JSValueRef jsGlobalValue; // non-stack value for testing JSValueProtect()
 
 /* MyObject pseudo-class */
@@ -115,6 +161,7 @@ static bool MyObject_hasProperty(JSContextRef context, JSObjectRef object, JSStr
 
     if (JSStringIsEqualToUTF8CString(propertyName, "alwaysOne")
         || JSStringIsEqualToUTF8CString(propertyName, "cantFind")
+        || JSStringIsEqualToUTF8CString(propertyName, "throwOnGet")
         || JSStringIsEqualToUTF8CString(propertyName, "myPropertyName")
         || JSStringIsEqualToUTF8CString(propertyName, "hasPropertyLie")
         || JSStringIsEqualToUTF8CString(propertyName, "0")) {
@@ -141,12 +188,20 @@ static JSValueRef MyObject_getProperty(JSContextRef context, JSObjectRef object,
         return JSValueMakeUndefined(context);
     }
     
+    if (JSStringIsEqualToUTF8CString(propertyName, "hasPropertyLie")) {
+        return 0;
+    }
+
+    if (JSStringIsEqualToUTF8CString(propertyName, "throwOnGet")) {
+        return JSEvaluateScript(context, JSStringCreateWithUTF8CString("throw 'an exception'"), object, JSStringCreateWithUTF8CString("test script"), 1, exception);
+    }
+
     if (JSStringIsEqualToUTF8CString(propertyName, "0")) {
         *exception = JSValueMakeNumber(context, 1);
         return JSValueMakeNumber(context, 1);
     }
     
-    return NULL;
+    return JSValueMakeNull(context);
 }
 
 static bool MyObject_setProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
@@ -159,6 +214,10 @@ static bool MyObject_setProperty(JSContextRef context, JSObjectRef object, JSStr
     if (JSStringIsEqualToUTF8CString(propertyName, "cantSet"))
         return true; // pretend we set the property in order to swallow it
     
+    if (JSStringIsEqualToUTF8CString(propertyName, "throwOnSet")) {
+        JSEvaluateScript(context, JSStringCreateWithUTF8CString("throw 'an exception'"), object, JSStringCreateWithUTF8CString("test script"), 1, exception);
+    }
+    
     return false;
 }
 
@@ -171,7 +230,7 @@ static bool MyObject_deleteProperty(JSContextRef context, JSObjectRef object, JS
         return true;
     
     if (JSStringIsEqualToUTF8CString(propertyName, "throwOnDelete")) {
-        *exception = JSValueMakeNumber(context, 2);
+        JSEvaluateScript(context, JSStringCreateWithUTF8CString("throw 'an exception'"), object, JSStringCreateWithUTF8CString("test script"), 1, exception);
         return false;
     }
 
@@ -201,6 +260,11 @@ static JSValueRef MyObject_callAsFunction(JSContextRef context, JSObjectRef obje
     UNUSED_PARAM(thisObject);
     UNUSED_PARAM(exception);
 
+    if (argumentCount > 0 && JSValueIsString(context, arguments[0]) && JSStringIsEqualToUTF8CString(JSValueToStringCopy(context, arguments[0], 0), "throwOnCall")) {
+        JSEvaluateScript(context, JSStringCreateWithUTF8CString("throw 'an exception'"), object, JSStringCreateWithUTF8CString("test script"), 1, exception);
+        return JSValueMakeUndefined(context);
+    }
+
     if (argumentCount > 0 && JSValueIsStrictEqual(context, arguments[0], JSValueMakeNumber(context, 0)))
         return JSValueMakeNumber(context, 1);
     
@@ -212,6 +276,11 @@ static JSObjectRef MyObject_callAsConstructor(JSContextRef context, JSObjectRef
     UNUSED_PARAM(context);
     UNUSED_PARAM(object);
 
+    if (argumentCount > 0 && JSValueIsString(context, arguments[0]) && JSStringIsEqualToUTF8CString(JSValueToStringCopy(context, arguments[0], 0), "throwOnConstruct")) {
+        JSEvaluateScript(context, JSStringCreateWithUTF8CString("throw 'an exception'"), object, JSStringCreateWithUTF8CString("test script"), 1, exception);
+        return object;
+    }
+
     if (argumentCount > 0 && JSValueIsStrictEqual(context, arguments[0], JSValueMakeNumber(context, 0)))
         return JSValueToObject(context, JSValueMakeNumber(context, 1), exception);
     
@@ -223,6 +292,11 @@ static bool MyObject_hasInstance(JSContextRef context, JSObjectRef constructor,
     UNUSED_PARAM(context);
     UNUSED_PARAM(constructor);
 
+    if (JSValueIsString(context, possibleValue) && JSStringIsEqualToUTF8CString(JSValueToStringCopy(context, possibleValue, 0), "throwOnHasInstance")) {
+        JSEvaluateScript(context, JSStringCreateWithUTF8CString("throw 'an exception'"), constructor, JSStringCreateWithUTF8CString("test script"), 1, exception);
+        return false;
+    }
+
     JSStringRef numberString = JSStringCreateWithUTF8CString("Number");
     JSObjectRef numberConstructor = JSValueToObject(context, JSObjectGetProperty(context, JSContextGetGlobalObject(context), numberString, exception), exception);
     JSStringRelease(numberString);
@@ -250,11 +324,32 @@ static JSValueRef MyObject_convertToType(JSContextRef context, JSObjectRef objec
     }
 
     // string conversion -- forward to default object class
-    return NULL;
+    return JSValueMakeNull(context);
+}
+
+static JSValueRef MyObject_convertToTypeWrapper(JSContextRef context, JSObjectRef object, JSType type, JSValueRef* exception)
+{
+    UNUSED_PARAM(context);
+    UNUSED_PARAM(object);
+    UNUSED_PARAM(type);
+    UNUSED_PARAM(exception);
+    // Forward to default object class
+    return 0;
+}
+
+static bool MyObject_set_nullGetForwardSet(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
+{
+    UNUSED_PARAM(ctx);
+    UNUSED_PARAM(object);
+    UNUSED_PARAM(propertyName);
+    UNUSED_PARAM(value);
+    UNUSED_PARAM(exception);
+    return false; // Forward to parent class.
 }
 
 static JSStaticValue evilStaticValues[] = {
     { "nullGetSet", 0, 0, kJSPropertyAttributeNone },
+    { "nullGetForwardSet", 0, MyObject_set_nullGetForwardSet, kJSPropertyAttributeNone },
     { 0, 0, 0, 0 }
 };
 
@@ -286,17 +381,294 @@ JSClassDefinition MyObject_definition = {
     MyObject_convertToType,
 };
 
+JSClassDefinition MyObject_convertToTypeWrapperDefinition = {
+    0,
+    kJSClassAttributeNone,
+    
+    "MyObject",
+    NULL,
+    
+    NULL,
+    NULL,
+    
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    MyObject_convertToTypeWrapper,
+};
+
+JSClassDefinition MyObject_nullWrapperDefinition = {
+    0,
+    kJSClassAttributeNone,
+    
+    "MyObject",
+    NULL,
+    
+    NULL,
+    NULL,
+    
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+};
+
 static JSClassRef MyObject_class(JSContextRef context)
 {
     UNUSED_PARAM(context);
 
+    static JSClassRef jsClass;
+    if (!jsClass) {
+        JSClassRef baseClass = JSClassCreate(&MyObject_definition);
+        MyObject_convertToTypeWrapperDefinition.parentClass = baseClass;
+        JSClassRef wrapperClass = JSClassCreate(&MyObject_convertToTypeWrapperDefinition);
+        MyObject_nullWrapperDefinition.parentClass = wrapperClass;
+        jsClass = JSClassCreate(&MyObject_nullWrapperDefinition);
+    }
+
+    return jsClass;
+}
+
+static JSValueRef PropertyCatchalls_getProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
+{
+    UNUSED_PARAM(context);
+    UNUSED_PARAM(object);
+    UNUSED_PARAM(propertyName);
+    UNUSED_PARAM(exception);
+
+    if (JSStringIsEqualToUTF8CString(propertyName, "x")) {
+        static size_t count;
+        if (count++ < 5)
+            return NULL;
+
+        // Swallow all .x gets after 5, returning null.
+        return JSValueMakeNull(context);
+    }
+
+    if (JSStringIsEqualToUTF8CString(propertyName, "y")) {
+        static size_t count;
+        if (count++ < 5)
+            return NULL;
+
+        // Swallow all .y gets after 5, returning null.
+        return JSValueMakeNull(context);
+    }
+    
+    if (JSStringIsEqualToUTF8CString(propertyName, "z")) {
+        static size_t count;
+        if (count++ < 5)
+            return NULL;
+
+        // Swallow all .y gets after 5, returning null.
+        return JSValueMakeNull(context);
+    }
+
+    return NULL;
+}
+
+static bool PropertyCatchalls_setProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
+{
+    UNUSED_PARAM(context);
+    UNUSED_PARAM(object);
+    UNUSED_PARAM(propertyName);
+    UNUSED_PARAM(value);
+    UNUSED_PARAM(exception);
+
+    if (JSStringIsEqualToUTF8CString(propertyName, "x")) {
+        static size_t count;
+        if (count++ < 5)
+            return false;
+
+        // Swallow all .x sets after 4.
+        return true;
+    }
+
+    if (JSStringIsEqualToUTF8CString(propertyName, "make_throw") || JSStringIsEqualToUTF8CString(propertyName, "0")) {
+        *exception = JSValueMakeNumber(context, 5);
+        return true;
+    }
+
+    return false;
+}
+
+static void PropertyCatchalls_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef propertyNames)
+{
+    UNUSED_PARAM(context);
+    UNUSED_PARAM(object);
+
+    static size_t count;
+    static const char* numbers[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
+    
+    // Provide a property of a different name every time.
+    JSStringRef propertyName = JSStringCreateWithUTF8CString(numbers[count++ % 10]);
+    JSPropertyNameAccumulatorAddName(propertyNames, propertyName);
+    JSStringRelease(propertyName);
+}
+
+JSClassDefinition PropertyCatchalls_definition = {
+    0,
+    kJSClassAttributeNone,
+    
+    "PropertyCatchalls",
+    NULL,
+    
+    NULL,
+    NULL,
+    
+    NULL,
+    NULL,
+    NULL,
+    PropertyCatchalls_getProperty,
+    PropertyCatchalls_setProperty,
+    NULL,
+    PropertyCatchalls_getPropertyNames,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+};
+
+static JSClassRef PropertyCatchalls_class(JSContextRef context)
+{
+    UNUSED_PARAM(context);
+
+    static JSClassRef jsClass;
+    if (!jsClass)
+        jsClass = JSClassCreate(&PropertyCatchalls_definition);
+    
+    return jsClass;
+}
+
+static bool EvilExceptionObject_hasInstance(JSContextRef context, JSObjectRef constructor, JSValueRef possibleValue, JSValueRef* exception)
+{
+    UNUSED_PARAM(context);
+    UNUSED_PARAM(constructor);
+    
+    JSStringRef hasInstanceName = JSStringCreateWithUTF8CString("hasInstance");
+    JSValueRef hasInstance = JSObjectGetProperty(context, constructor, hasInstanceName, exception);
+    JSStringRelease(hasInstanceName);
+    if (!hasInstance)
+        return false;
+    JSObjectRef function = JSValueToObject(context, hasInstance, exception);
+    JSValueRef result = JSObjectCallAsFunction(context, function, constructor, 1, &possibleValue, exception);
+    return result && JSValueToBoolean(context, result);
+}
+
+static JSValueRef EvilExceptionObject_convertToType(JSContextRef context, JSObjectRef object, JSType type, JSValueRef* exception)
+{
+    UNUSED_PARAM(object);
+    UNUSED_PARAM(exception);
+    JSStringRef funcName;
+    switch (type) {
+    case kJSTypeNumber:
+        funcName = JSStringCreateWithUTF8CString("toNumber");
+        break;
+    case kJSTypeString:
+        funcName = JSStringCreateWithUTF8CString("toStringExplicit");
+        break;
+    default:
+        return JSValueMakeNull(context);
+        break;
+    }
+    
+    JSValueRef func = JSObjectGetProperty(context, object, funcName, exception);
+    JSStringRelease(funcName);    
+    JSObjectRef function = JSValueToObject(context, func, exception);
+    if (!function)
+        return JSValueMakeNull(context);
+    JSValueRef value = JSObjectCallAsFunction(context, function, object, 0, NULL, exception);
+    if (!value) {
+        JSStringRef errorString = JSStringCreateWithUTF8CString("convertToType failed"); 
+        JSValueRef errorStringRef = JSValueMakeString(context, errorString);
+        JSStringRelease(errorString);
+        return errorStringRef;
+    }
+    return value;
+}
+
+JSClassDefinition EvilExceptionObject_definition = {
+    0,
+    kJSClassAttributeNone,
+
+    "EvilExceptionObject",
+    NULL,
+
+    NULL,
+    NULL,
+
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    EvilExceptionObject_hasInstance,
+    EvilExceptionObject_convertToType,
+};
+
+static JSClassRef EvilExceptionObject_class(JSContextRef context)
+{
+    UNUSED_PARAM(context);
+    
+    static JSClassRef jsClass;
+    if (!jsClass)
+        jsClass = JSClassCreate(&EvilExceptionObject_definition);
+    
+    return jsClass;
+}
+
+JSClassDefinition EmptyObject_definition = {
+    0,
+    kJSClassAttributeNone,
+    
+    NULL,
+    NULL,
+    
+    NULL,
+    NULL,
+    
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+};
+
+static JSClassRef EmptyObject_class(JSContextRef context)
+{
+    UNUSED_PARAM(context);
+    
     static JSClassRef jsClass;
     if (!jsClass)
-        jsClass = JSClassCreate(&MyObject_definition);
+        jsClass = JSClassCreate(&EmptyObject_definition);
     
     return jsClass;
 }
 
+
 static JSValueRef Base_get(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
 {
     UNUSED_PARAM(object);
@@ -327,9 +699,22 @@ static JSValueRef Base_callAsFunction(JSContextRef ctx, JSObjectRef function, JS
     return JSValueMakeNumber(ctx, 1); // distinguish base call from derived call
 }
 
+static JSValueRef Base_returnHardNull(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+    UNUSED_PARAM(ctx);
+    UNUSED_PARAM(function);
+    UNUSED_PARAM(thisObject);
+    UNUSED_PARAM(argumentCount);
+    UNUSED_PARAM(arguments);
+    UNUSED_PARAM(exception);
+    
+    return 0; // should convert to undefined!
+}
+
 static JSStaticFunction Base_staticFunctions[] = {
     { "baseProtoDup", NULL, kJSPropertyAttributeNone },
     { "baseProto", Base_callAsFunction, kJSPropertyAttributeNone },
+    { "baseHardNull", Base_returnHardNull, kJSPropertyAttributeNone },
     { 0, 0, 0 }
 };
 
@@ -454,14 +839,27 @@ static JSClassRef Derived_class(JSContextRef context)
     return jsClass;
 }
 
-static JSValueRef print_callAsFunction(JSContextRef context, JSObjectRef functionObject, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+static JSClassRef Derived2_class(JSContextRef context)
+{
+    static JSClassRef jsClass;
+    if (!jsClass) {
+        JSClassDefinition definition = kJSClassDefinitionEmpty;
+        definition.parentClass = Derived_class(context);
+        jsClass = JSClassCreate(&definition);
+    }
+    return jsClass;
+}
+
+static JSValueRef print_callAsFunction(JSContextRef ctx, JSObjectRef functionObject, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
 {
     UNUSED_PARAM(functionObject);
     UNUSED_PARAM(thisObject);
     UNUSED_PARAM(exception);
+
+    ASSERT(JSContextGetGlobalContext(ctx) == context);
     
     if (argumentCount > 0) {
-        JSStringRef string = JSValueToStringCopy(context, arguments[0], NULL);
+        JSStringRef string = JSValueToStringCopy(ctx, arguments[0], NULL);
         size_t sizeUTF8 = JSStringGetMaximumUTF8CStringSize(string);
         char* stringUTF8 = (char*)malloc(sizeUTF8);
         JSStringGetUTF8CString(string, stringUTF8, sizeUTF8);
@@ -470,7 +868,7 @@ static JSValueRef print_callAsFunction(JSContextRef context, JSObjectRef functio
         JSStringRelease(string);
     }
     
-    return JSValueMakeUndefined(context);
+    return JSValueMakeUndefined(ctx);
 }
 
 static JSObjectRef myConstructor_callAsConstructor(JSContextRef context, JSObjectRef constructorObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
@@ -488,6 +886,17 @@ static JSObjectRef myConstructor_callAsConstructor(JSContextRef context, JSObjec
     return result;
 }
 
+static JSObjectRef myBadConstructor_callAsConstructor(JSContextRef context, JSObjectRef constructorObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+    UNUSED_PARAM(context);
+    UNUSED_PARAM(constructorObject);
+    UNUSED_PARAM(argumentCount);
+    UNUSED_PARAM(arguments);
+    UNUSED_PARAM(exception);
+    
+    return 0;
+}
+
 
 static void globalObject_initialize(JSContextRef context, JSObjectRef object)
 {
@@ -539,6 +948,17 @@ static JSValueRef globalObject_call(JSContextRef ctx, JSObjectRef function, JSOb
     return JSValueMakeNumber(ctx, 3);
 }
 
+static JSValueRef functionGC(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+    UNUSED_PARAM(function);
+    UNUSED_PARAM(thisObject);
+    UNUSED_PARAM(argumentCount);
+    UNUSED_PARAM(arguments);
+    UNUSED_PARAM(exception);
+    JSGarbageCollect(context);
+    return JSValueMakeUndefined(context);
+}
+
 static JSStaticValue globalObject_staticValues[] = {
     { "globalStaticValue", globalObject_get, globalObject_set, kJSPropertyAttributeNone },
     { 0, 0, 0, 0 }
@@ -546,6 +966,7 @@ static JSStaticValue globalObject_staticValues[] = {
 
 static JSStaticFunction globalObject_staticFunctions[] = {
     { "globalStaticFunction", globalObject_call, kJSPropertyAttributeNone },
+    { "gc", functionGC, kJSPropertyAttributeNone },
     { 0, 0, 0 }
 };
 
@@ -558,8 +979,153 @@ static void testInitializeFinalize()
     ASSERT(JSObjectGetPrivate(o) == (void*)3);
 }
 
+static JSValueRef jsNumberValue =  NULL;
+
+static JSObjectRef aHeapRef = NULL;
+
+static void makeGlobalNumberValue(JSContextRef context) {
+    JSValueRef v = JSValueMakeNumber(context, 420);
+    JSValueProtect(context, v);
+    jsNumberValue = v;
+    v = NULL;
+}
+
+static bool assertTrue(bool value, const char* message)
+{
+    if (!value) {
+        if (message)
+            fprintf(stderr, "assertTrue failed: '%s'\n", message);
+        else
+            fprintf(stderr, "assertTrue failed.\n");
+        failed = 1;
+    }
+    return value;
+}
+
+static bool checkForCycleInPrototypeChain()
+{
+    bool result = true;
+    JSGlobalContextRef context = JSGlobalContextCreate(0);
+    JSObjectRef object1 = JSObjectMake(context, /* jsClass */ 0, /* data */ 0);
+    JSObjectRef object2 = JSObjectMake(context, /* jsClass */ 0, /* data */ 0);
+    JSObjectRef object3 = JSObjectMake(context, /* jsClass */ 0, /* data */ 0);
+
+    JSObjectSetPrototype(context, object1, JSValueMakeNull(context));
+    ASSERT(JSValueIsNull(context, JSObjectGetPrototype(context, object1)));
+
+    // object1 -> object1
+    JSObjectSetPrototype(context, object1, object1);
+    result &= assertTrue(JSValueIsNull(context, JSObjectGetPrototype(context, object1)), "It is possible to assign self as a prototype");
+
+    // object1 -> object2 -> object1
+    JSObjectSetPrototype(context, object2, object1);
+    ASSERT(JSValueIsStrictEqual(context, JSObjectGetPrototype(context, object2), object1));
+    JSObjectSetPrototype(context, object1, object2);
+    result &= assertTrue(JSValueIsNull(context, JSObjectGetPrototype(context, object1)), "It is possible to close a prototype chain cycle");
+
+    // object1 -> object2 -> object3 -> object1
+    JSObjectSetPrototype(context, object2, object3);
+    ASSERT(JSValueIsStrictEqual(context, JSObjectGetPrototype(context, object2), object3));
+    JSObjectSetPrototype(context, object1, object2);
+    ASSERT(JSValueIsStrictEqual(context, JSObjectGetPrototype(context, object1), object2));
+    JSObjectSetPrototype(context, object3, object1);
+    result &= assertTrue(!JSValueIsStrictEqual(context, JSObjectGetPrototype(context, object3), object1), "It is possible to close a prototype chain cycle");
+
+    JSValueRef exception;
+    JSStringRef code = JSStringCreateWithUTF8CString("o = { }; p = { }; o.__proto__ = p; p.__proto__ = o");
+    JSStringRef file = JSStringCreateWithUTF8CString("");
+    result &= assertTrue(!JSEvaluateScript(context, code, /* thisObject*/ 0, file, 1, &exception)
+                         , "An exception should be thrown");
+
+    JSStringRelease(code);
+    JSStringRelease(file);
+    JSGlobalContextRelease(context);
+    return result;
+}
+
+static void checkConstnessInJSObjectNames()
+{
+    JSStaticFunction fun;
+    fun.name = "something";
+    JSStaticValue val;
+    val.name = "something";
+}
+
+#if PLATFORM(MAC) || PLATFORM(IOS)
+static double currentCPUTime()
+{
+    mach_msg_type_number_t infoCount = THREAD_BASIC_INFO_COUNT;
+    thread_basic_info_data_t info;
+
+    /* Get thread information */
+    mach_port_t threadPort = mach_thread_self();
+    thread_info(threadPort, THREAD_BASIC_INFO, (thread_info_t)(&info), &infoCount);
+    mach_port_deallocate(mach_task_self(), threadPort);
+    
+    double time = info.user_time.seconds + info.user_time.microseconds / 1000000.;
+    time += info.system_time.seconds + info.system_time.microseconds / 1000000.;
+    
+    return time;
+}
+
+static JSValueRef currentCPUTime_callAsFunction(JSContextRef ctx, JSObjectRef functionObject, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+    UNUSED_PARAM(functionObject);
+    UNUSED_PARAM(thisObject);
+    UNUSED_PARAM(argumentCount);
+    UNUSED_PARAM(arguments);
+    UNUSED_PARAM(exception);
+
+    ASSERT(JSContextGetGlobalContext(ctx) == context);
+    return JSValueMakeNumber(ctx, currentCPUTime());
+}
+
+bool shouldTerminateCallbackWasCalled = false;
+static bool shouldTerminateCallback(JSContextRef ctx, void* context)
+{
+    UNUSED_PARAM(ctx);
+    UNUSED_PARAM(context);
+    shouldTerminateCallbackWasCalled = true;
+    return true;
+}
+
+bool cancelTerminateCallbackWasCalled = false;
+static bool cancelTerminateCallback(JSContextRef ctx, void* context)
+{
+    UNUSED_PARAM(ctx);
+    UNUSED_PARAM(context);
+    cancelTerminateCallbackWasCalled = true;
+    return false;
+}
+
+int extendTerminateCallbackCalled = 0;
+static bool extendTerminateCallback(JSContextRef ctx, void* context)
+{
+    UNUSED_PARAM(context);
+    extendTerminateCallbackCalled++;
+    if (extendTerminateCallbackCalled == 1) {
+        JSContextGroupRef contextGroup = JSContextGetGroup(ctx);
+        JSContextGroupSetExecutionTimeLimit(contextGroup, .200f, extendTerminateCallback, 0);
+        return false;
+    }
+    return true;
+}
+#endif /* PLATFORM(MAC) || PLATFORM(IOS) */
+
+
 int main(int argc, char* argv[])
 {
+#if OS(WINDOWS)
+    // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for
+    // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the
+    // error mode here to work around Cygwin's behavior. See <http://webkit.org/b/55222>.
+    ::SetErrorMode(0);
+#endif
+
+#if JSC_OBJC_API_ENABLED
+    testObjectiveCAPI();
+#endif
+
     const char *scriptPath = "testapi.js";
     if (argc > 1) {
         scriptPath = argv[1];
@@ -582,8 +1148,11 @@ int main(int argc, char* argv[])
     JSClassRef globalObjectClass = JSClassCreate(&globalObjectClassDefinition);
     context = JSGlobalContextCreateInGroup(NULL, globalObjectClass);
 
+    JSContextGroupRef contextGroup = JSContextGetGroup(context);
+    
     JSGlobalContextRetain(context);
     JSGlobalContextRelease(context);
+    ASSERT(JSContextGetGlobalContext(context) == context);
     
     JSReportExtraMemoryCost(context, 0);
     JSReportExtraMemoryCost(context, 1);
@@ -637,6 +1206,12 @@ int main(int argc, char* argv[])
     free(buffer);
     JSValueRef jsCFEmptyStringWithCharacters = JSValueMakeString(context, jsCFEmptyIStringWithCharacters);
 
+    JSChar constantString[] = { 'H', 'e', 'l', 'l', 'o', };
+    JSStringRef constantStringRef = JSStringCreateWithCharactersNoCopy(constantString, sizeof(constantString) / sizeof(constantString[0]));
+    ASSERT(JSStringGetCharactersPtr(constantStringRef) == constantString);
+    JSStringRelease(constantStringRef);
+
+    ASSERT(JSValueGetType(context, NULL) == kJSTypeNull);
     ASSERT(JSValueGetType(context, jsUndefined) == kJSTypeUndefined);
     ASSERT(JSValueGetType(context, jsNull) == kJSTypeNull);
     ASSERT(JSValueGetType(context, jsTrue) == kJSTypeBoolean);
@@ -651,13 +1226,175 @@ int main(int argc, char* argv[])
     ASSERT(JSValueGetType(context, jsCFEmptyString) == kJSTypeString);
     ASSERT(JSValueGetType(context, jsCFEmptyStringWithCharacters) == kJSTypeString);
 
+    ASSERT(!JSValueIsBoolean(context, NULL));
+    ASSERT(!JSValueIsObject(context, NULL));
+    ASSERT(!JSValueIsString(context, NULL));
+    ASSERT(!JSValueIsNumber(context, NULL));
+    ASSERT(!JSValueIsUndefined(context, NULL));
+    ASSERT(JSValueIsNull(context, NULL));
+    ASSERT(!JSObjectCallAsFunction(context, NULL, NULL, 0, NULL, NULL));
+    ASSERT(!JSObjectCallAsConstructor(context, NULL, 0, NULL, NULL));
+    ASSERT(!JSObjectIsConstructor(context, NULL));
+    ASSERT(!JSObjectIsFunction(context, NULL));
+
+    JSStringRef nullString = JSStringCreateWithUTF8CString(0);
+    const JSChar* characters = JSStringGetCharactersPtr(nullString);
+    if (characters) {
+        printf("FAIL: Didn't return null when accessing character pointer of a null String.\n");
+        failed = 1;
+    } else
+        printf("PASS: returned null when accessing character pointer of a null String.\n");
+
+    size_t length = JSStringGetLength(nullString);
+    if (length) {
+        printf("FAIL: Didn't return 0 length for null String.\n");
+        failed = 1;
+    } else
+        printf("PASS: returned 0 length for null String.\n");
+    JSStringRelease(nullString);
+
+    JSObjectRef propertyCatchalls = JSObjectMake(context, PropertyCatchalls_class(context), NULL);
+    JSStringRef propertyCatchallsString = JSStringCreateWithUTF8CString("PropertyCatchalls");
+    JSObjectSetProperty(context, globalObject, propertyCatchallsString, propertyCatchalls, kJSPropertyAttributeNone, NULL);
+    JSStringRelease(propertyCatchallsString);
+
     JSObjectRef myObject = JSObjectMake(context, MyObject_class(context), NULL);
     JSStringRef myObjectIString = JSStringCreateWithUTF8CString("MyObject");
     JSObjectSetProperty(context, globalObject, myObjectIString, myObject, kJSPropertyAttributeNone, NULL);
     JSStringRelease(myObjectIString);
     
-    JSValueRef exception;
+    JSObjectRef EvilExceptionObject = JSObjectMake(context, EvilExceptionObject_class(context), NULL);
+    JSStringRef EvilExceptionObjectIString = JSStringCreateWithUTF8CString("EvilExceptionObject");
+    JSObjectSetProperty(context, globalObject, EvilExceptionObjectIString, EvilExceptionObject, kJSPropertyAttributeNone, NULL);
+    JSStringRelease(EvilExceptionObjectIString);
+    
+    JSObjectRef EmptyObject = JSObjectMake(context, EmptyObject_class(context), NULL);
+    JSStringRef EmptyObjectIString = JSStringCreateWithUTF8CString("EmptyObject");
+    JSObjectSetProperty(context, globalObject, EmptyObjectIString, EmptyObject, kJSPropertyAttributeNone, NULL);
+    JSStringRelease(EmptyObjectIString);
+    
+    JSStringRef lengthStr = JSStringCreateWithUTF8CString("length");
+    JSObjectRef aStackRef = JSObjectMakeArray(context, 0, 0, 0);
+    aHeapRef = aStackRef;
+    JSObjectSetProperty(context, aHeapRef, lengthStr, JSValueMakeNumber(context, 10), 0, 0);
+    JSStringRef privatePropertyName = JSStringCreateWithUTF8CString("privateProperty");
+    if (!JSObjectSetPrivateProperty(context, myObject, privatePropertyName, aHeapRef)) {
+        printf("FAIL: Could not set private property.\n");
+        failed = 1;
+    } else
+        printf("PASS: Set private property.\n");
+    aStackRef = 0;
+    if (JSObjectSetPrivateProperty(context, aHeapRef, privatePropertyName, aHeapRef)) {
+        printf("FAIL: JSObjectSetPrivateProperty should fail on non-API objects.\n");
+        failed = 1;
+    } else
+        printf("PASS: Did not allow JSObjectSetPrivateProperty on a non-API object.\n");
+    if (JSObjectGetPrivateProperty(context, myObject, privatePropertyName) != aHeapRef) {
+        printf("FAIL: Could not retrieve private property.\n");
+        failed = 1;
+    } else
+        printf("PASS: Retrieved private property.\n");
+    if (JSObjectGetPrivateProperty(context, aHeapRef, privatePropertyName)) {
+        printf("FAIL: JSObjectGetPrivateProperty should return NULL when called on a non-API object.\n");
+        failed = 1;
+    } else
+        printf("PASS: JSObjectGetPrivateProperty return NULL.\n");
+
+    if (JSObjectGetProperty(context, myObject, privatePropertyName, 0) == aHeapRef) {
+        printf("FAIL: Accessed private property through ordinary property lookup.\n");
+        failed = 1;
+    } else
+        printf("PASS: Cannot access private property through ordinary property lookup.\n");
 
+    JSGarbageCollect(context);
+
+    for (int i = 0; i < 10000; i++)
+        JSObjectMake(context, 0, 0);
+
+    aHeapRef = JSValueToObject(context, JSObjectGetPrivateProperty(context, myObject, privatePropertyName), 0);
+    if (JSValueToNumber(context, JSObjectGetProperty(context, aHeapRef, lengthStr, 0), 0) != 10) {
+        printf("FAIL: Private property has been collected.\n");
+        failed = 1;
+    } else
+        printf("PASS: Private property does not appear to have been collected.\n");
+    JSStringRelease(lengthStr);
+
+    if (!JSObjectSetPrivateProperty(context, myObject, privatePropertyName, 0)) {
+        printf("FAIL: Could not set private property to NULL.\n");
+        failed = 1;
+    } else
+        printf("PASS: Set private property to NULL.\n");
+    if (JSObjectGetPrivateProperty(context, myObject, privatePropertyName)) {
+        printf("FAIL: Could not retrieve private property.\n");
+        failed = 1;
+    } else
+        printf("PASS: Retrieved private property.\n");
+
+    JSStringRef nullJSON = JSStringCreateWithUTF8CString(0);
+    JSValueRef nullJSONObject = JSValueMakeFromJSONString(context, nullJSON);
+    if (nullJSONObject) {
+        printf("FAIL: Did not parse null String as JSON correctly\n");
+        failed = 1;
+    } else
+        printf("PASS: Parsed null String as JSON correctly.\n");
+    JSStringRelease(nullJSON);
+
+    JSStringRef validJSON = JSStringCreateWithUTF8CString("{\"aProperty\":true}");
+    JSValueRef jsonObject = JSValueMakeFromJSONString(context, validJSON);
+    JSStringRelease(validJSON);
+    if (!JSValueIsObject(context, jsonObject)) {
+        printf("FAIL: Did not parse valid JSON correctly\n");
+        failed = 1;
+    } else
+        printf("PASS: Parsed valid JSON string.\n");
+    JSStringRef propertyName = JSStringCreateWithUTF8CString("aProperty");
+    assertEqualsAsBoolean(JSObjectGetProperty(context, JSValueToObject(context, jsonObject, 0), propertyName, 0), true);
+    JSStringRelease(propertyName);
+    JSStringRef invalidJSON = JSStringCreateWithUTF8CString("fail!");
+    if (JSValueMakeFromJSONString(context, invalidJSON)) {
+        printf("FAIL: Should return null for invalid JSON data\n");
+        failed = 1;
+    } else
+        printf("PASS: Correctly returned null for invalid JSON data.\n");
+    JSValueRef exception;
+    JSStringRef str = JSValueCreateJSONString(context, jsonObject, 0, 0);
+    if (!JSStringIsEqualToUTF8CString(str, "{\"aProperty\":true}")) {
+        printf("FAIL: Did not correctly serialise with indent of 0.\n");
+        failed = 1;
+    } else
+        printf("PASS: Correctly serialised with indent of 0.\n");
+    JSStringRelease(str);
+
+    str = JSValueCreateJSONString(context, jsonObject, 4, 0);
+    if (!JSStringIsEqualToUTF8CString(str, "{\n    \"aProperty\": true\n}")) {
+        printf("FAIL: Did not correctly serialise with indent of 4.\n");
+        failed = 1;
+    } else
+        printf("PASS: Correctly serialised with indent of 4.\n");
+    JSStringRelease(str);
+    JSStringRef src = JSStringCreateWithUTF8CString("({get a(){ throw '';}})");
+    JSValueRef unstringifiableObj = JSEvaluateScript(context, src, NULL, NULL, 1, NULL);
+    
+    str = JSValueCreateJSONString(context, unstringifiableObj, 4, 0);
+    if (str) {
+        printf("FAIL: Didn't return null when attempting to serialize unserializable value.\n");
+        JSStringRelease(str);
+        failed = 1;
+    } else
+        printf("PASS: returned null when attempting to serialize unserializable value.\n");
+    
+    str = JSValueCreateJSONString(context, unstringifiableObj, 4, &exception);
+    if (str) {
+        printf("FAIL: Didn't return null when attempting to serialize unserializable value.\n");
+        JSStringRelease(str);
+        failed = 1;
+    } else
+        printf("PASS: returned null when attempting to serialize unserializable value.\n");
+    if (!exception) {
+        printf("FAIL: Did not set exception on serialisation error\n");
+        failed = 1;
+    } else
+        printf("PASS: set exception on serialisation error\n");
     // Conversions that throw exceptions
     exception = NULL;
     ASSERT(NULL == JSValueToObject(context, jsNull, &exception));
@@ -741,6 +1478,8 @@ int main(int argc, char* argv[])
     assertEqualsAsUTF8String(jsCFEmptyString, "");
     assertEqualsAsUTF8String(jsCFEmptyStringWithCharacters, "");
     
+    checkConstnessInJSObjectNames();
+    
     ASSERT(JSValueIsStrictEqual(context, jsTrue, jsTrue));
     ASSERT(!JSValueIsStrictEqual(context, jsOne, jsOneString));
 
@@ -758,15 +1497,20 @@ int main(int argc, char* argv[])
     CFRelease(cfEmptyString);
     
     jsGlobalValue = JSObjectMake(context, NULL, NULL);
+    makeGlobalNumberValue(context);
     JSValueProtect(context, jsGlobalValue);
     JSGarbageCollect(context);
     ASSERT(JSValueIsObject(context, jsGlobalValue));
     JSValueUnprotect(context, jsGlobalValue);
+    JSValueUnprotect(context, jsNumberValue);
 
     JSStringRef goodSyntax = JSStringCreateWithUTF8CString("x = 1;");
-    JSStringRef badSyntax = JSStringCreateWithUTF8CString("x := 1;");
+    const char* badSyntaxConstant = "x := 1;";
+    JSStringRef badSyntax = JSStringCreateWithUTF8CString(badSyntaxConstant);
     ASSERT(JSCheckScriptSyntax(context, goodSyntax, NULL, 0, NULL));
     ASSERT(!JSCheckScriptSyntax(context, badSyntax, NULL, 0, NULL));
+    ASSERT(!JSScriptCreateFromString(contextGroup, 0, 0, badSyntax, 0, 0));
+    ASSERT(!JSScriptCreateReferencingImmortalASCIIText(contextGroup, 0, 0, badSyntaxConstant, strlen(badSyntaxConstant), 0, 0));
 
     JSValueRef result;
     JSValueRef v;
@@ -841,12 +1585,12 @@ int main(int argc, char* argv[])
     function = JSObjectMakeFunction(context, foo, 1, argumentNames, functionBody, NULL, 1, &exception);
     ASSERT(function && !exception);
     JSValueRef arguments[] = { JSValueMakeNumber(context, 2) };
-    v = JSObjectCallAsFunction(context, function, NULL, 1, arguments, &exception);
+    JSObjectCallAsFunction(context, function, NULL, 1, arguments, &exception);
     JSStringRelease(foo);
     JSStringRelease(functionBody);
     
     string = JSValueToStringCopy(context, function, NULL);
-    assertEqualsAsUTF8String(JSValueMakeString(context, string), "function foo(foo) {return foo;}");
+    assertEqualsAsUTF8String(JSValueMakeString(context, string), "function foo(foo) { return foo;\n}");
     JSStringRelease(string);
 
     JSStringRef print = JSStringCreateWithUTF8CString("print");
@@ -862,14 +1606,29 @@ int main(int argc, char* argv[])
     JSObjectSetProperty(context, globalObject, myConstructorIString, myConstructor, kJSPropertyAttributeNone, NULL);
     JSStringRelease(myConstructorIString);
     
+    JSStringRef myBadConstructorIString = JSStringCreateWithUTF8CString("MyBadConstructor");
+    JSObjectRef myBadConstructor = JSObjectMakeConstructor(context, NULL, myBadConstructor_callAsConstructor);
+    JSObjectSetProperty(context, globalObject, myBadConstructorIString, myBadConstructor, kJSPropertyAttributeNone, NULL);
+    JSStringRelease(myBadConstructorIString);
+    
     ASSERT(!JSObjectSetPrivate(myConstructor, (void*)1));
     ASSERT(!JSObjectGetPrivate(myConstructor));
     
+    string = JSStringCreateWithUTF8CString("Base");
+    JSObjectRef baseConstructor = JSObjectMakeConstructor(context, Base_class(context), NULL);
+    JSObjectSetProperty(context, globalObject, string, baseConstructor, kJSPropertyAttributeNone, NULL);
+    JSStringRelease(string);
+    
     string = JSStringCreateWithUTF8CString("Derived");
     JSObjectRef derivedConstructor = JSObjectMakeConstructor(context, Derived_class(context), NULL);
     JSObjectSetProperty(context, globalObject, string, derivedConstructor, kJSPropertyAttributeNone, NULL);
     JSStringRelease(string);
     
+    string = JSStringCreateWithUTF8CString("Derived2");
+    JSObjectRef derived2Constructor = JSObjectMakeConstructor(context, Derived2_class(context), NULL);
+    JSObjectSetProperty(context, globalObject, string, derived2Constructor, kJSPropertyAttributeNone, NULL);
+    JSStringRelease(string);
+
     o = JSObjectMake(context, NULL, NULL);
     JSObjectSetProperty(context, o, jsOneIString, JSValueMakeNumber(context, 1), kJSPropertyAttributeNone, NULL);
     JSObjectSetProperty(context, o, jsCFIString,  JSValueMakeNumber(context, 1), kJSPropertyAttributeDontEnum, NULL);
@@ -898,7 +1657,8 @@ int main(int argc, char* argv[])
 
     JSValueRef argumentsDateValues[] = { JSValueMakeNumber(context, 0) };
     o = JSObjectMakeDate(context, 1, argumentsDateValues, NULL);
-    assertEqualsAsUTF8String(o, "Wed Dec 31 1969 16:00:00 GMT-0800 (PST)");
+    if (timeZoneIsPST())
+        assertEqualsAsUTF8String(o, "Wed Dec 31 1969 16:00:00 GMT-0800 (PST)");
 
     string = JSStringCreateWithUTF8CString("an error message");
     JSValueRef argumentsErrorValues[] = { JSValueMakeString(context, string) };
@@ -939,13 +1699,21 @@ int main(int argc, char* argv[])
     v = JSObjectCallAsFunction(context, function, o, 0, NULL, NULL);
     ASSERT(JSValueIsEqual(context, v, o, NULL));
 
-    JSStringRef script = JSStringCreateWithUTF8CString("this;");
+    const char* thisScript = "this;";
+    JSStringRef script = JSStringCreateWithUTF8CString(thisScript);
     v = JSEvaluateScript(context, script, NULL, NULL, 1, NULL);
     ASSERT(JSValueIsEqual(context, v, globalObject, NULL));
     v = JSEvaluateScript(context, script, o, NULL, 1, NULL);
     ASSERT(JSValueIsEqual(context, v, o, NULL));
     JSStringRelease(script);
 
+    JSScriptRef scriptObject = JSScriptCreateReferencingImmortalASCIIText(contextGroup, 0, 0, thisScript, strlen(thisScript), 0, 0);
+    v = JSScriptEvaluate(context, scriptObject, NULL, NULL);
+    ASSERT(JSValueIsEqual(context, v, globalObject, NULL));
+    v = JSScriptEvaluate(context, scriptObject, o, NULL);
+    ASSERT(JSValueIsEqual(context, v, o, NULL));
+    JSScriptRelease(scriptObject);
+
     script = JSStringCreateWithUTF8CString("eval(this);");
     v = JSEvaluateScript(context, script, NULL, NULL, 1, NULL);
     ASSERT(JSValueIsEqual(context, v, globalObject, NULL));
@@ -953,13 +1721,36 @@ int main(int argc, char* argv[])
     ASSERT(JSValueIsEqual(context, v, o, NULL));
     JSStringRelease(script);
 
+    // Verify that creating a constructor for a class with no static functions does not trigger
+    // an assert inside putDirect or lead to a crash during GC. <https://bugs.webkit.org/show_bug.cgi?id=25785>
+    nullDefinition = kJSClassDefinitionEmpty;
+    nullClass = JSClassCreate(&nullDefinition);
+    JSObjectMakeConstructor(context, nullClass, 0);
+    JSClassRelease(nullClass);
+
     char* scriptUTF8 = createStringWithContentsOfFile(scriptPath);
-    if (!scriptUTF8)
+    if (!scriptUTF8) {
         printf("FAIL: Test script could not be loaded.\n");
-    else {
-        script = JSStringCreateWithUTF8CString(scriptUTF8);
-        result = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
-        if (JSValueIsUndefined(context, result))
+        failed = 1;
+    } else {
+        JSStringRef url = JSStringCreateWithUTF8CString(scriptPath);
+        JSStringRef script = JSStringCreateWithUTF8CString(scriptUTF8);
+        JSStringRef errorMessage = 0;
+        int errorLine = 0;
+        JSScriptRef scriptObject = JSScriptCreateFromString(contextGroup, url, 1, script, &errorMessage, &errorLine);
+        ASSERT((!scriptObject) != (!errorMessage));
+        if (!scriptObject) {
+            printf("FAIL: Test script did not parse\n\t%s:%d\n\t", scriptPath, errorLine);
+            CFStringRef errorCF = JSStringCopyCFString(kCFAllocatorDefault, errorMessage);
+            CFShow(errorCF);
+            CFRelease(errorCF);
+            JSStringRelease(errorMessage);
+            failed = 1;
+        }
+
+        JSStringRelease(script);
+        result = scriptObject ? JSScriptEvaluate(context, scriptObject, 0, &exception) : 0;
+        if (result && JSValueIsUndefined(context, result))
             printf("PASS: Test script executed successfully.\n");
         else {
             printf("FAIL: Test script returned unexpected value:\n");
@@ -968,16 +1759,171 @@ int main(int argc, char* argv[])
             CFShow(exceptionCF);
             CFRelease(exceptionCF);
             JSStringRelease(exceptionIString);
+            failed = 1;
         }
-        JSStringRelease(script);
+        JSScriptRelease(scriptObject);
         free(scriptUTF8);
     }
 
+#if PLATFORM(MAC) || PLATFORM(IOS)
+    JSStringRef currentCPUTimeStr = JSStringCreateWithUTF8CString("currentCPUTime");
+    JSObjectRef currentCPUTimeFunction = JSObjectMakeFunctionWithCallback(context, currentCPUTimeStr, currentCPUTime_callAsFunction);
+    JSObjectSetProperty(context, globalObject, currentCPUTimeStr, currentCPUTimeFunction, kJSPropertyAttributeNone, NULL); 
+    JSStringRelease(currentCPUTimeStr);
+
+    /* Test script timeout: */
+    JSContextGroupSetExecutionTimeLimit(contextGroup, .10f, shouldTerminateCallback, 0);
+    {
+        const char* loopForeverScript = "var startTime = currentCPUTime(); while (true) { if (currentCPUTime() - startTime > .150) break; } ";
+        JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
+        double startTime;
+        double endTime;
+        exception = NULL;
+        shouldTerminateCallbackWasCalled = false;
+        startTime = currentCPUTime();
+        v = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
+        endTime = currentCPUTime();
+
+        if (((endTime - startTime) < .150f) && shouldTerminateCallbackWasCalled)
+            printf("PASS: script timed out as expected.\n");
+        else {
+            if (!((endTime - startTime) < .150f))
+                printf("FAIL: script did not timed out as expected.\n");
+            if (!shouldTerminateCallbackWasCalled)
+                printf("FAIL: script timeout callback was not called.\n");
+            failed = true;
+        }
+
+        if (!exception) {
+            printf("FAIL: TerminatedExecutionException was not thrown.\n");
+            failed = true;
+        }
+    }
+
+    /* Test the script timeout's TerminatedExecutionException should NOT be catchable: */
+    JSContextGroupSetExecutionTimeLimit(contextGroup, 0.10f, shouldTerminateCallback, 0);
+    {
+        const char* loopForeverScript = "var startTime = currentCPUTime(); try { while (true) { if (currentCPUTime() - startTime > .150) break; } } catch(e) { }";
+        JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
+        double startTime;
+        double endTime;
+        exception = NULL;
+        shouldTerminateCallbackWasCalled = false;
+        startTime = currentCPUTime();
+        v = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
+        endTime = currentCPUTime();
+
+        if (((endTime - startTime) >= .150f) || !shouldTerminateCallbackWasCalled) {
+            if (!((endTime - startTime) < .150f))
+                printf("FAIL: script did not timed out as expected.\n");
+            if (!shouldTerminateCallbackWasCalled)
+                printf("FAIL: script timeout callback was not called.\n");
+            failed = true;
+        }
+
+        if (exception)
+            printf("PASS: TerminatedExecutionException was not catchable as expected.\n");
+        else {
+            printf("FAIL: TerminatedExecutionException was caught.\n");
+            failed = true;
+        }
+    }
+
+    /* Test script timeout with no callback: */
+    JSContextGroupSetExecutionTimeLimit(contextGroup, .10f, 0, 0);
+    {
+        const char* loopForeverScript = "var startTime = currentCPUTime(); while (true) { if (currentCPUTime() - startTime > .150) break; } ";
+        JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
+        double startTime;
+        double endTime;
+        exception = NULL;
+        startTime = currentCPUTime();
+        v = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
+        endTime = currentCPUTime();
+
+        if (((endTime - startTime) < .150f) && shouldTerminateCallbackWasCalled)
+            printf("PASS: script timed out as expected when no callback is specified.\n");
+        else {
+            if (!((endTime - startTime) < .150f))
+                printf("FAIL: script did not timed out as expected when no callback is specified.\n");
+            failed = true;
+        }
+
+        if (!exception) {
+            printf("FAIL: TerminatedExecutionException was not thrown.\n");
+            failed = true;
+        }
+    }
+
+    /* Test script timeout cancellation: */
+    JSContextGroupSetExecutionTimeLimit(contextGroup, 0.10f, cancelTerminateCallback, 0);
+    {
+        const char* loopForeverScript = "var startTime = currentCPUTime(); while (true) { if (currentCPUTime() - startTime > .150) break; } ";
+        JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
+        double startTime;
+        double endTime;
+        exception = NULL;
+        startTime = currentCPUTime();
+        v = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
+        endTime = currentCPUTime();
+
+        if (((endTime - startTime) >= .150f) && cancelTerminateCallbackWasCalled && !exception)
+            printf("PASS: script timeout was cancelled as expected.\n");
+        else {
+            if (((endTime - startTime) < .150) || exception)
+                printf("FAIL: script timeout was not cancelled.\n");
+            if (!cancelTerminateCallbackWasCalled)
+                printf("FAIL: script timeout callback was not called.\n");
+            failed = true;
+        }
+
+        if (exception) {
+            printf("FAIL: Unexpected TerminatedExecutionException thrown.\n");
+            failed = true;
+        }
+    }
+
+    /* Test script timeout extension: */
+    JSContextGroupSetExecutionTimeLimit(contextGroup, 0.100f, extendTerminateCallback, 0);
+    {
+        const char* loopForeverScript = "var startTime = currentCPUTime(); while (true) { if (currentCPUTime() - startTime > .500) break; } ";
+        JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
+        double startTime;
+        double endTime;
+        double deltaTime;
+        exception = NULL;
+        startTime = currentCPUTime();
+        v = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
+        endTime = currentCPUTime();
+        deltaTime = endTime - startTime;
+
+        if ((deltaTime >= .300f) && (deltaTime < .500f) && (extendTerminateCallbackCalled == 2) && exception)
+            printf("PASS: script timeout was extended as expected.\n");
+        else {
+            if (deltaTime < .200f)
+                printf("FAIL: script timeout was not extended as expected.\n");
+            else if (deltaTime >= .500f)
+                printf("FAIL: script did not timeout.\n");
+
+            if (extendTerminateCallbackCalled < 1)
+                printf("FAIL: script timeout callback was not called.\n");
+            if (extendTerminateCallbackCalled < 2)
+                printf("FAIL: script timeout callback was not called after timeout extension.\n");
+
+            if (!exception)
+                printf("FAIL: TerminatedExecutionException was not thrown during timeout extension test.\n");
+
+            failed = true;
+        }
+    }
+#endif /* PLATFORM(MAC) || PLATFORM(IOS) */
+
     // Clear out local variables pointing at JSObjectRefs to allow their values to be collected
     function = NULL;
     v = NULL;
     o = NULL;
     globalObject = NULL;
+    myConstructor = NULL;
 
     JSStringRelease(jsEmptyIString);
     JSStringRelease(jsOneIString);
@@ -991,6 +1937,34 @@ int main(int argc, char* argv[])
     JSGlobalContextRelease(context);
     JSClassRelease(globalObjectClass);
 
+    // Test for an infinite prototype chain that used to be created. This test
+    // passes if the call to JSObjectHasProperty() does not hang.
+
+    JSClassDefinition prototypeLoopClassDefinition = kJSClassDefinitionEmpty;
+    prototypeLoopClassDefinition.staticFunctions = globalObject_staticFunctions;
+    JSClassRef prototypeLoopClass = JSClassCreate(&prototypeLoopClassDefinition);
+    JSGlobalContextRef prototypeLoopContext = JSGlobalContextCreateInGroup(NULL, prototypeLoopClass);
+
+    JSStringRef nameProperty = JSStringCreateWithUTF8CString("name");
+    JSObjectHasProperty(prototypeLoopContext, JSContextGetGlobalObject(prototypeLoopContext), nameProperty);
+
+    JSGlobalContextRelease(prototypeLoopContext);
+    JSClassRelease(prototypeLoopClass);
+
+    printf("PASS: Infinite prototype chain does not occur.\n");
+
+    if (checkForCycleInPrototypeChain())
+        printf("PASS: A cycle in a prototype chain can't be created.\n");
+    else {
+        printf("FAIL: A cycle in a prototype chain can be created.\n");
+        failed = true;
+    }
+
+    if (failed) {
+        printf("FAIL: Some tests failed.\n");
+        return 1;
+    }
+
     printf("PASS: Program exited normally.\n");
     return 0;
 }
@@ -1006,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;
     }