diff --git a/CocosDenshion/proj.ios/CocosDenshion.xcodeproj/project.pbxproj b/CocosDenshion/proj.ios/CocosDenshion.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..ab6a7027d6 --- /dev/null +++ b/CocosDenshion/proj.ios/CocosDenshion.xcodeproj/project.pbxproj @@ -0,0 +1,342 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 41E01E4E16D5D5E600ED686C /* Export.h in Headers */ = {isa = PBXBuildFile; fileRef = 41E01E4116D5D5E600ED686C /* Export.h */; }; + 41E01E4F16D5D5E600ED686C /* SimpleAudioEngine.h in Headers */ = {isa = PBXBuildFile; fileRef = 41E01E4216D5D5E600ED686C /* SimpleAudioEngine.h */; }; + 41E01E5016D5D5E600ED686C /* CDAudioManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 41E01E4416D5D5E600ED686C /* CDAudioManager.h */; }; + 41E01E5116D5D5E600ED686C /* CDAudioManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 41E01E4516D5D5E600ED686C /* CDAudioManager.m */; }; + 41E01E5216D5D5E600ED686C /* CDConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 41E01E4616D5D5E600ED686C /* CDConfig.h */; }; + 41E01E5316D5D5E600ED686C /* CDOpenALSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 41E01E4716D5D5E600ED686C /* CDOpenALSupport.h */; }; + 41E01E5416D5D5E600ED686C /* CDOpenALSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 41E01E4816D5D5E600ED686C /* CDOpenALSupport.m */; }; + 41E01E5516D5D5E600ED686C /* CocosDenshion.h in Headers */ = {isa = PBXBuildFile; fileRef = 41E01E4916D5D5E600ED686C /* CocosDenshion.h */; }; + 41E01E5616D5D5E600ED686C /* CocosDenshion.m in Sources */ = {isa = PBXBuildFile; fileRef = 41E01E4A16D5D5E600ED686C /* CocosDenshion.m */; }; + 41E01E5716D5D5E600ED686C /* SimpleAudioEngine.mm in Sources */ = {isa = PBXBuildFile; fileRef = 41E01E4B16D5D5E600ED686C /* SimpleAudioEngine.mm */; }; + 41E01E5816D5D5E600ED686C /* SimpleAudioEngine_objc.h in Headers */ = {isa = PBXBuildFile; fileRef = 41E01E4C16D5D5E600ED686C /* SimpleAudioEngine_objc.h */; }; + 41E01E5916D5D5E600ED686C /* SimpleAudioEngine_objc.m in Sources */ = {isa = PBXBuildFile; fileRef = 41E01E4D16D5D5E600ED686C /* SimpleAudioEngine_objc.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 41E01E4116D5D5E600ED686C /* Export.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Export.h; sourceTree = ""; }; + 41E01E4216D5D5E600ED686C /* SimpleAudioEngine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimpleAudioEngine.h; sourceTree = ""; }; + 41E01E4416D5D5E600ED686C /* CDAudioManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDAudioManager.h; sourceTree = ""; }; + 41E01E4516D5D5E600ED686C /* CDAudioManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDAudioManager.m; sourceTree = ""; }; + 41E01E4616D5D5E600ED686C /* CDConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDConfig.h; sourceTree = ""; }; + 41E01E4716D5D5E600ED686C /* CDOpenALSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDOpenALSupport.h; sourceTree = ""; }; + 41E01E4816D5D5E600ED686C /* CDOpenALSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDOpenALSupport.m; sourceTree = ""; }; + 41E01E4916D5D5E600ED686C /* CocosDenshion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CocosDenshion.h; sourceTree = ""; }; + 41E01E4A16D5D5E600ED686C /* CocosDenshion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CocosDenshion.m; sourceTree = ""; }; + 41E01E4B16D5D5E600ED686C /* SimpleAudioEngine.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SimpleAudioEngine.mm; sourceTree = ""; }; + 41E01E4C16D5D5E600ED686C /* SimpleAudioEngine_objc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimpleAudioEngine_objc.h; sourceTree = ""; }; + 41E01E4D16D5D5E600ED686C /* SimpleAudioEngine_objc.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SimpleAudioEngine_objc.m; sourceTree = ""; }; + D87CC2E5154FC6C500AAFE11 /* libCocosDenshion.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libCocosDenshion.a; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + D87CC2E2154FC6C500AAFE11 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 4193AF8416C39EB1007E21D7 /* CocosDenshion */ = { + isa = PBXGroup; + children = ( + 41E01E4016D5D5E600ED686C /* include */, + 41E01E4316D5D5E600ED686C /* ios */, + ); + name = CocosDenshion; + sourceTree = ""; + }; + 41E01E4016D5D5E600ED686C /* include */ = { + isa = PBXGroup; + children = ( + 41E01E4116D5D5E600ED686C /* Export.h */, + 41E01E4216D5D5E600ED686C /* SimpleAudioEngine.h */, + ); + name = include; + path = ../include; + sourceTree = ""; + }; + 41E01E4316D5D5E600ED686C /* ios */ = { + isa = PBXGroup; + children = ( + 41E01E4416D5D5E600ED686C /* CDAudioManager.h */, + 41E01E4516D5D5E600ED686C /* CDAudioManager.m */, + 41E01E4616D5D5E600ED686C /* CDConfig.h */, + 41E01E4716D5D5E600ED686C /* CDOpenALSupport.h */, + 41E01E4816D5D5E600ED686C /* CDOpenALSupport.m */, + 41E01E4916D5D5E600ED686C /* CocosDenshion.h */, + 41E01E4A16D5D5E600ED686C /* CocosDenshion.m */, + 41E01E4B16D5D5E600ED686C /* SimpleAudioEngine.mm */, + 41E01E4C16D5D5E600ED686C /* SimpleAudioEngine_objc.h */, + 41E01E4D16D5D5E600ED686C /* SimpleAudioEngine_objc.m */, + ); + name = ios; + path = ../ios; + sourceTree = ""; + }; + D87CC2BB154FC66100AAFE11 = { + isa = PBXGroup; + children = ( + 4193AF8416C39EB1007E21D7 /* CocosDenshion */, + D87CC2C9154FC66100AAFE11 /* Frameworks */, + D87CC2C7154FC66100AAFE11 /* Products */, + ); + sourceTree = ""; + }; + D87CC2C7154FC66100AAFE11 /* Products */ = { + isa = PBXGroup; + children = ( + D87CC2E5154FC6C500AAFE11 /* libCocosDenshion.a */, + ); + name = Products; + sourceTree = ""; + }; + D87CC2C9154FC66100AAFE11 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + D87CC2E3154FC6C500AAFE11 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 41E01E4E16D5D5E600ED686C /* Export.h in Headers */, + 41E01E4F16D5D5E600ED686C /* SimpleAudioEngine.h in Headers */, + 41E01E5016D5D5E600ED686C /* CDAudioManager.h in Headers */, + 41E01E5216D5D5E600ED686C /* CDConfig.h in Headers */, + 41E01E5316D5D5E600ED686C /* CDOpenALSupport.h in Headers */, + 41E01E5516D5D5E600ED686C /* CocosDenshion.h in Headers */, + 41E01E5816D5D5E600ED686C /* SimpleAudioEngine_objc.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + D87CC2E4154FC6C500AAFE11 /* CocosDenshion */ = { + isa = PBXNativeTarget; + buildConfigurationList = D87CC2ED154FC6C500AAFE11 /* Build configuration list for PBXNativeTarget "CocosDenshion" */; + buildPhases = ( + D87CC2E1154FC6C500AAFE11 /* Sources */, + D87CC2E2154FC6C500AAFE11 /* Frameworks */, + D87CC2E3154FC6C500AAFE11 /* Headers */, + 185ADB8915A3935900CD7CE0 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = CocosDenshion; + productName = SPII; + productReference = D87CC2E5154FC6C500AAFE11 /* libCocosDenshion.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + D87CC2BD154FC66100AAFE11 /* Project object */ = { + isa = PBXProject; + attributes = { + CLASSPREFIX = SP; + LastUpgradeCheck = 0430; + ORGANIZATIONNAME = Cocoachina; + }; + buildConfigurationList = D87CC2C0154FC66100AAFE11 /* Build configuration list for PBXProject "CocosDenshion" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + "zh-Hans", + "zh-Hant", + ); + mainGroup = D87CC2BB154FC66100AAFE11; + productRefGroup = D87CC2C7154FC66100AAFE11 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + D87CC2E4154FC6C500AAFE11 /* CocosDenshion */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXShellScriptBuildPhase section */ + 185ADB8915A3935900CD7CE0 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = ""; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + D87CC2E1154FC6C500AAFE11 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 41E01E5116D5D5E600ED686C /* CDAudioManager.m in Sources */, + 41E01E5416D5D5E600ED686C /* CDOpenALSupport.m in Sources */, + 41E01E5616D5D5E600ED686C /* CocosDenshion.m in Sources */, + 41E01E5716D5D5E600ED686C /* SimpleAudioEngine.mm in Sources */, + 41E01E5916D5D5E600ED686C /* SimpleAudioEngine_objc.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + D87CC2DC154FC66100AAFE11 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "compiler-default"; + CLANG_CXX_LIBRARY = "compiler-default"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = c89; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ""; + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_VERSION = ""; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ""; + IPHONEOS_DEPLOYMENT_TARGET = 4.3; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALID_ARCHS = armv7; + }; + name = Debug; + }; + D87CC2DD154FC66100AAFE11 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "compiler-default"; + CLANG_CXX_LIBRARY = "compiler-default"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + GCC_C_LANGUAGE_STANDARD = c89; + GCC_PREPROCESSOR_DEFINITIONS = ""; + GCC_VERSION = ""; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ""; + IPHONEOS_DEPLOYMENT_TARGET = 4.3; + OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VALID_ARCHS = armv7; + }; + name = Release; + }; + D87CC2EE154FC6C500AAFE11 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = YES; + DSTROOT = /tmp/SPII.dst; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_C_LANGUAGE_STANDARD = "compiler-default"; + GCC_INCREASE_PRECOMPILED_HEADER_SHARING = NO; + GCC_INLINES_ARE_PRIVATE_EXTERN = YES; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_PREFIX_HEADER = ""; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "COCOS2D_DEBUG=1", + CC_TARGET_OS_IPHONE, + ); + GENERATE_PKGINFO_FILE = NO; + HEADER_SEARCH_PATHS = "\"$(SDKROOT)/usr/include/libxml2\""; + IPHONEOS_DEPLOYMENT_TARGET = 4.3; + LIBRARY_SEARCH_PATHS = ""; + OTHER_LDFLAGS = "-ObjC"; + PRIVATE_HEADERS_FOLDER_PATH = /usr/local/include; + PRODUCT_NAME = CocosDenshion; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + USER_HEADER_SEARCH_PATHS = "../../cocos2dx/platform/ios ../../cocos2dx/include ../../cocos2dx/kazmath/include ../../cocos2dx/"; + VALID_ARCHS = armv7; + }; + name = Debug; + }; + D87CC2EF154FC6C500AAFE11 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = YES; + DSTROOT = /tmp/SPII.dst; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_C_LANGUAGE_STANDARD = "compiler-default"; + GCC_INCREASE_PRECOMPILED_HEADER_SHARING = NO; + GCC_INLINES_ARE_PRIVATE_EXTERN = YES; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_PREFIX_HEADER = ""; + GCC_PREPROCESSOR_DEFINITIONS = CC_TARGET_OS_IPHONE; + GENERATE_PKGINFO_FILE = NO; + HEADER_SEARCH_PATHS = "\"$(SDKROOT)/usr/include/libxml2\""; + IPHONEOS_DEPLOYMENT_TARGET = 4.3; + LIBRARY_SEARCH_PATHS = ""; + OTHER_LDFLAGS = "-ObjC"; + PRIVATE_HEADERS_FOLDER_PATH = /usr/local/include; + PRODUCT_NAME = CocosDenshion; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + USER_HEADER_SEARCH_PATHS = "../../cocos2dx/platform/ios ../../cocos2dx/include ../../cocos2dx/kazmath/include ../../cocos2dx/"; + VALID_ARCHS = armv7; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + D87CC2C0154FC66100AAFE11 /* Build configuration list for PBXProject "CocosDenshion" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D87CC2DC154FC66100AAFE11 /* Debug */, + D87CC2DD154FC66100AAFE11 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D87CC2ED154FC6C500AAFE11 /* Build configuration list for PBXNativeTarget "CocosDenshion" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D87CC2EE154FC6C500AAFE11 /* Debug */, + D87CC2EF154FC6C500AAFE11 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = D87CC2BD154FC66100AAFE11 /* Project object */; +} diff --git a/cocos2dx/Android.mk b/cocos2dx/Android.mk index 16966e86c8..fa26f984f8 100644 --- a/cocos2dx/Android.mk +++ b/cocos2dx/Android.mk @@ -106,6 +106,7 @@ sprite_nodes/CCSprite.cpp \ sprite_nodes/CCSpriteBatchNode.cpp \ sprite_nodes/CCSpriteFrame.cpp \ sprite_nodes/CCSpriteFrameCache.cpp \ +support/ccUTF8.cpp \ support/CCNotificationCenter.cpp \ support/CCProfiling.cpp \ support/CCPointExtension.cpp \ diff --git a/cocos2dx/include/cocos2d.h b/cocos2dx/include/cocos2d.h index 2e1a365bbd..6fd7816e2b 100755 --- a/cocos2dx/include/cocos2d.h +++ b/cocos2dx/include/cocos2d.h @@ -210,6 +210,7 @@ THE SOFTWARE. #include "sprite_nodes/CCSpriteFrameCache.h" // support +#include "support/ccUTF8.h" #include "support/CCNotificationCenter.h" #include "support/CCPointExtension.h" #include "support/CCProfiling.h" diff --git a/cocos2dx/label_nodes/CCLabelBMFont.cpp b/cocos2dx/label_nodes/CCLabelBMFont.cpp index 448ec439fd..c13fce8172 100644 --- a/cocos2dx/label_nodes/CCLabelBMFont.cpp +++ b/cocos2dx/label_nodes/CCLabelBMFont.cpp @@ -41,313 +41,12 @@ http://www.angelcode.com/products/bmfont/ (Free, Windows only) #include "platform/CCFileUtils.h" #include "CCDirector.h" #include "textures/CCTextureCache.h" +#include "support/ccUTF8.h" using namespace std; NS_CC_BEGIN - -static int cc_wcslen(const unsigned short* str) -{ - int i=0; - while(*str++) i++; - return i; -} - -/* Code from GLIB gutf8.c starts here. */ - -#define UTF8_COMPUTE(Char, Mask, Len) \ - if (Char < 128) \ - { \ - Len = 1; \ - Mask = 0x7f; \ - } \ - else if ((Char & 0xe0) == 0xc0) \ - { \ - Len = 2; \ - Mask = 0x1f; \ - } \ - else if ((Char & 0xf0) == 0xe0) \ - { \ - Len = 3; \ - Mask = 0x0f; \ - } \ - else if ((Char & 0xf8) == 0xf0) \ - { \ - Len = 4; \ - Mask = 0x07; \ - } \ - else if ((Char & 0xfc) == 0xf8) \ - { \ - Len = 5; \ - Mask = 0x03; \ - } \ - else if ((Char & 0xfe) == 0xfc) \ - { \ - Len = 6; \ - Mask = 0x01; \ - } \ - else \ - Len = -1; - -#define UTF8_LENGTH(Char) \ - ((Char) < 0x80 ? 1 : \ - ((Char) < 0x800 ? 2 : \ - ((Char) < 0x10000 ? 3 : \ - ((Char) < 0x200000 ? 4 : \ - ((Char) < 0x4000000 ? 5 : 6))))) - - -#define UTF8_GET(Result, Chars, Count, Mask, Len) \ - (Result) = (Chars)[0] & (Mask); \ - for ((Count) = 1; (Count) < (Len); ++(Count)) \ - { \ - if (((Chars)[(Count)] & 0xc0) != 0x80) \ - { \ - (Result) = -1; \ - break; \ - } \ - (Result) <<= 6; \ - (Result) |= ((Chars)[(Count)] & 0x3f); \ - } - -#define UNICODE_VALID(Char) \ - ((Char) < 0x110000 && \ - (((Char) & 0xFFFFF800) != 0xD800) && \ - ((Char) < 0xFDD0 || (Char) > 0xFDEF) && \ - ((Char) & 0xFFFE) != 0xFFFE) - - -static const char utf8_skip_data[256] = { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, - 5, 5, 5, 6, 6, 1, 1 -}; - -static const char *const g_utf8_skip = utf8_skip_data; - -#define cc_utf8_next_char(p) (char *)((p) + g_utf8_skip[*(unsigned char *)(p)]) - -/* - * @str: the string to search through. - * @c: the character to find. - * - * Returns the index of the first occurrence of the character, if found. Otherwise -1 is returned. - * - * Return value: the index of the first occurrence of the character if found or -1 otherwise. - * */ -static unsigned int cc_utf8_find_char(std::vector str, unsigned short c) -{ - unsigned int len = str.size(); - - for (unsigned int i = 0; i < len; ++i) - if (str[i] == c) return i; - - return -1; -} - -/* - * @str: the string to search through. - * @c: the character to not look for. - * - * Return value: the index of the last character that is not c. - * */ -static unsigned int cc_utf8_find_last_not_char(std::vector str, unsigned short c) -{ - int len = str.size(); - - int i = len - 1; - for (; i >= 0; --i) - if (str[i] != c) return i; - - return i; -} - -/* - * @str: the string to trim - * @index: the index to start trimming from. - * - * Trims str st str=[0, index) after the operation. - * - * Return value: the trimmed string. - * */ -static void cc_utf8_trim_from(std::vector* str, int index) -{ - int size = str->size(); - if (index >= size || index < 0) - return; - - str->erase(str->begin() + index, str->begin() + size); -} - -/* - * @ch is the unicode character whitespace? - * - * Reference: http://en.wikipedia.org/wiki/Whitespace_character#Unicode - * - * Return value: weather the character is a whitespace character. - * */ -static bool isspace_unicode(unsigned short ch) -{ - return (ch >= 0x0009 && ch <= 0x000D) || ch == 0x0020 || ch == 0x0085 || ch == 0x00A0 || ch == 0x1680 - || (ch >= 0x2000 && ch <= 0x200A) || ch == 0x2028 || ch == 0x2029 || ch == 0x202F - || ch == 0x205F || ch == 0x3000; -} - -static void cc_utf8_trim_ws(std::vector* str) -{ - int len = str->size(); - - if ( len <= 0 ) - return; - - int last_index = len - 1; - - // Only start trimming if the last character is whitespace.. - if (isspace_unicode((*str)[last_index])) - { - for (int i = last_index - 1; i >= 0; --i) - { - if (isspace_unicode((*str)[i])) - last_index = i; - else - break; - } - - cc_utf8_trim_from(str, last_index); - } -} - -/* - * g_utf8_strlen: - * @p: pointer to the start of a UTF-8 encoded string. - * @max: the maximum number of bytes to examine. If @max - * is less than 0, then the string is assumed to be - * null-terminated. If @max is 0, @p will not be examined and - * may be %NULL. - * - * Returns the length of the string in characters. - * - * Return value: the length of the string in characters - **/ -CC_DLL long -cc_utf8_strlen (const char * p, int max) -{ - long len = 0; - const char *start = p; - - if (!(p != NULL || max == 0)) - { - return 0; - } - - if (max < 0) - { - while (*p) - { - p = cc_utf8_next_char (p); - ++len; - } - } - else - { - if (max == 0 || !*p) - return 0; - - p = cc_utf8_next_char (p); - - while (p - start < max && *p) - { - ++len; - p = cc_utf8_next_char (p); - } - - /* only do the last len increment if we got a complete - * char (don't count partial chars) - */ - if (p - start == max) - ++len; - } - - return len; -} - -/* - * g_utf8_get_char: - * @p: a pointer to Unicode character encoded as UTF-8 - * - * Converts a sequence of bytes encoded as UTF-8 to a Unicode character. - * If @p does not point to a valid UTF-8 encoded character, results are - * undefined. If you are not sure that the bytes are complete - * valid Unicode characters, you should use g_utf8_get_char_validated() - * instead. - * - * Return value: the resulting character - **/ -static unsigned int -cc_utf8_get_char (const char * p) -{ - int i, mask = 0, len; - unsigned int result; - unsigned char c = (unsigned char) *p; - - UTF8_COMPUTE (c, mask, len); - if (len == -1) - return (unsigned int) - 1; - UTF8_GET (result, p, i, mask, len); - - return result; -} - -/* - * cc_utf16_from_utf8: - * @str_old: pointer to the start of a C string. - * - * Creates a utf8 string from a cstring. - * - * Return value: the newly created utf8 string. - * */ -static unsigned short* cc_utf16_from_utf8(const char* str_old) -{ - int len = cc_utf8_strlen(str_old, -1); - - unsigned short* str_new = new unsigned short[len + 1]; - str_new[len] = 0; - - for (int i = 0; i < len; ++i) - { - str_new[i] = cc_utf8_get_char(str_old); - str_old = cc_utf8_next_char(str_old); - } - - return str_new; -} - -static std::vector cc_utf16_vec_from_utf16_str(const unsigned short* str) -{ - int len = cc_wcslen(str); - std::vector str_new; - - for (int i = 0; i < len; ++i) - { - str_new.push_back(str[i]); - } - return str_new; -} - // //FNTConfig Cache - free functions // @@ -1017,7 +716,7 @@ void CCLabelBMFont::setString(const char *newString) void CCLabelBMFont::setString(const char *newString, bool fromUpdate) { CC_SAFE_DELETE_ARRAY(m_sString); - m_sString = cc_utf16_from_utf8(newString); + m_sString = cc_utf8_to_utf16(newString); // MARMALADE CHANGE // THE ASSIGMENT OF STRINGS BELOW PERFORMS AN OVERLAPPING MEMCPY, WHEN fromUpdate IS TRUE diff --git a/cocos2dx/proj.ios/cocos2dx.xcodeproj/project.pbxproj.REMOVED.git-id b/cocos2dx/proj.ios/cocos2dx.xcodeproj/project.pbxproj.REMOVED.git-id index e6fa28491c..b0e53498e9 100644 --- a/cocos2dx/proj.ios/cocos2dx.xcodeproj/project.pbxproj.REMOVED.git-id +++ b/cocos2dx/proj.ios/cocos2dx.xcodeproj/project.pbxproj.REMOVED.git-id @@ -1 +1 @@ -07189afafe45b5ce86fe17e77400ffa055cd1831 \ No newline at end of file +8e581cccdac41ebd5f1a9c26b08f02b948560fba \ No newline at end of file diff --git a/cocos2dx/proj.linux/Makefile b/cocos2dx/proj.linux/Makefile index 86b429cd2a..d741870c18 100644 --- a/cocos2dx/proj.linux/Makefile +++ b/cocos2dx/proj.linux/Makefile @@ -104,6 +104,7 @@ OBJECTS = ../actions/CCAction.o \ ../sprite_nodes/CCSpriteBatchNode.o \ ../sprite_nodes/CCSpriteFrame.o \ ../sprite_nodes/CCSpriteFrameCache.o \ +../support/ccUTF8.o \ ../support/CCPointExtension.o \ ../support/CCProfiling.o \ ../support/CCUserDefault.o \ diff --git a/cocos2dx/proj.mac/cocos2dx.xcodeproj/project.pbxproj.REMOVED.git-id b/cocos2dx/proj.mac/cocos2dx.xcodeproj/project.pbxproj.REMOVED.git-id index fc1091c2d2..677b1d3b33 100644 --- a/cocos2dx/proj.mac/cocos2dx.xcodeproj/project.pbxproj.REMOVED.git-id +++ b/cocos2dx/proj.mac/cocos2dx.xcodeproj/project.pbxproj.REMOVED.git-id @@ -1 +1 @@ -4f8a2ffda386a2d2e263781a283c32d39b6dc6a7 \ No newline at end of file +cc07100d26f9435ecf7b2588f14adb3d22980883 \ No newline at end of file diff --git a/cocos2dx/proj.marmalade/cocos2dx.mkf b/cocos2dx/proj.marmalade/cocos2dx.mkf index cec3bb8747..7ea9c89697 100644 --- a/cocos2dx/proj.marmalade/cocos2dx.mkf +++ b/cocos2dx/proj.marmalade/cocos2dx.mkf @@ -156,35 +156,29 @@ files [sprite_nodes] "*.h" "*.cpp" - - ("../support") - [support] - "base64.cpp" - "base64.h" -# "CCArray.cpp" - "CCPointExtension.cpp" - "CCProfiling.cpp" - "CCProfiling.h" - "CCUserDefault.cpp" - "ccUtils.cpp" - "ccUtils.h" - "TransformUtils.cpp" - "TransformUtils.h" ("../support") [support] "*.h" "*.cpp" + ("../support/data_support") + [support/data_support] "*.cpp" # MH: Many ccCArray linker errors without ccCArray.cpp "*.h" - ("../support/image_support") + + ("../support/image_support") + [support/image_support] "*.h" "*.cpp" - ("../support/tinyxml2") - "*.h" - "*.cpp" + + ("../support/tinyxml2") + [support/tinyxml2] + "*.h" + "*.cpp" + ("../support/zip_support") + [support/zip_support] "*.h" "*.cpp" diff --git a/cocos2dx/proj.win32/cocos2d.vcxproj b/cocos2dx/proj.win32/cocos2d.vcxproj index ce3d221f3c..e04a6488e5 100644 --- a/cocos2dx/proj.win32/cocos2d.vcxproj +++ b/cocos2dx/proj.win32/cocos2d.vcxproj @@ -215,6 +215,7 @@ xcopy /Y /Q "$(ProjectDir)..\platform\third_party\win32\libraries\*.*" "$(OutDir + @@ -366,6 +367,7 @@ xcopy /Y /Q "$(ProjectDir)..\platform\third_party\win32\libraries\*.*" "$(OutDir + diff --git a/cocos2dx/proj.win32/cocos2d.vcxproj.filters b/cocos2dx/proj.win32/cocos2d.vcxproj.filters index 8e35204db1..f80eba0d96 100644 --- a/cocos2dx/proj.win32/cocos2d.vcxproj.filters +++ b/cocos2dx/proj.win32/cocos2d.vcxproj.filters @@ -450,6 +450,9 @@ platform\win32 + + support + @@ -901,5 +904,8 @@ platform\win32 + + support + \ No newline at end of file diff --git a/cocos2dx/support/ccUTF8.cpp b/cocos2dx/support/ccUTF8.cpp new file mode 100644 index 0000000000..d4ffcd033a --- /dev/null +++ b/cocos2dx/support/ccUTF8.cpp @@ -0,0 +1,529 @@ +/* + * This file uses some implementations of gutf8.c in glib. + * + * gutf8.c - Operations on UTF-8 strings. + * + * Copyright (C) 1999 Tom Tromey + * Copyright (C) 2000 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "ccUTF8.h" +#include "platform/CCCommon.h" + +NS_CC_BEGIN + +int cc_wcslen(const unsigned short* str) +{ + int i=0; + while(*str++) i++; + return i; +} + +/* Code from GLIB gutf8.c starts here. */ + +#define UTF8_COMPUTE(Char, Mask, Len) \ +if (Char < 128) \ +{ \ +Len = 1; \ +Mask = 0x7f; \ +} \ +else if ((Char & 0xe0) == 0xc0) \ +{ \ +Len = 2; \ +Mask = 0x1f; \ +} \ +else if ((Char & 0xf0) == 0xe0) \ +{ \ +Len = 3; \ +Mask = 0x0f; \ +} \ +else if ((Char & 0xf8) == 0xf0) \ +{ \ +Len = 4; \ +Mask = 0x07; \ +} \ +else if ((Char & 0xfc) == 0xf8) \ +{ \ +Len = 5; \ +Mask = 0x03; \ +} \ +else if ((Char & 0xfe) == 0xfc) \ +{ \ +Len = 6; \ +Mask = 0x01; \ +} \ +else \ +Len = -1; + +#define UTF8_LENGTH(Char) \ +((Char) < 0x80 ? 1 : \ +((Char) < 0x800 ? 2 : \ +((Char) < 0x10000 ? 3 : \ +((Char) < 0x200000 ? 4 : \ +((Char) < 0x4000000 ? 5 : 6))))) + + +#define UTF8_GET(Result, Chars, Count, Mask, Len) \ +(Result) = (Chars)[0] & (Mask); \ +for ((Count) = 1; (Count) < (Len); ++(Count)) \ +{ \ +if (((Chars)[(Count)] & 0xc0) != 0x80) \ +{ \ +(Result) = -1; \ +break; \ +} \ +(Result) <<= 6; \ +(Result) |= ((Chars)[(Count)] & 0x3f); \ +} + +#define UNICODE_VALID(Char) \ +((Char) < 0x110000 && \ +(((Char) & 0xFFFFF800) != 0xD800) && \ +((Char) < 0xFDD0 || (Char) > 0xFDEF) && \ +((Char) & 0xFFFE) != 0xFFFE) + + +static const char utf8_skip_data[256] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, + 5, 5, 5, 6, 6, 1, 1 +}; + +static const char *const g_utf8_skip = utf8_skip_data; + +#define cc_utf8_next_char(p) (char *)((p) + g_utf8_skip[*(unsigned char *)(p)]) + +/* + * @str: the string to search through. + * @c: the character to find. + * + * Returns the index of the first occurrence of the character, if found. Otherwise -1 is returned. + * + * Return value: the index of the first occurrence of the character if found or -1 otherwise. + * */ +static unsigned int cc_utf8_find_char(std::vector str, unsigned short c) +{ + unsigned int len = str.size(); + + for (unsigned int i = 0; i < len; ++i) + if (str[i] == c) return i; + + return -1; +} + +/* + * @str: the string to search through. + * @c: the character to not look for. + * + * Return value: the index of the last character that is not c. + * */ +unsigned int cc_utf8_find_last_not_char(std::vector str, unsigned short c) +{ + int len = str.size(); + + int i = len - 1; + for (; i >= 0; --i) + if (str[i] != c) return i; + + return i; +} + +/* + * @str: the string to trim + * @index: the index to start trimming from. + * + * Trims str st str=[0, index) after the operation. + * + * Return value: the trimmed string. + * */ +static void cc_utf8_trim_from(std::vector* str, int index) +{ + int size = str->size(); + if (index >= size || index < 0) + return; + + str->erase(str->begin() + index, str->begin() + size); +} + +/* + * @ch is the unicode character whitespace? + * + * Reference: http://en.wikipedia.org/wiki/Whitespace_character#Unicode + * + * Return value: weather the character is a whitespace character. + * */ +bool isspace_unicode(unsigned short ch) +{ + return (ch >= 0x0009 && ch <= 0x000D) || ch == 0x0020 || ch == 0x0085 || ch == 0x00A0 || ch == 0x1680 + || (ch >= 0x2000 && ch <= 0x200A) || ch == 0x2028 || ch == 0x2029 || ch == 0x202F + || ch == 0x205F || ch == 0x3000; +} + +void cc_utf8_trim_ws(std::vector* str) +{ + int len = str->size(); + + if ( len <= 0 ) + return; + + int last_index = len - 1; + + // Only start trimming if the last character is whitespace.. + if (isspace_unicode((*str)[last_index])) + { + for (int i = last_index - 1; i >= 0; --i) + { + if (isspace_unicode((*str)[i])) + last_index = i; + else + break; + } + + cc_utf8_trim_from(str, last_index); + } +} + +/* + * cc_utf8_strlen: + * @p: pointer to the start of a UTF-8 encoded string. + * @max: the maximum number of bytes to examine. If @max + * is less than 0, then the string is assumed to be + * null-terminated. If @max is 0, @p will not be examined and + * may be %NULL. + * + * Returns the length of the string in characters. + * + * Return value: the length of the string in characters + **/ +long +cc_utf8_strlen (const char * p, int max) +{ + long len = 0; + const char *start = p; + + if (!(p != NULL || max == 0)) + { + return 0; + } + + if (max < 0) + { + while (*p) + { + p = cc_utf8_next_char (p); + ++len; + } + } + else + { + if (max == 0 || !*p) + return 0; + + p = cc_utf8_next_char (p); + + while (p - start < max && *p) + { + ++len; + p = cc_utf8_next_char (p); + } + + /* only do the last len increment if we got a complete + * char (don't count partial chars) + */ + if (p - start == max) + ++len; + } + + return len; +} + +/* + * g_utf8_get_char: + * @p: a pointer to Unicode character encoded as UTF-8 + * + * Converts a sequence of bytes encoded as UTF-8 to a Unicode character. + * If @p does not point to a valid UTF-8 encoded character, results are + * undefined. If you are not sure that the bytes are complete + * valid Unicode characters, you should use g_utf8_get_char_validated() + * instead. + * + * Return value: the resulting character + **/ +static unsigned int +cc_utf8_get_char (const char * p) +{ + int i, mask = 0, len; + unsigned int result; + unsigned char c = (unsigned char) *p; + + UTF8_COMPUTE (c, mask, len); + if (len == -1) + return (unsigned int) - 1; + UTF8_GET (result, p, i, mask, len); + + return result; +} + + +unsigned short* cc_utf8_to_utf16(const char* str_old) +{ + int len = cc_utf8_strlen(str_old, -1); + + unsigned short* str_new = new unsigned short[len + 1]; + str_new[len] = 0; + + for (int i = 0; i < len; ++i) + { + str_new[i] = cc_utf8_get_char(str_old); + str_old = cc_utf8_next_char(str_old); + } + + return str_new; +} + +std::vector cc_utf16_vec_from_utf16_str(const unsigned short* str) +{ + int len = cc_wcslen(str); + std::vector str_new; + + for (int i = 0; i < len; ++i) + { + str_new.push_back(str[i]); + } + return str_new; +} + +/** + * cc_unichar_to_utf8: + * @c: a ISO10646 character code + * @outbuf: output buffer, must have at least 6 bytes of space. + * If %NULL, the length will be computed and returned + * and nothing will be written to @outbuf. + * + * Converts a single character to UTF-8. + * + * Return value: number of bytes written + **/ +int +cc_unichar_to_utf8 (unsigned short c, + char *outbuf) +{ + unsigned int len = 0; + int first; + int i; + + if (c < 0x80) + { + first = 0; + len = 1; + } + else if (c < 0x800) + { + first = 0xc0; + len = 2; + } + else if (c < 0x10000) + { + first = 0xe0; + len = 3; + } + else if (c < 0x200000) + { + first = 0xf0; + len = 4; + } + else if (c < 0x4000000) + { + first = 0xf8; + len = 5; + } + else + { + first = 0xfc; + len = 6; + } + + if (outbuf) + { + for (i = len - 1; i > 0; --i) + { + outbuf[i] = (c & 0x3f) | 0x80; + c >>= 6; + } + outbuf[0] = c | first; + } + + return len; +} + +#define SURROGATE_VALUE(h,l) (((h) - 0xd800) * 0x400 + (l) - 0xdc00 + 0x10000) + +/** + * cc_utf16_to_utf8: + * @str: a UTF-16 encoded string + * @len: the maximum length of @str to use. If @len < 0, then + * the string is terminated with a 0 character. + * @items_read: location to store number of words read, or %NULL. + * If %NULL, then %G_CONVERT_ERROR_PARTIAL_INPUT will be + * returned in case @str contains a trailing partial + * character. If an error occurs then the index of the + * invalid input is stored here. + * @items_written: location to store number of bytes written, or %NULL. + * The value stored here does not include the trailing + * 0 byte. + * @error: location to store the error occuring, or %NULL to ignore + * errors. Any of the errors in #GConvertError other than + * %G_CONVERT_ERROR_NO_CONVERSION may occur. + * + * Convert a string from UTF-16 to UTF-8. The result will be + * terminated with a 0 byte. + * + * Return value: a pointer to a newly allocated UTF-8 string. + * This value must be freed with free(). If an + * error occurs, %NULL will be returned and + * @error set. + **/ +char * +cc_utf16_to_utf8 (const unsigned short *str, + long len, + long *items_read, + long *items_written) +{ + /* This function and g_utf16_to_ucs4 are almost exactly identical - The lines that differ + * are marked. + */ + const unsigned short *in; + char *out; + char *result = NULL; + int n_bytes; + unsigned short high_surrogate; + + if (str == 0) return NULL; + + n_bytes = 0; + in = str; + high_surrogate = 0; + while ((len < 0 || in - str < len) && *in) + { + unsigned short c = *in; + unsigned short wc; + + if (c >= 0xdc00 && c < 0xe000) /* low surrogate */ + { + if (high_surrogate) + { + wc = SURROGATE_VALUE (high_surrogate, c); + high_surrogate = 0; + } + else + { + CCLOGERROR("Invalid sequence in conversion input"); + goto err_out; + } + } + else + { + if (high_surrogate) + { + CCLOGERROR("Invalid sequence in conversion input"); + goto err_out; + } + + if (c >= 0xd800 && c < 0xdc00) /* high surrogate */ + { + high_surrogate = c; + goto next1; + } + else + wc = c; + } + + /********** DIFFERENT for UTF8/UCS4 **********/ + n_bytes += UTF8_LENGTH (wc); + + next1: + in++; + } + + if (high_surrogate && !items_read) + { + CCLOGERROR("Partial character sequence at end of input"); + goto err_out; + } + + /* At this point, everything is valid, and we just need to convert + */ + /********** DIFFERENT for UTF8/UCS4 **********/ + result = new char[n_bytes + 1]; + + high_surrogate = 0; + out = result; + in = str; + while (out < result + n_bytes) + { + unsigned short c = *in; + unsigned short wc; + + if (c >= 0xdc00 && c < 0xe000) /* low surrogate */ + { + wc = SURROGATE_VALUE (high_surrogate, c); + high_surrogate = 0; + } + else if (c >= 0xd800 && c < 0xdc00) /* high surrogate */ + { + high_surrogate = c; + goto next2; + } + else + wc = c; + + /********** DIFFERENT for UTF8/UCS4 **********/ + out += cc_unichar_to_utf8 (wc, out); + + next2: + in++; + } + + /********** DIFFERENT for UTF8/UCS4 **********/ + *out = '\0'; + + if (items_written) + /********** DIFFERENT for UTF8/UCS4 **********/ + *items_written = out - result; + +err_out: + if (items_read) + *items_read = in - str; + + return result; +} + +NS_CC_END diff --git a/cocos2dx/support/ccUTF8.h b/cocos2dx/support/ccUTF8.h new file mode 100644 index 0000000000..d7750a4d34 --- /dev/null +++ b/cocos2dx/support/ccUTF8.h @@ -0,0 +1,97 @@ +// +// ccUTF8.h +// cocos2dx +// +// Created by James Chen on 2/27/13. +// + +#ifndef __cocos2dx__ccUTF8__ +#define __cocos2dx__ccUTF8__ + +#include "platform/CCPlatformMacros.h" +#include + +NS_CC_BEGIN + +CC_DLL int cc_wcslen(const unsigned short* str); + +CC_DLL void cc_utf8_trim_ws(std::vector* str); + +/* + * @ch is the unicode character whitespace? + * + * Reference: http://en.wikipedia.org/wiki/Whitespace_character#Unicode + * + * Return value: weather the character is a whitespace character. + * */ +CC_DLL bool isspace_unicode(unsigned short ch); + +/* + * cc_utf8_strlen: + * @p: pointer to the start of a UTF-8 encoded string. + * @max: the maximum number of bytes to examine. If @max + * is less than 0, then the string is assumed to be + * null-terminated. If @max is 0, @p will not be examined and + * may be %NULL. + * + * Returns the length of the string in characters. + * + * Return value: the length of the string in characters + **/ +CC_DLL long +cc_utf8_strlen (const char * p, int max); + +/* + * @str: the string to search through. + * @c: the character to not look for. + * + * Return value: the index of the last character that is not c. + * */ +CC_DLL unsigned int cc_utf8_find_last_not_char(std::vector str, unsigned short c); + +CC_DLL std::vector cc_utf16_vec_from_utf16_str(const unsigned short* str); + +/* + * cc_utf8_to_utf16: + * @str_old: pointer to the start of a C string. + * + * Creates a utf8 string from a cstring. + * + * Return value: the newly created utf8 string. + * */ +CC_DLL unsigned short* cc_utf8_to_utf16(const char* str_old); + +/** + * cc_utf16_to_utf8: + * @str: a UTF-16 encoded string + * @len: the maximum length of @str to use. If @len < 0, then + * the string is terminated with a 0 character. + * @items_read: location to store number of words read, or %NULL. + * If %NULL, then %G_CONVERT_ERROR_PARTIAL_INPUT will be + * returned in case @str contains a trailing partial + * character. If an error occurs then the index of the + * invalid input is stored here. + * @items_written: location to store number of bytes written, or %NULL. + * The value stored here does not include the trailing + * 0 byte. + * @error: location to store the error occuring, or %NULL to ignore + * errors. Any of the errors in #GConvertError other than + * %G_CONVERT_ERROR_NO_CONVERSION may occur. + * + * Convert a string from UTF-16 to UTF-8. The result will be + * terminated with a 0 byte. + * + * Return value: a pointer to a newly allocated UTF-8 string. + * This value must be freed with free(). If an + * error occurs, %NULL will be returned and + * @error set. + **/ +CC_DLL char * +cc_utf16_to_utf8 (const unsigned short *str, + long len, + long *items_read, + long *items_written); + +NS_CC_END + +#endif /* defined(__cocos2dx__ccUTF8__) */ diff --git a/extensions/GUI/CCEditBox/CCEditBoxImplAndroid.cpp b/extensions/GUI/CCEditBox/CCEditBoxImplAndroid.cpp index e0c70b06c4..d5dfde8cf6 100644 --- a/extensions/GUI/CCEditBox/CCEditBoxImplAndroid.cpp +++ b/extensions/GUI/CCEditBox/CCEditBoxImplAndroid.cpp @@ -31,11 +31,6 @@ #include "jni/Java_org_cocos2dx_lib_Cocos2dxBitmap.h" #include "jni/Java_org_cocos2dx_lib_Cocos2dxHelper.h" -// This function is implemented in CCLabelBMFont.cpp - -NS_CC_BEGIN -extern long cc_utf8_strlen (const char * p, int max); -NS_CC_END NS_CC_EXT_BEGIN diff --git a/extensions/GUI/CCEditBox/CCEditBoxImplWin.cpp b/extensions/GUI/CCEditBox/CCEditBoxImplWin.cpp index 4caf0b6ee9..118ddc40c7 100644 --- a/extensions/GUI/CCEditBox/CCEditBoxImplWin.cpp +++ b/extensions/GUI/CCEditBox/CCEditBoxImplWin.cpp @@ -29,10 +29,6 @@ #include "CCEditBox.h" #include "proj.win32/Win32InputBox.h" -NS_CC_BEGIN -extern CC_DLL long cc_utf8_strlen (const char * p, int max); -NS_CC_END - NS_CC_EXT_BEGIN CCEditBoxImpl* __createSystemEditBox(CCEditBox* pEditBox) diff --git a/extensions/GUI/CCScrollView/CCTableView.cpp b/extensions/GUI/CCScrollView/CCTableView.cpp index a1015cac21..bd03cfbab3 100644 --- a/extensions/GUI/CCScrollView/CCTableView.cpp +++ b/extensions/GUI/CCScrollView/CCTableView.cpp @@ -101,6 +101,7 @@ CCTableViewVerticalFillOrder CCTableView::getVerticalFillOrder() void CCTableView::reloadData() { + m_eOldDirection = kCCScrollViewDirectionNone; CCObject* pObj = NULL; CCARRAY_FOREACH(m_pCellsUsed, pObj) { diff --git a/samples/Javascript/Shared b/samples/Javascript/Shared index cc6d8acc73..0455fdce1e 160000 --- a/samples/Javascript/Shared +++ b/samples/Javascript/Shared @@ -1 +1 @@ -Subproject commit cc6d8acc731c5a6970e96cc2ba231718ba693dfc +Subproject commit 0455fdce1ec89d6bf335e395b48882aaf875d38b diff --git a/samples/Lua/TestLua/Resources/luaScript/ActionsProgressTest/ActionsProgressTest.lua b/samples/Lua/TestLua/Resources/luaScript/ActionsProgressTest/ActionsProgressTest.lua index 7f9cf8ed7f..4a973d5f71 100644 --- a/samples/Lua/TestLua/Resources/luaScript/ActionsProgressTest/ActionsProgressTest.lua +++ b/samples/Lua/TestLua/Resources/luaScript/ActionsProgressTest/ActionsProgressTest.lua @@ -190,7 +190,7 @@ local function SpriteProgressToRadialMidpointChanged() left:setType(kCCProgressTimerTypeRadial) left:setMidpoint(CCPointMake(0.25, 0.75)) left:setPosition(CCPointMake(100, s.height / 2)) - left:runAction(CCRepeatForever:create(action:copy():autorelease())) + left:runAction(CCRepeatForever:create(CCProgressTo:create(2, 100))) layer:addChild(left) -- Our image on the left should be a radial progress indicator, counter clockwise @@ -203,7 +203,7 @@ local function SpriteProgressToRadialMidpointChanged() we get a counter clockwise progress. ]] right:setPosition(CCPointMake(s.width - 100, s.height / 2)) - right:runAction(CCRepeatForever:create(action:copy():autorelease())) + right:runAction(CCRepeatForever:create(CCProgressTo:create(2, 100))) layer:addChild(right) subtitleLabel:setString("Radial w/ Different Midpoints") @@ -227,7 +227,7 @@ local function SpriteProgressBarVarious() -- Setup for a vertical bar since the bar change rate is 0 for x meaning no horizontal change left:setBarChangeRate(CCPointMake(1, 0)) left:setPosition(CCPointMake(100, s.height / 2)) - left:runAction(CCRepeatForever:create(to:copy():autorelease())) + left:runAction(CCRepeatForever:create(CCProgressTo:create(2, 100))) layer:addChild(left) local middle = CCProgressTimer:create(CCSprite:create(s_pPathSister2)) @@ -237,7 +237,7 @@ local function SpriteProgressBarVarious() -- Setup for a vertical bar since the bar change rate is 0 for x meaning no horizontal change middle:setBarChangeRate(CCPointMake(1, 1)) middle:setPosition(CCPointMake(s.width/2, s.height/2)) - middle:runAction(CCRepeatForever:create(to:copy():autorelease())) + middle:runAction(CCRepeatForever:create(CCProgressTo:create(2, 100))) layer:addChild(middle) local right = CCProgressTimer:create(CCSprite:create(s_pPathSister2)) @@ -247,7 +247,7 @@ local function SpriteProgressBarVarious() -- Setup for a vertical bar since the bar change rate is 0 for x meaning no horizontal change right:setBarChangeRate(CCPointMake(0, 1)) right:setPosition(CCPointMake(s.width-100, s.height/2)) - right:runAction(CCRepeatForever:create(to:copy():autorelease())) + right:runAction(CCRepeatForever:create(CCProgressTo:create(2, 100))) layer:addChild(right) subtitleLabel:setString("ProgressTo Bar Mid") @@ -279,8 +279,8 @@ local function SpriteProgressBarTintAndFade() -- Setup for a vertical bar since the bar change rate is 0 for x meaning no horizontal change left:setBarChangeRate(CCPointMake(1, 0)) left:setPosition(CCPointMake(100, s.height / 2)) - left:runAction(CCRepeatForever:create(to:copy():autorelease())) - left:runAction(CCRepeatForever:create(tint:copy():autorelease())) + left:runAction(CCRepeatForever:create(CCProgressTo:create(6, 100))) + left:runAction(CCRepeatForever:create(CCSequence:create(array))) layer:addChild(left) left:addChild(CCLabelTTF:create("Tint", "Marker Felt", 20.0)) @@ -292,8 +292,12 @@ local function SpriteProgressBarTintAndFade() -- Setup for a vertical bar since the bar change rate is 0 for x meaning no horizontal change middle:setBarChangeRate(CCPointMake(1, 1)) middle:setPosition(CCPointMake(s.width / 2, s.height / 2)) - middle:runAction(CCRepeatForever:create(to:copy():autorelease())) - middle:runAction(CCRepeatForever:create(fade:copy():autorelease())) + middle:runAction(CCRepeatForever:create(CCProgressTo:create(6, 100))) + + local fade2 = CCSequence:createWithTwoActions( + CCFadeTo:create(1.0, 0), + CCFadeTo:create(1.0, 255)) + middle:runAction(CCRepeatForever:create(fade2)) layer:addChild(middle) middle:addChild(CCLabelTTF:create("Fade", "Marker Felt", 20.0)) @@ -305,9 +309,11 @@ local function SpriteProgressBarTintAndFade() -- Setup for a vertical bar since the bar change rate is 0 for x meaning no horizontal change right:setBarChangeRate(CCPointMake(0, 1)) right:setPosition(CCPointMake(s.width - 100, s.height / 2)) - right:runAction(CCRepeatForever:create(to:copy():autorelease())) - right:runAction(CCRepeatForever:create(tint:copy():autorelease())) - right:runAction(CCRepeatForever:create(fade:copy():autorelease())) + right:runAction(CCRepeatForever:create(CCProgressTo:create(6, 100))) + right:runAction(CCRepeatForever:create(CCSequence:create(array))) + right:runAction(CCRepeatForever:create(CCSequence:createWithTwoActions( + CCFadeTo:create(1.0, 0), + CCFadeTo:create(1.0, 255)))) layer:addChild(right) right:addChild(CCLabelTTF:create("Tint and Fade", "Marker Felt", 20.0)) @@ -334,7 +340,7 @@ local function SpriteProgressWithSpriteFrame() -- Setup for a vertical bar since the bar change rate is 0 for x meaning no horizontal change left:setBarChangeRate(CCPointMake(1, 0)) left:setPosition(CCPointMake(100, s.height / 2)) - left:runAction(CCRepeatForever:create(to:copy():autorelease())) + left:runAction(CCRepeatForever:create(CCProgressTo:create(6, 100))) layer:addChild(left) local middle = CCProgressTimer:create(CCSprite:createWithSpriteFrameName("grossini_dance_02.png")) @@ -344,7 +350,7 @@ local function SpriteProgressWithSpriteFrame() -- Setup for a vertical bar since the bar change rate is 0 for x meaning no horizontal change middle:setBarChangeRate(CCPointMake(1, 1)) middle:setPosition(CCPointMake(s.width / 2, s.height / 2)) - middle:runAction(CCRepeatForever:create(to:copy():autorelease())) + middle:runAction(CCRepeatForever:create(CCProgressTo:create(6, 100))) layer:addChild(middle) local right = CCProgressTimer:create(CCSprite:createWithSpriteFrameName("grossini_dance_03.png")) @@ -354,7 +360,7 @@ local function SpriteProgressWithSpriteFrame() -- Setup for a vertical bar since the bar change rate is 0 for x meaning no horizontal change right:setBarChangeRate(CCPointMake(0, 1)) right:setPosition(CCPointMake(s.width - 100, s.height / 2)) - right:runAction(CCRepeatForever:create(to:copy():autorelease())) + right:runAction(CCRepeatForever:create(CCProgressTo:create(6, 100))) layer:addChild(right) subtitleLabel:setString("Progress With Sprite Frame") diff --git a/samples/Lua/TestLua/Resources/luaScript/ActionsTest/ActionsTest.lua b/samples/Lua/TestLua/Resources/luaScript/ActionsTest/ActionsTest.lua index 4c0f089713..90b4a759f6 100644 --- a/samples/Lua/TestLua/Resources/luaScript/ActionsTest/ActionsTest.lua +++ b/samples/Lua/TestLua/Resources/luaScript/ActionsTest/ActionsTest.lua @@ -213,7 +213,9 @@ local function ActionRotate() local actionByBack = actionBy:reverse() grossini:runAction(CCSequence:createWithTwoActions(actionBy, actionByBack)) - kathia:runAction(CCSequence:createWithTwoActions(actionTo2, actionTo0:copy():autorelease())) + local action0Retain = CCRotateTo:create(2 , 0) + + kathia:runAction(CCSequence:createWithTwoActions(actionTo2, action0Retain)) subtitleLabel:setString("RotateTo / RotateBy") return layer @@ -699,7 +701,9 @@ local function ActionRepeat() local a1 = CCMoveBy:create(1, ccp(150,0)) local action1 = CCRepeat:create(CCSequence:createWithTwoActions(CCPlace:create(ccp(60,60)), a1), 3) - local action2 = CCRepeatForever:create(CCSequence:createWithTwoActions(a1:copy():autorelease(), a1:reverse())) + + local a2 = CCMoveBy:create(1, ccp(150,0)) + local action2 = CCRepeatForever:create(CCSequence:createWithTwoActions(a2, a1:reverse())) kathia:runAction(action1) tamara:runAction(action2) @@ -746,7 +750,8 @@ local function ActionRotateToRepeat() local act2 = CCRotateTo:create(1, 0) local seq = CCSequence:createWithTwoActions(act1, act2) local rep1 = CCRepeatForever:create(seq) - local rep2 = CCRepeat:create(seq:copy():autorelease(), 10) + local seq2 = CCSequence:createWithTwoActions(act1, act2) + local rep2 = CCRepeat:create(seq2, 10) tamara:runAction(rep1) kathia:runAction(rep2) @@ -770,7 +775,12 @@ local function ActionRotateJerk() CCRotateTo:create(0.5, 20)) local rep1 = CCRepeat:create(seq, 10) - local rep2 = CCRepeatForever:create(seq:copy():autorelease()) + + local seq2 = CCSequence:createWithTwoActions( + CCRotateTo:create(0.5, -20), + CCRotateTo:create(0.5, 20)) + + local rep2 = CCRepeatForever:create(seq2) tamara:runAction(rep1) kathia:runAction(rep2) @@ -932,8 +942,12 @@ local function ActionOrbit() local seq = CCSequence:createWithTwoActions(move, move_back) local rfe = CCRepeatForever:create(seq) kathia:runAction(rfe) - tamara:runAction(rfe:copy():autorelease()) - grossini:runAction(rfe:copy():autorelease()) + + local rfe2 = CCRepeatForever:create(seq) + tamara:runAction(rfe2) + + local rfe3 = CCRepeatForever:create(seq) + grossini:runAction(rfe3) subtitleLabel:setString("OrbitCamera action") @@ -973,9 +987,9 @@ local function ActionTargeted() centerSprites(2) local jump1 = CCJumpBy:create(2, ccp(0, 0), 100, 3) - local jump2 = jump1:copy():autorelease() + local jump2 = CCJumpBy:create(2, ccp(0, 0), 100, 3) local rot1 = CCRotateBy:create(1, 360) - local rot2 = rot1:copy():autorelease() + local rot2 = CCRotateBy:create(1, 360) local t1 = CCTargetedAction:create(kathia, jump2) local t2 = CCTargetedAction:create(kathia, rot2) diff --git a/samples/Lua/TestLua/Resources/luaScript/EffectsTest/EffectsTest.lua b/samples/Lua/TestLua/Resources/luaScript/EffectsTest/EffectsTest.lua index 8ac2829b94..b4f0614f07 100644 --- a/samples/Lua/TestLua/Resources/luaScript/EffectsTest/EffectsTest.lua +++ b/samples/Lua/TestLua/Resources/luaScript/EffectsTest/EffectsTest.lua @@ -164,7 +164,7 @@ end -- ShakyTiles3DDemo -------------------------------------- local function ShakyTiles3DDemo(t) - return CCShakyTiles3D:create(t, CCSizeMake(16,12), 5, false) ; + return CCShakyTiles3D:create(t, CCSizeMake(16,12), 5, false); end -------------------------------------- diff --git a/scripting/javascript/bindings/ScriptingCore.cpp b/scripting/javascript/bindings/ScriptingCore.cpp index f9b46e3fd9..d69e293603 100644 --- a/scripting/javascript/bindings/ScriptingCore.cpp +++ b/scripting/javascript/bindings/ScriptingCore.cpp @@ -332,7 +332,8 @@ ScriptingCore::ScriptingCore() , debugGlobal_(NULL) { // set utf8 strings internally (we don't need utf16) - JS_SetCStringsAreUTF8(); + // XXX: Removed in SpiderMonkey 19.0 + //JS_SetCStringsAreUTF8(); this->addRegisterCallback(registerDefaultClasses); this->runLoop = new SimpleRunLoop(); } @@ -417,7 +418,7 @@ void ScriptingCore::createGlobalContext() { this->rt_ = NULL; } //JS_SetCStringsAreUTF8(); - this->rt_ = JS_NewRuntime(10 * 1024 * 1024); + this->rt_ = JS_NewRuntime(10 * 1024 * 1024, JS_NO_HELPER_THREADS); this->cx_ = JS_NewContext(rt_, 10240); JS_SetOptions(this->cx_, JSOPTION_TYPE_INFERENCE); JS_SetVersion(this->cx_, JSVERSION_LATEST); @@ -456,17 +457,25 @@ JSBool ScriptingCore::runScript(const char *path, JSObject* global, JSContext* c cx = cx_; } + js::RootedObject obj(cx, global); + JS::CompileOptions options(cx); + options.setUTF8(true).setFileAndLine(rpath.c_str(), 1); + // this will always compile the script, we can actually check if the script // was compiled before, because it can be in the global map -#ifdef ANDROID +#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) unsigned char *content = NULL; unsigned long contentSize = 0; content = (unsigned char*)CCString::createWithContentsOfFile(rpath.c_str())->getCString(); contentSize = strlen((char*)content); - JSScript* script = JS_CompileScript(cx, global, (char*)content, contentSize, path, 1); + // Not supported in SpiderMonkey 19.0 + //JSScript* script = JS_CompileScript(cx, global, (char*)content, contentSize, path, 1); + JSScript *script = JS::Compile(cx, obj, options, (char*)content, contentSize); #else - JSScript* script = JS_CompileUTF8File(cx, global, rpath.c_str()); + // Removed in SpiderMonkey 19.0 + //JSScript* script = JS_CompileUTF8File(cx, global, rpath.c_str()); + JSScript *script = JS::Compile(cx, obj, options, rpath.c_str()); #endif JSBool evaluatedOK = false; if (script) { @@ -963,15 +972,20 @@ JSBool jsval_to_uint16( JSContext *cx, jsval vp, uint16_t *outval ) return ok; } -JSBool jsval_to_long_long(JSContext *cx, jsval v, long long* ret) { - JSBool ok = JS_TRUE; - JSObject *tmp = JSVAL_TO_OBJECT(v); - ok &= JS_IsTypedArrayObject(tmp, cx) && JS_GetTypedArrayByteLength(tmp, cx) == 8; - JSB_PRECONDITION2(ok, cx, JS_FALSE, "Error processing arguments"); - - uint32_t *data = (uint32_t *)JS_GetUint32ArrayData(tmp, cx); - *ret = (long long)(*data); - return ok; +JSBool jsval_to_long_long(JSContext *cx, jsval vp, long long* r) { + JSObject *tmp_arg; + JSBool ok = JS_ValueToObject( cx, vp, &tmp_arg ); + JSB_PRECONDITION2( ok, cx, JS_FALSE, "Error converting value to object"); + JSB_PRECONDITION2( tmp_arg && JS_IsTypedArrayObject( tmp_arg ), cx, JS_FALSE, "Not a TypedArray object"); + JSB_PRECONDITION2( JS_GetTypedArrayByteLength( tmp_arg ) == sizeof(long long), cx, JS_FALSE, "Invalid Typed Array length"); + + uint32_t* arg_array = (uint32_t*)JS_GetArrayBufferViewData( tmp_arg ); + long long ret = arg_array[0]; + ret = ret << 32; + ret |= arg_array[1]; + + *r = ret; + return JS_TRUE; } JSBool jsval_to_std_string(JSContext *cx, jsval v, std::string* ret) { @@ -1337,7 +1351,7 @@ jsval uint32_to_jsval( JSContext *cx, uint32_t number ) jsval long_long_to_jsval(JSContext* cx, long long v) { JSObject *tmp = JS_NewUint32Array(cx, 2); - uint32_t *data = (uint32_t *)JS_GetArrayBufferViewData(tmp, cx); + uint32_t *data = (uint32_t *)JS_GetArrayBufferViewData(tmp); data[0] = ((uint32_t *)(&v))[0]; data[1] = ((uint32_t *)(&v))[1]; return OBJECT_TO_JSVAL(tmp); @@ -1611,12 +1625,12 @@ JSBool JSBDebug_BufferWrite(JSContext* cx, unsigned argc, jsval* vp) const char* str; JSString* jsstr = JS_ValueToString(cx, argv[0]); - str = JS_EncodeString(cx, jsstr); - + // Not supported in SpiderMonkey v19 + //str = JS_EncodeString(cx, jsstr); + JSStringWrapper strWrapper(jsstr); + // this is safe because we're already inside a lock (from clearBuffers) - outData.append(str); - - JS_free(cx, (void*)str); + outData.append(strWrapper.get()); } return JS_TRUE; } diff --git a/scripting/javascript/bindings/ScriptingCore.h b/scripting/javascript/bindings/ScriptingCore.h index 29a9e32824..a18e145dd6 100644 --- a/scripting/javascript/bindings/ScriptingCore.h +++ b/scripting/javascript/bindings/ScriptingCore.h @@ -267,16 +267,13 @@ public: } ~JSStringWrapper() { if (buffer) { - JS_free(ScriptingCore::getInstance()->getGlobalContext(), (void*)buffer); +// JS_free(ScriptingCore::getInstance()->getGlobalContext(), (void*)buffer); + CC_SAFE_DELETE_ARRAY(buffer); } } void set(jsval val, JSContext* cx) { if (val.isString()) { - string = val.toString(); - if (!cx) { - cx = ScriptingCore::getInstance()->getGlobalContext(); - } - buffer = JS_EncodeString(cx, string); + this->set(val.toString(), cx); } else { buffer = NULL; } @@ -286,7 +283,14 @@ public: if (!cx) { cx = ScriptingCore::getInstance()->getGlobalContext(); } - buffer = JS_EncodeString(cx, string); + // Not suppored in SpiderMonkey v19 + //buffer = JS_EncodeString(cx, string); + + const jschar *chars = JS_GetStringCharsZ(cx, string); + size_t l = JS_GetStringLength(string); + char* pUTF8Str = cc_utf16_to_utf8((const unsigned short*)chars, l, NULL, NULL); + buffer = pUTF8Str; + } std::string get() { return buffer; diff --git a/scripting/javascript/bindings/cocos2d_specifics.cpp.REMOVED.git-id b/scripting/javascript/bindings/cocos2d_specifics.cpp.REMOVED.git-id index 7a8fb51d54..86822eed37 100644 --- a/scripting/javascript/bindings/cocos2d_specifics.cpp.REMOVED.git-id +++ b/scripting/javascript/bindings/cocos2d_specifics.cpp.REMOVED.git-id @@ -1 +1 @@ -e0892eb5a623d56700e48e717dbc04c367b3b48a \ No newline at end of file +e4b7d86461a17a555283bbc28cba2ab48cab21cd \ No newline at end of file diff --git a/scripting/javascript/bindings/generated b/scripting/javascript/bindings/generated index a4cf50ac9b..9918c2317e 160000 --- a/scripting/javascript/bindings/generated +++ b/scripting/javascript/bindings/generated @@ -1 +1 @@ -Subproject commit a4cf50ac9b5541a1ba948bb459fa5bf7b4917e65 +Subproject commit 9918c2317e922dd8d866e4df09b039aba8d48134 diff --git a/scripting/javascript/bindings/js_bindings_chipmunk_auto_classes.cpp.REMOVED.git-id b/scripting/javascript/bindings/js_bindings_chipmunk_auto_classes.cpp.REMOVED.git-id index 3174249af2..8d20837ae5 100644 --- a/scripting/javascript/bindings/js_bindings_chipmunk_auto_classes.cpp.REMOVED.git-id +++ b/scripting/javascript/bindings/js_bindings_chipmunk_auto_classes.cpp.REMOVED.git-id @@ -1 +1 @@ -9594a5677c70358b9e128381d6481eeed0e80723 \ No newline at end of file +f432fb0be7f47a71e1ceeb380fa604fab9e2ac31 \ No newline at end of file diff --git a/scripting/javascript/bindings/js_bindings_chipmunk_manual.cpp b/scripting/javascript/bindings/js_bindings_chipmunk_manual.cpp index 8e59d756dd..f8dfaeca45 100644 --- a/scripting/javascript/bindings/js_bindings_chipmunk_manual.cpp +++ b/scripting/javascript/bindings/js_bindings_chipmunk_manual.cpp @@ -258,12 +258,12 @@ void JSB_CCPhysicsDebugNode_createClass(JSContext *cx, JSObject* globalObj, cons {0, 0, 0, 0, 0} }; static JSFunctionSpec funcs[] = { - JS_FN("_setSpace", JSB_CCPhysicsDebugNode_setSpace_, 1, JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_ENUMERATE), - JS_FN("getSpace", JSB_CCPhysicsDebugNode_space, 0, JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_ENUMERATE), + JS_FN("_setSpace", JSB_CCPhysicsDebugNode_setSpace_, 1, JSPROP_PERMANENT | JSPROP_ENUMERATE), + JS_FN("getSpace", JSB_CCPhysicsDebugNode_space, 0, JSPROP_PERMANENT | JSPROP_ENUMERATE), JS_FS_END }; static JSFunctionSpec st_funcs[] = { - JS_FN("_create", JSB_CCPhysicsDebugNode_debugNodeForCPSpace__static, 1, JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_ENUMERATE), + JS_FN("_create", JSB_CCPhysicsDebugNode_debugNodeForCPSpace__static, 1, JSPROP_PERMANENT | JSPROP_ENUMERATE), JS_FS_END }; @@ -443,16 +443,16 @@ void JSPROXY_CCPhysicsSprite_createClass(JSContext *cx, JSObject* globalObj) {0, 0, 0, 0, 0} }; static JSFunctionSpec funcs[] = { - JS_FN("getCPBody", JSPROXY_CCPhysicsSprite_getCPBody, 0, JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_ENUMERATE), - JS_FN("getIgnoreBodyRotation", JSPROXY_CCPhysicsSprite_ignoreBodyRotation, 0, JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_ENUMERATE), - JS_FN("_setCPBody", JSPROXY_CCPhysicsSprite_setCPBody_, 1, JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_ENUMERATE), - JS_FN("setIgnoreBodyRotation", JSPROXY_CCPhysicsSprite_setIgnoreBodyRotation_, 1, JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_ENUMERATE), + JS_FN("getCPBody", JSPROXY_CCPhysicsSprite_getCPBody, 0, JSPROP_PERMANENT | JSPROP_ENUMERATE), + JS_FN("getIgnoreBodyRotation", JSPROXY_CCPhysicsSprite_ignoreBodyRotation, 0, JSPROP_PERMANENT | JSPROP_ENUMERATE), + JS_FN("_setCPBody", JSPROXY_CCPhysicsSprite_setCPBody_, 1, JSPROP_PERMANENT | JSPROP_ENUMERATE), + JS_FN("setIgnoreBodyRotation", JSPROXY_CCPhysicsSprite_setIgnoreBodyRotation_, 1, JSPROP_PERMANENT | JSPROP_ENUMERATE), JS_FS_END }; static JSFunctionSpec st_funcs[] = { - JS_FN("create", JSPROXY_CCPhysicsSprite_spriteWithFile_rect__static, 2, JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_ENUMERATE), - JS_FN("createWithSpriteFrame", JSPROXY_CCPhysicsSprite_spriteWithSpriteFrame__static, 1, JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_ENUMERATE), - JS_FN("createWithSpriteFrameName", JSPROXY_CCPhysicsSprite_spriteWithSpriteFrameName__static, 1, JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_ENUMERATE), + JS_FN("create", JSPROXY_CCPhysicsSprite_spriteWithFile_rect__static, 2, JSPROP_PERMANENT | JSPROP_ENUMERATE), + JS_FN("createWithSpriteFrame", JSPROXY_CCPhysicsSprite_spriteWithSpriteFrame__static, 1, JSPROP_PERMANENT | JSPROP_ENUMERATE), + JS_FN("createWithSpriteFrameName", JSPROXY_CCPhysicsSprite_spriteWithSpriteFrameName__static, 1, JSPROP_PERMANENT | JSPROP_ENUMERATE), JS_FS_END }; @@ -1528,8 +1528,8 @@ void JSB_cpBase_createClass(JSContext *cx, JSObject* globalObj, const char* name {0, 0, 0, 0, 0} }; static JSFunctionSpec funcs[] = { - JS_FN("getHandle", JSB_cpBase_getHandle, 0, JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_ENUMERATE), - JS_FN("setHandle", JSB_cpBase_setHandle, 1, JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_ENUMERATE), + JS_FN("getHandle", JSB_cpBase_getHandle, 0, JSPROP_PERMANENT | JSPROP_ENUMERATE), + JS_FN("setHandle", JSB_cpBase_setHandle, 1, JSPROP_PERMANENT | JSPROP_ENUMERATE), JS_FS_END }; static JSFunctionSpec st_funcs[] = { diff --git a/scripting/javascript/bindings/js_bindings_core.cpp b/scripting/javascript/bindings/js_bindings_core.cpp index abc719b2f2..d3b7971c34 100644 --- a/scripting/javascript/bindings/js_bindings_core.cpp +++ b/scripting/javascript/bindings/js_bindings_core.cpp @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ - #include "js_bindings_config.h" #include "js_bindings_core.h" @@ -29,6 +28,7 @@ // cocos2d + chipmunk registration files #include "js_bindings_chipmunk_registration.h" + #pragma mark - Hash using namespace cocos2d; @@ -80,8 +80,12 @@ JSBool JSBCore_log(JSContext *cx, uint32_t argc, jsval *vp) JSString *string = NULL; JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "S", &string); if (string) { - char *cstr = JS_EncodeString(cx, string); - fprintf(stderr, "%s\n", cstr); + // Not supported in SpiderMonkey v19 + //char *cstr = JS_EncodeString(cx, string); + const jschar *chars = JS_GetStringCharsZ(cx, string); + size_t l = JS_GetStringLength(string); + char* pUTF8Str = cc_utf16_to_utf8((const unsigned short*)chars, l, NULL, NULL); + CCLOG(pUTF8Str); } return JS_TRUE; diff --git a/scripting/javascript/bindings/js_manual_conversions.cpp b/scripting/javascript/bindings/js_manual_conversions.cpp index a3158da026..7bf9db68c8 100644 --- a/scripting/javascript/bindings/js_manual_conversions.cpp +++ b/scripting/javascript/bindings/js_manual_conversions.cpp @@ -78,14 +78,12 @@ JSBool jsval_to_long( JSContext *cx, jsval vp, long *r ) JSBool jsval_to_longlong( JSContext *cx, jsval vp, long long *r ) { JSObject *tmp_arg; - if( ! JS_ValueToObject( cx, vp, &tmp_arg ) ) - return JS_FALSE; + JSBool ok = JS_ValueToObject( cx, vp, &tmp_arg ); + JSB_PRECONDITION2( ok, cx, JS_FALSE, "Error converting value to object"); + JSB_PRECONDITION2( tmp_arg && JS_IsTypedArrayObject( tmp_arg ), cx, JS_FALSE, "Not a TypedArray object"); + JSB_PRECONDITION2( JS_GetTypedArrayByteLength( tmp_arg ) == sizeof(long long), cx, JS_FALSE, "Invalid Typed Array length"); - JSB_PRECONDITION( tmp_arg && JS_IsTypedArrayObject( tmp_arg, cx ), "Not a TypedArray object"); - - JSB_PRECONDITION( JS_GetTypedArrayByteLength( tmp_arg, cx ) == sizeof(long long), "Invalid Typed Array lenght"); - - int32_t* arg_array = (int32_t*)JS_GetArrayBufferViewData( tmp_arg, cx ); + uint32_t* arg_array = (uint32_t*)JS_GetArrayBufferViewData( tmp_arg ); long long ret = arg_array[0]; ret = ret << 32; ret |= arg_array[1]; @@ -201,17 +199,16 @@ JSBool jsval_to_charptr( JSContext *cx, jsval vp, const char **ret ) // root it vp = STRING_TO_JSVAL(jsstr); - char *ptr = JS_EncodeString(cx, jsstr); - - JSB_PRECONDITION2(ptr, cx, JS_FALSE, "Error encoding string"); - + // Not supported in SpiderMonkey v19 + //char *ptr = JS_EncodeString(cx, jsstr); + JSStringWrapper strWrapper(jsstr); + // XXX: It is converted to CCString and then back to char* to autorelease the created object. - CCString *tmp = CCString::create(ptr); + CCString *tmp = CCString::create(strWrapper.get()); JSB_PRECONDITION2( tmp, cx, JS_FALSE, "Error creating string from UTF8"); *ret = tmp->getCString(); - JS_free( cx, ptr ); return JS_TRUE; } diff --git a/scripting/javascript/spidermonkey-android/include/gc/Barrier.h b/scripting/javascript/spidermonkey-android/include/gc/Barrier.h deleted file mode 100644 index 443cddb7a3..0000000000 --- a/scripting/javascript/spidermonkey-android/include/gc/Barrier.h +++ /dev/null @@ -1,659 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef jsgc_barrier_h___ -#define jsgc_barrier_h___ - -#include "jsapi.h" - -#include "gc/Heap.h" -#include "gc/Root.h" -#include "js/HashTable.h" - -/* - * A write barrier is a mechanism used by incremental or generation GCs to - * ensure that every value that needs to be marked is marked. In general, the - * write barrier should be invoked whenever a write can cause the set of things - * traced through by the GC to change. This includes: - * - writes to object properties - * - writes to array slots - * - writes to fields like JSObject::shape_ that we trace through - * - writes to fields in private data, like JSGenerator::obj - * - writes to non-markable fields like JSObject::private that point to - * markable data - * The last category is the trickiest. Even though the private pointers does not - * point to a GC thing, changing the private pointer may change the set of - * objects that are traced by the GC. Therefore it needs a write barrier. - * - * Every barriered write should have the following form: - * - * obj->field = value; // do the actual write - * - * The pre-barrier is used for incremental GC and the post-barrier is for - * generational GC. - * - * PRE-BARRIER - * - * To understand the pre-barrier, let's consider how incremental GC works. The - * GC itself is divided into "slices". Between each slice, JS code is allowed to - * run. Each slice should be short so that the user doesn't notice the - * interruptions. In our GC, the structure of the slices is as follows: - * - * 1. ... JS work, which leads to a request to do GC ... - * 2. [first GC slice, which performs all root marking and possibly more marking] - * 3. ... more JS work is allowed to run ... - * 4. [GC mark slice, which runs entirely in drainMarkStack] - * 5. ... more JS work ... - * 6. [GC mark slice, which runs entirely in drainMarkStack] - * 7. ... more JS work ... - * 8. [GC marking finishes; sweeping done non-incrementally; GC is done] - * 9. ... JS continues uninterrupted now that GC is finishes ... - * - * Of course, there may be a different number of slices depending on how much - * marking is to be done. - * - * The danger inherent in this scheme is that the JS code in steps 3, 5, and 7 - * might change the heap in a way that causes the GC to collect an object that - * is actually reachable. The write barrier prevents this from happening. We use - * a variant of incremental GC called "snapshot at the beginning." This approach - * guarantees the invariant that if an object is reachable in step 2, then we - * will mark it eventually. The name comes from the idea that we take a - * theoretical "snapshot" of all reachable objects in step 2; all objects in - * that snapshot should eventually be marked. (Note that the write barrier - * verifier code takes an actual snapshot.) - * - * The basic correctness invariant of a snapshot-at-the-beginning collector is - * that any object reachable at the end of the GC (step 9) must either: - * (1) have been reachable at the beginning (step 2) and thus in the snapshot - * (2) or must have been newly allocated, in steps 3, 5, or 7. - * To deal with case (2), any objects allocated during an incremental GC are - * automatically marked black. - * - * This strategy is actually somewhat conservative: if an object becomes - * unreachable between steps 2 and 8, it would be safe to collect it. We won't, - * mainly for simplicity. (Also, note that the snapshot is entirely - * theoretical. We don't actually do anything special in step 2 that we wouldn't - * do in a non-incremental GC. - * - * It's the pre-barrier's job to maintain the snapshot invariant. Consider the - * write "obj->field = value". Let the prior value of obj->field be - * value0. Since it's possible that value0 may have been what obj->field - * contained in step 2, when the snapshot was taken, the barrier marks - * value0. Note that it only does this if we're in the middle of an incremental - * GC. Since this is rare, the cost of the write barrier is usually just an - * extra branch. - * - * In practice, we implement the pre-barrier differently based on the type of - * value0. E.g., see JSObject::writeBarrierPre, which is used if obj->field is - * a JSObject*. It takes value0 as a parameter. - * - * POST-BARRIER - * - * These are not yet implemented. Once we get generational GC, they will allow - * us to keep track of pointers from non-nursery space into the nursery. - * - * IMPLEMENTATION DETAILS - * - * Since it would be awkward to change every write to memory into a function - * call, this file contains a bunch of C++ classes and templates that use - * operator overloading to take care of barriers automatically. In many cases, - * all that's necessary to make some field be barriered is to replace - * Type *field; - * with - * HeapPtr field; - * There are also special classes HeapValue and HeapId, which barrier js::Value - * and jsid, respectively. - * - * One additional note: not all object writes need to be barriered. Writes to - * newly allocated objects do not need a pre-barrier. In these cases, we use - * the "obj->field.init(value)" method instead of "obj->field = value". We use - * the init naming idiom in many places to signify that a field is being - * assigned for the first time. - */ - -struct JSXML; - -namespace js { - -template -class EncapsulatedPtr -{ - protected: - union { - T *value; - Unioned other; - }; - - public: - EncapsulatedPtr() : value(NULL) {} - EncapsulatedPtr(T *v) : value(v) {} - explicit EncapsulatedPtr(const EncapsulatedPtr &v) : value(v.value) {} - - ~EncapsulatedPtr() { pre(); } - - /* Use to set the pointer to NULL. */ - void clear() { - pre(); - value = NULL; - } - - EncapsulatedPtr &operator=(T *v) { - pre(); - JS_ASSERT(!IsPoisonedPtr(v)); - value = v; - return *this; - } - - EncapsulatedPtr &operator=(const EncapsulatedPtr &v) { - pre(); - JS_ASSERT(!IsPoisonedPtr(v.value)); - value = v.value; - return *this; - } - - /* Use this if the automatic coercion to T* isn't working. */ - T *get() const { return value; } - - /* - * Use these if you want to change the value without invoking the barrier. - * Obviously this is dangerous unless you know the barrier is not needed. - */ - T **unsafeGet() { return &value; } - void unsafeSet(T *v) { value = v; } - - Unioned *unsafeGetUnioned() { return &other; } - - T &operator*() const { return *value; } - T *operator->() const { return value; } - - operator T*() const { return value; } - - protected: - void pre(); -}; - -template -class HeapPtr : public EncapsulatedPtr -{ - public: - HeapPtr() : EncapsulatedPtr(NULL) {} - explicit HeapPtr(T *v) : EncapsulatedPtr(v) { post(); } - explicit HeapPtr(const HeapPtr &v) - : EncapsulatedPtr(v) { post(); } - - void init(T *v) { - JS_ASSERT(!IsPoisonedPtr(v)); - this->value = v; - post(); - } - - HeapPtr &operator=(T *v) { - this->pre(); - JS_ASSERT(!IsPoisonedPtr(v)); - this->value = v; - post(); - return *this; - } - - HeapPtr &operator=(const HeapPtr &v) { - this->pre(); - JS_ASSERT(!IsPoisonedPtr(v.value)); - this->value = v.value; - post(); - return *this; - } - - protected: - void post() { T::writeBarrierPost(this->value, (void *)&this->value); } - - /* Make this friend so it can access pre() and post(). */ - template - friend inline void - BarrieredSetPair(JSCompartment *comp, - HeapPtr &v1, T1 *val1, - HeapPtr &v2, T2 *val2); -}; - -/* - * FixedHeapPtr is designed for one very narrow case: replacing immutable raw - * pointers to GC-managed things, implicitly converting to a handle type for - * ease of use. Pointers encapsulated by this type must: - * - * be immutable (no incremental write barriers), - * never point into the nursery (no generational write barriers), and - * be traced via MarkRuntime (we use fromMarkedLocation). - * - * In short: you *really* need to know what you're doing before you use this - * class! - */ -template -class FixedHeapPtr -{ - T *value; - - public: - operator T*() const { return value; } - T * operator->() const { return value; } - - operator Handle() const { - return Handle::fromMarkedLocation(&value); - } - - void init(T *ptr) { - value = ptr; - } -}; - -template -class RelocatablePtr : public EncapsulatedPtr -{ - public: - RelocatablePtr() : EncapsulatedPtr(NULL) {} - explicit RelocatablePtr(T *v) : EncapsulatedPtr(v) { - if (v) - post(); - } - explicit RelocatablePtr(const RelocatablePtr &v) : EncapsulatedPtr(v) { - if (this->value) - post(); - } - - ~RelocatablePtr() { - if (this->value) - relocate(this->value->compartment()); - } - - RelocatablePtr &operator=(T *v) { - this->pre(); - JS_ASSERT(!IsPoisonedPtr(v)); - if (v) { - this->value = v; - post(); - } else if (this->value) { - JSCompartment *comp = this->value->compartment(); - this->value = v; - relocate(comp); - } - return *this; - } - - RelocatablePtr &operator=(const RelocatablePtr &v) { - this->pre(); - JS_ASSERT(!IsPoisonedPtr(v.value)); - if (v.value) { - this->value = v.value; - post(); - } else if (this->value) { - JSCompartment *comp = this->value->compartment(); - this->value = v; - relocate(comp); - } - return *this; - } - - protected: - inline void post(); - inline void relocate(JSCompartment *comp); -}; - -/* - * This is a hack for RegExpStatics::updateFromMatch. It allows us to do two - * barriers with only one branch to check if we're in an incremental GC. - */ -template -static inline void -BarrieredSetPair(JSCompartment *comp, - HeapPtr &v1, T1 *val1, - HeapPtr &v2, T2 *val2) -{ - if (T1::needWriteBarrierPre(comp)) { - v1.pre(); - v2.pre(); - } - v1.unsafeSet(val1); - v2.unsafeSet(val2); - v1.post(); - v2.post(); -} - -struct Shape; -class BaseShape; -namespace types { struct TypeObject; } - -typedef EncapsulatedPtr EncapsulatedPtrObject; -typedef EncapsulatedPtr EncapsulatedPtrScript; - -typedef RelocatablePtr RelocatablePtrObject; -typedef RelocatablePtr RelocatablePtrScript; - -typedef HeapPtr HeapPtrObject; -typedef HeapPtr HeapPtrFunction; -typedef HeapPtr HeapPtrString; -typedef HeapPtr HeapPtrScript; -typedef HeapPtr HeapPtrShape; -typedef HeapPtr HeapPtrBaseShape; -typedef HeapPtr HeapPtrTypeObject; -typedef HeapPtr HeapPtrXML; - -/* Useful for hashtables with a HeapPtr as key. */ -template -struct HeapPtrHasher -{ - typedef HeapPtr Key; - typedef T *Lookup; - - static HashNumber hash(Lookup obj) { return DefaultHasher::hash(obj); } - static bool match(const Key &k, Lookup l) { return k.get() == l; } -}; - -/* Specialized hashing policy for HeapPtrs. */ -template -struct DefaultHasher< HeapPtr > : HeapPtrHasher { }; - -template -struct EncapsulatedPtrHasher -{ - typedef EncapsulatedPtr Key; - typedef T *Lookup; - - static HashNumber hash(Lookup obj) { return DefaultHasher::hash(obj); } - static bool match(const Key &k, Lookup l) { return k.get() == l; } -}; - -template -struct DefaultHasher< EncapsulatedPtr > : EncapsulatedPtrHasher { }; - -class EncapsulatedValue : public ValueOperations -{ - protected: - Value value; - - /* - * Ensure that EncapsulatedValue is not constructable, except by our - * implementations. - */ - EncapsulatedValue() MOZ_DELETE; - EncapsulatedValue(const EncapsulatedValue &v) MOZ_DELETE; - EncapsulatedValue &operator=(const Value &v) MOZ_DELETE; - EncapsulatedValue &operator=(const EncapsulatedValue &v) MOZ_DELETE; - - EncapsulatedValue(const Value &v) : value(v) {} - ~EncapsulatedValue() {} - - public: - bool operator==(const EncapsulatedValue &v) const { return value == v.value; } - bool operator!=(const EncapsulatedValue &v) const { return value != v.value; } - - const Value &get() const { return value; } - Value *unsafeGet() { return &value; } - operator const Value &() const { return value; } - - JSGCTraceKind gcKind() const { return value.gcKind(); } - - uint64_t asRawBits() const { return value.asRawBits(); } - - static inline void writeBarrierPre(const Value &v); - static inline void writeBarrierPre(JSCompartment *comp, const Value &v); - - protected: - inline void pre(); - inline void pre(JSCompartment *comp); - - private: - friend class ValueOperations; - const Value * extract() const { return &value; } -}; - -class HeapValue : public EncapsulatedValue -{ - public: - explicit inline HeapValue(); - explicit inline HeapValue(const Value &v); - explicit inline HeapValue(const HeapValue &v); - inline ~HeapValue(); - - inline void init(const Value &v); - inline void init(JSCompartment *comp, const Value &v); - - inline HeapValue &operator=(const Value &v); - inline HeapValue &operator=(const HeapValue &v); - - /* - * This is a faster version of operator=. Normally, operator= has to - * determine the compartment of the value before it can decide whether to do - * the barrier. If you already know the compartment, it's faster to pass it - * in. - */ - inline void set(JSCompartment *comp, const Value &v); - - static inline void writeBarrierPost(const Value &v, Value *addr); - static inline void writeBarrierPost(JSCompartment *comp, const Value &v, Value *addr); - - private: - inline void post(); - inline void post(JSCompartment *comp); -}; - -class RelocatableValue : public EncapsulatedValue -{ - public: - explicit inline RelocatableValue(); - explicit inline RelocatableValue(const Value &v); - inline RelocatableValue(const RelocatableValue &v); - inline ~RelocatableValue(); - - inline RelocatableValue &operator=(const Value &v); - inline RelocatableValue &operator=(const RelocatableValue &v); - - private: - inline void post(); - inline void post(JSCompartment *comp); - inline void relocate(); -}; - -class HeapSlot : public EncapsulatedValue -{ - /* - * Operator= is not valid for HeapSlot because is must take the object and - * slot offset to provide to the post/generational barrier. - */ - inline HeapSlot &operator=(const Value &v) MOZ_DELETE; - inline HeapSlot &operator=(const HeapValue &v) MOZ_DELETE; - inline HeapSlot &operator=(const HeapSlot &v) MOZ_DELETE; - - public: - explicit inline HeapSlot() MOZ_DELETE; - explicit inline HeapSlot(JSObject *obj, uint32_t slot, const Value &v); - explicit inline HeapSlot(JSObject *obj, uint32_t slot, const HeapSlot &v); - inline ~HeapSlot(); - - inline void init(JSObject *owner, uint32_t slot, const Value &v); - inline void init(JSCompartment *comp, JSObject *owner, uint32_t slot, const Value &v); - - inline void set(JSObject *owner, uint32_t slot, const Value &v); - inline void set(JSCompartment *comp, JSObject *owner, uint32_t slot, const Value &v); - - static inline void writeBarrierPost(JSObject *obj, uint32_t slot); - static inline void writeBarrierPost(JSCompartment *comp, JSObject *obj, uint32_t slot); - - private: - inline void post(JSObject *owner, uint32_t slot); - inline void post(JSCompartment *comp, JSObject *owner, uint32_t slot); -}; - -/* - * NOTE: This is a placeholder for bug 619558. - * - * Run a post write barrier that encompasses multiple contiguous slots in a - * single step. - */ -inline void -SlotRangeWriteBarrierPost(JSCompartment *comp, JSObject *obj, uint32_t start, uint32_t count); - -/* - * This is a post barrier for HashTables whose key can be moved during a GC. - */ -template -inline void -HashTableWriteBarrierPost(JSCompartment *comp, const Map *map, const Key &key) -{ -#ifdef JS_GCGENERATIONAL - if (key && comp->gcNursery.isInside(key)) - comp->gcStoreBuffer.putGeneric(HashKeyRef(map, key)); -#endif -} - -static inline const Value * -Valueify(const EncapsulatedValue *array) -{ - JS_STATIC_ASSERT(sizeof(HeapValue) == sizeof(Value)); - JS_STATIC_ASSERT(sizeof(HeapSlot) == sizeof(Value)); - return (const Value *)array; -} - -static inline HeapValue * -HeapValueify(Value *v) -{ - JS_STATIC_ASSERT(sizeof(HeapValue) == sizeof(Value)); - JS_STATIC_ASSERT(sizeof(HeapSlot) == sizeof(Value)); - return (HeapValue *)v; -} - -class HeapSlotArray -{ - HeapSlot *array; - - public: - HeapSlotArray(HeapSlot *array) : array(array) {} - - operator const Value *() const { return Valueify(array); } - operator HeapSlot *() const { return array; } - - HeapSlotArray operator +(int offset) const { return HeapSlotArray(array + offset); } - HeapSlotArray operator +(uint32_t offset) const { return HeapSlotArray(array + offset); } -}; - -class EncapsulatedId -{ - protected: - jsid value; - - private: - EncapsulatedId(const EncapsulatedId &v) MOZ_DELETE; - - public: - explicit EncapsulatedId() : value(JSID_VOID) {} - explicit EncapsulatedId(jsid id) : value(id) {} - ~EncapsulatedId(); - - inline EncapsulatedId &operator=(const EncapsulatedId &v); - - bool operator==(jsid id) const { return value == id; } - bool operator!=(jsid id) const { return value != id; } - - jsid get() const { return value; } - jsid *unsafeGet() { return &value; } - operator jsid() const { return value; } - - protected: - inline void pre(); -}; - -class RelocatableId : public EncapsulatedId -{ - public: - explicit RelocatableId() : EncapsulatedId() {} - explicit inline RelocatableId(jsid id) : EncapsulatedId(id) {} - inline ~RelocatableId(); - - inline RelocatableId &operator=(jsid id); - inline RelocatableId &operator=(const RelocatableId &v); -}; - -class HeapId : public EncapsulatedId -{ - public: - explicit HeapId() : EncapsulatedId() {} - explicit inline HeapId(jsid id); - inline ~HeapId(); - - inline void init(jsid id); - - inline HeapId &operator=(jsid id); - inline HeapId &operator=(const HeapId &v); - - private: - inline void post(); - - HeapId(const HeapId &v) MOZ_DELETE; -}; - -/* - * Incremental GC requires that weak pointers have read barriers. This is mostly - * an issue for empty shapes stored in JSCompartment. The problem happens when, - * during an incremental GC, some JS code stores one of the compartment's empty - * shapes into an object already marked black. Normally, this would not be a - * problem, because the empty shape would have been part of the initial snapshot - * when the GC started. However, since this is a weak pointer, it isn't. So we - * may collect the empty shape even though a live object points to it. To fix - * this, we mark these empty shapes black whenever they get read out. - */ -template -class ReadBarriered -{ - T *value; - - public: - ReadBarriered() : value(NULL) {} - ReadBarriered(T *value) : value(value) {} - - T *get() const { - if (!value) - return NULL; - T::readBarrier(value); - return value; - } - - operator T*() const { return get(); } - - T &operator*() const { return *get(); } - T *operator->() const { return get(); } - - T **unsafeGet() { return &value; } - - void set(T *v) { value = v; } - - operator bool() { return !!value; } -}; - -class ReadBarrieredValue -{ - Value value; - - public: - ReadBarrieredValue() : value(UndefinedValue()) {} - ReadBarrieredValue(const Value &value) : value(value) {} - - inline const Value &get() const; - Value *unsafeGet() { return &value; } - inline operator const Value &() const; - - inline JSObject &toObject() const; -}; - -namespace tl { - -template struct IsRelocatableHeapType > - { static const bool result = false; }; -template <> struct IsRelocatableHeapType { static const bool result = false; }; -template <> struct IsRelocatableHeapType { static const bool result = false; }; -template <> struct IsRelocatableHeapType { static const bool result = false; }; - -} /* namespace tl */ -} /* namespace js */ - -#endif /* jsgc_barrier_h___ */ diff --git a/scripting/javascript/spidermonkey-android/include/gc/Heap.h b/scripting/javascript/spidermonkey-android/include/gc/Heap.h deleted file mode 100644 index 9a53deb24b..0000000000 --- a/scripting/javascript/spidermonkey-android/include/gc/Heap.h +++ /dev/null @@ -1,1032 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef gc_heap_h___ -#define gc_heap_h___ - -#include "mozilla/Attributes.h" -#include "mozilla/StandardInteger.h" - -#include - -#include "jstypes.h" -#include "jsutil.h" - -#include "ds/BitArray.h" - -struct JSCompartment; - -extern "C" { -struct JSRuntime; -} - -namespace js { - -class FreeOp; - -namespace gc { - -struct Arena; -struct ArenaHeader; -struct Chunk; - -/* - * Live objects are marked black. How many other additional colors are available - * depends on the size of the GCThing. Objects marked gray are eligible for - * cycle collection. - */ -static const uint32_t BLACK = 0; -static const uint32_t GRAY = 1; - -/* The GC allocation kinds. */ -enum AllocKind { - FINALIZE_OBJECT0, - FINALIZE_OBJECT0_BACKGROUND, - FINALIZE_OBJECT2, - FINALIZE_OBJECT2_BACKGROUND, - FINALIZE_OBJECT4, - FINALIZE_OBJECT4_BACKGROUND, - FINALIZE_OBJECT8, - FINALIZE_OBJECT8_BACKGROUND, - FINALIZE_OBJECT12, - FINALIZE_OBJECT12_BACKGROUND, - FINALIZE_OBJECT16, - FINALIZE_OBJECT16_BACKGROUND, - FINALIZE_OBJECT_LAST = FINALIZE_OBJECT16_BACKGROUND, - FINALIZE_SCRIPT, - FINALIZE_SHAPE, - FINALIZE_BASE_SHAPE, - FINALIZE_TYPE_OBJECT, -#if JS_HAS_XML_SUPPORT - FINALIZE_XML, -#endif - FINALIZE_SHORT_STRING, - FINALIZE_STRING, - FINALIZE_EXTERNAL_STRING, - FINALIZE_IONCODE, - FINALIZE_LAST = FINALIZE_IONCODE -}; - -static const unsigned FINALIZE_LIMIT = FINALIZE_LAST + 1; -static const unsigned FINALIZE_OBJECT_LIMIT = FINALIZE_OBJECT_LAST + 1; - -/* - * This must be an upper bound, but we do not need the least upper bound, so - * we just exclude non-background objects. - */ -static const size_t MAX_BACKGROUND_FINALIZE_KINDS = FINALIZE_LIMIT - FINALIZE_OBJECT_LIMIT / 2; - -/* - * A GC cell is the base class for all GC things. - */ -struct Cell -{ - static const size_t CellShift = 3; - static const size_t CellSize = size_t(1) << CellShift; - static const size_t CellMask = CellSize - 1; - - inline uintptr_t address() const; - inline ArenaHeader *arenaHeader() const; - inline Chunk *chunk() const; - inline AllocKind getAllocKind() const; - MOZ_ALWAYS_INLINE bool isMarked(uint32_t color = BLACK) const; - MOZ_ALWAYS_INLINE bool markIfUnmarked(uint32_t color = BLACK) const; - MOZ_ALWAYS_INLINE void unmark(uint32_t color) const; - - inline JSCompartment *compartment() const; - -#ifdef DEBUG - inline bool isAligned() const; -#endif -}; - -/* - * Page size must be static to support our arena pointer optimizations, so we - * are forced to support each platform with non-4096 pages as a special case. - * Note: The freelist supports a maximum arena shift of 15. - * Note: Do not use JS_CPU_SPARC here, this header is used outside JS. - * Bug 692267: Move page size definition to gc/Memory.h and include it - * directly once jsgc.h is no longer an installed header. - */ -#if (defined(SOLARIS) || defined(__FreeBSD__)) && \ - (defined(__sparc) || defined(__sparcv9) || defined(__ia64)) -const size_t PageShift = 13; -const size_t ArenaShift = PageShift; -#elif defined(__powerpc64__) -const size_t PageShift = 16; -const size_t ArenaShift = 12; -#else -const size_t PageShift = 12; -const size_t ArenaShift = PageShift; -#endif -const size_t PageSize = size_t(1) << PageShift; -const size_t ArenaSize = size_t(1) << ArenaShift; -const size_t ArenaMask = ArenaSize - 1; - -const size_t ChunkShift = 20; -const size_t ChunkSize = size_t(1) << ChunkShift; -const size_t ChunkMask = ChunkSize - 1; - -/* - * This is the maximum number of arenas we allow in the FreeCommitted state - * before we trigger a GC_SHRINK to release free arenas to the OS. - */ -const static uint32_t FreeCommittedArenasThreshold = (32 << 20) / ArenaSize; - -/* - * The mark bitmap has one bit per each GC cell. For multi-cell GC things this - * wastes space but allows to avoid expensive devisions by thing's size when - * accessing the bitmap. In addition this allows to use some bits for colored - * marking during the cycle GC. - */ -const size_t ArenaCellCount = size_t(1) << (ArenaShift - Cell::CellShift); -const size_t ArenaBitmapBits = ArenaCellCount; -const size_t ArenaBitmapBytes = ArenaBitmapBits / 8; -const size_t ArenaBitmapWords = ArenaBitmapBits / JS_BITS_PER_WORD; - -/* - * A FreeSpan represents a contiguous sequence of free cells in an Arena. - * |first| is the address of the first free cell in the span. |last| is the - * address of the last free cell in the span. This last cell holds a FreeSpan - * data structure for the next span unless this is the last span on the list - * of spans in the arena. For this last span |last| points to the last byte of - * the last thing in the arena and no linkage is stored there, so - * |last| == arenaStart + ArenaSize - 1. If the space at the arena end is - * fully used this last span is empty and |first| == |last + 1|. - * - * Thus |first| < |last| implies that we have either the last span with at least - * one element or that the span is not the last and contains at least 2 - * elements. In both cases to allocate a thing from this span we need simply - * to increment |first| by the allocation size. - * - * |first| == |last| implies that we have a one element span that records the - * next span. So to allocate from it we need to update the span list head - * with a copy of the span stored at |last| address so the following - * allocations will use that span. - * - * |first| > |last| implies that we have an empty last span and the arena is - * fully used. - * - * Also only for the last span (|last| & 1)! = 0 as all allocation sizes are - * multiples of Cell::CellSize. - */ -struct FreeSpan -{ - uintptr_t first; - uintptr_t last; - - public: - FreeSpan() {} - - FreeSpan(uintptr_t first, uintptr_t last) - : first(first), last(last) { - checkSpan(); - } - - /* - * To minimize the size of the arena header the first span is encoded - * there as offsets from the arena start. - */ - static size_t encodeOffsets(size_t firstOffset, size_t lastOffset) { - /* Check that we can pack the offsets into uint16. */ - JS_STATIC_ASSERT(ArenaShift < 16); - JS_ASSERT(firstOffset <= ArenaSize); - JS_ASSERT(lastOffset < ArenaSize); - JS_ASSERT(firstOffset <= ((lastOffset + 1) & ~size_t(1))); - return firstOffset | (lastOffset << 16); - } - - /* - * Encoded offsets for a full arena when its first span is the last one - * and empty. - */ - static const size_t FullArenaOffsets = ArenaSize | ((ArenaSize - 1) << 16); - - static FreeSpan decodeOffsets(uintptr_t arenaAddr, size_t offsets) { - JS_ASSERT(!(arenaAddr & ArenaMask)); - - size_t firstOffset = offsets & 0xFFFF; - size_t lastOffset = offsets >> 16; - JS_ASSERT(firstOffset <= ArenaSize); - JS_ASSERT(lastOffset < ArenaSize); - - /* - * We must not use | when calculating first as firstOffset is - * ArenaMask + 1 for the empty span. - */ - return FreeSpan(arenaAddr + firstOffset, arenaAddr | lastOffset); - } - - void initAsEmpty(uintptr_t arenaAddr = 0) { - JS_ASSERT(!(arenaAddr & ArenaMask)); - first = arenaAddr + ArenaSize; - last = arenaAddr | (ArenaSize - 1); - JS_ASSERT(isEmpty()); - } - - bool isEmpty() const { - checkSpan(); - return first > last; - } - - bool hasNext() const { - checkSpan(); - return !(last & uintptr_t(1)); - } - - const FreeSpan *nextSpan() const { - JS_ASSERT(hasNext()); - return reinterpret_cast(last); - } - - FreeSpan *nextSpanUnchecked(size_t thingSize) const { -#ifdef DEBUG - uintptr_t lastOffset = last & ArenaMask; - JS_ASSERT(!(lastOffset & 1)); - JS_ASSERT((ArenaSize - lastOffset) % thingSize == 0); -#endif - return reinterpret_cast(last); - } - - uintptr_t arenaAddressUnchecked() const { - return last & ~ArenaMask; - } - - uintptr_t arenaAddress() const { - checkSpan(); - return arenaAddressUnchecked(); - } - - ArenaHeader *arenaHeader() const { - return reinterpret_cast(arenaAddress()); - } - - bool isSameNonEmptySpan(const FreeSpan *another) const { - JS_ASSERT(!isEmpty()); - JS_ASSERT(!another->isEmpty()); - return first == another->first && last == another->last; - } - - bool isWithinArena(uintptr_t arenaAddr) const { - JS_ASSERT(!(arenaAddr & ArenaMask)); - - /* Return true for the last empty span as well. */ - return arenaAddress() == arenaAddr; - } - - size_t encodeAsOffsets() const { - /* - * We must use first - arenaAddress(), not first & ArenaMask as - * first == ArenaMask + 1 for an empty span. - */ - uintptr_t arenaAddr = arenaAddress(); - return encodeOffsets(first - arenaAddr, last & ArenaMask); - } - - /* See comments before FreeSpan for details. */ - MOZ_ALWAYS_INLINE void *allocate(size_t thingSize) { - JS_ASSERT(thingSize % Cell::CellSize == 0); - checkSpan(); - uintptr_t thing = first; - if (thing < last) { - /* Bump-allocate from the current span. */ - first = thing + thingSize; - } else if (JS_LIKELY(thing == last)) { - /* - * Move to the next span. We use JS_LIKELY as without PGO - * compilers mis-predict == here as unlikely to succeed. - */ - *this = *reinterpret_cast(thing); - } else { - return NULL; - } - checkSpan(); - return reinterpret_cast(thing); - } - - /* A version of allocate when we know that the span is not empty. */ - MOZ_ALWAYS_INLINE void *infallibleAllocate(size_t thingSize) { - JS_ASSERT(thingSize % Cell::CellSize == 0); - checkSpan(); - uintptr_t thing = first; - if (thing < last) { - first = thing + thingSize; - } else { - JS_ASSERT(thing == last); - *this = *reinterpret_cast(thing); - } - checkSpan(); - return reinterpret_cast(thing); - } - - /* - * Allocate from a newly allocated arena. We do not move the free list - * from the arena. Rather we set the arena up as fully used during the - * initialization so to allocate we simply return the first thing in the - * arena and set the free list to point to the second. - */ - MOZ_ALWAYS_INLINE void *allocateFromNewArena(uintptr_t arenaAddr, size_t firstThingOffset, - size_t thingSize) { - JS_ASSERT(!(arenaAddr & ArenaMask)); - uintptr_t thing = arenaAddr | firstThingOffset; - first = thing + thingSize; - last = arenaAddr | ArenaMask; - checkSpan(); - return reinterpret_cast(thing); - } - - void checkSpan() const { -#ifdef DEBUG - /* We do not allow spans at the end of the address space. */ - JS_ASSERT(last != uintptr_t(-1)); - JS_ASSERT(first); - JS_ASSERT(last); - JS_ASSERT(first - 1 <= last); - uintptr_t arenaAddr = arenaAddressUnchecked(); - if (last & 1) { - /* The span is the last. */ - JS_ASSERT((last & ArenaMask) == ArenaMask); - - if (first - 1 == last) { - /* The span is last and empty. The above start != 0 check - * implies that we are not at the end of the address space. - */ - return; - } - size_t spanLength = last - first + 1; - JS_ASSERT(spanLength % Cell::CellSize == 0); - - /* Start and end must belong to the same arena. */ - JS_ASSERT((first & ~ArenaMask) == arenaAddr); - return; - } - - /* The span is not the last and we have more spans to follow. */ - JS_ASSERT(first <= last); - size_t spanLengthWithoutOneThing = last - first; - JS_ASSERT(spanLengthWithoutOneThing % Cell::CellSize == 0); - - JS_ASSERT((first & ~ArenaMask) == arenaAddr); - - /* - * If there is not enough space before the arena end to allocate one - * more thing, then the span must be marked as the last one to avoid - * storing useless empty span reference. - */ - size_t beforeTail = ArenaSize - (last & ArenaMask); - JS_ASSERT(beforeTail >= sizeof(FreeSpan) + Cell::CellSize); - - FreeSpan *next = reinterpret_cast(last); - - /* - * The GC things on the list of free spans come from one arena - * and the spans are linked in ascending address order with - * at least one non-free thing between spans. - */ - JS_ASSERT(last < next->first); - JS_ASSERT(arenaAddr == next->arenaAddressUnchecked()); - - if (next->first > next->last) { - /* - * The next span is the empty span that terminates the list for - * arenas that do not have any free things at the end. - */ - JS_ASSERT(next->first - 1 == next->last); - JS_ASSERT(arenaAddr + ArenaSize == next->first); - } -#endif - } - -}; - -/* Every arena has a header. */ -struct ArenaHeader -{ - friend struct FreeLists; - - JSCompartment *compartment; - - /* - * ArenaHeader::next has two purposes: when unallocated, it points to the - * next available Arena's header. When allocated, it points to the next - * arena of the same size class and compartment. - */ - ArenaHeader *next; - - private: - /* - * The first span of free things in the arena. We encode it as the start - * and end offsets within the arena, not as FreeSpan structure, to - * minimize the header size. - */ - size_t firstFreeSpanOffsets; - - /* - * One of AllocKind constants or FINALIZE_LIMIT when the arena does not - * contain any GC things and is on the list of empty arenas in the GC - * chunk. The latter allows to quickly check if the arena is allocated - * during the conservative GC scanning without searching the arena in the - * list. - * - * We use 8 bits for the allocKind so the compiler can use byte-level memory - * instructions to access it. - */ - size_t allocKind : 8; - - /* - * When collecting we sometimes need to keep an auxillary list of arenas, - * for which we use the following fields. This happens for several reasons: - * - * When recursive marking uses too much stack the marking is delayed and the - * corresponding arenas are put into a stack. To distinguish the bottom of - * the stack from the arenas not present in the stack we use the - * markOverflow flag to tag arenas on the stack. - * - * Delayed marking is also used for arenas that we allocate into during an - * incremental GC. In this case, we intend to mark all the objects in the - * arena, and it's faster to do this marking in bulk. - * - * When sweeping we keep track of which arenas have been allocated since the - * end of the mark phase. This allows us to tell whether a pointer to an - * unmarked object is yet to be finalized or has already been reallocated. - * We set the allocatedDuringIncremental flag for this and clear it at the - * end of the sweep phase. - * - * To minimize the ArenaHeader size we record the next linkage as - * arenaAddress() >> ArenaShift and pack it with the allocKind field and the - * flags. - */ - public: - size_t hasDelayedMarking : 1; - size_t allocatedDuringIncremental : 1; - size_t markOverflow : 1; - size_t auxNextLink : JS_BITS_PER_WORD - 8 - 1 - 1 - 1; - - static void staticAsserts() { - /* We must be able to fit the allockind into uint8_t. */ - JS_STATIC_ASSERT(FINALIZE_LIMIT <= 255); - - /* - * auxNextLink packing assumes that ArenaShift has enough bits - * to cover allocKind and hasDelayedMarking. - */ - JS_STATIC_ASSERT(ArenaShift >= 8 + 1 + 1 + 1); - } - - inline uintptr_t address() const; - inline Chunk *chunk() const; - - bool allocated() const { - JS_ASSERT(allocKind <= size_t(FINALIZE_LIMIT)); - return allocKind < size_t(FINALIZE_LIMIT); - } - - void init(JSCompartment *comp, AllocKind kind) { - JS_ASSERT(!allocated()); - JS_ASSERT(!markOverflow); - JS_ASSERT(!allocatedDuringIncremental); - JS_ASSERT(!hasDelayedMarking); - compartment = comp; - - JS_STATIC_ASSERT(FINALIZE_LIMIT <= 255); - allocKind = size_t(kind); - - /* See comments in FreeSpan::allocateFromNewArena. */ - firstFreeSpanOffsets = FreeSpan::FullArenaOffsets; - } - - void setAsNotAllocated() { - allocKind = size_t(FINALIZE_LIMIT); - markOverflow = 0; - allocatedDuringIncremental = 0; - hasDelayedMarking = 0; - auxNextLink = 0; - } - - inline uintptr_t arenaAddress() const; - inline Arena *getArena(); - - AllocKind getAllocKind() const { - JS_ASSERT(allocated()); - return AllocKind(allocKind); - } - - inline size_t getThingSize() const; - - bool hasFreeThings() const { - return firstFreeSpanOffsets != FreeSpan::FullArenaOffsets; - } - - inline bool isEmpty() const; - - void setAsFullyUsed() { - firstFreeSpanOffsets = FreeSpan::FullArenaOffsets; - } - - inline FreeSpan getFirstFreeSpan() const; - inline void setFirstFreeSpan(const FreeSpan *span); - -#ifdef DEBUG - void checkSynchronizedWithFreeList() const; -#endif - - inline ArenaHeader *getNextDelayedMarking() const; - inline void setNextDelayedMarking(ArenaHeader *aheader); - inline void unsetDelayedMarking(); - - inline ArenaHeader *getNextAllocDuringSweep() const; - inline void setNextAllocDuringSweep(ArenaHeader *aheader); - inline void unsetAllocDuringSweep(); -}; - -struct Arena -{ - /* - * Layout of an arena: - * An arena is 4K in size and 4K-aligned. It starts with the ArenaHeader - * descriptor followed by some pad bytes. The remainder of the arena is - * filled with the array of T things. The pad bytes ensure that the thing - * array ends exactly at the end of the arena. - * - * +-------------+-----+----+----+-----+----+ - * | ArenaHeader | pad | T0 | T1 | ... | Tn | - * +-------------+-----+----+----+-----+----+ - * - * <----------------------------------------> = ArenaSize bytes - * <-------------------> = first thing offset - */ - ArenaHeader aheader; - uint8_t data[ArenaSize - sizeof(ArenaHeader)]; - - private: - static JS_FRIEND_DATA(const uint32_t) ThingSizes[]; - static JS_FRIEND_DATA(const uint32_t) FirstThingOffsets[]; - - public: - static void staticAsserts(); - - static size_t thingSize(AllocKind kind) { - return ThingSizes[kind]; - } - - static size_t firstThingOffset(AllocKind kind) { - return FirstThingOffsets[kind]; - } - - static size_t thingsPerArena(size_t thingSize) { - JS_ASSERT(thingSize % Cell::CellSize == 0); - - /* We should be able to fit FreeSpan in any GC thing. */ - JS_ASSERT(thingSize >= sizeof(FreeSpan)); - - return (ArenaSize - sizeof(ArenaHeader)) / thingSize; - } - - static size_t thingsSpan(size_t thingSize) { - return thingsPerArena(thingSize) * thingSize; - } - - static bool isAligned(uintptr_t thing, size_t thingSize) { - /* Things ends at the arena end. */ - uintptr_t tailOffset = (ArenaSize - thing) & ArenaMask; - return tailOffset % thingSize == 0; - } - - uintptr_t address() const { - return aheader.address(); - } - - uintptr_t thingsStart(AllocKind thingKind) { - return address() | firstThingOffset(thingKind); - } - - uintptr_t thingsEnd() { - return address() + ArenaSize; - } - - template - bool finalize(FreeOp *fop, AllocKind thingKind, size_t thingSize); -}; - -inline size_t -ArenaHeader::getThingSize() const -{ - JS_ASSERT(allocated()); - return Arena::thingSize(getAllocKind()); -} - -/* The chunk header (located at the end of the chunk to preserve arena alignment). */ -struct ChunkInfo -{ - Chunk *next; - Chunk **prevp; - - /* Free arenas are linked together with aheader.next. */ - ArenaHeader *freeArenasHead; - - /* - * Decommitted arenas are tracked by a bitmap in the chunk header. We use - * this offset to start our search iteration close to a decommitted arena - * that we can allocate. - */ - uint32_t lastDecommittedArenaOffset; - - /* Number of free arenas, either committed or decommitted. */ - uint32_t numArenasFree; - - /* Number of free, committed arenas. */ - uint32_t numArenasFreeCommitted; - - /* Number of GC cycles this chunk has survived. */ - uint32_t age; -}; - -/* - * Calculating ArenasPerChunk: - * - * In order to figure out how many Arenas will fit in a chunk, we need to know - * how much extra space is available after we allocate the header data. This - * is a problem because the header size depends on the number of arenas in the - * chunk. The two dependent fields are bitmap and decommittedArenas. - * - * For the mark bitmap, we know that each arena will use a fixed number of full - * bytes: ArenaBitmapBytes. The full size of the header data is this number - * multiplied by the eventual number of arenas we have in the header. We, - * conceptually, distribute this header data among the individual arenas and do - * not include it in the header. This way we do not have to worry about its - * variable size: it gets attached to the variable number we are computing. - * - * For the decommitted arena bitmap, we only have 1 bit per arena, so this - * technique will not work. Instead, we observe that we do not have enough - * header info to fill 8 full arenas: it is currently 4 on 64bit, less on - * 32bit. Thus, with current numbers, we need 64 bytes for decommittedArenas. - * This will not become 63 bytes unless we double the data required in the - * header. Therefore, we just compute the number of bytes required to track - * every possible arena and do not worry about slop bits, since there are too - * few to usefully allocate. - * - * To actually compute the number of arenas we can allocate in a chunk, we - * divide the amount of available space less the header info (not including - * the mark bitmap which is distributed into the arena size) by the size of - * the arena (with the mark bitmap bytes it uses). - */ -const size_t BytesPerArenaWithHeader = ArenaSize + ArenaBitmapBytes; -const size_t ChunkDecommitBitmapBytes = ChunkSize / ArenaSize / JS_BITS_PER_BYTE; -const size_t ChunkBytesAvailable = ChunkSize - sizeof(ChunkInfo) - ChunkDecommitBitmapBytes; -const size_t ArenasPerChunk = ChunkBytesAvailable / BytesPerArenaWithHeader; - -/* A chunk bitmap contains enough mark bits for all the cells in a chunk. */ -struct ChunkBitmap -{ - uintptr_t bitmap[ArenaBitmapWords * ArenasPerChunk]; - - MOZ_ALWAYS_INLINE void getMarkWordAndMask(const Cell *cell, uint32_t color, - uintptr_t **wordp, uintptr_t *maskp); - - MOZ_ALWAYS_INLINE bool isMarked(const Cell *cell, uint32_t color) { - uintptr_t *word, mask; - getMarkWordAndMask(cell, color, &word, &mask); - return *word & mask; - } - - MOZ_ALWAYS_INLINE bool markIfUnmarked(const Cell *cell, uint32_t color) { - uintptr_t *word, mask; - getMarkWordAndMask(cell, BLACK, &word, &mask); - if (*word & mask) - return false; - *word |= mask; - if (color != BLACK) { - /* - * We use getMarkWordAndMask to recalculate both mask and word as - * doing just mask << color may overflow the mask. - */ - getMarkWordAndMask(cell, color, &word, &mask); - if (*word & mask) - return false; - *word |= mask; - } - return true; - } - - MOZ_ALWAYS_INLINE void unmark(const Cell *cell, uint32_t color) { - uintptr_t *word, mask; - getMarkWordAndMask(cell, color, &word, &mask); - *word &= ~mask; - } - - void clear() { - PodArrayZero(bitmap); - } - - uintptr_t *arenaBits(ArenaHeader *aheader) { - /* - * We assume that the part of the bitmap corresponding to the arena - * has the exact number of words so we do not need to deal with a word - * that covers bits from two arenas. - */ - JS_STATIC_ASSERT(ArenaBitmapBits == ArenaBitmapWords * JS_BITS_PER_WORD); - - uintptr_t *word, unused; - getMarkWordAndMask(reinterpret_cast(aheader->address()), BLACK, &word, &unused); - return word; - } -}; - -JS_STATIC_ASSERT(ArenaBitmapBytes * ArenasPerChunk == sizeof(ChunkBitmap)); - -typedef BitArray PerArenaBitmap; - -const size_t ChunkPadSize = ChunkSize - - (sizeof(Arena) * ArenasPerChunk) - - sizeof(ChunkBitmap) - - sizeof(PerArenaBitmap) - - sizeof(ChunkInfo); -JS_STATIC_ASSERT(ChunkPadSize < BytesPerArenaWithHeader); - -/* - * Chunks contain arenas and associated data structures (mark bitmap, delayed - * marking state). - */ -struct Chunk -{ - Arena arenas[ArenasPerChunk]; - - /* Pad to full size to ensure cache alignment of ChunkInfo. */ - uint8_t padding[ChunkPadSize]; - - ChunkBitmap bitmap; - PerArenaBitmap decommittedArenas; - ChunkInfo info; - - static Chunk *fromAddress(uintptr_t addr) { - addr &= ~ChunkMask; - return reinterpret_cast(addr); - } - - static bool withinArenasRange(uintptr_t addr) { - uintptr_t offset = addr & ChunkMask; - return offset < ArenasPerChunk * ArenaSize; - } - - static size_t arenaIndex(uintptr_t addr) { - JS_ASSERT(withinArenasRange(addr)); - return (addr & ChunkMask) >> ArenaShift; - } - - uintptr_t address() const { - uintptr_t addr = reinterpret_cast(this); - JS_ASSERT(!(addr & ChunkMask)); - return addr; - } - - bool unused() const { - return info.numArenasFree == ArenasPerChunk; - } - - bool hasAvailableArenas() const { - return info.numArenasFree != 0; - } - - inline void addToAvailableList(JSCompartment *compartment); - inline void insertToAvailableList(Chunk **insertPoint); - inline void removeFromAvailableList(); - - ArenaHeader *allocateArena(JSCompartment *comp, AllocKind kind); - - void releaseArena(ArenaHeader *aheader); - - static Chunk *allocate(JSRuntime *rt); - - /* Must be called with the GC lock taken. */ - static inline void release(JSRuntime *rt, Chunk *chunk); - static inline void releaseList(JSRuntime *rt, Chunk *chunkListHead); - - /* Must be called with the GC lock taken. */ - inline void prepareToBeFreed(JSRuntime *rt); - - /* - * Assuming that the info.prevp points to the next field of the previous - * chunk in a doubly-linked list, get that chunk. - */ - Chunk *getPrevious() { - JS_ASSERT(info.prevp); - return fromPointerToNext(info.prevp); - } - - /* Get the chunk from a pointer to its info.next field. */ - static Chunk *fromPointerToNext(Chunk **nextFieldPtr) { - uintptr_t addr = reinterpret_cast(nextFieldPtr); - JS_ASSERT((addr & ChunkMask) == offsetof(Chunk, info.next)); - return reinterpret_cast(addr - offsetof(Chunk, info.next)); - } - - private: - inline void init(); - - /* Search for a decommitted arena to allocate. */ - unsigned findDecommittedArenaOffset(); - ArenaHeader* fetchNextDecommittedArena(); - - public: - /* Unlink and return the freeArenasHead. */ - inline ArenaHeader* fetchNextFreeArena(JSRuntime *rt); - - inline void addArenaToFreeList(JSRuntime *rt, ArenaHeader *aheader); -}; - -JS_STATIC_ASSERT(sizeof(Chunk) == ChunkSize); - -inline uintptr_t -Cell::address() const -{ - uintptr_t addr = uintptr_t(this); - JS_ASSERT(addr % Cell::CellSize == 0); - JS_ASSERT(Chunk::withinArenasRange(addr)); - return addr; -} - -inline uintptr_t -ArenaHeader::address() const -{ - uintptr_t addr = reinterpret_cast(this); - JS_ASSERT(!(addr & ArenaMask)); - JS_ASSERT(Chunk::withinArenasRange(addr)); - return addr; -} - -inline Chunk * -ArenaHeader::chunk() const -{ - return Chunk::fromAddress(address()); -} - -inline uintptr_t -ArenaHeader::arenaAddress() const -{ - return address(); -} - -inline Arena * -ArenaHeader::getArena() -{ - return reinterpret_cast(arenaAddress()); -} - -inline bool -ArenaHeader::isEmpty() const -{ - /* Arena is empty if its first span covers the whole arena. */ - JS_ASSERT(allocated()); - size_t firstThingOffset = Arena::firstThingOffset(getAllocKind()); - return firstFreeSpanOffsets == FreeSpan::encodeOffsets(firstThingOffset, ArenaMask); -} - -FreeSpan -ArenaHeader::getFirstFreeSpan() const -{ -#ifdef DEBUG - checkSynchronizedWithFreeList(); -#endif - return FreeSpan::decodeOffsets(arenaAddress(), firstFreeSpanOffsets); -} - -void -ArenaHeader::setFirstFreeSpan(const FreeSpan *span) -{ - JS_ASSERT(span->isWithinArena(arenaAddress())); - firstFreeSpanOffsets = span->encodeAsOffsets(); -} - -inline ArenaHeader * -ArenaHeader::getNextDelayedMarking() const -{ - JS_ASSERT(hasDelayedMarking); - return &reinterpret_cast(auxNextLink << ArenaShift)->aheader; -} - -inline void -ArenaHeader::setNextDelayedMarking(ArenaHeader *aheader) -{ - JS_ASSERT(!(uintptr_t(aheader) & ArenaMask)); - JS_ASSERT(!auxNextLink && !hasDelayedMarking); - hasDelayedMarking = 1; - auxNextLink = aheader->arenaAddress() >> ArenaShift; -} - -inline void -ArenaHeader::unsetDelayedMarking() -{ - JS_ASSERT(hasDelayedMarking); - hasDelayedMarking = 0; - auxNextLink = 0; -} - -inline ArenaHeader * -ArenaHeader::getNextAllocDuringSweep() const -{ - JS_ASSERT(allocatedDuringIncremental); - return &reinterpret_cast(auxNextLink << ArenaShift)->aheader; -} - -inline void -ArenaHeader::setNextAllocDuringSweep(ArenaHeader *aheader) -{ - JS_ASSERT(!auxNextLink && !allocatedDuringIncremental); - allocatedDuringIncremental = 1; - auxNextLink = aheader->arenaAddress() >> ArenaShift; -} - -inline void -ArenaHeader::unsetAllocDuringSweep() -{ - JS_ASSERT(allocatedDuringIncremental); - allocatedDuringIncremental = 0; - auxNextLink = 0; -} - -JS_ALWAYS_INLINE void -ChunkBitmap::getMarkWordAndMask(const Cell *cell, uint32_t color, - uintptr_t **wordp, uintptr_t *maskp) -{ - size_t bit = (cell->address() & ChunkMask) / Cell::CellSize + color; - JS_ASSERT(bit < ArenaBitmapBits * ArenasPerChunk); - *maskp = uintptr_t(1) << (bit % JS_BITS_PER_WORD); - *wordp = &bitmap[bit / JS_BITS_PER_WORD]; -} - -static void -AssertValidColor(const void *thing, uint32_t color) -{ -#ifdef DEBUG - ArenaHeader *aheader = reinterpret_cast(thing)->arenaHeader(); - JS_ASSERT_IF(color, color < aheader->getThingSize() / Cell::CellSize); -#endif -} - -inline ArenaHeader * -Cell::arenaHeader() const -{ - uintptr_t addr = address(); - addr &= ~ArenaMask; - return reinterpret_cast(addr); -} - -Chunk * -Cell::chunk() const -{ - uintptr_t addr = uintptr_t(this); - JS_ASSERT(addr % Cell::CellSize == 0); - addr &= ~(ChunkSize - 1); - return reinterpret_cast(addr); -} - -AllocKind -Cell::getAllocKind() const -{ - return arenaHeader()->getAllocKind(); -} - -bool -Cell::isMarked(uint32_t color /* = BLACK */) const -{ - AssertValidColor(this, color); - return chunk()->bitmap.isMarked(this, color); -} - -bool -Cell::markIfUnmarked(uint32_t color /* = BLACK */) const -{ - AssertValidColor(this, color); - return chunk()->bitmap.markIfUnmarked(this, color); -} - -void -Cell::unmark(uint32_t color) const -{ - JS_ASSERT(color != BLACK); - AssertValidColor(this, color); - chunk()->bitmap.unmark(this, color); -} - -JSCompartment * -Cell::compartment() const -{ - return arenaHeader()->compartment; -} - -#ifdef DEBUG -bool -Cell::isAligned() const -{ - return Arena::isAligned(address(), arenaHeader()->getThingSize()); -} -#endif - -} /* namespace gc */ - -} /* namespace js */ - -#endif /* gc_heap_h___ */ diff --git a/scripting/javascript/spidermonkey-android/include/gc/Root.h b/scripting/javascript/spidermonkey-android/include/gc/Root.h index d83e21856f..33c648c7b5 100644 --- a/scripting/javascript/spidermonkey-android/include/gc/Root.h +++ b/scripting/javascript/spidermonkey-android/include/gc/Root.h @@ -77,8 +77,14 @@ class MutableHandleBase {}; namespace JS { +class AutoAssertNoGC; + template class MutableHandle; +JS_FRIEND_API(void) EnterAssertNoGCScope(); +JS_FRIEND_API(void) LeaveAssertNoGCScope(); +JS_FRIEND_API(bool) InNoGCScope(); + /* * Handle provides an implicit constructor for NullPtr so that, given: * foo(Handle h); @@ -316,6 +322,169 @@ class InternalHandle } }; +#ifdef DEBUG +template +class IntermediateNoGC +{ + T t_; + + public: + IntermediateNoGC(const T &t) : t_(t) { + EnterAssertNoGCScope(); + } + IntermediateNoGC(const IntermediateNoGC &) { + EnterAssertNoGCScope(); + } + ~IntermediateNoGC() { + LeaveAssertNoGCScope(); + } + + const T &operator->() { return t_; } + operator const T &() { return t_; } +}; +#endif + +/* + * Return wraps GC things that are returned from accessor methods. The + * wrapper helps to ensure correct rooting of the returned pointer and safe + * access while unrooted. + * + * Example usage in a method declaration: + * + * class Foo { + * HeapPtrScript script_; + * ... + * public: + * Return script() { return script_; } + * }; + * + * Example usage of method (1): + * + * Foo foo(...); + * RootedScript script(cx, foo->script()); + * + * Example usage of method (2): + * + * Foo foo(...); + * foo->script()->needsArgsObj(); + * + * The purpose of this class is to assert eagerly on incorrect use of GC thing + * pointers. For example: + * + * RootedShape shape(cx, ...); + * shape->parent.init(js_NewGCThing(cx, ...)); + * + * In this expression, C++ is allowed to order these calls as follows: + * + * Call Effect + * ---- ------ + * 1) RootedShape::operator-> Stores shape::ptr_ to stack. + * 2) js_NewGCThing Triggers GC and compaction of shapes. This + * moves shape::ptr_ to a new location. + * 3) HeapPtrObject::init This call takes the relocated shape::ptr_ + * as |this|, crashing or, worse, corrupting + * the program's state on the first access + * to a member variable. + * + * If Shape::parent were an accessor function returning a Return, this + * could not happen: Return ensures either immediate rooting or no GC within + * the same expression. + */ +template +class Return +{ + friend class Rooted; + + const T ptr_; + + public: + template + Return(const S &ptr, + typename mozilla::EnableIf::value, int>::Type dummy = 0) + : ptr_(ptr) + {} + + Return(NullPtr) : ptr_(NULL) {} + + /* + * |get(AutoAssertNoGC &)| is the safest way to access a Return without + * rooting it first: it is impossible to call this method without an + * AutoAssertNoGC in scope, so the compiler will automatically catch any + * incorrect usage. + * + * Example: + * AutoAssertNoGC nogc; + * RawScript script = fun->script().get(nogc); + */ + const T &get(AutoAssertNoGC &) const { + return ptr_; + } + + /* + * |operator->|'s result cannot be stored in a local variable, so it is safe + * to use in a CanGC context iff no GC can occur anywhere within the same + * expression (generally from one |;| to the next). |operator->| uses a + * temporary object as a guard and will assert if a CanGC context is + * encountered before the next C++ Sequence Point. + * + * INCORRECT: + * fun->script()->bindings = myBindings->clone(cx, ...); + * + * The compiler is allowed to reorder |fun->script()::operator->()| above + * the call to |clone(cx, ...)|. In this case, the RawScript C++ stores on + * the stack may be corrupted by a GC under |clone|. The subsequent + * dereference of this pointer to get |bindings| will result in an invalid + * access. This wrapper ensures that such usage asserts in DEBUG builds when + * it encounters this situation. Without this assertion, it is possible for + * such access to corrupt program state instead of crashing immediately. + * + * CORRECT: + * RootedScript clone(cx, myBindings->clone(cx, ...)); + * fun->script()->bindings = clone; + */ +#ifdef DEBUG + IntermediateNoGC operator->() const { + return IntermediateNoGC(ptr_); + } +#else + const T &operator->() const { + return ptr_; + } +#endif + + /* + * |unsafeGet()| is unsafe for most uses. Although it performs similar + * checking to |operator->|, its result can be stored to a local variable. + * For this reason, it should only be used when it would be incorrect or + * absurd to create a new Rooted for its use: e.g. for assertions. + */ +#ifdef DEBUG + IntermediateNoGC unsafeGet() const { + return IntermediateNoGC(ptr_); + } +#else + const T &unsafeGet() const { + return ptr_; + } +#endif + + /* + * |operator==| is safe to use in any context. It is present to allow: + * JS_ASSERT(myScript == fun->script().unsafeGet()); + * + * To be rewritten as: + * JS_ASSERT(fun->script() == myScript); + * + * Note: the new order tells C++ to use |Return::operator=| + * instead of direct pointer comparison. + */ + bool operator==(const T &other) { return ptr_ == other; } + bool operator!=(const T &other) { return ptr_ != other; } + bool operator==(const Return &other) { return ptr_ == other.ptr_; } + bool operator==(const JS::Handle &other) { return ptr_ == other.get(); } + inline bool operator==(const Rooted &other); +}; + /* * By default, pointers should use the inheritance hierarchy to find their * ThingRootKind. Some pointer types are explicitly set in jspubtd.h so that @@ -332,12 +501,6 @@ struct RootMethods static bool poisoned(T *v) { return IsPoisonedPtr(v); } }; -#if !(defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)) -// Defined in vm/String.h. -template <> -class Rooted; -#endif - template class RootedBase {}; @@ -356,27 +519,23 @@ class Rooted : public RootedBase { #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) ContextFriendFields *cx = ContextFriendFields::get(cxArg); - - ThingRootKind kind = RootMethods::kind(); - this->stack = reinterpret_cast**>(&cx->thingGCRooters[kind]); - this->prev = *stack; - *stack = this; - - JS_ASSERT(!RootMethods::poisoned(ptr)); + commonInit(cx->thingGCRooters); #endif } void init(JSRuntime *rtArg) { #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) - RuntimeFriendFields *rt = const_cast(RuntimeFriendFields::get(rtArg)); + PerThreadDataFriendFields *pt = PerThreadDataFriendFields::getMainThread(rtArg); + commonInit(pt->thingGCRooters); +#endif + } - ThingRootKind kind = RootMethods::kind(); - this->stack = reinterpret_cast**>(&rt->thingGCRooters[kind]); - this->prev = *stack; - *stack = this; - - JS_ASSERT(!RootMethods::poisoned(ptr)); + void init(js::PerThreadData *ptArg) + { +#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) + PerThreadDataFriendFields *pt = PerThreadDataFriendFields::get(ptArg); + commonInit(pt->thingGCRooters); #endif } @@ -413,6 +572,40 @@ class Rooted : public RootedBase init(cx); } + Rooted(js::PerThreadData *pt + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : ptr(RootMethods::initial()) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + init(pt); + } + + Rooted(js::PerThreadData *pt, T initial + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : ptr(initial) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + init(pt); + } + + template + Rooted(JSContext *cx, const Return &initial + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : ptr(initial.ptr_) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + init(cx); + } + + template + Rooted(js::PerThreadData *pt, const Return &initial + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : ptr(initial.ptr_) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + init(pt); + } + ~Rooted() { #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) @@ -445,7 +638,25 @@ class Rooted : public RootedBase return ptr; } + template + T & operator =(const Return &value) + { + ptr = value.ptr_; + return ptr; + } + private: + void commonInit(Rooted **thingGCRooters) { +#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) + ThingRootKind kind = RootMethods::kind(); + this->stack = reinterpret_cast**>(&thingGCRooters[kind]); + this->prev = *stack; + *stack = this; + + JS_ASSERT(!RootMethods::poisoned(ptr)); +#endif + } + #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) Rooted **stack, *prev; #endif @@ -455,6 +666,19 @@ class Rooted : public RootedBase Rooted(const Rooted &) MOZ_DELETE; }; +#if !(defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)) +// Defined in vm/String.h. +template <> +class Rooted; +#endif + +template +bool +Return::operator==(const Rooted &other) +{ + return ptr_ == other.get(); +} + typedef Rooted RootedObject; typedef Rooted RootedFunction; typedef Rooted RootedScript; @@ -550,10 +774,6 @@ MutableHandle::MutableHandle(js::Rooted *root, ptr = root->address(); } -JS_FRIEND_API(void) EnterAssertNoGCScope(); -JS_FRIEND_API(void) LeaveAssertNoGCScope(); -JS_FRIEND_API(bool) InNoGCScope(); - /* * The scoped guard object AutoAssertNoGC forces the GC to assert if a GC is * attempted while the guard object is live. If you have a GC-unsafe operation @@ -581,15 +801,11 @@ public: /* * AssertCanGC will assert if it is called inside of an AutoAssertNoGC region. */ -#ifdef DEBUG JS_ALWAYS_INLINE void AssertCanGC() { JS_ASSERT(!InNoGCScope()); } -#else -# define AssertCanGC() -#endif #if defined(DEBUG) && defined(JS_GC_ZEAL) && defined(JSGC_ROOT_ANALYSIS) && !defined(JS_THREADSAFE) extern void diff --git a/scripting/javascript/spidermonkey-android/include/gc/Statistics.h b/scripting/javascript/spidermonkey-android/include/gc/Statistics.h deleted file mode 100644 index e0434b4d61..0000000000 --- a/scripting/javascript/spidermonkey-android/include/gc/Statistics.h +++ /dev/null @@ -1,193 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef jsgc_statistics_h___ -#define jsgc_statistics_h___ - -#include - -#include "jsfriendapi.h" -#include "jspubtd.h" -#include "jsutil.h" - -struct JSCompartment; - -namespace js { -namespace gcstats { - -enum Phase { - PHASE_GC_BEGIN, - PHASE_WAIT_BACKGROUND_THREAD, - PHASE_PURGE, - PHASE_MARK, - PHASE_MARK_DISCARD_CODE, - PHASE_MARK_ROOTS, - PHASE_MARK_TYPES, - PHASE_MARK_DELAYED, - PHASE_MARK_WEAK, - PHASE_MARK_GRAY, - PHASE_MARK_GRAY_WEAK, - PHASE_FINALIZE_START, - PHASE_SWEEP, - PHASE_SWEEP_ATOMS, - PHASE_SWEEP_COMPARTMENTS, - PHASE_SWEEP_TABLES, - PHASE_SWEEP_OBJECT, - PHASE_SWEEP_STRING, - PHASE_SWEEP_SCRIPT, - PHASE_SWEEP_SHAPE, - PHASE_SWEEP_IONCODE, - PHASE_SWEEP_DISCARD_CODE, - PHASE_DISCARD_ANALYSIS, - PHASE_DISCARD_TI, - PHASE_FREE_TI_ARENA, - PHASE_SWEEP_TYPES, - PHASE_CLEAR_SCRIPT_ANALYSIS, - PHASE_FINALIZE_END, - PHASE_DESTROY, - PHASE_GC_END, - - PHASE_LIMIT -}; - -enum Stat { - STAT_NEW_CHUNK, - STAT_DESTROY_CHUNK, - - STAT_LIMIT -}; - -class StatisticsSerializer; - -struct Statistics { - Statistics(JSRuntime *rt); - ~Statistics(); - - void beginPhase(Phase phase); - void endPhase(Phase phase); - - void beginSlice(int collectedCount, int compartmentCount, gcreason::Reason reason); - void endSlice(); - - void reset(const char *reason) { slices.back().resetReason = reason; } - void nonincremental(const char *reason) { nonincrementalReason = reason; } - - void count(Stat s) { - JS_ASSERT(s < STAT_LIMIT); - counts[s]++; - } - - int64_t beginSCC(); - void endSCC(unsigned scc, int64_t start); - - jschar *formatMessage(); - jschar *formatJSON(uint64_t timestamp); - - private: - JSRuntime *runtime; - - int64_t startupTime; - - FILE *fp; - bool fullFormat; - - /* - * GCs can't really nest, but a second GC can be triggered from within the - * JSGC_END callback. - */ - int gcDepth; - - int collectedCount; - int compartmentCount; - const char *nonincrementalReason; - - struct SliceData { - SliceData(gcreason::Reason reason, int64_t start, size_t startFaults) - : reason(reason), resetReason(NULL), start(start), startFaults(startFaults) - { - PodArrayZero(phaseTimes); - } - - gcreason::Reason reason; - const char *resetReason; - int64_t start, end; - size_t startFaults, endFaults; - int64_t phaseTimes[PHASE_LIMIT]; - - int64_t duration() const { return end - start; } - }; - - Vector slices; - - /* Most recent time when the given phase started. */ - int64_t phaseStartTimes[PHASE_LIMIT]; - - /* Total time in a given phase for this GC. */ - int64_t phaseTimes[PHASE_LIMIT]; - - /* Total time in a given phase over all GCs. */ - int64_t phaseTotals[PHASE_LIMIT]; - - /* Number of events of this type for this GC. */ - unsigned int counts[STAT_LIMIT]; - - /* Allocated space before the GC started. */ - size_t preBytes; - - /* Sweep times for SCCs of compartments. */ - Vector sccTimes; - - void beginGC(); - void endGC(); - - void gcDuration(int64_t *total, int64_t *maxPause); - void sccDurations(int64_t *total, int64_t *maxPause); - void printStats(); - bool formatData(StatisticsSerializer &ss, uint64_t timestamp); - - double computeMMU(int64_t resolution); -}; - -struct AutoGCSlice { - AutoGCSlice(Statistics &stats, int collectedCount, int compartmentCount, gcreason::Reason reason - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : stats(stats) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; - stats.beginSlice(collectedCount, compartmentCount, reason); - } - ~AutoGCSlice() { stats.endSlice(); } - - Statistics &stats; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - -struct AutoPhase { - AutoPhase(Statistics &stats, Phase phase JS_GUARD_OBJECT_NOTIFIER_PARAM) - : stats(stats), phase(phase) { JS_GUARD_OBJECT_NOTIFIER_INIT; stats.beginPhase(phase); } - ~AutoPhase() { stats.endPhase(phase); } - - Statistics &stats; - Phase phase; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - -struct AutoSCC { - AutoSCC(Statistics &stats, unsigned scc JS_GUARD_OBJECT_NOTIFIER_PARAM) - : stats(stats), scc(scc) { JS_GUARD_OBJECT_NOTIFIER_INIT; start = stats.beginSCC(); } - ~AutoSCC() { stats.endSCC(scc, start); } - - Statistics &stats; - unsigned scc; - int64_t start; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - -} /* namespace gcstats */ -} /* namespace js */ - -#endif /* jsgc_statistics_h___ */ diff --git a/scripting/javascript/spidermonkey-android/include/gc/StoreBuffer.h b/scripting/javascript/spidermonkey-android/include/gc/StoreBuffer.h deleted file mode 100644 index 9240e93f10..0000000000 --- a/scripting/javascript/spidermonkey-android/include/gc/StoreBuffer.h +++ /dev/null @@ -1,398 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifdef JSGC_GENERATIONAL -#ifndef jsgc_storebuffer_h___ -#define jsgc_storebuffer_h___ - -#include "jsgc.h" -#include "jsalloc.h" - -#include "gc/Marking.h" - -namespace js { -namespace gc { - -/* - * Note: this is a stub Nursery that does not actually contain a heap, just a - * set of pointers which are "inside" the nursery to implement verification. - */ -class Nursery -{ - HashSet, SystemAllocPolicy> nursery; - - public: - Nursery() : nursery() {} - - bool enable() { - if (!nursery.initialized()) - return nursery.init(); - return true; - } - - void disable() { - if (!nursery.initialized()) - return; - nursery.finish(); - } - - bool isInside(void *cell) const { - JS_ASSERT((uintptr_t(cell) & 0x3) == 0); - return nursery.initialized() && nursery.has(cell); - } - - void insertPointer(void *cell) { - nursery.putNew(cell); - } -}; - -/* - * BufferableRef represents an abstract reference for use in the generational - * GC's remembered set. Entries in the store buffer that cannot be represented - * with the simple pointer-to-a-pointer scheme must derive from this class and - * use the generic store buffer interface. - */ -class BufferableRef -{ - public: - virtual bool match(void *location) = 0; - virtual void mark(JSTracer *trc) = 0; -}; - -/* - * HashKeyRef represents a reference to a HashTable key. Manual HashTable - * barriers should should instantiate this template with their own table/key - * type to insert into the generic buffer with putGeneric. - */ -template -class HashKeyRef : public BufferableRef -{ - Map *map; - Key key; - - typedef typename Map::Ptr Ptr; - - public: - HashKeyRef(Map *m, const Key &k) : map(m), key(k) {} - - bool match(void *location) { - Ptr p = map->lookup(key); - if (!p) - return false; - return &p->key == location; - } - - void mark(JSTracer *trc) {} -}; - -/* - * The StoreBuffer observes all writes that occur in the system and performs - * efficient filtering of them to derive a remembered set for nursery GC. - */ -class StoreBuffer -{ - /* TODO: profile to find the ideal size for these. */ - static const size_t ValueBufferSize = 1 * 1024 * sizeof(Value *); - static const size_t CellBufferSize = 2 * 1024 * sizeof(Cell **); - static const size_t SlotBufferSize = 2 * 1024 * (sizeof(JSObject *) + sizeof(uint32_t)); - static const size_t RelocValueBufferSize = 1 * 1024 * sizeof(Value *); - static const size_t RelocCellBufferSize = 1 * 1024 * sizeof(Cell **); - static const size_t GenericBufferSize = 1 * 1024 * sizeof(int); - static const size_t TotalSize = ValueBufferSize + CellBufferSize + - SlotBufferSize + RelocValueBufferSize + RelocCellBufferSize + - GenericBufferSize; - - typedef HashSet, SystemAllocPolicy> EdgeSet; - - /* - * This buffer holds only a single type of edge. Using this buffer is more - * efficient than the generic buffer when many writes will be to the same - * type of edge: e.g. Value or Cell*. - */ - template - class MonoTypeBuffer - { - friend class StoreBuffer; - - StoreBuffer *owner; - Nursery *nursery; - - T *base; /* Pointer to the start of the buffer. */ - T *pos; /* Pointer to the current insertion position. */ - T *top; /* Pointer to one element after the end. */ - - MonoTypeBuffer(StoreBuffer *owner, Nursery *nursery) - : owner(owner), nursery(nursery), base(NULL), pos(NULL), top(NULL) - {} - - MonoTypeBuffer &operator=(const MonoTypeBuffer& other) MOZ_DELETE; - - bool enable(uint8_t *region, size_t len); - void disable(); - - bool isEmpty() const { return pos == base; } - bool isFull() const { JS_ASSERT(pos <= top); return pos == top; } - - /* Compaction algorithms. */ - void compactNotInSet(); - - /* - * Attempts to reduce the usage of the buffer by removing unnecessary - * entries. - */ - virtual void compact(); - - /* Add one item to the buffer. */ - void put(const T &v); - - /* For verification. */ - bool accumulateEdges(EdgeSet &edges); - }; - - /* - * Overrides the MonoTypeBuffer to support pointers that may be moved in - * memory outside of the GC's control. - */ - template - class RelocatableMonoTypeBuffer : public MonoTypeBuffer - { - friend class StoreBuffer; - - RelocatableMonoTypeBuffer(StoreBuffer *owner, Nursery *nursery) - : MonoTypeBuffer(owner, nursery) - {} - - /* Override compaction to filter out removed items. */ - void compactMoved(); - virtual void compact(); - - /* Record a removal from the buffer. */ - void unput(const T &v); - }; - - class GenericBuffer - { - friend class StoreBuffer; - - StoreBuffer *owner; - Nursery *nursery; - - uint8_t *base; /* Pointer to start of buffer. */ - uint8_t *pos; /* Pointer to current buffer position. */ - uint8_t *top; /* Pointer to one past the last entry. */ - - GenericBuffer(StoreBuffer *owner, Nursery *nursery) - : owner(owner), nursery(nursery) - {} - - GenericBuffer &operator=(const GenericBuffer& other) MOZ_DELETE; - - bool enable(uint8_t *region, size_t len); - void disable(); - - /* Check if a pointer is present in the buffer. */ - bool containsEdge(void *location) const; - - template - void put(const T &t) { - /* Check if we have been enabled. */ - if (!pos) - return; - - /* Check for overflow. */ - if (top - pos < (unsigned)(sizeof(unsigned) + sizeof(T))) { - owner->setOverflowed(); - return; - } - - *((unsigned *)pos) = sizeof(T); - pos += sizeof(unsigned); - - T *p = (T *)pos; - new (p) T(t); - pos += sizeof(T); - } - }; - - class CellPtrEdge - { - friend class StoreBuffer; - friend class StoreBuffer::MonoTypeBuffer; - friend class StoreBuffer::RelocatableMonoTypeBuffer; - - Cell **edge; - - CellPtrEdge(Cell **v) : edge(v) {} - bool operator==(const CellPtrEdge &other) const { return edge == other.edge; } - bool operator!=(const CellPtrEdge &other) const { return edge != other.edge; } - - void *location() const { return (void *)edge; } - - bool inRememberedSet(Nursery *n) { - return !n->isInside(edge) && n->isInside(*edge); - } - - bool isNullEdge() const { - return !*edge; - } - - CellPtrEdge tagged() const { return CellPtrEdge((Cell **)(uintptr_t(edge) | 1)); } - CellPtrEdge untagged() const { return CellPtrEdge((Cell **)(uintptr_t(edge) & ~1)); } - bool isTagged() const { return bool(uintptr_t(edge) & 1); } - }; - - class ValueEdge - { - friend class StoreBuffer; - friend class StoreBuffer::MonoTypeBuffer; - friend class StoreBuffer::RelocatableMonoTypeBuffer; - - Value *edge; - - ValueEdge(Value *v) : edge(v) {} - bool operator==(const ValueEdge &other) const { return edge == other.edge; } - bool operator!=(const ValueEdge &other) const { return edge != other.edge; } - - void *deref() const { return edge->isGCThing() ? edge->toGCThing() : NULL; } - void *location() const { return (void *)edge; } - - bool inRememberedSet(Nursery *n) { - return !n->isInside(edge) && n->isInside(deref()); - } - - bool isNullEdge() const { - return !deref(); - } - - ValueEdge tagged() const { return ValueEdge((Value *)(uintptr_t(edge) | 1)); } - ValueEdge untagged() const { return ValueEdge((Value *)(uintptr_t(edge) & ~1)); } - bool isTagged() const { return bool(uintptr_t(edge) & 1); } - }; - - struct SlotEdge - { - friend class StoreBuffer; - friend class StoreBuffer::MonoTypeBuffer; - - JSObject *object; - uint32_t offset; - - SlotEdge(JSObject *object, uint32_t offset) : object(object), offset(offset) {} - - bool operator==(const SlotEdge &other) const { - return object == other.object && offset == other.offset; - } - - bool operator!=(const SlotEdge &other) const { - return object != other.object || offset != other.offset; - } - - HeapSlot *slotLocation() const { - if (object->isDenseArray()) { - if (offset >= object->getDenseArrayInitializedLength()) - return NULL; - return (HeapSlot *)&object->getDenseArrayElement(offset); - } - if (offset >= object->slotSpan()) - return NULL; - return &object->getSlotRef(offset); - } - - void *deref() const { - HeapSlot *loc = slotLocation(); - return (loc && loc->isGCThing()) ? loc->toGCThing() : NULL; - } - - void *location() const { - return (void *)slotLocation(); - } - - bool inRememberedSet(Nursery *n) { - return !n->isInside(object) && n->isInside(deref()); - } - - bool isNullEdge() const { - return !deref(); - } - }; - - MonoTypeBuffer bufferVal; - MonoTypeBuffer bufferCell; - MonoTypeBuffer bufferSlot; - RelocatableMonoTypeBuffer bufferRelocVal; - RelocatableMonoTypeBuffer bufferRelocCell; - GenericBuffer bufferGeneric; - - Nursery *nursery; - - void *buffer; - - bool overflowed; - bool enabled; - - /* For the verifier. */ - EdgeSet edgeSet; - - /* For use by our owned buffers. */ - void setOverflowed() { overflowed = true; } - - public: - StoreBuffer(Nursery *n) - : bufferVal(this, n), bufferCell(this, n), bufferSlot(this, n), - bufferRelocVal(this, n), bufferRelocCell(this, n), bufferGeneric(this, n), - nursery(n), buffer(NULL), overflowed(false), enabled(false) - {} - - bool enable(); - void disable(); - bool isEnabled() { return enabled; } - - /* Get the overflowed status. */ - bool hasOverflowed() const { return overflowed; } - - /* Insert a single edge into the buffer/remembered set. */ - void putValue(Value *v) { - bufferVal.put(v); - } - void putCell(Cell **o) { - bufferCell.put(o); - } - void putSlot(JSObject *obj, uint32_t slot) { - bufferSlot.put(SlotEdge(obj, slot)); - } - - /* Insert or update a single edge in the Relocatable buffer. */ - void putRelocatableValue(Value *v) { - bufferRelocVal.put(v); - } - void putRelocatableCell(Cell **c) { - bufferRelocCell.put(c); - } - void removeRelocatableValue(Value *v) { - bufferRelocVal.unput(v); - } - void removeRelocatableCell(Cell **c) { - bufferRelocCell.unput(c); - } - - /* Insert an entry into the generic buffer. */ - template - void putGeneric(const T &t) { - bufferGeneric.put(t); - } - - /* For the verifier. */ - bool coalesceForVerification(); - void releaseVerificationData(); - bool containsEdgeAt(void *loc) const; -}; - -} /* namespace gc */ -} /* namespace js */ - -#endif /* jsgc_storebuffer_h___ */ -#endif /* JSGC_GENERATIONAL */ diff --git a/scripting/javascript/spidermonkey-android/include/js-config.h b/scripting/javascript/spidermonkey-android/include/js-config.h index a543aedf25..cdd698eae3 100644 --- a/scripting/javascript/spidermonkey-android/include/js-config.h +++ b/scripting/javascript/spidermonkey-android/include/js-config.h @@ -20,7 +20,7 @@ /* Define to 1 if SpiderMonkey should support the ability to perform entirely too much GC. */ -#define JS_GC_ZEAL 1 +/* #undef JS_GC_ZEAL */ /* Define to 1 if the header is present and useable. See jscpucfg.h. */ diff --git a/scripting/javascript/spidermonkey-android/include/js.msg b/scripting/javascript/spidermonkey-android/include/js.msg index 92d4519c6f..bb2f65627c 100644 --- a/scripting/javascript/spidermonkey-android/include/js.msg +++ b/scripting/javascript/spidermonkey-android/include/js.msg @@ -375,3 +375,5 @@ MSG_DEF(JSMSG_MUST_REPORT_UNDEFINED, 321, 0, JSEXN_TYPEERR, "proxy must report MSG_DEF(JSMSG_CANT_SET_NW_NC, 322, 0, JSEXN_TYPEERR, "proxy can't successfully set a non-writable, non-configurable property") MSG_DEF(JSMSG_CANT_SET_WO_SETTER, 323, 0, JSEXN_TYPEERR, "proxy can't succesfully set an accessor property without a setter") MSG_DEF(JSMSG_DEBUG_BAD_REFERENT, 324, 2, JSEXN_TYPEERR, "{0} does not refer to {1}") +MSG_DEF(JSMSG_DEBUG_WRAPPER_IN_WAY, 325, 2, JSEXN_TYPEERR, "{0} is a wrapper around {1}, but a direct reference is required") +MSG_DEF(JSMSG_UNWRAP_DENIED, 326, 0, JSEXN_ERR, "permission denied to unwrap object") diff --git a/scripting/javascript/spidermonkey-android/include/js/HashTable.h b/scripting/javascript/spidermonkey-android/include/js/HashTable.h index bef5851548..d5710aed71 100644 --- a/scripting/javascript/spidermonkey-android/include/js/HashTable.h +++ b/scripting/javascript/spidermonkey-android/include/js/HashTable.h @@ -5,74 +5,621 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef jshashtable_h_ -#define jshashtable_h_ +#ifndef js_HashTable_h__ +#define js_HashTable_h__ +#include "js/TemplateLib.h" +#include "js/Utility.h" #include "mozilla/Attributes.h" -#include "TemplateLib.h" -#include "Utility.h" - namespace js { class TempAllocPolicy; +template struct DefaultHasher; +template class HashMapEntry; +namespace detail { + template class HashTableEntry; + template class HashTable; +} /*****************************************************************************/ +// A JS-friendly, STL-like container providing a hash-based map from keys to +// values. In particular, HashMap calls constructors and destructors of all +// objects added so non-PODs may be used safely. +// +// Key/Value requirements: +// - movable, destructible, assignable +// HashPolicy requirements: +// - see Hash Policy section below +// AllocPolicy: +// - see jsalloc.h +// +// Note: +// - HashMap is not reentrant: Key/Value/HashPolicy/AllocPolicy members +// called by HashMap must not call back into the same HashMap object. +// - Due to the lack of exception handling, the user must call |init()|. +template , + class AllocPolicy = TempAllocPolicy> +class HashMap +{ + typedef HashMapEntry TableEntry; + + struct MapHashPolicy : HashPolicy + { + typedef Key KeyType; + static const Key &getKey(TableEntry &e) { return e.key; } + static void setKey(TableEntry &e, Key &k) { const_cast(e.key) = k; } + }; + + typedef detail::HashTable Impl; + Impl impl; + + public: + typedef typename HashPolicy::Lookup Lookup; + typedef TableEntry Entry; + + // HashMap construction is fallible (due to OOM); thus the user must call + // init after constructing a HashMap and check the return value. + HashMap(AllocPolicy a = AllocPolicy()) : impl(a) {} + bool init(uint32_t len = 16) { return impl.init(len); } + bool initialized() const { return impl.initialized(); } + + // Return whether the given lookup value is present in the map. E.g.: + // + // typedef HashMap HM; + // HM h; + // if (HM::Ptr p = h.lookup(3)) { + // const HM::Entry &e = *p; // p acts like a pointer to Entry + // assert(p->key == 3); // Entry contains the key + // char val = p->value; // and value + // } + // + // Also see the definition of Ptr in HashTable above (with T = Entry). + typedef typename Impl::Ptr Ptr; + Ptr lookup(const Lookup &l) const { return impl.lookup(l); } + + // Assuming |p.found()|, remove |*p|. + void remove(Ptr p) { impl.remove(p); } + + // Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient + // insertion of Key |k| (where |HashPolicy::match(k,l) == true|) using + // |add(p,k,v)|. After |add(p,k,v)|, |p| points to the new Entry. E.g.: + // + // typedef HashMap HM; + // HM h; + // HM::AddPtr p = h.lookupForAdd(3); + // if (!p) { + // if (!h.add(p, 3, 'a')) + // return false; + // } + // const HM::Entry &e = *p; // p acts like a pointer to Entry + // assert(p->key == 3); // Entry contains the key + // char val = p->value; // and value + // + // Also see the definition of AddPtr in HashTable above (with T = Entry). + // + // N.B. The caller must ensure that no mutating hash table operations + // occur between a pair of |lookupForAdd| and |add| calls. To avoid + // looking up the key a second time, the caller may use the more efficient + // relookupOrAdd method. This method reuses part of the hashing computation + // to more efficiently insert the key if it has not been added. For + // example, a mutation-handling version of the previous example: + // + // HM::AddPtr p = h.lookupForAdd(3); + // if (!p) { + // call_that_may_mutate_h(); + // if (!h.relookupOrAdd(p, 3, 'a')) + // return false; + // } + // const HM::Entry &e = *p; + // assert(p->key == 3); + // char val = p->value; + typedef typename Impl::AddPtr AddPtr; + AddPtr lookupForAdd(const Lookup &l) const { + return impl.lookupForAdd(l); + } + + template + bool add(AddPtr &p, const KeyInput &k, const ValueInput &v) { + Entry e(k, v); + return impl.add(p, Move(e)); + } + + bool add(AddPtr &p, const Key &k) { + Entry e(k, Value()); + return impl.add(p, Move(e)); + } + + template + bool relookupOrAdd(AddPtr &p, const KeyInput &k, const ValueInput &v) { + Entry e(k, v); + return impl.relookupOrAdd(p, k, Move(e)); + } + + // |all()| returns a Range containing |count()| elements. E.g.: + // + // typedef HashMap HM; + // HM h; + // for (HM::Range r = h.all(); !r.empty(); r.popFront()) + // char c = r.front().value; + // + // Also see the definition of Range in HashTable above (with T = Entry). + typedef typename Impl::Range Range; + Range all() const { return impl.all(); } + + // Typedef for the enumeration class. An Enum may be used to examine and + // remove table entries: + // + // typedef HashMap HM; + // HM s; + // for (HM::Enum e(s); !e.empty(); e.popFront()) + // if (e.front().value == 'l') + // e.removeFront(); + // + // Table resize may occur in Enum's destructor. Also see the definition of + // Enum in HashTable above (with T = Entry). + typedef typename Impl::Enum Enum; + + // Remove all entries. This does not shrink the table. For that consider + // using the finish() method. + void clear() { impl.clear(); } + + // Remove all the entries and release all internal buffers. The map must + // be initialized again before any use. + void finish() { impl.finish(); } + + // Does the table contain any entries? + bool empty() const { return impl.empty(); } + + // Number of live elements in the map. + uint32_t count() const { return impl.count(); } + + // Total number of allocation in the dynamic table. Note: resize will + // happen well before count() == capacity(). + size_t capacity() const { return impl.capacity(); } + + // Don't just call |impl.sizeOfExcludingThis()| because there's no + // guarantee that |impl| is the first field in HashMap. + size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const { + return impl.sizeOfExcludingThis(mallocSizeOf); + } + size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const { + return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf); + } + + // If |generation()| is the same before and after a HashMap operation, + // pointers into the table remain valid. + unsigned generation() const { return impl.generation(); } + + /************************************************** Shorthand operations */ + + bool has(const Lookup &l) const { + return impl.lookup(l) != NULL; + } + + // Overwrite existing value with v. Return false on oom. + template + bool put(const KeyInput &k, const ValueInput &v) { + AddPtr p = lookupForAdd(k); + if (p) { + p->value = v; + return true; + } + return add(p, k, v); + } + + // Like put, but assert that the given key is not already present. + template + bool putNew(const KeyInput &k, const ValueInput &v) { + Entry e(k, v); + return impl.putNew(k, Move(e)); + } + + // Add (k,defaultValue) if |k| is not found. Return a false-y Ptr on oom. + Ptr lookupWithDefault(const Key &k, const Value &defaultValue) { + AddPtr p = lookupForAdd(k); + if (p) + return p; + (void)add(p, k, defaultValue); // p is left false-y on oom. + return p; + } + + // Remove if present. + void remove(const Lookup &l) { + if (Ptr p = lookup(l)) + remove(p); + } + + private: + // Not implicitly copyable (expensive). May add explicit |clone| later. + HashMap(const HashMap &hm) MOZ_DELETE; + HashMap &operator=(const HashMap &hm) MOZ_DELETE; + + friend class Impl::Enum; + + typedef typename tl::StaticAssert::result>::result keyAssert; + typedef typename tl::StaticAssert::result>::result valAssert; +}; + +/*****************************************************************************/ + +// A JS-friendly, STL-like container providing a hash-based set of values. In +// particular, HashSet calls constructors and destructors of all objects added +// so non-PODs may be used safely. +// +// T requirements: +// - movable, destructible, assignable +// HashPolicy requirements: +// - see Hash Policy section below +// AllocPolicy: +// - see jsalloc.h +// +// Note: +// - HashSet is not reentrant: T/HashPolicy/AllocPolicy members called by +// HashSet must not call back into the same HashSet object. +// - Due to the lack of exception handling, the user must call |init()|. +template , + class AllocPolicy = TempAllocPolicy> +class HashSet +{ + struct SetOps : HashPolicy + { + typedef T KeyType; + static const KeyType &getKey(const T &t) { return t; } + static void setKey(T &t, KeyType &k) { t = k; } + }; + + typedef detail::HashTable Impl; + Impl impl; + + public: + typedef typename HashPolicy::Lookup Lookup; + typedef T Entry; + + // HashSet construction is fallible (due to OOM); thus the user must call + // init after constructing a HashSet and check the return value. + HashSet(AllocPolicy a = AllocPolicy()) : impl(a) {} + bool init(uint32_t len = 16) { return impl.init(len); } + bool initialized() const { return impl.initialized(); } + + // Return whether the given lookup value is present in the map. E.g.: + // + // typedef HashSet HS; + // HS h; + // if (HS::Ptr p = h.lookup(3)) { + // assert(*p == 3); // p acts like a pointer to int + // } + // + // Also see the definition of Ptr in HashTable above. + typedef typename Impl::Ptr Ptr; + Ptr lookup(const Lookup &l) const { return impl.lookup(l); } + + // Assuming |p.found()|, remove |*p|. + void remove(Ptr p) { impl.remove(p); } + + // Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient + // insertion of T value |t| (where |HashPolicy::match(t,l) == true|) using + // |add(p,t)|. After |add(p,t)|, |p| points to the new element. E.g.: + // + // typedef HashSet HS; + // HS h; + // HS::AddPtr p = h.lookupForAdd(3); + // if (!p) { + // if (!h.add(p, 3)) + // return false; + // } + // assert(*p == 3); // p acts like a pointer to int + // + // Also see the definition of AddPtr in HashTable above. + // + // N.B. The caller must ensure that no mutating hash table operations + // occur between a pair of |lookupForAdd| and |add| calls. To avoid + // looking up the key a second time, the caller may use the more efficient + // relookupOrAdd method. This method reuses part of the hashing computation + // to more efficiently insert the key if it has not been added. For + // example, a mutation-handling version of the previous example: + // + // HS::AddPtr p = h.lookupForAdd(3); + // if (!p) { + // call_that_may_mutate_h(); + // if (!h.relookupOrAdd(p, 3, 3)) + // return false; + // } + // assert(*p == 3); + // + // Note that relookupOrAdd(p,l,t) performs Lookup using |l| and adds the + // entry |t|, where the caller ensures match(l,t). + typedef typename Impl::AddPtr AddPtr; + AddPtr lookupForAdd(const Lookup &l) const { return impl.lookupForAdd(l); } + + bool add(AddPtr &p, const T &t) { return impl.add(p, t); } + + bool relookupOrAdd(AddPtr &p, const Lookup &l, const T &t) { + return impl.relookupOrAdd(p, l, t); + } + + // |all()| returns a Range containing |count()| elements: + // + // typedef HashSet HS; + // HS h; + // for (HS::Range r = h.all(); !r.empty(); r.popFront()) + // int i = r.front(); + // + // Also see the definition of Range in HashTable above. + typedef typename Impl::Range Range; + Range all() const { return impl.all(); } + + // Typedef for the enumeration class. An Enum may be used to examine and + // remove table entries: + // + // typedef HashSet HS; + // HS s; + // for (HS::Enum e(s); !e.empty(); e.popFront()) + // if (e.front() == 42) + // e.removeFront(); + // + // Table resize may occur in Enum's destructor. Also see the definition of + // Enum in HashTable above. + typedef typename Impl::Enum Enum; + + // Remove all entries. This does not shrink the table. For that consider + // using the finish() method. + void clear() { impl.clear(); } + + // Remove all the entries and release all internal buffers. The set must + // be initialized again before any use. + void finish() { impl.finish(); } + + // Does the table contain any entries? + bool empty() const { return impl.empty(); } + + // Number of live elements in the map. + uint32_t count() const { return impl.count(); } + + // Total number of allocation in the dynamic table. Note: resize will + // happen well before count() == capacity(). + size_t capacity() const { return impl.capacity(); } + + // Don't just call |impl.sizeOfExcludingThis()| because there's no + // guarantee that |impl| is the first field in HashSet. + size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const { + return impl.sizeOfExcludingThis(mallocSizeOf); + } + size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const { + return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf); + } + + // If |generation()| is the same before and after a HashSet operation, + // pointers into the table remain valid. + unsigned generation() const { return impl.generation(); } + + /************************************************** Shorthand operations */ + + bool has(const Lookup &l) const { + return impl.lookup(l) != NULL; + } + + // Overwrite existing value with v. Return false on oom. + bool put(const T &t) { + AddPtr p = lookupForAdd(t); + return p ? true : add(p, t); + } + + // Like put, but assert that the given key is not already present. + bool putNew(const T &t) { + return impl.putNew(t, t); + } + + bool putNew(const Lookup &l, const T &t) { + return impl.putNew(l, t); + } + + void remove(const Lookup &l) { + if (Ptr p = lookup(l)) + remove(p); + } + + private: + // Not implicitly copyable (expensive). May add explicit |clone| later. + HashSet(const HashSet &hs) MOZ_DELETE; + HashSet &operator=(const HashSet &hs) MOZ_DELETE; + + friend class Impl::Enum; + + typedef typename tl::StaticAssert::result>::result _; +}; + +/*****************************************************************************/ + +// Hash Policy +// +// A hash policy P for a hash table with key-type Key must provide: +// - a type |P::Lookup| to use to lookup table entries; +// - a static member function |P::hash| with signature +// +// static js::HashNumber hash(Lookup) +// +// to use to hash the lookup type; and +// - a static member function |P::match| with signature +// +// static bool match(Key, Lookup) +// +// to use to test equality of key and lookup values. +// +// Normally, Lookup = Key. In general, though, different values and types of +// values can be used to lookup and store. If a Lookup value |l| is != to the +// added Key value |k|, the user must ensure that |P::match(k,l)|. E.g.: +// +// js::HashSet::AddPtr p = h.lookup(l); +// if (!p) { +// assert(P::match(k, l)); // must hold +// h.add(p, k); +// } + +// Pointer hashing policy that strips the lowest zeroBits when calculating the +// hash to improve key distribution. +template +struct PointerHasher +{ + typedef Key Lookup; + static HashNumber hash(const Lookup &l) { + size_t word = reinterpret_cast(l) >> zeroBits; + JS_STATIC_ASSERT(sizeof(HashNumber) == 4); +#if JS_BYTES_PER_WORD == 4 + return HashNumber(word); +#else + JS_STATIC_ASSERT(sizeof word == 8); + return HashNumber((word >> 32) ^ word); +#endif + } + static bool match(const Key &k, const Lookup &l) { + return k == l; + } +}; + +// Default hash policy: just use the 'lookup' value. This of course only +// works if the lookup value is integral. HashTable applies ScrambleHashCode to +// the result of the 'hash' which means that it is 'ok' if the lookup value is +// not well distributed over the HashNumber domain. +template +struct DefaultHasher +{ + typedef Key Lookup; + static HashNumber hash(const Lookup &l) { + // Hash if can implicitly cast to hash number type. + return l; + } + static bool match(const Key &k, const Lookup &l) { + // Use builtin or overloaded operator==. + return k == l; + } +}; + +// Specialize hashing policy for pointer types. It assumes that the type is +// at least word-aligned. For types with smaller size use PointerHasher. +template +struct DefaultHasher : PointerHasher::result> +{}; + +/*****************************************************************************/ + +// Both HashMap and HashSet are implemented by a single HashTable that is even +// more heavily parameterized than the other two. This leaves HashTable gnarly +// and extremely coupled to HashMap and HashSet; thus code should not use +// HashTable directly. + +template +class HashMapEntry +{ + template friend class detail::HashTable; + template friend class detail::HashTableEntry; + + HashMapEntry(const HashMapEntry &) MOZ_DELETE; + void operator=(const HashMapEntry &) MOZ_DELETE; + + public: + template + HashMapEntry(const KeyInput &k, const ValueInput &v) : key(k), value(v) {} + + HashMapEntry(MoveRef rhs) + : key(Move(rhs->key)), value(Move(rhs->value)) { } + + const Key key; + Value value; +}; + +namespace tl { + +template +struct IsPodType > { + static const bool result = IsPodType::result; +}; + +template +struct IsPodType > +{ + static const bool result = IsPodType::result && IsPodType::result; +}; + +} // namespace tl + namespace detail { template class HashTable; template -class HashTableEntry { - HashNumber keyHash; - +class HashTableEntry +{ + template friend class HashTable; typedef typename tl::StripConst::result NonConstT; + HashNumber keyHash; + mozilla::AlignedStorage2 mem; + static const HashNumber sFreeKey = 0; static const HashNumber sRemovedKey = 1; static const HashNumber sCollisionBit = 1; - template friend class HashTable; + // Assumed by calloc in createTable. + JS_STATIC_ASSERT(sFreeKey == 0); static bool isLiveHash(HashNumber hash) { return hash > sRemovedKey; } + HashTableEntry(const HashTableEntry &) MOZ_DELETE; + void operator=(const HashTableEntry &) MOZ_DELETE; + ~HashTableEntry() MOZ_DELETE; + public: - HashTableEntry() : keyHash(0), t() {} - HashTableEntry(MoveRef rhs) : keyHash(rhs->keyHash), t(Move(rhs->t)) { } - void operator=(const HashTableEntry &rhs) { keyHash = rhs.keyHash; t = rhs.t; } - void operator=(MoveRef rhs) { keyHash = rhs->keyHash; t = Move(rhs->t); } + // NB: HashTableEntry is treated as a POD: no constructor or destructor calls. - NonConstT t; - - bool isFree() const { return keyHash == sFreeKey; } - void setFree() { keyHash = sFreeKey; t = T(); } - bool isRemoved() const { return keyHash == sRemovedKey; } - void setRemoved() { keyHash = sRemovedKey; t = T(); } - bool isLive() const { return isLiveHash(keyHash); } - void setLive(HashNumber hn) { JS_ASSERT(isLiveHash(hn)); keyHash = hn; } - - void setCollision() { JS_ASSERT(isLive()); keyHash |= sCollisionBit; } - void setCollision(HashNumber collisionBit) { - JS_ASSERT(isLive()); keyHash |= collisionBit; + void destroyIfLive() { + if (isLive()) + mem.addr()->~T(); + } + + void destroy() { + JS_ASSERT(isLive()); + mem.addr()->~T(); + } + + void swap(HashTableEntry *other) { + Swap(keyHash, other->keyHash); + Swap(mem, other->mem); + } + + T &get() { JS_ASSERT(isLive()); return *mem.addr(); } + + bool isFree() const { return keyHash == sFreeKey; } + void clearLive() { JS_ASSERT(isLive()); keyHash = sFreeKey; mem.addr()->~T(); } + void clear() { if (isLive()) mem.addr()->~T(); keyHash = sFreeKey; } + bool isRemoved() const { return keyHash == sRemovedKey; } + void removeLive() { JS_ASSERT(isLive()); keyHash = sRemovedKey; mem.addr()->~T(); } + bool isLive() const { return isLiveHash(keyHash); } + void setCollision() { JS_ASSERT(isLive()); keyHash |= sCollisionBit; } + void setCollision(HashNumber bit) { JS_ASSERT(isLive()); keyHash |= bit; } + void unsetCollision() { keyHash &= ~sCollisionBit; } + bool hasCollision() const { return keyHash & sCollisionBit; } + bool matchHash(HashNumber hn) { return (keyHash & ~sCollisionBit) == hn; } + HashNumber getKeyHash() const { return keyHash & ~sCollisionBit; } + + template + void setLive(HashNumber hn, const U &u) + { + JS_ASSERT(!isLive()); + keyHash = hn; + new(mem.addr()) T(u); + JS_ASSERT(isLive()); } - void unsetCollision() { keyHash &= ~sCollisionBit; } - bool hasCollision() const { return keyHash & sCollisionBit; } - bool matchHash(HashNumber hn) { return (keyHash & ~sCollisionBit) == hn; } - HashNumber getKeyHash() const { JS_ASSERT(!hasCollision()); return keyHash; } }; -/* - * js::detail::HashTable is an implementation detail of the js::HashMap and - * js::HashSet templates. For js::Hash{Map,Set} API documentation and examples, - * skip to the end of the detail namespace. - */ - -/* Reusable implementation of HashMap and HashSet. */ template class HashTable : private AllocPolicy { @@ -83,12 +630,10 @@ class HashTable : private AllocPolicy public: typedef HashTableEntry Entry; - /* - * A nullable pointer to a hash table element. A Ptr |p| can be tested - * either explicitly |if (p.found()) p->...| or using boolean conversion - * |if (p) p->...|. Ptr objects must not be used after any mutating hash - * table operations unless |generation()| is tested. - */ + // A nullable pointer to a hash table element. A Ptr |p| can be tested + // either explicitly |if (p.found()) p->...| or using boolean conversion + // |if (p) p->...|. Ptr objects must not be used after any mutating hash + // table operations unless |generation()| is tested. class Ptr { friend class HashTable; @@ -101,7 +646,7 @@ class HashTable : private AllocPolicy Ptr(Entry &entry) : entry(&entry) {} public: - /* Leaves Ptr uninitialized. */ + // Leaves Ptr uninitialized. Ptr() { #ifdef DEBUG entry = (Entry *)0xbad; @@ -113,29 +658,27 @@ class HashTable : private AllocPolicy bool operator==(const Ptr &rhs) const { JS_ASSERT(found() && rhs.found()); return entry == rhs.entry; } bool operator!=(const Ptr &rhs) const { return !(*this == rhs); } - T &operator*() const { return entry->t; } - T *operator->() const { return &entry->t; } + T &operator*() const { return entry->get(); } + T *operator->() const { return &entry->get(); } }; - /* A Ptr that can be used to add a key after a failed lookup. */ + // A Ptr that can be used to add a key after a failed lookup. class AddPtr : public Ptr { friend class HashTable; HashNumber keyHash; - DebugOnly mutationCount; + mozilla::DebugOnly mutationCount; AddPtr(Entry &entry, HashNumber hn) : Ptr(entry), keyHash(hn) {} public: - /* Leaves AddPtr uninitialized. */ + // Leaves AddPtr uninitialized. AddPtr() {} }; - /* - * A collection of hash table entries. The collection is enumerated by - * calling |front()| followed by |popFront()| as long as |!empty()|. As - * with Ptr/AddPtr, Range objects must not be used after any mutating hash - * table operation unless the |generation()| is tested. - */ + // A collection of hash table entries. The collection is enumerated by + // calling |front()| followed by |popFront()| as long as |!empty()|. As + // with Ptr/AddPtr, Range objects must not be used after any mutating hash + // table operation unless the |generation()| is tested. class Range { protected: @@ -147,7 +690,7 @@ class HashTable : private AllocPolicy } Entry *cur, *end; - DebugOnly validEntry; + mozilla::DebugOnly validEntry; public: Range() : cur(NULL), end(NULL), validEntry(false) {} @@ -159,7 +702,7 @@ class HashTable : private AllocPolicy T &front() const { JS_ASSERT(validEntry); JS_ASSERT(!empty()); - return cur->t; + return cur->get(); } void popFront() { @@ -170,15 +713,13 @@ class HashTable : private AllocPolicy } }; - /* - * A Range whose lifetime delimits a mutating enumeration of a hash table. - * Since rehashing when elements were removed during enumeration would be - * bad, it is postponed until |endEnumeration()| is called. If - * |endEnumeration()| is not called before an Enum's constructor, it will - * be called automatically. Since |endEnumeration()| touches the hash - * table, the user must ensure that the hash table is still alive when this - * happens. - */ + // A Range whose lifetime delimits a mutating enumeration of a hash table. + // Since rehashing when elements were removed during enumeration would be + // bad, it is postponed until |endEnumeration()| is called. If + // |endEnumeration()| is not called before an Enum's constructor, it will + // be called automatically. Since |endEnumeration()| touches the hash + // table, the user must ensure that the hash table is still alive when this + // happens. class Enum : public Range { friend class HashTable; @@ -195,31 +736,27 @@ class HashTable : private AllocPolicy template explicit Enum(Map &map) : Range(map.all()), table(map.impl), rekeyed(false), removed(false) {} - /* - * Removes the |front()| element from the table, leaving |front()| - * invalid until the next call to |popFront()|. For example: - * - * HashSet s; - * for (HashSet::Enum e(s); !e.empty(); e.popFront()) - * if (e.front() == 42) - * e.removeFront(); - */ + // Removes the |front()| element from the table, leaving |front()| + // invalid until the next call to |popFront()|. For example: + // + // HashSet s; + // for (HashSet::Enum e(s); !e.empty(); e.popFront()) + // if (e.front() == 42) + // e.removeFront(); void removeFront() { table.remove(*this->cur); removed = true; this->validEntry = false; } - /* - * Removes the |front()| element and re-inserts it into the table with - * a new key at the new Lookup position. |front()| is invalid after - * this operation until the next call to |popFront()|. - */ + // Removes the |front()| element and re-inserts it into the table with + // a new key at the new Lookup position. |front()| is invalid after + // this operation until the next call to |popFront()|. void rekeyFront(const Lookup &l, const Key &k) { - typename HashTableEntry::NonConstT t = this->cur->t; + typename HashTableEntry::NonConstT t(Move(this->cur->get())); HashPolicy::setKey(t, const_cast(k)); table.remove(*this->cur); - table.putNewInfallible(l, t); + table.putNewInfallible(l, Move(t)); rekeyed = true; this->validEntry = false; } @@ -228,7 +765,7 @@ class HashTable : private AllocPolicy rekeyFront(k, k); } - /* Potentially rehashes the table. */ + // Potentially rehashes the table. ~Enum() { if (rekeyed) table.checkOverRemoved(); @@ -238,29 +775,31 @@ class HashTable : private AllocPolicy }; private: - uint32_t hashShift; /* multiplicative hash shift */ - uint32_t entryCount; /* number of entries in table */ - uint32_t gen; /* entry storage generation number */ - uint32_t removedCount; /* removed entry sentinels in table */ - Entry *table; /* entry storage */ + uint32_t hashShift; // multiplicative hash shift + uint32_t entryCount; // number of entries in table + uint32_t gen; // entry storage generation number + uint32_t removedCount; // removed entry sentinels in table + Entry *table; // entry storage - void setTableSizeLog2(unsigned sizeLog2) { + void setTableSizeLog2(unsigned sizeLog2) + { hashShift = sHashBits - sizeLog2; } #ifdef DEBUG - mutable struct Stats { - uint32_t searches; /* total number of table searches */ - uint32_t steps; /* hash chain links traversed */ - uint32_t hits; /* searches that found key */ - uint32_t misses; /* searches that didn't find key */ - uint32_t addOverRemoved; /* adds that recycled a removed entry */ - uint32_t removes; /* calls to remove */ - uint32_t removeFrees; /* calls to remove that freed the entry */ - uint32_t grows; /* table expansions */ - uint32_t shrinks; /* table contractions */ - uint32_t compresses; /* table compressions */ - uint32_t rehashes; /* tombstone decontaminations */ + mutable struct Stats + { + uint32_t searches; // total number of table searches + uint32_t steps; // hash chain links traversed + uint32_t hits; // searches that found key + uint32_t misses; // searches that didn't find key + uint32_t addOverRemoved; // adds that recycled a removed entry + uint32_t removes; // calls to remove + uint32_t removeFrees; // calls to remove that freed the entry + uint32_t grows; // table expansions + uint32_t shrinks; // table contractions + uint32_t compresses; // table compressions + uint32_t rehashes; // tombstone decontaminations } stats; # define METER(x) x #else @@ -268,29 +807,25 @@ class HashTable : private AllocPolicy #endif friend class js::ReentrancyGuard; - mutable DebugOnly entered; - DebugOnly mutationCount; + mutable mozilla::DebugOnly entered; + mozilla::DebugOnly mutationCount; - /* The default initial capacity is 16, but you can ask for as small as 4. */ + // The default initial capacity is 16, but you can ask for as small as 4. static const unsigned sMinSizeLog2 = 2; static const unsigned sMinSize = 1 << sMinSizeLog2; - static const unsigned sDefaultInitSizeLog2 = 4; - public: - static const unsigned sDefaultInitSize = 1 << sDefaultInitSizeLog2; - private: static const unsigned sMaxInit = JS_BIT(23); static const unsigned sMaxCapacity = JS_BIT(24); static const unsigned sHashBits = tl::BitSize::result; - static const uint8_t sMinAlphaFrac = 64; /* (0x100 * .25) taken from jsdhash.h */ - static const uint8_t sMaxAlphaFrac = 192; /* (0x100 * .75) taken from jsdhash.h */ - static const uint8_t sInvMaxAlpha = 171; /* (ceil(0x100 / .75) >> 1) */ + static const uint8_t sMinAlphaFrac = 64; // (0x100 * .25) + static const uint8_t sMaxAlphaFrac = 192; // (0x100 * .75) + static const uint8_t sInvMaxAlpha = 171; // (ceil(0x100 / .75) >> 1) static const HashNumber sFreeKey = Entry::sFreeKey; static const HashNumber sRemovedKey = Entry::sRemovedKey; static const HashNumber sCollisionBit = Entry::sCollisionBit; static void staticAsserts() { - /* Rely on compiler "constant overflow warnings". */ + // Rely on compiler "constant overflow warnings". JS_STATIC_ASSERT(((sMaxInit * sInvMaxAlpha) >> 7) < sMaxCapacity); JS_STATIC_ASSERT((sMaxCapacity * sInvMaxAlpha) <= UINT32_MAX); JS_STATIC_ASSERT((sMaxCapacity * sizeof(Entry)) <= UINT32_MAX); @@ -305,7 +840,7 @@ class HashTable : private AllocPolicy { HashNumber keyHash = ScrambleHashCode(HashPolicy::hash(l)); - /* Avoid reserved hash codes. */ + // Avoid reserved hash codes. if (!isLiveHash(keyHash)) keyHash -= (sRemovedKey + 1); return keyHash & ~sCollisionBit; @@ -313,18 +848,14 @@ class HashTable : private AllocPolicy static Entry *createTable(AllocPolicy &alloc, uint32_t capacity) { - Entry *newTable = (Entry *)alloc.malloc_(capacity * sizeof(Entry)); - if (!newTable) - return NULL; - for (Entry *e = newTable, *end = e + capacity; e < end; ++e) - new(e) Entry(); - return newTable; + // See JS_STATIC_ASSERT(sFreeKey == 0) in HashTableEntry. + return (Entry *)alloc.calloc_(capacity * sizeof(Entry)); } static void destroyTable(AllocPolicy &alloc, Entry *oldTable, uint32_t capacity) { for (Entry *e = oldTable, *end = e + capacity; e < end; ++e) - e->~Entry(); + e->destroyIfLive(); alloc.free_(oldTable); } @@ -344,10 +875,8 @@ class HashTable : private AllocPolicy { JS_ASSERT(!initialized()); - /* - * Correct for sMaxAlphaFrac such that the table will not resize - * when adding 'length' entries. - */ + // Correct for sMaxAlphaFrac such that the table will not resize + // when adding 'length' entries. if (length > sMaxInit) { this->reportAllocOverflow(); return false; @@ -357,7 +886,7 @@ class HashTable : private AllocPolicy if (capacity < sMinSize) capacity = sMinSize; - /* FIXME: use JS_CEILING_LOG2 when PGO stops crashing (bug 543034). */ + // FIXME: use JS_CEILING_LOG2 when PGO stops crashing (bug 543034). uint32_t roundUp = sMinSize, roundUpLog2 = sMinSizeLog2; while (roundUp < capacity) { roundUp <<= 1; @@ -388,16 +917,19 @@ class HashTable : private AllocPolicy } private: - static HashNumber hash1(HashNumber hash0, uint32_t shift) { - return hash0 >> shift; + HashNumber hash1(HashNumber hash0) const + { + return hash0 >> hashShift; } - struct DoubleHash { + struct DoubleHash + { HashNumber h2; HashNumber sizeMask; }; - DoubleHash hash2(HashNumber curKeyHash, uint32_t hashShift) const { + DoubleHash hash2(HashNumber curKeyHash) const + { unsigned sizeLog2 = sHashBits - hashShift; DoubleHash dh = { ((curKeyHash << sizeLog2) >> hashShift) | 1, @@ -406,22 +938,26 @@ class HashTable : private AllocPolicy return dh; } - static HashNumber applyDoubleHash(HashNumber h1, const DoubleHash &dh) { + static HashNumber applyDoubleHash(HashNumber h1, const DoubleHash &dh) + { return (h1 - dh.h2) & dh.sizeMask; } - bool overloaded() { + bool overloaded() + { return entryCount + removedCount >= ((sMaxAlphaFrac * capacity()) >> 8); } - bool underloaded() { + bool underloaded() + { uint32_t tableCapacity = capacity(); return tableCapacity > sMinSize && entryCount <= ((sMinAlphaFrac * tableCapacity) >> 8); } - static bool match(Entry &e, const Lookup &l) { - return HashPolicy::match(HashPolicy::getKey(e.t), l); + static bool match(Entry &e, const Lookup &l) + { + return HashPolicy::match(HashPolicy::getKey(e.get()), l); } Entry &lookup(const Lookup &l, HashNumber keyHash, unsigned collisionBit) const @@ -432,26 +968,26 @@ class HashTable : private AllocPolicy JS_ASSERT(table); METER(stats.searches++); - /* Compute the primary hash address. */ - HashNumber h1 = hash1(keyHash, hashShift); + // Compute the primary hash address. + HashNumber h1 = hash1(keyHash); Entry *entry = &table[h1]; - /* Miss: return space for a new entry. */ + // Miss: return space for a new entry. if (entry->isFree()) { METER(stats.misses++); return *entry; } - /* Hit: return entry. */ + // Hit: return entry. if (entry->matchHash(keyHash) && match(*entry, l)) { METER(stats.hits++); return *entry; } - /* Collision: double hash. */ - DoubleHash dh = hash2(keyHash, hashShift); + // Collision: double hash. + DoubleHash dh = hash2(keyHash); - /* Save the first removed entry pointer so we can recycle later. */ + // Save the first removed entry pointer so we can recycle later. Entry *firstRemoved = NULL; while(true) { @@ -478,34 +1014,32 @@ class HashTable : private AllocPolicy } } - /* - * This is a copy of lookup hardcoded to the assumptions: - * 1. the lookup is a lookupForAdd - * 2. the key, whose |keyHash| has been passed is not in the table, - * 3. no entries have been removed from the table. - * This specialized search avoids the need for recovering lookup values - * from entries, which allows more flexible Lookup/Key types. - */ + // This is a copy of lookup hardcoded to the assumptions: + // 1. the lookup is a lookupForAdd + // 2. the key, whose |keyHash| has been passed is not in the table, + // 3. no entries have been removed from the table. + // This specialized search avoids the need for recovering lookup values + // from entries, which allows more flexible Lookup/Key types. Entry &findFreeEntry(HashNumber keyHash) { JS_ASSERT(!(keyHash & sCollisionBit)); JS_ASSERT(table); METER(stats.searches++); - /* N.B. the |keyHash| has already been distributed. */ + // We assume 'keyHash' has already been distributed. - /* Compute the primary hash address. */ - HashNumber h1 = hash1(keyHash, hashShift); + // Compute the primary hash address. + HashNumber h1 = hash1(keyHash); Entry *entry = &table[h1]; - /* Miss: return space for a new entry. */ + // Miss: return space for a new entry. if (!entry->isLive()) { METER(stats.misses++); return *entry; } - /* Collision: double hash. */ - DoubleHash dh = hash2(keyHash, hashShift); + // Collision: double hash. + DoubleHash dh = hash2(keyHash); while(true) { JS_ASSERT(!entry->isRemoved()); @@ -526,7 +1060,7 @@ class HashTable : private AllocPolicy RebuildStatus changeTableSize(int deltaLog2) { - /* Look, but don't touch, until we succeed in getting new entry store. */ + // Look, but don't touch, until we succeed in getting new entry store. Entry *oldTable = table; uint32_t oldCap = capacity(); uint32_t newLog2 = sHashBits - hashShift + deltaLog2; @@ -540,21 +1074,23 @@ class HashTable : private AllocPolicy if (!newTable) return RehashFailed; - /* We can't fail from here on, so update table parameters. */ + // We can't fail from here on, so update table parameters. setTableSizeLog2(newLog2); removedCount = 0; gen++; table = newTable; - /* Copy only live entries, leaving removed ones behind. */ + // Copy only live entries, leaving removed ones behind. for (Entry *src = oldTable, *end = src + oldCap; src < end; ++src) { if (src->isLive()) { - src->unsetCollision(); - findFreeEntry(src->getKeyHash()) = Move(*src); + HashNumber hn = src->getKeyHash(); + findFreeEntry(hn).setLive(hn, Move(src->get())); + src->destroy(); } } - destroyTable(*this, oldTable, oldCap); + // All entries have been destroyed, no need to destroyTable. + this->free_(oldTable); return Rehashed; } @@ -563,7 +1099,7 @@ class HashTable : private AllocPolicy if (!overloaded()) return NotOverloaded; - /* Compress if a quarter or more of all entries are removed. */ + // Compress if a quarter or more of all entries are removed. int deltaLog2; if (removedCount >= (capacity() >> 2)) { METER(stats.compresses++); @@ -576,7 +1112,7 @@ class HashTable : private AllocPolicy return changeTableSize(deltaLog2); } - /* Infallibly rehash the table if we are overloaded with removals. */ + // Infallibly rehash the table if we are overloaded with removals. void checkOverRemoved() { if (overloaded()) { @@ -592,11 +1128,11 @@ class HashTable : private AllocPolicy METER(stats.removes++); if (e.hasCollision()) { - e.setRemoved(); + e.removeLive(); removedCount++; } else { METER(stats.removeFrees++); - e.setFree(); + e.clearLive(); } entryCount--; mutationCount++; @@ -610,13 +1146,11 @@ class HashTable : private AllocPolicy } } - /* - * This is identical to changeTableSize(currentSize), but without requiring - * a second table. We do this by recycling the collision bits to tell us if - * the element is already inserted or still waiting to be inserted. Since - * already-inserted elements win any conflicts, we get the same table as we - * would have gotten through random insertion order. - */ + // This is identical to changeTableSize(currentSize), but without requiring + // a second table. We do this by recycling the collision bits to tell us if + // the element is already inserted or still waiting to be inserted. Since + // already-inserted elements win any conflicts, we get the same table as we + // would have gotten through random insertion order. void rehashTable() { removedCount = 0; @@ -632,12 +1166,12 @@ class HashTable : private AllocPolicy } HashNumber keyHash = src->getKeyHash(); - HashNumber h1 = hash1(keyHash, hashShift); - DoubleHash dh = hash2(keyHash, hashShift); + HashNumber h1 = hash1(keyHash); + DoubleHash dh = hash2(keyHash); Entry *tgt = &table[h1]; while (true) { if (!tgt->hasCollision()) { - Swap(*src, *tgt); + src->swap(tgt); tgt->setCollision(); break; } @@ -647,13 +1181,11 @@ class HashTable : private AllocPolicy } } - /* - * TODO: this algorithm leaves collision bits on *all* elements, even if - * they are on no collision path. We have the option of setting the - * collision bits correctly on a subsequent pass or skipping the rehash - * unless we are totally filled with tombstones: benchmark to find out - * which approach is best. - */ + // TODO: this algorithm leaves collision bits on *all* elements, even if + // they are on no collision path. We have the option of setting the + // collision bits correctly on a subsequent pass or skipping the rehash + // unless we are totally filled with tombstones: benchmark to find out + // which approach is best. } public: @@ -664,7 +1196,7 @@ class HashTable : private AllocPolicy } else { uint32_t tableCapacity = capacity(); for (Entry *e = table, *end = table + tableCapacity; e < end; ++e) - *e = Move(Entry()); + e->clear(); } removedCount = 0; entryCount = 0; @@ -686,46 +1218,55 @@ class HashTable : private AllocPolicy mutationCount++; } - Range all() const { + Range all() const + { JS_ASSERT(table); return Range(table, table + capacity()); } - bool empty() const { + bool empty() const + { JS_ASSERT(table); return !entryCount; } - uint32_t count() const { + uint32_t count() const + { JS_ASSERT(table); return entryCount; } - uint32_t capacity() const { + uint32_t capacity() const + { JS_ASSERT(table); return JS_BIT(sHashBits - hashShift); } - uint32_t generation() const { + uint32_t generation() const + { JS_ASSERT(table); return gen; } - size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const { + size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const + { return mallocSizeOf(table); } - size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const { + size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const + { return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf); } - Ptr lookup(const Lookup &l) const { + Ptr lookup(const Lookup &l) const + { ReentrancyGuard g(*this); HashNumber keyHash = prepareHash(l); return Ptr(lookup(l, keyHash, 0)); } - AddPtr lookupForAdd(const Lookup &l) const { + AddPtr lookupForAdd(const Lookup &l) const + { ReentrancyGuard g(*this); HashNumber keyHash = prepareHash(l); Entry &entry = lookup(l, keyHash, sCollisionBit); @@ -734,7 +1275,8 @@ class HashTable : private AllocPolicy return p; } - bool add(AddPtr &p) + template + bool add(AddPtr &p, const U &rhs) { ReentrancyGuard g(*this); JS_ASSERT(mutationCount == p.mutationCount); @@ -742,16 +1284,14 @@ class HashTable : private AllocPolicy JS_ASSERT(!p.found()); JS_ASSERT(!(p.keyHash & sCollisionBit)); - /* - * Changing an entry from removed to live does not affect whether we - * are overloaded and can be handled separately. - */ + // Changing an entry from removed to live does not affect whether we + // are overloaded and can be handled separately. if (p.entry->isRemoved()) { METER(stats.addOverRemoved++); removedCount--; p.keyHash |= sCollisionBit; } else { - /* Preserve the validity of |p.entry|. */ + // Preserve the validity of |p.entry|. RebuildStatus status = checkOverloaded(); if (status == RehashFailed) return false; @@ -759,34 +1299,14 @@ class HashTable : private AllocPolicy p.entry = &findFreeEntry(p.keyHash); } - p.entry->setLive(p.keyHash); + p.entry->setLive(p.keyHash, rhs); entryCount++; mutationCount++; return true; } - /* - * There is an important contract between the caller and callee for this - * function: if add() returns true, the caller must assign the T value - * which produced p before using the hashtable again. - */ - bool add(AddPtr &p, T** pentry) - { - if (!add(p)) - return false; - *pentry = &p.entry->t; - return true; - } - - bool add(AddPtr &p, const T &t) - { - if (!add(p)) - return false; - p.entry->t = t; - return true; - } - - void putNewInfallible(const Lookup &l, const T &t) + template + void putNewInfallible(const Lookup &l, const U &u) { JS_ASSERT(table); @@ -799,29 +1319,30 @@ class HashTable : private AllocPolicy keyHash |= sCollisionBit; } - entry->t = t; - entry->setLive(keyHash); + entry->setLive(keyHash, u); entryCount++; mutationCount++; } - bool putNew(const Lookup &l, const T &t) + template + bool putNew(const Lookup &l, const U &u) { if (checkOverloaded() == RehashFailed) return false; - putNewInfallible(l, t); + putNewInfallible(l, u); return true; } - bool relookupOrAdd(AddPtr& p, const Lookup &l, const T& t) + template + bool relookupOrAdd(AddPtr& p, const Lookup &l, const U &u) { p.mutationCount = mutationCount; { ReentrancyGuard g(*this); p.entry = &lookup(l, p.keyHash, sCollisionBit); } - return p.found() || add(p, t); + return p.found() || add(p, u); } void remove(Ptr p) @@ -836,593 +1357,8 @@ class HashTable : private AllocPolicy #undef METER }; -} /* namespace detail */ +} // namespace detail +} // namespace js -/*****************************************************************************/ +#endif // js_HashTable_h__ -/* - * Hash policy - * - * A hash policy P for a hash table with key-type Key must provide: - * - a type |P::Lookup| to use to lookup table entries; - * - a static member function |P::hash| with signature - * - * static js::HashNumber hash(Lookup) - * - * to use to hash the lookup type; and - * - a static member function |P::match| with signature - * - * static bool match(Key, Lookup) - * - * to use to test equality of key and lookup values. - * - * Normally, Lookup = Key. In general, though, different values and types of - * values can be used to lookup and store. If a Lookup value |l| is != to the - * added Key value |k|, the user must ensure that |P::match(k,l)|. E.g.: - * - * js::HashSet::AddPtr p = h.lookup(l); - * if (!p) { - * assert(P::match(k, l)); // must hold - * h.add(p, k); - * } - */ - -/* Default hashing policies. */ -template -struct DefaultHasher -{ - typedef Key Lookup; - static HashNumber hash(const Lookup &l) { - /* Hash if can implicitly cast to hash number type. */ - return l; - } - static bool match(const Key &k, const Lookup &l) { - /* Use builtin or overloaded operator==. */ - return k == l; - } -}; - -/* - * Pointer hashing policy that strips the lowest zeroBits when calculating the - * hash to improve key distribution. - */ -template -struct PointerHasher -{ - typedef Key Lookup; - static HashNumber hash(const Lookup &l) { - size_t word = reinterpret_cast(l) >> zeroBits; - JS_STATIC_ASSERT(sizeof(HashNumber) == 4); -#if JS_BYTES_PER_WORD == 4 - return HashNumber(word); -#else - JS_STATIC_ASSERT(sizeof word == 8); - return HashNumber((word >> 32) ^ word); -#endif - } - static bool match(const Key &k, const Lookup &l) { - return k == l; - } -}; - -template -struct TaggedPointerHasher -{ - typedef Key Lookup; - - static HashNumber hash(const Lookup &l) { - return PointerHasher::hash(l); - } - - static const uintptr_t COMPARE_MASK = uintptr_t(-1) - 1; - - static bool match(const Key &k, const Lookup &l) { - return (uintptr_t(k) & COMPARE_MASK) == uintptr_t(l); - } -}; - -/* - * Specialized hashing policy for pointer types. It assumes that the type is - * at least word-aligned. For types with smaller size use PointerHasher. - */ -template -struct DefaultHasher: PointerHasher::result> { }; - -/* Looking for a hasher for jsid? Try the DefaultHasher in jsatom.h. */ - -template -class HashMapEntry -{ - template friend class detail::HashTable; - template friend class detail::HashTableEntry; - void operator=(const HashMapEntry &rhs) { - const_cast(key) = rhs.key; - value = rhs.value; - } - - public: - HashMapEntry() : key(), value() {} - - template - HashMapEntry(const KeyInput &k, const ValueInput &v) : key(k), value(v) {} - - HashMapEntry(MoveRef rhs) - : key(Move(rhs->key)), value(Move(rhs->value)) { } - void operator=(MoveRef rhs) { - const_cast(key) = Move(rhs->key); - value = Move(rhs->value); - } - - const Key key; - Value value; -}; - -namespace tl { - -template -struct IsPodType > { - static const bool result = IsPodType::result; -}; - -template -struct IsPodType > -{ - static const bool result = IsPodType::result && IsPodType::result; -}; - -} /* namespace tl */ - -/* - * JS-friendly, STL-like container providing a hash-based map from keys to - * values. In particular, HashMap calls constructors and destructors of all - * objects added so non-PODs may be used safely. - * - * Key/Value requirements: - * - default constructible, copyable, destructible, assignable - * HashPolicy requirements: - * - see "Hash policy" above (default js::DefaultHasher) - * AllocPolicy: - * - see "Allocation policies" in jsalloc.h - * - * N.B: HashMap is not reentrant: Key/Value/HashPolicy/AllocPolicy members - * called by HashMap must not call back into the same HashMap object. - * N.B: Due to the lack of exception handling, the user must call |init()|. - */ -template , - class AllocPolicy = TempAllocPolicy> -class HashMap -{ - typedef typename tl::StaticAssert::result>::result keyAssert; - typedef typename tl::StaticAssert::result>::result valAssert; - - public: - typedef typename HashPolicy::Lookup Lookup; - - typedef HashMapEntry Entry; - - private: - /* Implement HashMap using HashTable. Lift |Key| operations to |Entry|. */ - struct MapHashPolicy : HashPolicy - { - typedef Key KeyType; - static const Key &getKey(Entry &e) { return e.key; } - static void setKey(Entry &e, Key &k) { const_cast(e.key) = k; } - }; - typedef detail::HashTable Impl; - - friend class Impl::Enum; - - Impl impl; - - public: - const static unsigned sDefaultInitSize = Impl::sDefaultInitSize; - - /* - * HashMap construction is fallible (due to OOM); thus the user must call - * init after constructing a HashMap and check the return value. - */ - HashMap(AllocPolicy a = AllocPolicy()) : impl(a) {} - bool init(uint32_t len = sDefaultInitSize) { return impl.init(len); } - bool initialized() const { return impl.initialized(); } - - /* - * Return whether the given lookup value is present in the map. E.g.: - * - * typedef HashMap HM; - * HM h; - * if (HM::Ptr p = h.lookup(3)) { - * const HM::Entry &e = *p; // p acts like a pointer to Entry - * assert(p->key == 3); // Entry contains the key - * char val = p->value; // and value - * } - * - * Also see the definition of Ptr in HashTable above (with T = Entry). - */ - typedef typename Impl::Ptr Ptr; - Ptr lookup(const Lookup &l) const { return impl.lookup(l); } - - /* Assuming |p.found()|, remove |*p|. */ - void remove(Ptr p) { impl.remove(p); } - - /* - * Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient - * insertion of Key |k| (where |HashPolicy::match(k,l) == true|) using - * |add(p,k,v)|. After |add(p,k,v)|, |p| points to the new Entry. E.g.: - * - * typedef HashMap HM; - * HM h; - * HM::AddPtr p = h.lookupForAdd(3); - * if (!p) { - * if (!h.add(p, 3, 'a')) - * return false; - * } - * const HM::Entry &e = *p; // p acts like a pointer to Entry - * assert(p->key == 3); // Entry contains the key - * char val = p->value; // and value - * - * Also see the definition of AddPtr in HashTable above (with T = Entry). - * - * N.B. The caller must ensure that no mutating hash table operations - * occur between a pair of |lookupForAdd| and |add| calls. To avoid - * looking up the key a second time, the caller may use the more efficient - * relookupOrAdd method. This method reuses part of the hashing computation - * to more efficiently insert the key if it has not been added. For - * example, a mutation-handling version of the previous example: - * - * HM::AddPtr p = h.lookupForAdd(3); - * if (!p) { - * call_that_may_mutate_h(); - * if (!h.relookupOrAdd(p, 3, 'a')) - * return false; - * } - * const HM::Entry &e = *p; - * assert(p->key == 3); - * char val = p->value; - */ - typedef typename Impl::AddPtr AddPtr; - AddPtr lookupForAdd(const Lookup &l) const { - return impl.lookupForAdd(l); - } - - template - bool add(AddPtr &p, const KeyInput &k, const ValueInput &v) { - Entry *pentry; - if (!impl.add(p, &pentry)) - return false; - const_cast(pentry->key) = k; - pentry->value = v; - return true; - } - - bool add(AddPtr &p, const Key &k, MoveRef v) { - Entry *pentry; - if (!impl.add(p, &pentry)) - return false; - const_cast(pentry->key) = k; - pentry->value = v; - return true; - } - - bool add(AddPtr &p, const Key &k) { - Entry *pentry; - if (!impl.add(p, &pentry)) - return false; - const_cast(pentry->key) = k; - return true; - } - - template - bool relookupOrAdd(AddPtr &p, const KeyInput &k, const ValueInput &v) { - return impl.relookupOrAdd(p, k, Entry(k, v)); - } - - /* - * |all()| returns a Range containing |count()| elements. E.g.: - * - * typedef HashMap HM; - * HM h; - * for (HM::Range r = h.all(); !r.empty(); r.popFront()) - * char c = r.front().value; - * - * Also see the definition of Range in HashTable above (with T = Entry). - */ - typedef typename Impl::Range Range; - Range all() const { return impl.all(); } - uint32_t count() const { return impl.count(); } - size_t capacity() const { return impl.capacity(); } - size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const { - return impl.sizeOfExcludingThis(mallocSizeOf); - } - size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const { - /* - * Don't just call |impl.sizeOfExcludingThis()| because there's no - * guarantee that |impl| is the first field in HashMap. - */ - return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf); - } - - /* - * Typedef for the enumeration class. An Enum may be used to examine and - * remove table entries: - * - * typedef HashMap HM; - * HM s; - * for (HM::Enum e(s); !e.empty(); e.popFront()) - * if (e.front().value == 'l') - * e.removeFront(); - * - * Table resize may occur in Enum's destructor. Also see the definition of - * Enum in HashTable above (with T = Entry). - */ - typedef typename Impl::Enum Enum; - - /* - * Remove all entries. This does not shrink the table. For that consider - * using the finish() method. - */ - void clear() { impl.clear(); } - - /* - * Remove all the entries and release all internal buffers. The map must - * be initialized again before any use. - */ - void finish() { impl.finish(); } - - /* Does the table contain any entries? */ - bool empty() const { return impl.empty(); } - - /* - * If |generation()| is the same before and after a HashMap operation, - * pointers into the table remain valid. - */ - unsigned generation() const { return impl.generation(); } - - /* Shorthand operations: */ - - bool has(const Lookup &l) const { - return impl.lookup(l) != NULL; - } - - /* Overwrite existing value with v. Return false on oom. */ - template - bool put(const KeyInput &k, const ValueInput &v) { - AddPtr p = lookupForAdd(k); - if (p) { - p->value = v; - return true; - } - return add(p, k, v); - } - - /* Like put, but assert that the given key is not already present. */ - bool putNew(const Key &k, const Value &v) { - return impl.putNew(k, Entry(k, v)); - } - - /* Add (k,defaultValue) if k no found. Return false-y Ptr on oom. */ - Ptr lookupWithDefault(const Key &k, const Value &defaultValue) { - AddPtr p = lookupForAdd(k); - if (p) - return p; - (void)add(p, k, defaultValue); /* p is left false-y on oom. */ - return p; - } - - /* Remove if present. */ - void remove(const Lookup &l) { - if (Ptr p = lookup(l)) - remove(p); - } - - private: - /* Not implicitly copyable (expensive). May add explicit |clone| later. */ - HashMap(const HashMap &hm) MOZ_DELETE; - HashMap &operator=(const HashMap &hm) MOZ_DELETE; -}; - -/* - * JS-friendly, STL-like container providing a hash-based set of values. In - * particular, HashSet calls constructors and destructors of all objects added - * so non-PODs may be used safely. - * - * T requirements: - * - default constructible, copyable, destructible, assignable - * HashPolicy requirements: - * - see "Hash policy" above (default js::DefaultHasher) - * AllocPolicy: - * - see "Allocation policies" in jsalloc.h - * - * N.B: HashSet is not reentrant: T/HashPolicy/AllocPolicy members called by - * HashSet must not call back into the same HashSet object. - * N.B: Due to the lack of exception handling, the user must call |init()|. - */ -template , class AllocPolicy = TempAllocPolicy> -class HashSet -{ - typedef typename HashPolicy::Lookup Lookup; - - /* Implement HashSet in terms of HashTable. */ - struct SetOps : HashPolicy { - typedef T KeyType; - static const KeyType &getKey(const T &t) { return t; } - static void setKey(T &t, KeyType &k) { t = k; } - }; - typedef detail::HashTable Impl; - - friend class Impl::Enum; - - Impl impl; - - public: - const static unsigned sDefaultInitSize = Impl::sDefaultInitSize; - - /* - * HashSet construction is fallible (due to OOM); thus the user must call - * init after constructing a HashSet and check the return value. - */ - HashSet(AllocPolicy a = AllocPolicy()) : impl(a) {} - bool init(uint32_t len = sDefaultInitSize) { return impl.init(len); } - bool initialized() const { return impl.initialized(); } - - /* - * Return whether the given lookup value is present in the map. E.g.: - * - * typedef HashSet HS; - * HS h; - * if (HS::Ptr p = h.lookup(3)) { - * assert(*p == 3); // p acts like a pointer to int - * } - * - * Also see the definition of Ptr in HashTable above. - */ - typedef typename Impl::Ptr Ptr; - Ptr lookup(const Lookup &l) const { return impl.lookup(l); } - - /* Assuming |p.found()|, remove |*p|. */ - void remove(Ptr p) { impl.remove(p); } - - /* - * Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient - * insertion of T value |t| (where |HashPolicy::match(t,l) == true|) using - * |add(p,t)|. After |add(p,t)|, |p| points to the new element. E.g.: - * - * typedef HashSet HS; - * HS h; - * HS::AddPtr p = h.lookupForAdd(3); - * if (!p) { - * if (!h.add(p, 3)) - * return false; - * } - * assert(*p == 3); // p acts like a pointer to int - * - * Also see the definition of AddPtr in HashTable above. - * - * N.B. The caller must ensure that no mutating hash table operations - * occur between a pair of |lookupForAdd| and |add| calls. To avoid - * looking up the key a second time, the caller may use the more efficient - * relookupOrAdd method. This method reuses part of the hashing computation - * to more efficiently insert the key if it has not been added. For - * example, a mutation-handling version of the previous example: - * - * HS::AddPtr p = h.lookupForAdd(3); - * if (!p) { - * call_that_may_mutate_h(); - * if (!h.relookupOrAdd(p, 3, 3)) - * return false; - * } - * assert(*p == 3); - * - * Note that relookupOrAdd(p,l,t) performs Lookup using l and adds the - * entry t, where the caller ensures match(l,t). - */ - typedef typename Impl::AddPtr AddPtr; - AddPtr lookupForAdd(const Lookup &l) const { - return impl.lookupForAdd(l); - } - - bool add(AddPtr &p, const T &t) { - return impl.add(p, t); - } - - bool relookupOrAdd(AddPtr &p, const Lookup &l, const T &t) { - return impl.relookupOrAdd(p, l, t); - } - - /* - * |all()| returns a Range containing |count()| elements: - * - * typedef HashSet HS; - * HS h; - * for (HS::Range r = h.all(); !r.empty(); r.popFront()) - * int i = r.front(); - * - * Also see the definition of Range in HashTable above. - */ - typedef typename Impl::Range Range; - Range all() const { return impl.all(); } - uint32_t count() const { return impl.count(); } - size_t capacity() const { return impl.capacity(); } - size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const { - return impl.sizeOfExcludingThis(mallocSizeOf); - } - size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const { - /* - * Don't just call |impl.sizeOfExcludingThis()| because there's no - * guarantee that |impl| is the first field in HashSet. - */ - return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf); - } - - /* - * Typedef for the enumeration class. An Enum may be used to examine and - * remove table entries: - * - * typedef HashSet HS; - * HS s; - * for (HS::Enum e(s); !e.empty(); e.popFront()) - * if (e.front() == 42) - * e.removeFront(); - * - * Table resize may occur in Enum's destructor. Also see the definition of - * Enum in HashTable above. - */ - typedef typename Impl::Enum Enum; - - /* - * Remove all entries. This does not shrink the table. For that consider - * using the finish() method. - */ - void clear() { impl.clear(); } - - /* - * Remove all the entries and release all internal buffers. The set must - * be initialized again before any use. - */ - void finish() { impl.finish(); } - - /* Does the table contain any entries? */ - bool empty() const { return impl.empty(); } - - /* - * If |generation()| is the same before and after a HashSet operation, - * pointers into the table remain valid. - */ - unsigned generation() const { return impl.generation(); } - - /* Shorthand operations: */ - - bool has(const Lookup &l) const { - return impl.lookup(l) != NULL; - } - - /* Overwrite existing value with v. Return false on oom. */ - bool put(const T &t) { - AddPtr p = lookupForAdd(t); - return p ? true : add(p, t); - } - - /* Like put, but assert that the given key is not already present. */ - bool putNew(const T &t) { - return impl.putNew(t, t); - } - - bool putNew(const Lookup &l, const T &t) { - return impl.putNew(l, t); - } - - void remove(const Lookup &l) { - if (Ptr p = lookup(l)) - remove(p); - } - - private: - /* Not implicitly copyable (expensive). May add explicit |clone| later. */ - HashSet(const HashSet &hs) MOZ_DELETE; - HashSet &operator=(const HashSet &hs) MOZ_DELETE; -}; - -} /* namespace js */ - -#endif diff --git a/scripting/javascript/spidermonkey-android/include/js/HeapAPI.h b/scripting/javascript/spidermonkey-android/include/js/HeapAPI.h new file mode 100644 index 0000000000..9e83bec84a --- /dev/null +++ b/scripting/javascript/spidermonkey-android/include/js/HeapAPI.h @@ -0,0 +1,76 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef js_heap_api_h___ +#define js_heap_api_h___ + +/* These values are private to the JS engine. */ +namespace js { +namespace gc { + +/* + * Page size must be static to support our arena pointer optimizations, so we + * are forced to support each platform with non-4096 pages as a special case. + * Note: The freelist supports a maximum arena shift of 15. + * Note: Do not use JS_CPU_SPARC here, this header is used outside JS. + */ +#if (defined(SOLARIS) || defined(__FreeBSD__)) && \ + (defined(__sparc) || defined(__sparcv9) || defined(__ia64)) +const size_t PageShift = 13; +const size_t ArenaShift = PageShift; +#elif defined(__powerpc64__) +const size_t PageShift = 16; +const size_t ArenaShift = 12; +#else +const size_t PageShift = 12; +const size_t ArenaShift = PageShift; +#endif +const size_t PageSize = size_t(1) << PageShift; +const size_t ArenaSize = size_t(1) << ArenaShift; +const size_t ArenaMask = ArenaSize - 1; + +const size_t ChunkShift = 20; +const size_t ChunkSize = size_t(1) << ChunkShift; +const size_t ChunkMask = ChunkSize - 1; + +} /* namespace gc */ +} /* namespace js */ + +namespace JS { + +namespace shadow { + +struct ArenaHeader +{ + JSCompartment *compartment; +}; + +} /* namespace shadow */ + +static inline shadow::ArenaHeader * +GetGCThingArena(void *thing) +{ + uintptr_t addr = uintptr_t(thing); + addr &= ~js::gc::ArenaMask; + return reinterpret_cast(addr); +} + +static inline JSCompartment * +GetGCThingCompartment(void *thing) +{ + JS_ASSERT(thing); + return GetGCThingArena(thing)->compartment; +} + +static inline JSCompartment * +GetObjectCompartment(JSObject *obj) +{ + return GetGCThingCompartment(obj); +} + +} /* namespace JS */ + +#endif /* js_heap_api_h___ */ diff --git a/scripting/javascript/spidermonkey-android/include/js/MemoryMetrics.h b/scripting/javascript/spidermonkey-android/include/js/MemoryMetrics.h index 823e491c30..2f7b78505f 100644 --- a/scripting/javascript/spidermonkey-android/include/js/MemoryMetrics.h +++ b/scripting/javascript/spidermonkey-android/include/js/MemoryMetrics.h @@ -38,22 +38,37 @@ namespace JS { struct TypeInferenceSizes { TypeInferenceSizes() - : scripts(0) - , objects(0) - , tables(0) - , temporary(0) + : typeScripts(0) + , typeResults(0) + , analysisPool(0) + , typePool(0) + , pendingArrays(0) + , allocationSiteTables(0) + , arrayTypeTables(0) + , objectTypeTables(0) + , typeObjects(0) {} - size_t scripts; - size_t objects; - size_t tables; - size_t temporary; + size_t typeScripts; + size_t typeResults; + size_t analysisPool; + size_t typePool; + size_t pendingArrays; + size_t allocationSiteTables; + size_t arrayTypeTables; + size_t objectTypeTables; + size_t typeObjects; void add(TypeInferenceSizes &sizes) { - this->scripts += sizes.scripts; - this->objects += sizes.objects; - this->tables += sizes.tables; - this->temporary += sizes.temporary; + this->typeScripts += sizes.typeScripts; + this->typeResults += sizes.typeResults; + this->analysisPool += sizes.analysisPool; + this->typePool += sizes.typePool; + this->pendingArrays += sizes.pendingArrays; + this->allocationSiteTables += sizes.allocationSiteTables; + this->arrayTypeTables += sizes.arrayTypeTables; + this->objectTypeTables += sizes.objectTypeTables; + this->typeObjects += sizes.typeObjects; } }; @@ -129,10 +144,15 @@ struct CompartmentStats , extra2(0) , gcHeapArenaAdmin(0) , gcHeapUnusedGcThings(0) - , gcHeapObjectsNonFunction(0) + , gcHeapObjectsOrdinary(0) , gcHeapObjectsFunction(0) - , gcHeapStrings(0) - , gcHeapShapesTree(0) + , gcHeapObjectsDenseArray(0) + , gcHeapObjectsSlowArray(0) + , gcHeapObjectsCrossCompartmentWrapper(0) + , gcHeapStringsNormal(0) + , gcHeapStringsShort(0) + , gcHeapShapesTreeGlobalParented(0) + , gcHeapShapesTreeNonGlobalParented(0) , gcHeapShapesDict(0) , gcHeapShapesBase(0) , gcHeapScripts(0) @@ -147,7 +167,7 @@ struct CompartmentStats , objectsExtraRegExpStatics(0) , objectsExtraPropertyIteratorData(0) , objectsExtraPrivate(0) - , nonHugeStringChars(0) + , stringCharsNonHuge(0) , shapesExtraTreeTables(0) , shapesExtraDictTables(0) , shapesExtraTreeShapeKids(0) @@ -156,7 +176,7 @@ struct CompartmentStats , jaegerData(0) , ionData(0) , compartmentObject(0) - , crossCompartmentWrappers(0) + , crossCompartmentWrappersTable(0) , regexpCompartment(0) , debuggeesSet(0) {} @@ -166,10 +186,15 @@ struct CompartmentStats , extra2(other.extra2) , gcHeapArenaAdmin(other.gcHeapArenaAdmin) , gcHeapUnusedGcThings(other.gcHeapUnusedGcThings) - , gcHeapObjectsNonFunction(other.gcHeapObjectsNonFunction) + , gcHeapObjectsOrdinary(other.gcHeapObjectsOrdinary) , gcHeapObjectsFunction(other.gcHeapObjectsFunction) - , gcHeapStrings(other.gcHeapStrings) - , gcHeapShapesTree(other.gcHeapShapesTree) + , gcHeapObjectsDenseArray(other.gcHeapObjectsDenseArray) + , gcHeapObjectsSlowArray(other.gcHeapObjectsSlowArray) + , gcHeapObjectsCrossCompartmentWrapper(other.gcHeapObjectsCrossCompartmentWrapper) + , gcHeapStringsNormal(other.gcHeapStringsNormal) + , gcHeapStringsShort(other.gcHeapStringsShort) + , gcHeapShapesTreeGlobalParented(other.gcHeapShapesTreeGlobalParented) + , gcHeapShapesTreeNonGlobalParented(other.gcHeapShapesTreeNonGlobalParented) , gcHeapShapesDict(other.gcHeapShapesDict) , gcHeapShapesBase(other.gcHeapShapesBase) , gcHeapScripts(other.gcHeapScripts) @@ -184,7 +209,7 @@ struct CompartmentStats , objectsExtraRegExpStatics(other.objectsExtraRegExpStatics) , objectsExtraPropertyIteratorData(other.objectsExtraPropertyIteratorData) , objectsExtraPrivate(other.objectsExtraPrivate) - , nonHugeStringChars(other.nonHugeStringChars) + , stringCharsNonHuge(other.stringCharsNonHuge) , shapesExtraTreeTables(other.shapesExtraTreeTables) , shapesExtraDictTables(other.shapesExtraDictTables) , shapesExtraTreeShapeKids(other.shapesExtraTreeShapeKids) @@ -193,7 +218,7 @@ struct CompartmentStats , jaegerData(other.jaegerData) , ionData(other.ionData) , compartmentObject(other.compartmentObject) - , crossCompartmentWrappers(other.crossCompartmentWrappers) + , crossCompartmentWrappersTable(other.crossCompartmentWrappersTable) , regexpCompartment(other.regexpCompartment) , debuggeesSet(other.debuggeesSet) , typeInferenceSizes(other.typeInferenceSizes) @@ -210,10 +235,15 @@ struct CompartmentStats size_t gcHeapArenaAdmin; size_t gcHeapUnusedGcThings; - size_t gcHeapObjectsNonFunction; + size_t gcHeapObjectsOrdinary; size_t gcHeapObjectsFunction; - size_t gcHeapStrings; - size_t gcHeapShapesTree; + size_t gcHeapObjectsDenseArray; + size_t gcHeapObjectsSlowArray; + size_t gcHeapObjectsCrossCompartmentWrapper; + size_t gcHeapStringsNormal; + size_t gcHeapStringsShort; + size_t gcHeapShapesTreeGlobalParented; + size_t gcHeapShapesTreeNonGlobalParented; size_t gcHeapShapesDict; size_t gcHeapShapesBase; size_t gcHeapScripts; @@ -229,7 +259,7 @@ struct CompartmentStats size_t objectsExtraRegExpStatics; size_t objectsExtraPropertyIteratorData; size_t objectsExtraPrivate; - size_t nonHugeStringChars; + size_t stringCharsNonHuge; size_t shapesExtraTreeTables; size_t shapesExtraDictTables; size_t shapesExtraTreeShapeKids; @@ -238,7 +268,7 @@ struct CompartmentStats size_t jaegerData; size_t ionData; size_t compartmentObject; - size_t crossCompartmentWrappers; + size_t crossCompartmentWrappersTable; size_t regexpCompartment; size_t debuggeesSet; @@ -253,10 +283,15 @@ struct CompartmentStats ADD(gcHeapArenaAdmin); ADD(gcHeapUnusedGcThings); - ADD(gcHeapObjectsNonFunction); + ADD(gcHeapObjectsOrdinary); ADD(gcHeapObjectsFunction); - ADD(gcHeapStrings); - ADD(gcHeapShapesTree); + ADD(gcHeapObjectsDenseArray); + ADD(gcHeapObjectsSlowArray); + ADD(gcHeapObjectsCrossCompartmentWrapper); + ADD(gcHeapStringsNormal); + ADD(gcHeapStringsShort); + ADD(gcHeapShapesTreeGlobalParented); + ADD(gcHeapShapesTreeNonGlobalParented); ADD(gcHeapShapesDict); ADD(gcHeapShapesBase); ADD(gcHeapScripts); @@ -272,7 +307,7 @@ struct CompartmentStats ADD(objectsExtraRegExpStatics); ADD(objectsExtraPropertyIteratorData); ADD(objectsExtraPrivate); - ADD(nonHugeStringChars); + ADD(stringCharsNonHuge); ADD(shapesExtraTreeTables); ADD(shapesExtraDictTables); ADD(shapesExtraTreeShapeKids); @@ -281,7 +316,7 @@ struct CompartmentStats ADD(jaegerData); ADD(ionData); ADD(compartmentObject); - ADD(crossCompartmentWrappers); + ADD(crossCompartmentWrappersTable); ADD(regexpCompartment); ADD(debuggeesSet); diff --git a/scripting/javascript/spidermonkey-android/include/js/Utility.h b/scripting/javascript/spidermonkey-android/include/js/Utility.h index 3b03615515..7e4d4d5beb 100644 --- a/scripting/javascript/spidermonkey-android/include/js/Utility.h +++ b/scripting/javascript/spidermonkey-android/include/js/Utility.h @@ -21,7 +21,6 @@ #include "jstypes.h" -#ifdef __cplusplus # include "js/TemplateLib.h" # include "mozilla/Scoped.h" @@ -36,12 +35,8 @@ namespace js { /* The private namespace is a superset of the public/shared namespaces. */ using namespace JS; -using namespace mozilla; } /* namespace js */ -#endif /* __cplusplus */ - -JS_BEGIN_EXTERN_C /* * Pattern used to overwrite freed memory. If you are accessing an object with @@ -103,7 +98,7 @@ PrintBacktrace() int32_t OOM_traceIdx = 0; OOM_traceSize = backtrace(OOM_trace, JS_OOM_BACKTRACE_SIZE); OOM_traceSymbols = backtrace_symbols(OOM_trace, OOM_traceSize); - + if (!OOM_traceSymbols) return; @@ -171,6 +166,8 @@ static JS_INLINE void js_free(void* p) } #endif/* JS_USE_CUSTOM_ALLOCATOR */ +JS_BEGIN_EXTERN_C + /* * Replace bit-scanning code sequences with CPU-specific instructions to * speedup calculations of ceiling/floor log2. @@ -330,6 +327,8 @@ JS_PUBLIC_API(size_t) js_FloorLog2wImpl(size_t n); # error "NOT SUPPORTED" #endif +JS_END_EXTERN_C + /* * Internal function. * Compute the log of the least power of 2 greater than or equal to n. This is @@ -371,9 +370,6 @@ JS_FLOOR_LOG2W(size_t n) #define JS_ROTATE_LEFT32(a, bits) (((a) << (bits)) | ((a) >> (32 - (bits)))) #endif -JS_END_EXTERN_C - -#ifdef __cplusplus #include /* @@ -903,8 +899,6 @@ inline bool IsPoisonedPtr(T *v) } -#endif /* defined(__cplusplus) */ - /* * This is SpiderMonkey's equivalent to |nsMallocSizeOfFun|. */ diff --git a/scripting/javascript/spidermonkey-android/include/js/Vector.h b/scripting/javascript/spidermonkey-android/include/js/Vector.h index 05480632be..9f2377fbe6 100644 --- a/scripting/javascript/spidermonkey-android/include/js/Vector.h +++ b/scripting/javascript/spidermonkey-android/include/js/Vector.h @@ -242,7 +242,7 @@ class Vector : private AllocPolicy size_t mReserved; /* Max elements of reserved or used space in this vector. */ #endif - AlignedStorage storage; + mozilla::AlignedStorage storage; #ifdef DEBUG friend class ReentrancyGuard; @@ -255,7 +255,11 @@ class Vector : private AllocPolicy /* private accessors */ bool usingInlineStorage() const { - return mBegin == (T *)storage.addr(); + return mBegin == inlineStorage(); + } + + T *inlineStorage() const { + return (T *)storage.addr(); } T *beginNoCheck() const { @@ -427,7 +431,7 @@ class Vector : private AllocPolicy internalAppendN(t, n); } template void infallibleAppend(const U *begin, const U *end) { - internalAppend(begin, PointerRangeSize(begin, end)); + internalAppend(begin, mozilla::PointerRangeSize(begin, end)); } template void infallibleAppend(const U *begin, size_t length) { internalAppend(begin, length); @@ -479,6 +483,8 @@ class Vector : private AllocPolicy * object (which must be heap-allocated) itself. */ size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const; + + void swap(Vector &other); }; /* This does the re-entrancy check plus several other sanity checks. */ @@ -506,6 +512,9 @@ template JS_ALWAYS_INLINE Vector::Vector(MoveRef rhs) : AllocPolicy(rhs) +#ifdef DEBUG + , entered(false) +#endif { mLength = rhs->mLength; mCapacity = rhs->mCapacity; @@ -856,7 +865,7 @@ JS_ALWAYS_INLINE bool Vector::append(const U *insBegin, const U *insEnd) { REENTRANCY_GUARD_ET_AL; - size_t needed = PointerRangeSize(insBegin, insEnd); + size_t needed = mozilla::PointerRangeSize(insBegin, insEnd); if (mLength + needed > mCapacity && !growStorageBy(needed)) return false; @@ -995,6 +1004,33 @@ Vector::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf); } +template +inline void +Vector::swap(Vector &other) +{ + // TODO Implement N != 0 + JS_STATIC_ASSERT(N == 0); + + // This only works when inline storage is always empty. + if (!usingInlineStorage() && other.usingInlineStorage()) { + other.mBegin = mBegin; + mBegin = inlineStorage(); + } else if (usingInlineStorage() && !other.usingInlineStorage()) { + mBegin = other.mBegin; + other.mBegin = other.inlineStorage(); + } else if (!usingInlineStorage() && !other.usingInlineStorage()) { + Swap(mBegin, other.mBegin); + } else { + // This case is a no-op, since we'd set both to use their inline storage. + } + + Swap(mLength, other.mLength); + Swap(mCapacity, other.mCapacity); +#ifdef DEBUG + Swap(mReserved, other.mReserved); +#endif +} + } /* namespace js */ #ifdef _MSC_VER diff --git a/scripting/javascript/spidermonkey-android/include/jsalloc.h b/scripting/javascript/spidermonkey-android/include/jsalloc.h index a5eeca7534..259456ff7e 100644 --- a/scripting/javascript/spidermonkey-android/include/jsalloc.h +++ b/scripting/javascript/spidermonkey-android/include/jsalloc.h @@ -18,6 +18,8 @@ namespace js { * - public copy constructor, assignment, destructor * - void *malloc_(size_t) * Responsible for OOM reporting on NULL return value. + * - void *calloc_(size_t) + * Responsible for OOM reporting on NULL return value. * - void *realloc_(size_t) * Responsible for OOM reporting on NULL return value. * The *used* bytes of the previous buffer is passed in @@ -33,6 +35,7 @@ class SystemAllocPolicy { public: void *malloc_(size_t bytes) { return js_malloc(bytes); } + void *calloc_(size_t bytes) { return js_calloc(bytes); } void *realloc_(void *p, size_t oldBytes, size_t bytes) { return js_realloc(p, bytes); } void free_(void *p) { js_free(p); } void reportAllocOverflow() const {} @@ -71,6 +74,13 @@ class TempAllocPolicy return p; } + void *calloc_(size_t bytes) { + void *p = js_calloc(bytes); + if (JS_UNLIKELY(!p)) + p = onOutOfMemory(NULL, bytes); + return p; + } + void *realloc_(void *p, size_t oldBytes, size_t bytes) { void *p2 = js_realloc(p, bytes); if (JS_UNLIKELY(!p2)) diff --git a/scripting/javascript/spidermonkey-android/include/jsapi.h.REMOVED.git-id b/scripting/javascript/spidermonkey-android/include/jsapi.h.REMOVED.git-id index 80ca60e448..4ed0fd8565 100644 --- a/scripting/javascript/spidermonkey-android/include/jsapi.h.REMOVED.git-id +++ b/scripting/javascript/spidermonkey-android/include/jsapi.h.REMOVED.git-id @@ -1 +1 @@ -4f78a759104eea6e7790c03ce0130299eeaa3968 \ No newline at end of file +ba4ed5550f4042bc9963d0e15cce943b9f0be17a \ No newline at end of file diff --git a/scripting/javascript/spidermonkey-android/include/jsdbgapi.h b/scripting/javascript/spidermonkey-android/include/jsdbgapi.h index 4fb4ee1ecf..5f4d68dd46 100644 --- a/scripting/javascript/spidermonkey-android/include/jsdbgapi.h +++ b/scripting/javascript/spidermonkey-android/include/jsdbgapi.h @@ -13,7 +13,6 @@ #include "jsapi.h" #include "jsprvtd.h" -#if defined(__cplusplus) namespace JS { struct FrameDescription @@ -47,9 +46,6 @@ JS_FRIEND_API(void) js_DumpValue(const js::Value &val); JS_FRIEND_API(void) js_DumpId(jsid id); JS_FRIEND_API(void) js_DumpStackFrame(JSContext *cx, js::StackFrame *start = NULL); # endif -#endif - -JS_BEGIN_EXTERN_C JS_FRIEND_API(void) js_DumpBacktrace(JSContext *cx); @@ -432,6 +428,4 @@ JS_UnwrapObjectAndInnerize(JSObject *obj); extern JS_FRIEND_API(JSBool) js_CallContextDebugHandler(JSContext *cx); -JS_END_EXTERN_C - #endif /* jsdbgapi_h___ */ diff --git a/scripting/javascript/spidermonkey-android/include/jsdhash.h b/scripting/javascript/spidermonkey-android/include/jsdhash.h index 7af17046b0..9553658b57 100644 --- a/scripting/javascript/spidermonkey-android/include/jsdhash.h +++ b/scripting/javascript/spidermonkey-android/include/jsdhash.h @@ -14,8 +14,6 @@ #include "jstypes.h" #include "jsutil.h" -JS_BEGIN_EXTERN_C - #if defined(__GNUC__) && defined(__i386__) && (__GNUC__ >= 3) && !defined(XP_OS2) #define JS_DHASH_FASTCALL __attribute__ ((regparm (3),stdcall)) #elif defined(XP_WIN) @@ -598,6 +596,4 @@ extern JS_PUBLIC_API(void) JS_DHashTableDumpMeter(JSDHashTable *table, JSDHashEnumerator dump, FILE *fp); #endif -JS_END_EXTERN_C - #endif /* jsdhash_h___ */ diff --git a/scripting/javascript/spidermonkey-android/include/jsfriendapi.h b/scripting/javascript/spidermonkey-android/include/jsfriendapi.h index 63e79aeb14..51e7b90533 100644 --- a/scripting/javascript/spidermonkey-android/include/jsfriendapi.h +++ b/scripting/javascript/spidermonkey-android/include/jsfriendapi.h @@ -8,12 +8,28 @@ #define jsfriendapi_h___ #include "jsclass.h" +#include "jscpucfg.h" #include "jspubtd.h" #include "jsprvtd.h" +#include "js/HeapAPI.h" + #include "mozilla/GuardObjects.h" -JS_BEGIN_EXTERN_C +/* + * This macro checks if the stack pointer has exceeded a given limit. If + * |tolerance| is non-zero, it returns true only if the stack pointer has + * exceeded the limit by more than |tolerance| bytes. + */ +#if JS_STACK_GROWTH_DIRECTION > 0 +# define JS_CHECK_STACK_SIZE_WITH_TOLERANCE(limit, sp, tolerance) \ + ((uintptr_t)(sp) < (limit)+(tolerance)) +#else +# define JS_CHECK_STACK_SIZE_WITH_TOLERANCE(limit, sp, tolerance) \ + ((uintptr_t)(sp) > (limit)-(tolerance)) +#endif + +#define JS_CHECK_STACK_SIZE(limit, lval) JS_CHECK_STACK_SIZE_WITH_TOLERANCE(limit, lval, 0) extern JS_FRIEND_API(void) JS_SetGrayGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data); @@ -139,8 +155,6 @@ extern JS_FRIEND_API(void) js_DumpChars(const jschar *s, size_t n); #endif -#ifdef __cplusplus - extern JS_FRIEND_API(bool) JS_CopyPropertiesFrom(JSContext *cx, JSObject *target, JSObject *obj); @@ -171,12 +185,6 @@ struct JSFunctionSpecWithHelp { extern JS_FRIEND_API(bool) JS_DefineFunctionsWithHelp(JSContext *cx, JSObject *obj, const JSFunctionSpecWithHelp *fs); -#endif - -JS_END_EXTERN_C - -#ifdef __cplusplus - typedef bool (* JS_SourceHook)(JSContext *cx, JSScript *script, jschar **src, uint32_t *length); extern JS_FRIEND_API(void) @@ -184,6 +192,8 @@ JS_SetSourceHook(JSRuntime *rt, JS_SourceHook hook); namespace js { +extern mozilla::ThreadLocal TlsPerThreadData; + inline JSRuntime * GetRuntime(const JSContext *cx) { @@ -259,15 +269,34 @@ TraceWeakMaps(WeakMapTracer *trc); extern JS_FRIEND_API(bool) GCThingIsMarkedGray(void *thing); +extern JS_FRIEND_API(bool) +AreGCGrayBitsValid(JSRuntime *rt); + +/* + * Unsets the gray bit for anything reachable from |thing|. |kind| should not be + * JSTRACE_SHAPE. |thing| should be non-null. + */ +extern JS_FRIEND_API(void) +UnmarkGrayGCThingRecursively(void *thing, JSGCTraceKind kind); + typedef void -(GCThingCallback)(void *closure, void *gcthing); +(*GCThingCallback)(void *closure, void *gcthing); extern JS_FRIEND_API(void) -VisitGrayWrapperTargets(JSCompartment *comp, GCThingCallback *callback, void *closure); +VisitGrayWrapperTargets(JSCompartment *comp, GCThingCallback callback, void *closure); extern JS_FRIEND_API(JSObject *) GetWeakmapKeyDelegate(JSObject *key); +JS_FRIEND_API(JSGCTraceKind) +GCThingTraceKind(void *thing); + +/* + * Invoke cellCallback on every gray JS_OBJECT in the given compartment. + */ +extern JS_FRIEND_API(void) +IterateGrayObjects(JSCompartment *compartment, GCThingCallback cellCallback, void *data); + /* * Shadow declarations of JS internal structures, for access by inline access * functions below. Do not use these structures in any other way. When adding @@ -527,7 +556,13 @@ GetNativeStackLimit(const JSRuntime *rt) return RuntimeFriendFields::get(rt)->nativeStackLimit; } -#define JS_CHECK_RECURSION(cx, onerror) \ +/* + * These macros report a stack overflow and run |onerror| if we are close to + * using up the C stack. The JS_CHECK_CHROME_RECURSION variant gives us a little + * extra space so that we can ensure that crucial code is able to run. + */ + +#define JS_CHECK_RECURSION(cx, onerror) \ JS_BEGIN_MACRO \ int stackDummy_; \ if (!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(js::GetRuntime(cx)), &stackDummy_)) { \ @@ -536,6 +571,18 @@ GetNativeStackLimit(const JSRuntime *rt) } \ JS_END_MACRO +#define JS_CHECK_CHROME_RECURSION(cx, onerror) \ + JS_BEGIN_MACRO \ + int stackDummy_; \ + if (!JS_CHECK_STACK_SIZE_WITH_TOLERANCE(js::GetNativeStackLimit(js::GetRuntime(cx)), \ + &stackDummy_, \ + 1024 * sizeof(size_t))) \ + { \ + js_ReportOverRecursed(cx); \ + onerror; \ + } \ + JS_END_MACRO + JS_FRIEND_API(void) StartPCCountProfiling(JSContext *cx); @@ -979,8 +1026,6 @@ uint32_t GetListBaseExpandoSlot(); } /* namespace js */ -#endif - /* Implemented in jsdate.cpp. */ /* @@ -1017,8 +1062,6 @@ js_GetSCOffset(JSStructuredCloneWriter* writer); /* Typed Array functions, implemented in jstypedarray.cpp */ -#ifdef __cplusplus - namespace js { namespace ArrayBufferView { @@ -1038,6 +1081,12 @@ enum ViewType { */ TYPE_UINT8_CLAMPED, + /* + * Type returned for a DataView. Note that there is no single element type + * in this case. + */ + TYPE_DATAVIEW, + TYPE_MAX }; @@ -1045,9 +1094,6 @@ enum ViewType { } /* namespace js */ typedef js::ArrayBufferView::ViewType JSArrayBufferViewType; -#else -typedef uint32_t JSArrayBufferViewType; -#endif /* __cplusplus */ /* * Create a new typed array with nelements elements. @@ -1150,41 +1196,40 @@ JS_NewArrayBuffer(JSContext *cx, uint32_t nbytes); * the various accessor JSAPI calls defined below. */ extern JS_FRIEND_API(JSBool) -JS_IsTypedArrayObject(JSObject *obj, JSContext *cx); +JS_IsTypedArrayObject(JSObject *obj); /* * Check whether obj supports JS_GetArrayBufferView* APIs. Note that this may * return false if a security wrapper is encountered that denies the * unwrapping. If this test or one of the more specific tests succeeds, then it * is safe to call the various ArrayBufferView accessor JSAPI calls defined - * below. cx MUST be non-NULL and valid. + * below. */ extern JS_FRIEND_API(JSBool) -JS_IsArrayBufferViewObject(JSObject *obj, JSContext *cx); +JS_IsArrayBufferViewObject(JSObject *obj); /* * Test for specific typed array types (ArrayBufferView subtypes) */ extern JS_FRIEND_API(JSBool) -JS_IsInt8Array(JSObject *obj, JSContext *cx); +JS_IsInt8Array(JSObject *obj); extern JS_FRIEND_API(JSBool) -JS_IsUint8Array(JSObject *obj, JSContext *cx); +JS_IsUint8Array(JSObject *obj); extern JS_FRIEND_API(JSBool) -JS_IsUint8ClampedArray(JSObject *obj, JSContext *cx); +JS_IsUint8ClampedArray(JSObject *obj); extern JS_FRIEND_API(JSBool) -JS_IsInt16Array(JSObject *obj, JSContext *cx); +JS_IsInt16Array(JSObject *obj); extern JS_FRIEND_API(JSBool) -JS_IsUint16Array(JSObject *obj, JSContext *cx); +JS_IsUint16Array(JSObject *obj); extern JS_FRIEND_API(JSBool) -JS_IsInt32Array(JSObject *obj, JSContext *cx); +JS_IsInt32Array(JSObject *obj); extern JS_FRIEND_API(JSBool) -JS_IsUint32Array(JSObject *obj, JSContext *cx); +JS_IsUint32Array(JSObject *obj); extern JS_FRIEND_API(JSBool) -JS_IsFloat32Array(JSObject *obj, JSContext *cx); +JS_IsFloat32Array(JSObject *obj); extern JS_FRIEND_API(JSBool) -JS_IsFloat64Array(JSObject *obj, JSContext *cx); - +JS_IsFloat64Array(JSObject *obj); /* * Unwrap Typed arrays all at once. Return NULL without throwing if the object @@ -1192,38 +1237,37 @@ JS_IsFloat64Array(JSObject *obj, JSContext *cx); * success, filling both outparameters. */ extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsInt8Array(JSContext *cx, JSObject *obj, uint32_t *length, int8_t **data); +JS_GetObjectAsInt8Array(JSObject *obj, uint32_t *length, int8_t **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsUint8Array(JSContext *cx, JSObject *obj, uint32_t *length, uint8_t **data); +JS_GetObjectAsUint8Array(JSObject *obj, uint32_t *length, uint8_t **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsUint8ClampedArray(JSContext *cx, JSObject *obj, uint32_t *length, uint8_t **data); +JS_GetObjectAsUint8ClampedArray(JSObject *obj, uint32_t *length, uint8_t **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsInt16Array(JSContext *cx, JSObject *obj, uint32_t *length, int16_t **data); +JS_GetObjectAsInt16Array(JSObject *obj, uint32_t *length, int16_t **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsUint16Array(JSContext *cx, JSObject *obj, uint32_t *length, uint16_t **data); +JS_GetObjectAsUint16Array(JSObject *obj, uint32_t *length, uint16_t **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsInt32Array(JSContext *cx, JSObject *obj, uint32_t *length, int32_t **data); +JS_GetObjectAsInt32Array(JSObject *obj, uint32_t *length, int32_t **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsUint32Array(JSContext *cx, JSObject *obj, uint32_t *length, uint32_t **data); +JS_GetObjectAsUint32Array(JSObject *obj, uint32_t *length, uint32_t **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsFloat32Array(JSContext *cx, JSObject *obj, uint32_t *length, float **data); +JS_GetObjectAsFloat32Array(JSObject *obj, uint32_t *length, float **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsFloat64Array(JSContext *cx, JSObject *obj, uint32_t *length, double **data); +JS_GetObjectAsFloat64Array(JSObject *obj, uint32_t *length, double **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsArrayBufferView(JSContext *cx, JSObject *obj, uint32_t *length, uint8_t **data); +JS_GetObjectAsArrayBufferView(JSObject *obj, uint32_t *length, uint8_t **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsArrayBuffer(JSContext *cx, JSObject *obj, uint32_t *length, uint8_t **data); +JS_GetObjectAsArrayBuffer(JSObject *obj, uint32_t *length, uint8_t **data); /* - * Get the type of elements in a typed array. + * Get the type of elements in a typed array, or TYPE_DATAVIEW if a DataView. * - * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow - * be known that it would pass such a test: it is a typed array or a wrapper of - * a typed array, and the unwrapping will succeed. If cx is NULL, then DEBUG - * builds may be unable to assert when unwrapping should be disallowed. + * |obj| must have passed a JS_IsArrayBufferView/JS_Is*Array test, or somehow + * be known that it would pass such a test: it is an ArrayBufferView or a + * wrapper of an ArrayBufferView, and the unwrapping will succeed. */ extern JS_FRIEND_API(JSArrayBufferViewType) -JS_GetTypedArrayType(JSObject *obj, JSContext *maybecx); +JS_GetArrayBufferViewType(JSObject *obj); /* * Check whether obj supports the JS_GetArrayBuffer* APIs. Note that this may @@ -1232,18 +1276,17 @@ JS_GetTypedArrayType(JSObject *obj, JSContext *maybecx); * accessor JSAPI calls defined below. */ extern JS_FRIEND_API(JSBool) -JS_IsArrayBufferObject(JSObject *obj, JSContext *maybecx); +JS_IsArrayBufferObject(JSObject *obj); /* * Return the available byte length of an array buffer. * * |obj| must have passed a JS_IsArrayBufferObject test, or somehow be known * that it would pass such a test: it is an ArrayBuffer or a wrapper of an - * ArrayBuffer, and the unwrapping will succeed. If cx is NULL, then DEBUG - * builds may be unable to assert when unwrapping should be disallowed. + * ArrayBuffer, and the unwrapping will succeed. */ extern JS_FRIEND_API(uint32_t) -JS_GetArrayBufferByteLength(JSObject *obj, JSContext *maybecx); +JS_GetArrayBufferByteLength(JSObject *obj); /* * Return a pointer to an array buffer's data. The buffer is still owned by the @@ -1252,22 +1295,20 @@ JS_GetArrayBufferByteLength(JSObject *obj, JSContext *maybecx); * * |obj| must have passed a JS_IsArrayBufferObject test, or somehow be known * that it would pass such a test: it is an ArrayBuffer or a wrapper of an - * ArrayBuffer, and the unwrapping will succeed. If cx is NULL, then DEBUG - * builds may be unable to assert when unwrapping should be disallowed. + * ArrayBuffer, and the unwrapping will succeed. */ extern JS_FRIEND_API(uint8_t *) -JS_GetArrayBufferData(JSObject *obj, JSContext *maybecx); +JS_GetArrayBufferData(JSObject *obj); /* * Return the number of elements in a typed array. * * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow * be known that it would pass such a test: it is a typed array or a wrapper of - * a typed array, and the unwrapping will succeed. If cx is NULL, then DEBUG - * builds may be unable to assert when unwrapping should be disallowed. + * a typed array, and the unwrapping will succeed. */ extern JS_FRIEND_API(uint32_t) -JS_GetTypedArrayLength(JSObject *obj, JSContext *cx); +JS_GetTypedArrayLength(JSObject *obj); /* * Return the byte offset from the start of an array buffer to the start of a @@ -1275,22 +1316,20 @@ JS_GetTypedArrayLength(JSObject *obj, JSContext *cx); * * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow * be known that it would pass such a test: it is a typed array or a wrapper of - * a typed array, and the unwrapping will succeed. If cx is NULL, then DEBUG - * builds may be unable to assert when unwrapping should be disallowed. + * a typed array, and the unwrapping will succeed. */ extern JS_FRIEND_API(uint32_t) -JS_GetTypedArrayByteOffset(JSObject *obj, JSContext *cx); +JS_GetTypedArrayByteOffset(JSObject *obj); /* * Return the byte length of a typed array. * * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow * be known that it would pass such a test: it is a typed array or a wrapper of - * a typed array, and the unwrapping will succeed. If cx is NULL, then DEBUG - * builds may be unable to assert when unwrapping should be disallowed. + * a typed array, and the unwrapping will succeed. */ extern JS_FRIEND_API(uint32_t) -JS_GetTypedArrayByteLength(JSObject *obj, JSContext *cx); +JS_GetTypedArrayByteLength(JSObject *obj); /* * Check whether obj supports JS_ArrayBufferView* APIs. Note that this may @@ -1298,13 +1337,13 @@ JS_GetTypedArrayByteLength(JSObject *obj, JSContext *cx); * unwrapping. */ extern JS_FRIEND_API(JSBool) -JS_IsArrayBufferViewObject(JSObject *obj, JSContext *cx); +JS_IsArrayBufferViewObject(JSObject *obj); /* * More generic name for JS_GetTypedArrayByteLength to cover DataViews as well */ extern JS_FRIEND_API(uint32_t) -JS_GetArrayBufferViewByteLength(JSObject *obj, JSContext *cx); +JS_GetArrayBufferViewByteLength(JSObject *obj); /* * Return a pointer to the start of the data referenced by a typed array. The @@ -1313,43 +1352,48 @@ JS_GetArrayBufferViewByteLength(JSObject *obj, JSContext *cx); * * |obj| must have passed a JS_Is*Array test, or somehow be known that it would * pass such a test: it is a typed array or a wrapper of a typed array, and the - * unwrapping will succeed. If cx is NULL, then DEBUG builds may be unable to - * assert when unwrapping should be disallowed. + * unwrapping will succeed. */ extern JS_FRIEND_API(int8_t *) -JS_GetInt8ArrayData(JSObject *obj, JSContext *maybecx); +JS_GetInt8ArrayData(JSObject *obj); extern JS_FRIEND_API(uint8_t *) -JS_GetUint8ArrayData(JSObject *obj, JSContext *maybecx); +JS_GetUint8ArrayData(JSObject *obj); extern JS_FRIEND_API(uint8_t *) -JS_GetUint8ClampedArrayData(JSObject *obj, JSContext *maybecx); +JS_GetUint8ClampedArrayData(JSObject *obj); extern JS_FRIEND_API(int16_t *) -JS_GetInt16ArrayData(JSObject *obj, JSContext *maybecx); +JS_GetInt16ArrayData(JSObject *obj); extern JS_FRIEND_API(uint16_t *) -JS_GetUint16ArrayData(JSObject *obj, JSContext *maybecx); +JS_GetUint16ArrayData(JSObject *obj); extern JS_FRIEND_API(int32_t *) -JS_GetInt32ArrayData(JSObject *obj, JSContext *maybecx); +JS_GetInt32ArrayData(JSObject *obj); extern JS_FRIEND_API(uint32_t *) -JS_GetUint32ArrayData(JSObject *obj, JSContext *maybecx); +JS_GetUint32ArrayData(JSObject *obj); extern JS_FRIEND_API(float *) -JS_GetFloat32ArrayData(JSObject *obj, JSContext *maybecx); +JS_GetFloat32ArrayData(JSObject *obj); extern JS_FRIEND_API(double *) -JS_GetFloat64ArrayData(JSObject *obj, JSContext *maybecx); +JS_GetFloat64ArrayData(JSObject *obj); /* * Same as above, but for any kind of ArrayBufferView. Prefer the type-specific * versions when possible. */ extern JS_FRIEND_API(void *) -JS_GetArrayBufferViewData(JSObject *obj, JSContext *maybecx); +JS_GetArrayBufferViewData(JSObject *obj); /* - * Check whether obj supports JS_GetDataView* APIs. Note that this may fail and - * throw an exception if a security wrapper is encountered that denies the - * operation. + * Return the ArrayBuffer underlying an ArrayBufferView. If the buffer has been + * neutered, this will still return the neutered buffer. |obj| must be an + * object that would return true for JS_IsArrayBufferViewObject(). + */ +extern JS_FRIEND_API(JSObject *) +JS_GetArrayBufferViewBuffer(JSObject *obj); + +/* + * Check whether obj supports JS_GetDataView* APIs. */ JS_FRIEND_API(JSBool) -JS_IsDataViewObject(JSContext *cx, JSObject *obj, JSBool *isDataView); +JS_IsDataViewObject(JSObject *obj); /* * Return the byte offset of a data view into its array buffer. |obj| must be a @@ -1357,11 +1401,10 @@ JS_IsDataViewObject(JSContext *cx, JSObject *obj, JSBool *isDataView); * * |obj| must have passed a JS_IsDataViewObject test, or somehow be known that * it would pass such a test: it is a data view or a wrapper of a data view, - * and the unwrapping will succeed. If cx is NULL, then DEBUG builds may be - * unable to assert when unwrapping should be disallowed. + * and the unwrapping will succeed. */ JS_FRIEND_API(uint32_t) -JS_GetDataViewByteOffset(JSObject *obj, JSContext *maybecx); +JS_GetDataViewByteOffset(JSObject *obj); /* * Return the byte length of a data view. @@ -1372,7 +1415,7 @@ JS_GetDataViewByteOffset(JSObject *obj, JSContext *maybecx); * unable to assert when unwrapping should be disallowed. */ JS_FRIEND_API(uint32_t) -JS_GetDataViewByteLength(JSObject *obj, JSContext *maybecx); +JS_GetDataViewByteLength(JSObject *obj); /* * Return a pointer to the beginning of the data referenced by a DataView. @@ -1383,9 +1426,8 @@ JS_GetDataViewByteLength(JSObject *obj, JSContext *maybecx); * unable to assert when unwrapping should be disallowed. */ JS_FRIEND_API(void *) -JS_GetDataViewData(JSObject *obj, JSContext *maybecx); +JS_GetDataViewData(JSObject *obj); -#ifdef __cplusplus /* * This struct contains metadata passed from the DOM to the JS Engine for JIT * optimizations on DOM property accessors. Eventually, this should be made @@ -1420,15 +1462,16 @@ FUNCTION_VALUE_TO_JITINFO(const JS::Value& v) return reinterpret_cast(&v.toObject())->jitinfo; } +/* Statically asserted in jsfun.h. */ +static const unsigned JS_FUNCTION_INTERPRETED_BIT = 0x1; + static JS_ALWAYS_INLINE void SET_JITINFO(JSFunction * func, const JSJitInfo *info) { js::shadow::Function *fun = reinterpret_cast(func); - /* JS_ASSERT(func->isNative()). 0x4000 is JSFUN_INTERPRETED */ - JS_ASSERT(!(fun->flags & 0x4000)); + JS_ASSERT(!(fun->flags & JS_FUNCTION_INTERPRETED_BIT)); fun->jitinfo = info; } -#endif /* __cplusplus */ /* * Engine-internal extensions of jsid. This code is here only until we @@ -1494,8 +1537,6 @@ JSID_TO_ATOM(jsid id) JS_STATIC_ASSERT(sizeof(jsid) == JS_BYTES_PER_WORD); -#ifdef __cplusplus - namespace js { static JS_ALWAYS_INLINE Value @@ -1517,8 +1558,12 @@ IdToJsval(jsid id) return IdToValue(id); } +extern JS_FRIEND_API(bool) +IsReadOnlyDateMethod(JS::IsAcceptableThis test, JS::NativeImpl method); + +extern JS_FRIEND_API(bool) +IsTypedArrayThisCheck(JS::IsAcceptableThis test); + } /* namespace js */ -#endif /* __cplusplus */ - #endif /* jsfriendapi_h___ */ diff --git a/scripting/javascript/spidermonkey-android/include/jsgc.h b/scripting/javascript/spidermonkey-android/include/jsgc.h deleted file mode 100644 index 97e4610a44..0000000000 --- a/scripting/javascript/spidermonkey-android/include/jsgc.h +++ /dev/null @@ -1,1230 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef jsgc_h___ -#define jsgc_h___ - -/* - * JS Garbage Collector. - */ -#include - -#include "mozilla/Util.h" - -#include "jsalloc.h" -#include "jstypes.h" -#include "jsprvtd.h" -#include "jspubtd.h" -#include "jslock.h" -#include "jsutil.h" -#include "jsversion.h" - -#include "ds/BitArray.h" -#include "gc/Heap.h" -#include "gc/Statistics.h" -#include "js/HashTable.h" -#include "js/Vector.h" -#include "js/TemplateLib.h" - -struct JSCompartment; - -#if JS_STACK_GROWTH_DIRECTION > 0 -# define JS_CHECK_STACK_SIZE(limit, lval) ((uintptr_t)(lval) < limit) -#else -# define JS_CHECK_STACK_SIZE(limit, lval) ((uintptr_t)(lval) > limit) -#endif - -namespace js { - -class GCHelperThread; -struct Shape; -struct SliceBudget; - -namespace ion { - class IonCode; -} - -namespace gc { - -enum State { - NO_INCREMENTAL, - MARK_ROOTS, - MARK, - SWEEP, - SWEEP_END, - INVALID -}; - -class ChunkPool { - Chunk *emptyChunkListHead; - size_t emptyCount; - - public: - ChunkPool() - : emptyChunkListHead(NULL), - emptyCount(0) { } - - size_t getEmptyCount() const { - return emptyCount; - } - - inline bool wantBackgroundAllocation(JSRuntime *rt) const; - - /* Must be called with the GC lock taken. */ - inline Chunk *get(JSRuntime *rt); - - /* Must be called either during the GC or with the GC lock taken. */ - inline void put(Chunk *chunk); - - /* - * Return the list of chunks that can be released outside the GC lock. - * Must be called either during the GC or with the GC lock taken. - */ - Chunk *expire(JSRuntime *rt, bool releaseAll); - - /* Must be called with the GC lock taken. */ - void expireAndFree(JSRuntime *rt, bool releaseAll); -}; - -static inline JSGCTraceKind -MapAllocToTraceKind(AllocKind thingKind) -{ - static const JSGCTraceKind map[FINALIZE_LIMIT] = { - JSTRACE_OBJECT, /* FINALIZE_OBJECT0 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT0_BACKGROUND */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT2 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT2_BACKGROUND */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT4 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT4_BACKGROUND */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT8 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT8_BACKGROUND */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT12 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT12_BACKGROUND */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT16 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT16_BACKGROUND */ - JSTRACE_SCRIPT, /* FINALIZE_SCRIPT */ - JSTRACE_SHAPE, /* FINALIZE_SHAPE */ - JSTRACE_BASE_SHAPE, /* FINALIZE_BASE_SHAPE */ - JSTRACE_TYPE_OBJECT,/* FINALIZE_TYPE_OBJECT */ -#if JS_HAS_XML_SUPPORT /* FINALIZE_XML */ - JSTRACE_XML, -#endif - JSTRACE_STRING, /* FINALIZE_SHORT_STRING */ - JSTRACE_STRING, /* FINALIZE_STRING */ - JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING */ - JSTRACE_IONCODE, /* FINALIZE_IONCODE */ - }; - return map[thingKind]; -} - -static inline bool -IsNurseryAllocable(AllocKind kind) -{ - JS_ASSERT(kind >= 0 && unsigned(kind) < FINALIZE_LIMIT); - static const bool map[FINALIZE_LIMIT] = { - false, /* FINALIZE_OBJECT0 */ - true, /* FINALIZE_OBJECT0_BACKGROUND */ - false, /* FINALIZE_OBJECT2 */ - true, /* FINALIZE_OBJECT2_BACKGROUND */ - false, /* FINALIZE_OBJECT4 */ - true, /* FINALIZE_OBJECT4_BACKGROUND */ - false, /* FINALIZE_OBJECT8 */ - true, /* FINALIZE_OBJECT8_BACKGROUND */ - false, /* FINALIZE_OBJECT12 */ - true, /* FINALIZE_OBJECT12_BACKGROUND */ - false, /* FINALIZE_OBJECT16 */ - true, /* FINALIZE_OBJECT16_BACKGROUND */ - false, /* FINALIZE_SCRIPT */ - false, /* FINALIZE_SHAPE */ - false, /* FINALIZE_BASE_SHAPE */ - false, /* FINALIZE_TYPE_OBJECT */ -#if JS_HAS_XML_SUPPORT - false, /* FINALIZE_XML */ -#endif - true, /* FINALIZE_SHORT_STRING */ - true, /* FINALIZE_STRING */ - false /* FINALIZE_EXTERNAL_STRING */ - }; - return map[kind]; -} - -static inline bool -IsBackgroundFinalized(AllocKind kind) -{ - JS_ASSERT(kind >= 0 && unsigned(kind) < FINALIZE_LIMIT); - static const bool map[FINALIZE_LIMIT] = { - false, /* FINALIZE_OBJECT0 */ - true, /* FINALIZE_OBJECT0_BACKGROUND */ - false, /* FINALIZE_OBJECT2 */ - true, /* FINALIZE_OBJECT2_BACKGROUND */ - false, /* FINALIZE_OBJECT4 */ - true, /* FINALIZE_OBJECT4_BACKGROUND */ - false, /* FINALIZE_OBJECT8 */ - true, /* FINALIZE_OBJECT8_BACKGROUND */ - false, /* FINALIZE_OBJECT12 */ - true, /* FINALIZE_OBJECT12_BACKGROUND */ - false, /* FINALIZE_OBJECT16 */ - true, /* FINALIZE_OBJECT16_BACKGROUND */ - false, /* FINALIZE_SCRIPT */ - false, /* FINALIZE_SHAPE */ - false, /* FINALIZE_BASE_SHAPE */ - false, /* FINALIZE_TYPE_OBJECT */ -#if JS_HAS_XML_SUPPORT - false, /* FINALIZE_XML */ -#endif - true, /* FINALIZE_SHORT_STRING */ - true, /* FINALIZE_STRING */ - false /* FINALIZE_EXTERNAL_STRING */ - }; - return map[kind]; -} - -inline JSGCTraceKind -GetGCThingTraceKind(const void *thing); - -/* - * ArenaList::head points to the start of the list. Normally cursor points - * to the first arena in the list with some free things and all arenas - * before cursor are fully allocated. However, as the arena currently being - * allocated from is considered full while its list of free spans is moved - * into the freeList, during the GC or cell enumeration, when an - * unallocated freeList is moved back to the arena, we can see an arena - * with some free cells before the cursor. The cursor is an indirect - * pointer to allow for efficient list insertion at the cursor point and - * other list manipulations. - */ -struct ArenaList { - ArenaHeader *head; - ArenaHeader **cursor; - - ArenaList() { - clear(); - } - - void clear() { - head = NULL; - cursor = &head; - } - - void insert(ArenaHeader *arena); -}; - -struct ArenaLists { - - private: - /* - * For each arena kind its free list is represented as the first span with - * free things. Initially all the spans are initialized as empty. After we - * find a new arena with available things we move its first free span into - * the list and set the arena as fully allocated. way we do not need to - * update the arena header after the initial allocation. When starting the - * GC we only move the head of the of the list of spans back to the arena - * only for the arena that was not fully allocated. - */ - FreeSpan freeLists[FINALIZE_LIMIT]; - - ArenaList arenaLists[FINALIZE_LIMIT]; - - /* - * The background finalization adds the finalized arenas to the list at - * the *cursor position. backgroundFinalizeState controls the interaction - * between the GC lock and the access to the list from the allocation - * thread. - * - * BFS_DONE indicates that the finalizations is not running or cannot - * affect this arena list. The allocation thread can access the list - * outside the GC lock. - * - * In BFS_RUN and BFS_JUST_FINISHED the allocation thread must take the - * lock. The former indicates that the finalization still runs. The latter - * signals that finalization just added to the list finalized arenas. In - * that case the lock effectively serves as a read barrier to ensure that - * the allocation thread see all the writes done during finalization. - */ - enum BackgroundFinalizeState { - BFS_DONE, - BFS_RUN, - BFS_JUST_FINISHED - }; - - volatile uintptr_t backgroundFinalizeState[FINALIZE_LIMIT]; - - public: - /* For each arena kind, a list of arenas remaining to be swept. */ - ArenaHeader *arenaListsToSweep[FINALIZE_LIMIT]; - - public: - ArenaLists() { - for (size_t i = 0; i != FINALIZE_LIMIT; ++i) - freeLists[i].initAsEmpty(); - for (size_t i = 0; i != FINALIZE_LIMIT; ++i) - backgroundFinalizeState[i] = BFS_DONE; - for (size_t i = 0; i != FINALIZE_LIMIT; ++i) - arenaListsToSweep[i] = NULL; - } - - ~ArenaLists() { - for (size_t i = 0; i != FINALIZE_LIMIT; ++i) { - /* - * We can only call this during the shutdown after the last GC when - * the background finalization is disabled. - */ - JS_ASSERT(backgroundFinalizeState[i] == BFS_DONE); - ArenaHeader **headp = &arenaLists[i].head; - while (ArenaHeader *aheader = *headp) { - *headp = aheader->next; - aheader->chunk()->releaseArena(aheader); - } - } - } - - const FreeSpan *getFreeList(AllocKind thingKind) const { - return &freeLists[thingKind]; - } - - ArenaHeader *getFirstArena(AllocKind thingKind) const { - return arenaLists[thingKind].head; - } - - ArenaHeader *getFirstArenaToSweep(AllocKind thingKind) const { - return arenaListsToSweep[thingKind]; - } - - bool arenaListsAreEmpty() const { - for (size_t i = 0; i != FINALIZE_LIMIT; ++i) { - /* - * The arena cannot be empty if the background finalization is not yet - * done. - */ - if (backgroundFinalizeState[i] != BFS_DONE) - return false; - if (arenaLists[i].head) - return false; - } - return true; - } - - bool arenasAreFull(AllocKind thingKind) const { - return !*arenaLists[thingKind].cursor; - } - - void unmarkAll() { - for (size_t i = 0; i != FINALIZE_LIMIT; ++i) { - /* The background finalization must have stopped at this point. */ - JS_ASSERT(backgroundFinalizeState[i] == BFS_DONE || - backgroundFinalizeState[i] == BFS_JUST_FINISHED); - for (ArenaHeader *aheader = arenaLists[i].head; aheader; aheader = aheader->next) { - uintptr_t *word = aheader->chunk()->bitmap.arenaBits(aheader); - memset(word, 0, ArenaBitmapWords * sizeof(uintptr_t)); - } - } - } - - bool doneBackgroundFinalize(AllocKind kind) const { - return backgroundFinalizeState[kind] == BFS_DONE || - backgroundFinalizeState[kind] == BFS_JUST_FINISHED; - } - - /* - * Return the free list back to the arena so the GC finalization will not - * run the finalizers over unitialized bytes from free things. - */ - void purge() { - for (size_t i = 0; i != FINALIZE_LIMIT; ++i) { - FreeSpan *headSpan = &freeLists[i]; - if (!headSpan->isEmpty()) { - ArenaHeader *aheader = headSpan->arenaHeader(); - aheader->setFirstFreeSpan(headSpan); - headSpan->initAsEmpty(); - } - } - } - - inline void prepareForIncrementalGC(JSRuntime *rt); - - /* - * Temporarily copy the free list heads to the arenas so the code can see - * the proper value in ArenaHeader::freeList when accessing the latter - * outside the GC. - */ - void copyFreeListsToArenas() { - for (size_t i = 0; i != FINALIZE_LIMIT; ++i) - copyFreeListToArena(AllocKind(i)); - } - - void copyFreeListToArena(AllocKind thingKind) { - FreeSpan *headSpan = &freeLists[thingKind]; - if (!headSpan->isEmpty()) { - ArenaHeader *aheader = headSpan->arenaHeader(); - JS_ASSERT(!aheader->hasFreeThings()); - aheader->setFirstFreeSpan(headSpan); - } - } - - /* - * Clear the free lists in arenas that were temporarily set there using - * copyToArenas. - */ - void clearFreeListsInArenas() { - for (size_t i = 0; i != FINALIZE_LIMIT; ++i) - clearFreeListInArena(AllocKind(i)); - } - - - void clearFreeListInArena(AllocKind kind) { - FreeSpan *headSpan = &freeLists[kind]; - if (!headSpan->isEmpty()) { - ArenaHeader *aheader = headSpan->arenaHeader(); - JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(headSpan)); - aheader->setAsFullyUsed(); - } - } - - /* - * Check that the free list is either empty or were synchronized with the - * arena using copyToArena(). - */ - bool isSynchronizedFreeList(AllocKind kind) { - FreeSpan *headSpan = &freeLists[kind]; - if (headSpan->isEmpty()) - return true; - ArenaHeader *aheader = headSpan->arenaHeader(); - if (aheader->hasFreeThings()) { - /* - * If the arena has a free list, it must be the same as one in - * lists. - */ - JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(headSpan)); - return true; - } - return false; - } - - JS_ALWAYS_INLINE void *allocateFromFreeList(AllocKind thingKind, size_t thingSize) { - return freeLists[thingKind].allocate(thingSize); - } - - static void *refillFreeList(JSContext *cx, AllocKind thingKind); - - void checkEmptyFreeLists() { -#ifdef DEBUG - for (size_t i = 0; i < mozilla::ArrayLength(freeLists); ++i) - JS_ASSERT(freeLists[i].isEmpty()); -#endif - } - - void checkEmptyFreeList(AllocKind kind) { - JS_ASSERT(freeLists[kind].isEmpty()); - } - - void queueObjectsForSweep(FreeOp *fop); - void queueStringsForSweep(FreeOp *fop); - void queueShapesForSweep(FreeOp *fop); - void queueScriptsForSweep(FreeOp *fop); - void queueIonCodeForSweep(FreeOp *fop); - - bool foregroundFinalize(FreeOp *fop, AllocKind thingKind, SliceBudget &sliceBudget); - static void backgroundFinalize(FreeOp *fop, ArenaHeader *listHead, bool onBackgroundThread); - - private: - inline void finalizeNow(FreeOp *fop, AllocKind thingKind); - inline void queueForForegroundSweep(FreeOp *fop, AllocKind thingKind); - inline void queueForBackgroundSweep(FreeOp *fop, AllocKind thingKind); - - inline void *allocateFromArena(JSCompartment *comp, AllocKind thingKind); -}; - -/* - * Initial allocation size for data structures holding chunks is set to hold - * chunks with total capacity of 16MB to avoid buffer resizes during browser - * startup. - */ -const size_t INITIAL_CHUNK_CAPACITY = 16 * 1024 * 1024 / ChunkSize; - -/* The number of GC cycles an empty chunk can survive before been released. */ -const size_t MAX_EMPTY_CHUNK_AGE = 4; - -inline Cell * -AsCell(JSObject *obj) -{ - return reinterpret_cast(obj); -} - -} /* namespace gc */ - -struct GCPtrHasher -{ - typedef void *Lookup; - - static HashNumber hash(void *key) { - return HashNumber(uintptr_t(key) >> JS_GCTHING_ZEROBITS); - } - - static bool match(void *l, void *k) { return l == k; } -}; - -typedef HashMap GCLocks; - -struct RootInfo { - RootInfo() {} - RootInfo(const char *name, JSGCRootType type) : name(name), type(type) {} - const char *name; - JSGCRootType type; -}; - -typedef js::HashMap, - js::SystemAllocPolicy> RootedValueMap; - -} /* namespace js */ - -extern JS_FRIEND_API(JSGCTraceKind) -js_GetGCThingTraceKind(void *thing); - -extern JSBool -js_InitGC(JSRuntime *rt, uint32_t maxbytes); - -extern void -js_FinishGC(JSRuntime *rt); - -extern JSBool -js_AddRoot(JSContext *cx, js::Value *vp, const char *name); - -extern JSBool -js_AddGCThingRoot(JSContext *cx, void **rp, const char *name); - -#ifdef DEBUG -extern void -js_DumpNamedRoots(JSRuntime *rt, - void (*dump)(const char *name, void *rp, JSGCRootType type, void *data), - void *data); -#endif - -extern uint32_t -js_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data); - -/* Table of pointers with count valid members. */ -typedef struct JSPtrTable { - size_t count; - void **array; -} JSPtrTable; - -extern JSBool -js_LockGCThingRT(JSRuntime *rt, void *thing); - -extern void -js_UnlockGCThingRT(JSRuntime *rt, void *thing); - -extern bool -js_IsAddressableGCThing(JSRuntime *rt, uintptr_t w, js::gc::AllocKind *thingKind, void **thing); - -namespace js { - -extern void -MarkCompartmentActive(js::StackFrame *fp); - -extern void -TraceRuntime(JSTracer *trc); - -extern JS_FRIEND_API(void) -MarkContext(JSTracer *trc, JSContext *acx); - -/* Must be called with GC lock taken. */ -extern void -TriggerGC(JSRuntime *rt, js::gcreason::Reason reason); - -/* Must be called with GC lock taken. */ -extern void -TriggerCompartmentGC(JSCompartment *comp, js::gcreason::Reason reason); - -extern void -MaybeGC(JSContext *cx); - -extern void -ShrinkGCBuffers(JSRuntime *rt); - -extern void -ReleaseAllJITCode(FreeOp *op); - -extern JS_FRIEND_API(void) -PrepareForFullGC(JSRuntime *rt); - -/* - * Kinds of js_GC invocation. - */ -typedef enum JSGCInvocationKind { - /* Normal invocation. */ - GC_NORMAL = 0, - - /* Minimize GC triggers and release empty GC chunks right away. */ - GC_SHRINK = 1 -} JSGCInvocationKind; - -extern void -GC(JSRuntime *rt, JSGCInvocationKind gckind, js::gcreason::Reason reason); - -extern void -GCSlice(JSRuntime *rt, JSGCInvocationKind gckind, js::gcreason::Reason reason, int64_t millis = 0); - -extern void -GCFinalSlice(JSRuntime *rt, JSGCInvocationKind gckind, js::gcreason::Reason reason); - -extern void -GCDebugSlice(JSRuntime *rt, bool limit, int64_t objCount); - -extern void -PrepareForDebugGC(JSRuntime *rt); - -} /* namespace js */ - -namespace js { - -void -InitTracer(JSTracer *trc, JSRuntime *rt, JSTraceCallback callback); - -/* - * Helper that implements sweeping and allocation for kinds that can be swept - * and allocated off the main thread. - * - * In non-threadsafe builds, all actual sweeping and allocation is performed - * on the main thread, but GCHelperThread encapsulates this from clients as - * much as possible. - */ -class GCHelperThread { - enum State { - IDLE, - SWEEPING, - ALLOCATING, - CANCEL_ALLOCATION, - SHUTDOWN - }; - - /* - * During the finalization we do not free immediately. Rather we add the - * corresponding pointers to a buffer which we later release on a - * separated thread. - * - * The buffer is implemented as a vector of 64K arrays of pointers, not as - * a simple vector, to avoid realloc calls during the vector growth and to - * not bloat the binary size of the inlined freeLater method. Any OOM - * during buffer growth results in the pointer being freed immediately. - */ - static const size_t FREE_ARRAY_SIZE = size_t(1) << 16; - static const size_t FREE_ARRAY_LENGTH = FREE_ARRAY_SIZE / sizeof(void *); - - JSRuntime *const rt; - PRThread *thread; - PRCondVar *wakeup; - PRCondVar *done; - volatile State state; - - bool sweepFlag; - bool shrinkFlag; - - Vector freeVector; - void **freeCursor; - void **freeCursorEnd; - - bool backgroundAllocation; - - friend struct js::gc::ArenaLists; - - JS_FRIEND_API(void) - replenishAndFreeLater(void *ptr); - - static void freeElementsAndArray(void **array, void **end) { - JS_ASSERT(array <= end); - for (void **p = array; p != end; ++p) - js_free(*p); - js_free(array); - } - - static void threadMain(void* arg); - void threadLoop(); - - /* Must be called with the GC lock taken. */ - void doSweep(); - - public: - GCHelperThread(JSRuntime *rt) - : rt(rt), - thread(NULL), - wakeup(NULL), - done(NULL), - state(IDLE), - sweepFlag(false), - shrinkFlag(false), - freeCursor(NULL), - freeCursorEnd(NULL), - backgroundAllocation(true) - { } - - bool init(); - void finish(); - - /* Must be called with the GC lock taken. */ - void startBackgroundSweep(bool shouldShrink); - - /* Must be called with the GC lock taken. */ - void startBackgroundShrink(); - - /* Must be called without the GC lock taken. */ - void waitBackgroundSweepEnd(); - - /* Must be called without the GC lock taken. */ - void waitBackgroundSweepOrAllocEnd(); - - /* Must be called with the GC lock taken. */ - inline void startBackgroundAllocationIfIdle(); - - bool canBackgroundAllocate() const { - return backgroundAllocation; - } - - void disableBackgroundAllocation() { - backgroundAllocation = false; - } - - PRThread *getThread() const { - return thread; - } - - /* - * Outside the GC lock may give true answer when in fact the sweeping has - * been done. - */ - bool sweeping() const { - return state == SWEEPING; - } - - bool shouldShrink() const { - JS_ASSERT(sweeping()); - return shrinkFlag; - } - - void freeLater(void *ptr) { - JS_ASSERT(!sweeping()); - if (freeCursor != freeCursorEnd) - *freeCursor++ = ptr; - else - replenishAndFreeLater(ptr); - } -}; - - -struct GCChunkHasher { - typedef gc::Chunk *Lookup; - - /* - * Strip zeros for better distribution after multiplying by the golden - * ratio. - */ - static HashNumber hash(gc::Chunk *chunk) { - JS_ASSERT(!(uintptr_t(chunk) & gc::ChunkMask)); - return HashNumber(uintptr_t(chunk) >> gc::ChunkShift); - } - - static bool match(gc::Chunk *k, gc::Chunk *l) { - JS_ASSERT(!(uintptr_t(k) & gc::ChunkMask)); - JS_ASSERT(!(uintptr_t(l) & gc::ChunkMask)); - return k == l; - } -}; - -typedef HashSet GCChunkSet; - -template -struct MarkStack { - T *stack; - T *tos; - T *limit; - - T *ballast; - T *ballastLimit; - - size_t sizeLimit; - - MarkStack(size_t sizeLimit) - : stack(NULL), - tos(NULL), - limit(NULL), - ballast(NULL), - ballastLimit(NULL), - sizeLimit(sizeLimit) { } - - ~MarkStack() { - if (stack != ballast) - js_free(stack); - js_free(ballast); - } - - bool init(size_t ballastcap) { - JS_ASSERT(!stack); - - if (ballastcap == 0) - return true; - - ballast = js_pod_malloc(ballastcap); - if (!ballast) - return false; - ballastLimit = ballast + ballastcap; - initFromBallast(); - return true; - } - - void initFromBallast() { - stack = ballast; - limit = ballastLimit; - if (size_t(limit - stack) > sizeLimit) - limit = stack + sizeLimit; - tos = stack; - } - - void setSizeLimit(size_t size) { - JS_ASSERT(isEmpty()); - - sizeLimit = size; - reset(); - } - - bool push(T item) { - if (tos == limit) { - if (!enlarge()) - return false; - } - JS_ASSERT(tos < limit); - *tos++ = item; - return true; - } - - bool push(T item1, T item2, T item3) { - T *nextTos = tos + 3; - if (nextTos > limit) { - if (!enlarge()) - return false; - nextTos = tos + 3; - } - JS_ASSERT(nextTos <= limit); - tos[0] = item1; - tos[1] = item2; - tos[2] = item3; - tos = nextTos; - return true; - } - - bool isEmpty() const { - return tos == stack; - } - - T pop() { - JS_ASSERT(!isEmpty()); - return *--tos; - } - - ptrdiff_t position() const { - return tos - stack; - } - - void reset() { - if (stack != ballast) - js_free(stack); - initFromBallast(); - JS_ASSERT(stack == ballast); - } - - bool enlarge() { - size_t tosIndex = tos - stack; - size_t cap = limit - stack; - if (cap == sizeLimit) - return false; - size_t newcap = cap * 2; - if (newcap == 0) - newcap = 32; - if (newcap > sizeLimit) - newcap = sizeLimit; - - T *newStack; - if (stack == ballast) { - newStack = js_pod_malloc(newcap); - if (!newStack) - return false; - for (T *src = stack, *dst = newStack; src < tos; ) - *dst++ = *src++; - } else { - newStack = (T *)js_realloc(stack, sizeof(T) * newcap); - if (!newStack) - return false; - } - stack = newStack; - tos = stack + tosIndex; - limit = newStack + newcap; - return true; - } - - size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const { - size_t n = 0; - if (stack != ballast) - n += mallocSizeOf(stack); - n += mallocSizeOf(ballast); - return n; - } -}; - -/* - * This class records how much work has been done in a given GC slice, so that - * we can return before pausing for too long. Some slices are allowed to run for - * unlimited time, and others are bounded. To reduce the number of gettimeofday - * calls, we only check the time every 1000 operations. - */ -struct SliceBudget { - int64_t deadline; /* in microseconds */ - intptr_t counter; - - static const intptr_t CounterReset = 1000; - - static const int64_t Unlimited = 0; - static int64_t TimeBudget(int64_t millis); - static int64_t WorkBudget(int64_t work); - - /* Equivalent to SliceBudget(UnlimitedBudget). */ - SliceBudget(); - - /* Instantiate as SliceBudget(Time/WorkBudget(n)). */ - SliceBudget(int64_t budget); - - void reset() { - deadline = INT64_MAX; - counter = INTPTR_MAX; - } - - void step(intptr_t amt = 1) { - counter -= amt; - } - - bool checkOverBudget(); - - bool isOverBudget() { - if (counter >= 0) - return false; - return checkOverBudget(); - } -}; - -static const size_t MARK_STACK_LENGTH = 32768; - -struct GCMarker : public JSTracer { - private: - /* - * We use a common mark stack to mark GC things of different types and use - * the explicit tags to distinguish them when it cannot be deduced from - * the context of push or pop operation. - */ - enum StackTag { - ValueArrayTag, - ObjectTag, - TypeTag, - XmlTag, - ArenaTag, - SavedValueArrayTag, - IonCodeTag, - LastTag = IonCodeTag - }; - - static const uintptr_t StackTagMask = 7; - - static void staticAsserts() { - JS_STATIC_ASSERT(StackTagMask >= uintptr_t(LastTag)); - JS_STATIC_ASSERT(StackTagMask <= gc::Cell::CellMask); - } - - public: - explicit GCMarker(); - bool init(); - - void setSizeLimit(size_t size) { stack.setSizeLimit(size); } - size_t sizeLimit() const { return stack.sizeLimit; } - - void start(JSRuntime *rt); - void stop(); - void reset(); - - void pushObject(JSObject *obj) { - pushTaggedPtr(ObjectTag, obj); - } - - void pushArenaList(gc::ArenaHeader *firstArena) { - pushTaggedPtr(ArenaTag, firstArena); - } - - void pushType(types::TypeObject *type) { - pushTaggedPtr(TypeTag, type); - } - -#if JS_HAS_XML_SUPPORT - void pushXML(JSXML *xml) { - pushTaggedPtr(XmlTag, xml); - } - -#endif - - void pushIonCode(ion::IonCode *code) { - pushTaggedPtr(IonCodeTag, code); - } - - uint32_t getMarkColor() const { - return color; - } - - /* - * The only valid color transition during a GC is from black to gray. It is - * wrong to switch the mark color from gray to black. The reason is that the - * cycle collector depends on the invariant that there are no black to gray - * edges in the GC heap. This invariant lets the CC not trace through black - * objects. If this invariant is violated, the cycle collector may free - * objects that are still reachable. - */ - void setMarkColorGray() { - JS_ASSERT(isDrained()); - JS_ASSERT(color == gc::BLACK); - color = gc::GRAY; - } - - inline void delayMarkingArena(gc::ArenaHeader *aheader); - void delayMarkingChildren(const void *thing); - void markDelayedChildren(gc::ArenaHeader *aheader); - bool markDelayedChildren(SliceBudget &budget); - bool hasDelayedChildren() const { - return !!unmarkedArenaStackTop; - } - - bool isDrained() { - return isMarkStackEmpty() && !unmarkedArenaStackTop; - } - - bool drainMarkStack(SliceBudget &budget); - - /* - * Gray marking must be done after all black marking is complete. However, - * we do not have write barriers on XPConnect roots. Therefore, XPConnect - * roots must be accumulated in the first slice of incremental GC. We - * accumulate these roots in the GrayRootMarker and then mark them later, - * after black marking is complete. This accumulation can fail, but in that - * case we switch to non-incremental GC. - */ - bool hasBufferedGrayRoots() const; - void startBufferingGrayRoots(); - void endBufferingGrayRoots(); - void markBufferedGrayRoots(); - - static void GrayCallback(JSTracer *trc, void **thing, JSGCTraceKind kind); - - size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const; - - MarkStack stack; - - private: -#ifdef DEBUG - void checkCompartment(void *p); -#else - void checkCompartment(void *p) {} -#endif - - void pushTaggedPtr(StackTag tag, void *ptr) { - checkCompartment(ptr); - uintptr_t addr = reinterpret_cast(ptr); - JS_ASSERT(!(addr & StackTagMask)); - if (!stack.push(addr | uintptr_t(tag))) - delayMarkingChildren(ptr); - } - - void pushValueArray(JSObject *obj, void *start, void *end) { - checkCompartment(obj); - - JS_ASSERT(start <= end); - uintptr_t tagged = reinterpret_cast(obj) | GCMarker::ValueArrayTag; - uintptr_t startAddr = reinterpret_cast(start); - uintptr_t endAddr = reinterpret_cast(end); - - /* - * Push in the reverse order so obj will be on top. If we cannot push - * the array, we trigger delay marking for the whole object. - */ - if (!stack.push(endAddr, startAddr, tagged)) - delayMarkingChildren(obj); - } - - bool isMarkStackEmpty() { - return stack.isEmpty(); - } - - bool restoreValueArray(JSObject *obj, void **vpp, void **endp); - void saveValueRanges(); - inline void processMarkStackTop(SliceBudget &budget); - void processMarkStackOther(SliceBudget &budget, uintptr_t tag, uintptr_t addr); - - void appendGrayRoot(void *thing, JSGCTraceKind kind); - - /* The color is only applied to objects, functions and xml. */ - uint32_t color; - - DebugOnly started; - - /* Pointer to the top of the stack of arenas we are delaying marking on. */ - js::gc::ArenaHeader *unmarkedArenaStackTop; - /* Count of arenas that are currently in the stack. */ - DebugOnly markLaterArenas; - - struct GrayRoot { - void *thing; - JSGCTraceKind kind; -#ifdef DEBUG - JSTraceNamePrinter debugPrinter; - const void *debugPrintArg; - size_t debugPrintIndex; -#endif - - GrayRoot(void *thing, JSGCTraceKind kind) - : thing(thing), kind(kind) {} - }; - - bool grayFailed; - Vector grayRoots; -}; - -void -SetMarkStackLimit(JSRuntime *rt, size_t limit); - -void -MarkStackRangeConservatively(JSTracer *trc, Value *begin, Value *end); - -typedef void (*IterateChunkCallback)(JSRuntime *rt, void *data, gc::Chunk *chunk); -typedef void (*IterateArenaCallback)(JSRuntime *rt, void *data, gc::Arena *arena, - JSGCTraceKind traceKind, size_t thingSize); -typedef void (*IterateCellCallback)(JSRuntime *rt, void *data, void *thing, - JSGCTraceKind traceKind, size_t thingSize); - -/* - * This function calls |compartmentCallback| on every compartment, - * |arenaCallback| on every in-use arena, and |cellCallback| on every in-use - * cell in the GC heap. - */ -extern JS_FRIEND_API(void) -IterateCompartmentsArenasCells(JSRuntime *rt, void *data, - JSIterateCompartmentCallback compartmentCallback, - IterateArenaCallback arenaCallback, - IterateCellCallback cellCallback); - -/* - * Invoke chunkCallback on every in-use chunk. - */ -extern JS_FRIEND_API(void) -IterateChunks(JSRuntime *rt, void *data, IterateChunkCallback chunkCallback); - -/* - * Invoke cellCallback on every in-use object of the specified thing kind for - * the given compartment or for all compartments if it is null. - */ -extern JS_FRIEND_API(void) -IterateCells(JSRuntime *rt, JSCompartment *compartment, gc::AllocKind thingKind, - void *data, IterateCellCallback cellCallback); - -/* - * Invoke cellCallback on every gray JS_OBJECT in the given compartment. - */ -extern JS_FRIEND_API(void) -IterateGrayObjects(JSCompartment *compartment, GCThingCallback *cellCallback, void *data); - -} /* namespace js */ - -extern void -js_FinalizeStringRT(JSRuntime *rt, JSString *str); - -/* - * Macro to test if a traversal is the marking phase of the GC. - */ -#define IS_GC_MARKING_TRACER(trc) \ - ((trc)->callback == NULL || (trc)->callback == GCMarker::GrayCallback) - -namespace js { -namespace gc { - -JSCompartment * -NewCompartment(JSContext *cx, JSPrincipals *principals); - -/* Tries to run a GC no matter what (used for GC zeal). */ -void -RunDebugGC(JSContext *cx); - -void -SetDeterministicGC(JSContext *cx, bool enabled); - -void -SetValidateGC(JSContext *cx, bool enabled); - -const int ZealPokeValue = 1; -const int ZealAllocValue = 2; -const int ZealFrameGCValue = 3; -const int ZealVerifierPreValue = 4; -const int ZealFrameVerifierPreValue = 5; -const int ZealStackRootingSafeValue = 6; -const int ZealStackRootingValue = 7; -const int ZealIncrementalRootsThenFinish = 8; -const int ZealIncrementalMarkAllThenFinish = 9; -const int ZealIncrementalMultipleSlices = 10; -const int ZealVerifierPostValue = 11; -const int ZealFrameVerifierPostValue = 12; -const int ZealPurgeAnalysisValue = 13; - -enum VerifierType { - PreBarrierVerifier, - PostBarrierVerifier -}; - -#ifdef JS_GC_ZEAL - -/* Check that write barriers have been used correctly. See jsgc.cpp. */ -void -VerifyBarriers(JSRuntime *rt, VerifierType type); - -void -MaybeVerifyBarriers(JSContext *cx, bool always = false); - -#else - -static inline void -VerifyBarriers(JSRuntime *rt, VerifierType type) -{ -} - -static inline void -MaybeVerifyBarriers(JSContext *cx, bool always = false) -{ -} - -#endif - -} /* namespace gc */ - -static inline JSCompartment * -GetGCThingCompartment(void *thing) -{ - JS_ASSERT(thing); - return reinterpret_cast(thing)->compartment(); -} - -static inline JSCompartment * -GetObjectCompartment(JSObject *obj) -{ - return GetGCThingCompartment(obj); -} - -void -PurgeJITCaches(JSCompartment *c); - -} /* namespace js */ - -#endif /* jsgc_h___ */ diff --git a/scripting/javascript/spidermonkey-android/include/jslock.h b/scripting/javascript/spidermonkey-android/include/jslock.h index 11c2893c14..aa5e1c83e7 100644 --- a/scripting/javascript/spidermonkey-android/include/jslock.h +++ b/scripting/javascript/spidermonkey-android/include/jslock.h @@ -38,25 +38,4 @@ typedef struct PRLock PRLock; #endif /* JS_THREADSAFE */ -namespace js { - -class AutoAtomicIncrement -{ - int32_t *p; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER - - public: - AutoAtomicIncrement(int32_t *p JS_GUARD_OBJECT_NOTIFIER_PARAM) - : p(p) { - JS_GUARD_OBJECT_NOTIFIER_INIT; - JS_ATOMIC_INCREMENT(p); - } - - ~AutoAtomicIncrement() { - JS_ATOMIC_DECREMENT(p); - } -}; - -} /* namespace js */ - #endif /* jslock_h___ */ diff --git a/scripting/javascript/spidermonkey-android/include/json.h b/scripting/javascript/spidermonkey-android/include/json.h index 4794d64e65..bb21f91451 100644 --- a/scripting/javascript/spidermonkey-android/include/json.h +++ b/scripting/javascript/spidermonkey-android/include/json.h @@ -38,7 +38,7 @@ enum DecodingMode { STRICT, LEGACY }; namespace js { extern JS_FRIEND_API(JSBool) -ParseJSONWithReviver(JSContext *cx, const jschar *chars, size_t length, HandleValue filter, +ParseJSONWithReviver(JSContext *cx, JS::StableCharPtr chars, size_t length, HandleValue filter, MutableHandleValue vp, DecodingMode decodingMode = STRICT); } /* namespace js */ diff --git a/scripting/javascript/spidermonkey-android/include/jsprf.h b/scripting/javascript/spidermonkey-android/include/jsprf.h index 4c72b691f8..4c2bef91d2 100644 --- a/scripting/javascript/spidermonkey-android/include/jsprf.h +++ b/scripting/javascript/spidermonkey-android/include/jsprf.h @@ -16,10 +16,9 @@ ** %hd, %hu, %hx, %hX, %ho - 16-bit versions of above ** %ld, %lu, %lx, %lX, %lo - 32-bit versions of above ** %lld, %llu, %llx, %llX, %llo - 64 bit versions of above -** %s - string -** %hs - 16-bit version of above (only available if js_CStringsAreUTF8) +** %s - ascii string +** %hs - ucs2 string ** %c - character -** %hc - 16-bit version of above (only available if js_CStringsAreUTF8) ** %p - pointer (deals with machine dependent pointer size) ** %f - float ** %g - float @@ -28,8 +27,6 @@ #include #include -JS_BEGIN_EXTERN_C - /* ** sprintf into a fixed size buffer. Guarantees that a NUL is at the end ** of the buffer. Returns the length of the written output, NOT including @@ -77,6 +74,4 @@ extern JS_PUBLIC_API(char*) JS_vsmprintf(const char *fmt, va_list ap); extern JS_PUBLIC_API(char*) JS_vsprintf_append(char *last, const char *fmt, va_list ap); extern JS_PUBLIC_API(uint32_t) JS_vsxprintf(JSStuffFunc f, void *arg, const char *fmt, va_list ap); -JS_END_EXTERN_C - #endif /* jsprf_h___ */ diff --git a/scripting/javascript/spidermonkey-android/include/jsprototypes.h b/scripting/javascript/spidermonkey-android/include/jsprototypes.h index 5c65eb25cd..1abbc831a9 100644 --- a/scripting/javascript/spidermonkey-android/include/jsprototypes.h +++ b/scripting/javascript/spidermonkey-android/include/jsprototypes.h @@ -60,5 +60,6 @@ macro(Set, 38, js_InitSetClass) \ macro(DataView, 39, js_InitTypedArrayClasses) \ macro(ParallelArray, 40, js_InitParallelArrayClass) \ + macro(Intl, 41, js_InitIntlClass) \ #endif /* jsprototypes_h___ */ diff --git a/scripting/javascript/spidermonkey-android/include/jsproxy.h b/scripting/javascript/spidermonkey-android/include/jsproxy.h index 075abc7123..73ea5c8c70 100644 --- a/scripting/javascript/spidermonkey-android/include/jsproxy.h +++ b/scripting/javascript/spidermonkey-android/include/jsproxy.h @@ -26,7 +26,9 @@ class JS_FRIEND_API(Wrapper); * * Proxy traps are grouped into fundamental and derived traps. Every proxy has * to at least provide implementations for the fundamental traps, but the - * derived traps can be implemented in terms of the fundamental ones. + * derived traps can be implemented in terms of the fundamental ones + * BaseProxyHandler provides implementations of the derived traps in terms of + * the (pure virtual) fundamental traps. * * To minimize code duplication, a set of abstract proxy handler classes is * provided, from which other handlers may inherit. These abstract classes @@ -34,9 +36,9 @@ class JS_FRIEND_API(Wrapper); * * BaseProxyHandler * | - * IndirectProxyHandler - * | * DirectProxyHandler + * | + * Wrapper */ /* @@ -69,20 +71,6 @@ class JS_FRIEND_API(BaseProxyHandler) { return false; } - /* - * The function Wrapper::wrapperHandler takes a pointer to a - * BaseProxyHandler and returns a pointer to a Wrapper if and only if the - * BaseProxyHandler is a wrapper handler (otherwise, it returns NULL). - * - * Unfortunately, we can't inherit Wrapper from BaseProxyHandler, since that - * would create a dreaded diamond, and we can't use dynamic_cast to cast - * BaseProxyHandler to Wrapper, since that would require us to compile with - * run-time type information. Hence the need for this virtual function. - */ - virtual Wrapper *toWrapper() { - return NULL; - } - /* ES5 Harmony fundamental proxy traps. */ virtual bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, PropertyDescriptor *desc) = 0; @@ -130,17 +118,14 @@ class JS_FRIEND_API(BaseProxyHandler) { }; /* - * IndirectProxyHandler assumes that a target exists. Moreover, it assumes the - * target is a JSObject. Consequently, it provides default implementations for - * the fundamental traps that forward their behavior to the target. The derived - * traps, however, are inherited from BaseProxyHandler, and therefore still - * implemented in terms of the fundamental ones. This allows consumers of this - * class to define custom behavior without implementing the entire gamut of - * proxy traps. + * DirectProxyHandler includes a notion of a target object. All traps are + * reimplemented such that they forward their behavior to the target. This + * allows consumers of this class to forward to another object as transparently + * and efficiently as possible. */ -class JS_PUBLIC_API(IndirectProxyHandler) : public BaseProxyHandler { - public: - explicit IndirectProxyHandler(void *family); +class JS_PUBLIC_API(DirectProxyHandler) : public BaseProxyHandler { +public: + explicit DirectProxyHandler(void *family); /* ES5 Harmony fundamental proxy traps. */ virtual bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, @@ -158,11 +143,21 @@ class JS_PUBLIC_API(IndirectProxyHandler) : public BaseProxyHandler { virtual bool enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props) MOZ_OVERRIDE; + /* ES5 Harmony derived proxy traps. */ + virtual bool has(JSContext *cx, JSObject *proxy, jsid id, + bool *bp) MOZ_OVERRIDE; + virtual bool hasOwn(JSContext *cx, JSObject *proxy, jsid id, + bool *bp) MOZ_OVERRIDE; + virtual bool get(JSContext *cx, JSObject *proxy, JSObject *receiver, + jsid id, Value *vp) MOZ_OVERRIDE; + virtual bool set(JSContext *cx, JSObject *proxy, JSObject *receiver, + jsid id, bool strict, Value *vp) MOZ_OVERRIDE; + virtual bool keys(JSContext *cx, JSObject *proxy, + AutoIdVector &props) MOZ_OVERRIDE; + virtual bool iterate(JSContext *cx, JSObject *proxy, unsigned flags, + Value *vp) MOZ_OVERRIDE; + /* Spidermonkey extensions. */ - virtual bool call(JSContext *cx, JSObject *proxy, unsigned argc, - Value *vp) MOZ_OVERRIDE; - virtual bool construct(JSContext *cx, JSObject *proxy, unsigned argc, - Value *argv, Value *rval) MOZ_OVERRIDE; virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args) MOZ_OVERRIDE; virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, @@ -182,33 +177,6 @@ class JS_PUBLIC_API(IndirectProxyHandler) : public BaseProxyHandler { virtual JSObject *weakmapKeyDelegate(JSObject *proxy); }; -/* - * DirectProxyHandler has the same assumptions about the target as its base, - * IndirectProxyHandler. Its fundamental traps are inherited from this class, - * and therefore forward their behavior to the target. The derived traps, - * however, are overrided so that, they too, forward their behavior to the - * target. This allows consumers of this class to forward to another object as - * transparently as possible. - */ -class JS_PUBLIC_API(DirectProxyHandler) : public IndirectProxyHandler { -public: - explicit DirectProxyHandler(void *family); - - /* ES5 Harmony derived proxy traps. */ - virtual bool has(JSContext *cx, JSObject *proxy, jsid id, - bool *bp) MOZ_OVERRIDE; - virtual bool hasOwn(JSContext *cx, JSObject *proxy, jsid id, - bool *bp) MOZ_OVERRIDE; - virtual bool get(JSContext *cx, JSObject *proxy, JSObject *receiver, - jsid id, Value *vp) MOZ_OVERRIDE; - virtual bool set(JSContext *cx, JSObject *proxy, JSObject *receiver, - jsid id, bool strict, Value *vp) MOZ_OVERRIDE; - virtual bool keys(JSContext *cx, JSObject *proxy, - AutoIdVector &props) MOZ_OVERRIDE; - virtual bool iterate(JSContext *cx, JSObject *proxy, unsigned flags, - Value *vp) MOZ_OVERRIDE; -}; - /* Dispatch point for handlers that executes the appropriate C++ or scripted traps. */ class Proxy { public: @@ -350,13 +318,12 @@ NewProxyObject(JSContext *cx, BaseProxyHandler *handler, const Value &priv, JSObject *proto, JSObject *parent, JSObject *call = NULL, JSObject *construct = NULL); -} /* namespace js */ +JSObject * +RenewProxyObject(JSContext *cx, JSObject *obj, BaseProxyHandler *handler, Value priv); -JS_BEGIN_EXTERN_C +} /* namespace js */ extern JS_FRIEND_API(JSObject *) js_InitProxyClass(JSContext *cx, JSHandleObject obj); -JS_END_EXTERN_C - #endif diff --git a/scripting/javascript/spidermonkey-android/include/jsprvtd.h b/scripting/javascript/spidermonkey-android/include/jsprvtd.h index 2fbfc54aa1..d399b3fd93 100644 --- a/scripting/javascript/spidermonkey-android/include/jsprvtd.h +++ b/scripting/javascript/spidermonkey-android/include/jsprvtd.h @@ -29,8 +29,6 @@ #include "js/Vector.h" #endif -JS_BEGIN_EXTERN_C - /* * Convenience constants. */ @@ -129,7 +127,7 @@ class ScriptFrameIter; class Proxy; class JS_FRIEND_API(BaseProxyHandler); -class JS_FRIEND_API(DirectWrapper); +class JS_FRIEND_API(Wrapper); class JS_FRIEND_API(CrossCompartmentWrapper); class TempAllocPolicy; @@ -379,16 +377,5 @@ typedef JSObject * typedef JSObject * (* JSIteratorOp)(JSContext *cx, JSHandleObject obj, JSBool keysonly); -/* - * The following determines whether JS_EncodeCharacters and JS_DecodeBytes - * treat char[] as utf-8 or simply as bytes that need to be inflated/deflated. - */ -#ifdef JS_C_STRINGS_ARE_UTF8 -# define js_CStringsAreUTF8 JS_TRUE -#else -extern JSBool js_CStringsAreUTF8; -#endif - -JS_END_EXTERN_C #endif /* jsprvtd_h___ */ diff --git a/scripting/javascript/spidermonkey-android/include/jspubtd.h b/scripting/javascript/spidermonkey-android/include/jspubtd.h index c14ef29c24..caf75de0f6 100644 --- a/scripting/javascript/spidermonkey-android/include/jspubtd.h +++ b/scripting/javascript/spidermonkey-android/include/jspubtd.h @@ -39,6 +39,8 @@ namespace JS { class Value; } */ #ifdef __cplusplus +#define JS_NO_JSVAL_JSID_STRUCT_TYPES + # if defined(DEBUG) && !defined(JS_NO_JSVAL_JSID_STRUCT_TYPES) # define JS_USE_JSID_STRUCT_TYPES # endif @@ -60,8 +62,6 @@ typedef ptrdiff_t jsid; # define JSID_BITS(id) (id) #endif -JS_BEGIN_EXTERN_C - #ifdef WIN32 typedef wchar_t jschar; #else @@ -217,8 +217,6 @@ typedef JSBool JSCallOnceType; #endif typedef JSBool (*JSInitCallback)(void); -JS_END_EXTERN_C - #ifdef __cplusplus namespace js { @@ -311,14 +309,6 @@ struct RuntimeFriendFields { /* Limit pointer for checking native stack consumption. */ uintptr_t nativeStackLimit; -#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) - /* - * Stack allocated GC roots for stack GC heap pointers, which may be - * overwritten if moved during a GC. - */ - Rooted *thingGCRooters[THING_ROOT_LIMIT]; -#endif - RuntimeFriendFields() : interrupt(0), nativeStackLimit(0) { } @@ -328,6 +318,32 @@ struct RuntimeFriendFields { } }; +class PerThreadData; + +struct PerThreadDataFriendFields +{ + PerThreadDataFriendFields(); + +#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) + /* + * Stack allocated GC roots for stack GC heap pointers, which may be + * overwritten if moved during a GC. + */ + Rooted *thingGCRooters[THING_ROOT_LIMIT]; +#endif + + static PerThreadDataFriendFields *get(js::PerThreadData *pt) { + return reinterpret_cast(pt); + } + + static PerThreadDataFriendFields *getMainThread(JSRuntime *rt) { + // mainThread must always appear directly after |RuntimeFriendFields|. + // Tested by a JS_STATIC_ASSERT in |jsfriendapi.cpp| + return reinterpret_cast( + reinterpret_cast(rt) + sizeof(RuntimeFriendFields)); + } +}; + } /* namespace js */ #endif /* __cplusplus */ diff --git a/scripting/javascript/spidermonkey-android/include/jstypes.h b/scripting/javascript/spidermonkey-android/include/jstypes.h index d0cf183e0f..90f5d44a64 100644 --- a/scripting/javascript/spidermonkey-android/include/jstypes.h +++ b/scripting/javascript/spidermonkey-android/include/jstypes.h @@ -46,11 +46,11 @@ ** ***********************************************************************/ -#define JS_EXTERN_API(type) extern MOZ_EXPORT_API(type) -#define JS_EXPORT_API(type) MOZ_EXPORT_API(type) -#define JS_EXPORT_DATA(type) MOZ_EXPORT_DATA(type) -#define JS_IMPORT_API(type) MOZ_IMPORT_API(type) -#define JS_IMPORT_DATA(type) MOZ_IMPORT_DATA(type) +#define JS_EXTERN_API(type) extern MOZ_EXPORT type +#define JS_EXPORT_API(type) MOZ_EXPORT type +#define JS_EXPORT_DATA(type) MOZ_EXPORT type +#define JS_IMPORT_API(type) MOZ_IMPORT_API type +#define JS_IMPORT_DATA(type) MOZ_IMPORT_DATA type /* * The linkage of JS API functions differs depending on whether the file is @@ -62,11 +62,11 @@ # define JS_PUBLIC_API(t) t # define JS_PUBLIC_DATA(t) t #elif defined(EXPORT_JS_API) || defined(STATIC_EXPORTABLE_JS_API) -# define JS_PUBLIC_API(t) MOZ_EXPORT_API(t) -# define JS_PUBLIC_DATA(t) MOZ_EXPORT_DATA(t) +# define JS_PUBLIC_API(t) MOZ_EXPORT t +# define JS_PUBLIC_DATA(t) MOZ_EXPORT t #else -# define JS_PUBLIC_API(t) MOZ_IMPORT_API(t) -# define JS_PUBLIC_DATA(t) MOZ_IMPORT_DATA(t) +# define JS_PUBLIC_API(t) MOZ_IMPORT_API t +# define JS_PUBLIC_DATA(t) MOZ_IMPORT_DATA t #endif #define JS_FRIEND_API(t) JS_PUBLIC_API(t) @@ -181,8 +181,6 @@ #endif -JS_BEGIN_EXTERN_C - /************************************************************************ ** TYPES: JSBool ** DESCRIPTION: @@ -282,6 +280,4 @@ typedef int JSBool; # define JS_EXTENSION_(s) s #endif -JS_END_EXTERN_C - #endif /* jstypes_h___ */ diff --git a/scripting/javascript/spidermonkey-android/include/jsutil.h b/scripting/javascript/spidermonkey-android/include/jsutil.h index 9f0c922a23..ef7f94a824 100644 --- a/scripting/javascript/spidermonkey-android/include/jsutil.h +++ b/scripting/javascript/spidermonkey-android/include/jsutil.h @@ -369,23 +369,23 @@ class Compressor z_stream zs; const unsigned char *inp; size_t inplen; + size_t outbytes; + public: - Compressor(const unsigned char *inp, size_t inplen, unsigned char *out) - : inp(inp), - inplen(inplen) - { - JS_ASSERT(inplen > 0); - zs.opaque = NULL; - zs.next_in = (Bytef *)inp; - zs.avail_in = 0; - zs.next_out = out; - zs.avail_out = inplen; - } + enum Status { + MOREOUTPUT, + DONE, + CONTINUE, + OOM + }; + + Compressor(const unsigned char *inp, size_t inplen); + ~Compressor(); bool init(); + void setOutput(unsigned char *out, size_t outlen); + size_t outWritten() const { return outbytes; } /* Compress some of the input. Return true if it should be called again. */ - bool compressMore(); - /* Finalize compression. Return the length of the compressed input. */ - size_t finish(); + Status compressMore(); }; /* diff --git a/scripting/javascript/spidermonkey-android/include/jsval.h b/scripting/javascript/spidermonkey-android/include/jsval.h index 67c8be4252..949127aa0d 100644 --- a/scripting/javascript/spidermonkey-android/include/jsval.h +++ b/scripting/javascript/spidermonkey-android/include/jsval.h @@ -14,8 +14,6 @@ #include "js/Utility.h" -JS_BEGIN_EXTERN_C - /* * Try to get jsvals 64-bit aligned. We could almost assert that all values are * aligned, but MSVC and GCC occasionally break alignment. @@ -835,8 +833,6 @@ JS_CANONICALIZE_NAN(double d) return d; } -JS_END_EXTERN_C - #ifdef __cplusplus static jsval_layout JSVAL_TO_IMPL(JS::Value); static JS::Value IMPL_TO_JSVAL(jsval_layout); diff --git a/scripting/javascript/spidermonkey-android/include/jsversion.h b/scripting/javascript/spidermonkey-android/include/jsversion.h index 5a620c8fcc..475525177a 100644 --- a/scripting/javascript/spidermonkey-android/include/jsversion.h +++ b/scripting/javascript/spidermonkey-android/include/jsversion.h @@ -170,4 +170,7 @@ MOZ_NOT_REACHED("don't call this! to be used in the new object representation") #endif +/* ECMAScript Internationalization API isn't fully implemented yet. */ +#define ENABLE_INTL_API 0 + #endif /* jsversion_h___ */ diff --git a/scripting/javascript/spidermonkey-android/include/jswrapper.h b/scripting/javascript/spidermonkey-android/include/jswrapper.h index 1ec99cb4f8..e5ef59c6c9 100644 --- a/scripting/javascript/spidermonkey-android/include/jswrapper.h +++ b/scripting/javascript/spidermonkey-android/include/jswrapper.h @@ -18,36 +18,24 @@ namespace js { class DummyFrameGuard; /* - * A wrapper is essentially a proxy that restricts access to certain traps. The - * way in which a wrapper restricts access to its traps depends on the - * particular policy for that wrapper. To allow a wrapper's policy to be - * customized, the Wrapper base class contains two functions, enter/leave, which - * are called as a policy enforcement check before/after each trap is forwarded. - * - * To minimize code duplication, a set of abstract wrapper classes is - * provided, from which other wrappers may inherit. These abstract classes are - * organized in the following hierarchy: - * - * BaseProxyHandler Wrapper - * | | | - * IndirectProxyHandler | | - * | | | | - * | IndirectWrapper | - * | | - * DirectProxyHandler | - * | | - * DirectWrapper + * A wrapper is a proxy with a target object to which it generally forwards + * operations, but may restrict access to certain operations or instrument + * the trap operations in various ways. A wrapper is distinct from a Direct Proxy + * Handler in the sense that it can be "unwrapped" in C++, exposing the underlying + * object (Direct Proxy Handlers have an underlying target object, but don't + * expect to expose this object via any kind of unwrapping operation). Callers + * should be careful to avoid unwrapping security wrappers in the wrong context. */ -class JS_FRIEND_API(Wrapper) +class JS_FRIEND_API(Wrapper) : public DirectProxyHandler { unsigned mFlags; + bool mSafeToUnwrap; public: enum Action { GET, SET, - CALL, - PUNCTURE + CALL }; enum Flags { @@ -55,128 +43,42 @@ class JS_FRIEND_API(Wrapper) LAST_USED_FLAG = CROSS_COMPARTMENT }; - typedef enum { - PermitObjectAccess, - PermitPropertyAccess, - DenyAccess - } Permission; + /* + * Wrappers can explicitly specify that they are unsafe to unwrap from a + * security perspective (as is the case for SecurityWrappers). If a wrapper + * is not safe to unwrap, operations requiring full access to the underlying + * object (via UnwrapObjectChecked) will throw. Otherwise, they will succeed. + */ + void setSafeToUnwrap(bool safe) { mSafeToUnwrap = safe; }; + bool isSafeToUnwrap() { return mSafeToUnwrap; }; static JSObject *New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, Wrapper *handler); + static JSObject *Renew(JSContext *cx, JSObject *existing, JSObject *obj, Wrapper *handler); + static Wrapper *wrapperHandler(RawObject wrapper); static JSObject *wrappedObject(RawObject wrapper); - explicit Wrapper(unsigned flags); - unsigned flags() const { return mFlags; } - /* - * The function Wrapper::New takes a pointer to a Wrapper as the handler - * object. It then passes it on to the function NewProxyObject, which - * expects a pointer to a BaseProxyHandler as the handler object. We don't - * want to change Wrapper::New to take a pointer to a BaseProxyHandler, - * because that would allow the creation of wrappers with non-wrapper - * handlers. Unfortunately, we can't inherit Wrapper from BaseProxyHandler, - * since that would create a dreaded diamond, and we can't use dynamic_cast - * to cast Wrapper to BaseProxyHandler, since that would require us to - * compile with run time type information. Hence the need for this virtual - * function. - */ - virtual BaseProxyHandler *toBaseProxyHandler() = 0; - /* Policy enforcement traps. * * enter() allows the policy to specify whether the caller may perform |act| * on the underlying object's |id| property. In the case when |act| is CALL, * |id| is generally JSID_VOID. * - * The |act| parameter to enter() specifies the action being performed. GET, - * SET, and CALL are self-explanatory, but PUNCTURE requires more - * explanation: - * - * GET and SET allow for a very fine-grained security membrane, through - * which access can be granted or denied on a per-property, per-object, and - * per-action basis. Sometimes though, we just want to asks if we can access - * _everything_ behind the wrapper barrier. For example, when the structured - * clone algorithm runs up against a cross-compartment wrapper, it needs to - * know whether it can enter the compartment and keep cloning, or whether it - * should throw. This is the role of PUNCTURE. - * - * PUNCTURE allows the policy to specify whether the wrapper barrier may - * be lifted - that is to say, whether the caller is allowed to access - * anything that the wrapped object could access. This is a very powerful - * permission, and thus should generally be denied for security wrappers - * except under very special circumstances. When |act| is PUNCTURE, |id| - * should be JSID_VOID. + * The |act| parameter to enter() specifies the action being performed. */ virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, Action act, bool *bp); -}; -/* - * IndirectWrapper forwards its traps by forwarding them to - * IndirectProxyHandler. In effect, IndirectWrapper behaves the same as - * IndirectProxyHandler, except that it adds policy enforcement checks to each - * fundamental trap. - */ -class JS_FRIEND_API(IndirectWrapper) : public Wrapper, - public IndirectProxyHandler -{ - public: - explicit IndirectWrapper(unsigned flags); + explicit Wrapper(unsigned flags, bool hasPrototype = false); - virtual BaseProxyHandler* toBaseProxyHandler() { - return this; - } - - virtual Wrapper *toWrapper() { - return this; - } - - /* ES5 Harmony fundamental wrapper traps. */ - virtual bool getPropertyDescriptor(JSContext *cx, JSObject *wrapper, - jsid id, bool set, - PropertyDescriptor *desc) MOZ_OVERRIDE; - virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, - jsid id, bool set, - PropertyDescriptor *desc) MOZ_OVERRIDE; - virtual bool defineProperty(JSContext *cx, JSObject *wrapper, jsid id, - PropertyDescriptor *desc) MOZ_OVERRIDE; - virtual bool getOwnPropertyNames(JSContext *cx, JSObject *wrapper, - AutoIdVector &props) MOZ_OVERRIDE; - virtual bool delete_(JSContext *cx, JSObject *wrapper, jsid id, - bool *bp) MOZ_OVERRIDE; - virtual bool enumerate(JSContext *cx, JSObject *wrapper, - AutoIdVector &props) MOZ_OVERRIDE; - - /* Spidermonkey extensions. */ - virtual bool defaultValue(JSContext *cx, JSObject *wrapper_, JSType hint, - Value *vp) MOZ_OVERRIDE; -}; - -/* - * DirectWrapper forwards its traps by forwarding them to DirectProxyHandler. - * In effect, DirectWrapper behaves the same as DirectProxyHandler, except that - * it adds policy enforcement checks to each trap. - */ -class JS_FRIEND_API(DirectWrapper) : public Wrapper, public DirectProxyHandler -{ - public: - explicit DirectWrapper(unsigned flags, bool hasPrototype = false); - - virtual ~DirectWrapper(); - - virtual BaseProxyHandler* toBaseProxyHandler() { - return this; - } - - virtual Wrapper *toWrapper() { - return this; - } + virtual ~Wrapper(); /* ES5 Harmony fundamental wrapper traps. */ virtual bool getPropertyDescriptor(JSContext *cx, JSObject *wrapper, @@ -214,14 +116,14 @@ class JS_FRIEND_API(DirectWrapper) : public Wrapper, public DirectProxyHandler virtual bool defaultValue(JSContext *cx, JSObject *wrapper_, JSType hint, Value *vp) MOZ_OVERRIDE; - static DirectWrapper singleton; - static DirectWrapper singletonWithPrototype; + static Wrapper singleton; + static Wrapper singletonWithPrototype; static void *getWrapperFamily(); }; /* Base class for all cross compartment wrapper handlers. */ -class JS_FRIEND_API(CrossCompartmentWrapper) : public DirectWrapper +class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper { public: CrossCompartmentWrapper(unsigned flags, bool hasPrototype = false); @@ -280,13 +182,22 @@ class JS_FRIEND_API(SecurityWrapper) : public Base public: SecurityWrapper(unsigned flags); + virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, Wrapper::Action act, + bool *bp) MOZ_OVERRIDE; virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args) MOZ_OVERRIDE; virtual bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx) MOZ_OVERRIDE; virtual bool regexp_toShared(JSContext *cx, JSObject *proxy, RegExpGuard *g) MOZ_OVERRIDE; + + /* + * Allow our subclasses to select the superclass behavior they want without + * needing to specify an exact superclass. + */ + typedef Base Permissive; + typedef SecurityWrapper Restrictive; }; -typedef SecurityWrapper SameCompartmentSecurityWrapper; +typedef SecurityWrapper SameCompartmentSecurityWrapper; typedef SecurityWrapper CrossCompartmentSecurityWrapper; class JS_FRIEND_API(DeadObjectProxy) : public BaseProxyHandler @@ -327,7 +238,8 @@ class JS_FRIEND_API(DeadObjectProxy) : public BaseProxyHandler }; extern JSObject * -TransparentObjectWrapper(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSObject *parent, +TransparentObjectWrapper(JSContext *cx, JSObject *existing, JSObject *obj, + JSObject *wrappedProto, JSObject *parent, unsigned flags); // Proxy family for wrappers. Public so that IsWrapper() can be fully inlined by @@ -352,16 +264,19 @@ UnwrapObject(JSObject *obj, bool stopAtOuter = true, unsigned *flagsp = NULL); // code should never be unwrapping outer window wrappers, we always stop at // outer windows. JS_FRIEND_API(JSObject *) -UnwrapObjectChecked(JSContext *cx, RawObject obj); +UnwrapObjectChecked(RawObject obj); // Unwrap only the outermost security wrapper, with the same semantics as // above. This is the checked version of Wrapper::wrappedObject. JS_FRIEND_API(JSObject *) -UnwrapOneChecked(JSContext *cx, HandleObject obj); +UnwrapOneChecked(RawObject obj); JS_FRIEND_API(bool) IsCrossCompartmentWrapper(RawObject obj); +bool +IsDeadProxyObject(RawObject obj); + JSObject * NewDeadProxyObject(JSContext *cx, JSObject *parent); @@ -381,6 +296,34 @@ JS_FRIEND_API(bool) RecomputeWrappers(JSContext *cx, const CompartmentFilter &sourceFilter, const CompartmentFilter &targetFilter); +/* + * This auto class should be used around any code, such as brain transplants, + * that may touch dead compartments. Brain transplants can cause problems + * because they operate on all compartments, whether live or dead. A brain + * transplant can cause a formerly dead object to be "reanimated" by causing a + * read or write barrier to be invoked on it during the transplant. In this way, + * a compartment becomes a zombie, kept alive by repeatedly consuming + * (transplanted) brains. + * + * To work around this issue, we observe when mark bits are set on objects in + * dead compartments. If this happens during a brain transplant, we do a full, + * non-incremental GC at the end of the brain transplant. This will clean up any + * objects that were improperly marked. + */ +struct JS_FRIEND_API(AutoMaybeTouchDeadCompartments) +{ + // The version that takes an object just uses it for its runtime. + AutoMaybeTouchDeadCompartments(JSContext *cx); + AutoMaybeTouchDeadCompartments(JSObject *obj); + ~AutoMaybeTouchDeadCompartments(); + + private: + JSRuntime *runtime; + unsigned markCount; + bool inIncremental; + bool manipulatingDeadCompartments; +}; + } /* namespace js */ #endif diff --git a/scripting/javascript/spidermonkey-android/include/mozilla/Attributes.h b/scripting/javascript/spidermonkey-android/include/mozilla/Attributes.h index 6b4e81612c..ca990be15a 100644 --- a/scripting/javascript/spidermonkey-android/include/mozilla/Attributes.h +++ b/scripting/javascript/spidermonkey-android/include/mozilla/Attributes.h @@ -93,6 +93,8 @@ # endif # if __GNUC_MINOR__ >= 4 # define MOZ_HAVE_CXX11_DELETE +# endif +# if __GNUC_MINOR__ >= 5 # define MOZ_HAVE_CXX11_ENUM_TYPE # define MOZ_HAVE_CXX11_STRONG_ENUMS # endif @@ -356,6 +358,9 @@ * supported, as with MOZ_ENUM_TYPE(). For simplicity, it is currently * mandatory. As with MOZ_ENUM_TYPE(), it will do nothing on compilers that do * not support it. + * + * Note that the workaround implemented here is not compatible with enums + * nested inside a class. */ #if defined(MOZ_HAVE_CXX11_STRONG_ENUMS) /* All compilers that support strong enums also support an explicit diff --git a/scripting/javascript/spidermonkey-android/include/mozilla/BloomFilter.h b/scripting/javascript/spidermonkey-android/include/mozilla/BloomFilter.h index 9effa1756c..8680ef2907 100644 --- a/scripting/javascript/spidermonkey-android/include/mozilla/BloomFilter.h +++ b/scripting/javascript/spidermonkey-android/include/mozilla/BloomFilter.h @@ -13,6 +13,7 @@ #ifndef mozilla_BloomFilter_h_ #define mozilla_BloomFilter_h_ +#include "mozilla/Assertions.h" #include "mozilla/Likely.h" #include "mozilla/StandardInteger.h" #include "mozilla/Util.h" diff --git a/scripting/javascript/spidermonkey-android/include/mozilla/CheckedInt.h b/scripting/javascript/spidermonkey-android/include/mozilla/CheckedInt.h index 790fc6eabe..b56ac42b19 100644 --- a/scripting/javascript/spidermonkey-android/include/mozilla/CheckedInt.h +++ b/scripting/javascript/spidermonkey-android/include/mozilla/CheckedInt.h @@ -385,13 +385,13 @@ IsSubValid(T x, T y) } template::value, + bool IsTSigned = IsSigned::value, bool TwiceBiggerTypeIsSupported = IsSupported::Type>::value> struct IsMulValidImpl {}; -template -struct IsMulValidImpl +template +struct IsMulValidImpl { static bool run(T x, T y) { @@ -451,7 +451,7 @@ IsDivValid(T x, T y) } // This is just to shut up msvc warnings about negating unsigned ints. -template::value> +template::value> struct OppositeIfSignedImpl { static T run(T x) { return -x; } diff --git a/scripting/javascript/spidermonkey-android/include/mozilla/EnumSet.h b/scripting/javascript/spidermonkey-android/include/mozilla/EnumSet.h new file mode 100644 index 0000000000..b18b005669 --- /dev/null +++ b/scripting/javascript/spidermonkey-android/include/mozilla/EnumSet.h @@ -0,0 +1,175 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* A set abstraction for enumeration values. */ + +#ifndef mozilla_EnumSet_h +#define mozilla_EnumSet_h + +#include "mozilla/Assertions.h" +#include "mozilla/StandardInteger.h" + +namespace mozilla { + +/** + * EnumSet is a set of values defined by an enumeration. It is implemented + * using a 32 bit mask for each value so it will only work for enums with an int + * representation less than 32. It works both for enum and enum class types. + */ +template +class EnumSet +{ + public: + EnumSet() + : mBitField(0) + { } + + EnumSet(T aEnum) + : mBitField(aEnum) + { } + + EnumSet(T aEnum1, T aEnum2) + : mBitField(bitFor(aEnum1) | + bitFor(aEnum2)) + { } + + EnumSet(T aEnum1, T aEnum2, T aEnum3) + : mBitField(bitFor(aEnum1) | + bitFor(aEnum2) | + bitFor(aEnum3)) + { } + + EnumSet(T aEnum1, T aEnum2, T aEnum3, T aEnum4) + : mBitField(bitFor(aEnum1) | + bitFor(aEnum2) | + bitFor(aEnum3) | + bitFor(aEnum4)) + { } + + EnumSet(const EnumSet& aEnumSet) + : mBitField(aEnumSet.mBitField) + { } + + /** + * Add an element + */ + void operator+=(T aEnum) { + mBitField |= bitFor(aEnum); + } + + /** + * Add an element + */ + EnumSet operator+(T aEnum) const { + EnumSet result(*this); + result += aEnum; + return result; + } + + /** + * Union + */ + void operator+=(const EnumSet aEnumSet) { + mBitField |= aEnumSet.mBitField; + } + + /** + * Union + */ + EnumSet operator+(const EnumSet aEnumSet) const { + EnumSet result(*this); + result += aEnumSet; + return result; + } + + /** + * Remove an element + */ + void operator-=(T aEnum) { + mBitField &= ~(bitFor(aEnum)); + } + + /** + * Remove an element + */ + EnumSet operator-(T aEnum) const { + EnumSet result(*this); + result -= aEnum; + return result; + } + + /** + * Remove a set of elements + */ + void operator-=(const EnumSet aEnumSet) { + mBitField &= ~(aEnumSet.mBitField); + } + + /** + * Remove a set of elements + */ + EnumSet operator-(const EnumSet aEnumSet) const { + EnumSet result(*this); + result -= aEnumSet; + return result; + } + + /** + * Intersection + */ + void operator&=(const EnumSet aEnumSet) { + mBitField &= aEnumSet.mBitField; + } + + /** + * Intersection + */ + EnumSet operator&(const EnumSet aEnumSet) const { + EnumSet result(*this); + result &= aEnumSet; + return result; + } + + /** + * Equality + */ + + bool operator==(const EnumSet aEnumSet) const { + return mBitField == aEnumSet.mBitField; + } + + /** + * Test is an element is contained in the set + */ + bool contains(T aEnum) const { + return mBitField & bitFor(aEnum); + } + + /** + * Return the number of elements in the set + */ + + uint8_t size() { + uint8_t count = 0; + for (uint32_t bitField = mBitField; bitField; bitField >>= 1) { + if (bitField & 1) + count++; + } + return count; + } + + private: + static uint32_t bitFor(T aEnum) { + uint32_t bitNumber(aEnum); + MOZ_ASSERT(bitNumber < 32); + return 1U << bitNumber; + } + + uint32_t mBitField; +}; + +} // namespace mozilla + +#endif // mozilla_EnumSet_h_ diff --git a/scripting/javascript/spidermonkey-android/include/mozilla/GuardObjects.h b/scripting/javascript/spidermonkey-android/include/mozilla/GuardObjects.h index 95aa37a19d..e3297c8fcf 100644 --- a/scripting/javascript/spidermonkey-android/include/mozilla/GuardObjects.h +++ b/scripting/javascript/spidermonkey-android/include/mozilla/GuardObjects.h @@ -66,7 +66,7 @@ namespace detail { * For more details, and examples of using these macros, see * https://developer.mozilla.org/en/Using_RAII_classes_in_Mozilla */ -class MOZ_EXPORT_API(GuardObjectNotifier) +class MOZ_EXPORT GuardObjectNotifier { private: bool* statementDone; @@ -83,7 +83,7 @@ class MOZ_EXPORT_API(GuardObjectNotifier) } }; -class MOZ_EXPORT_API(GuardObjectNotificationReceiver) +class MOZ_EXPORT GuardObjectNotificationReceiver { private: bool statementDone; diff --git a/scripting/javascript/spidermonkey-android/include/mozilla/HashFunctions.h b/scripting/javascript/spidermonkey-android/include/mozilla/HashFunctions.h index badfc3c808..96242b629a 100644 --- a/scripting/javascript/spidermonkey-android/include/mozilla/HashFunctions.h +++ b/scripting/javascript/spidermonkey-android/include/mozilla/HashFunctions.h @@ -351,7 +351,7 @@ HashString(const wchar_t* str, size_t length) * same result out of HashBytes as you would out of HashString. */ MOZ_WARN_UNUSED_RESULT -extern MFBT_API(uint32_t) +extern MFBT_API uint32_t HashBytes(const void* bytes, size_t length); } /* namespace mozilla */ diff --git a/scripting/javascript/spidermonkey-android/include/mozilla/LinkedList.h b/scripting/javascript/spidermonkey-android/include/mozilla/LinkedList.h index d7d3b23607..5cfd60e4ac 100644 --- a/scripting/javascript/spidermonkey-android/include/mozilla/LinkedList.h +++ b/scripting/javascript/spidermonkey-android/include/mozilla/LinkedList.h @@ -13,6 +13,10 @@ * LinkedListElement. A given object may be in only one linked list at a * time. * + * A LinkedListElement automatically removes itself from the list upon + * destruction, and a LinkedList will fatally assert in debug builds if it's + * non-empty when it's destructed. + * * For example, you might use LinkedList in a simple observer list class as * follows. * @@ -36,6 +40,8 @@ * void removeObserver(Observer* observer) { * // Will assert if |observer| is not part of some list. * observer.remove(); + * // Or, will assert if |observer| is not part of |list| specifically. + * // observer.removeFrom(list); * } * * void notifyObservers(char* topic) { @@ -101,8 +107,19 @@ class LinkedListElement LinkedListElement* prev; const bool isSentinel; + LinkedListElement* thisDuringConstruction() { return this; } + public: - LinkedListElement() : next(this), prev(this), isSentinel(false) { } + LinkedListElement() + : next(thisDuringConstruction()), + prev(thisDuringConstruction()), + isSentinel(false) + { } + + ~LinkedListElement() { + if (!isSentinel && isInList()) + remove(); + } /* * Get the next element in the list, or NULL if this is the last element in @@ -158,6 +175,15 @@ class LinkedListElement prev = this; } + /* + * Identical to remove(), but also asserts in debug builds that this element + * is in list. + */ + void removeFrom(const LinkedList& list) { + list.assertContains(asT()); + remove(); + } + /* * Return true if |this| part is of a linked list, and false otherwise. */ @@ -175,11 +201,10 @@ class LinkedListElement }; LinkedListElement(NodeKind nodeKind) - : next(this), - prev(this), + : next(thisDuringConstruction()), + prev(thisDuringConstruction()), isSentinel(nodeKind == NODE_KIND_SENTINEL) - { - } + { } /* * Return |this| cast to T* if we're a normal node, or return NULL if we're @@ -240,6 +265,10 @@ class LinkedList public: LinkedList() : sentinel(LinkedListElement::NODE_KIND_SENTINEL) { } + ~LinkedList() { + MOZ_ASSERT(isEmpty()); + } + /* * Add elem to the front of the list. */ @@ -375,6 +404,21 @@ class LinkedList } private: + friend class LinkedListElement; + + void assertContains(const T* t) const { +#ifdef DEBUG + for (const T* elem = getFirst(); + elem; + elem = elem->getNext()) + { + if (elem == t) + return; + } + MOZ_NOT_REACHED("element wasn't found in this list!"); +#endif + } + LinkedList& operator=(const LinkedList& other) MOZ_DELETE; LinkedList(const LinkedList& other) MOZ_DELETE; }; diff --git a/scripting/javascript/spidermonkey-android/include/mozilla/NullPtr.h b/scripting/javascript/spidermonkey-android/include/mozilla/NullPtr.h index e6fc892759..c2ea7dd91f 100644 --- a/scripting/javascript/spidermonkey-android/include/mozilla/NullPtr.h +++ b/scripting/javascript/spidermonkey-android/include/mozilla/NullPtr.h @@ -20,7 +20,7 @@ # endif #elif defined(__GNUC__) # if defined(_GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L -# if (__GNUC__ * 1000 + __GNU_MINOR__) >= 4006 +# if (__GNUC__ * 1000 + __GNUC_MINOR__) >= 4006 # define MOZ_HAVE_CXX11_NULLPTR # endif # endif diff --git a/scripting/javascript/spidermonkey-android/include/mozilla/RangedPtr.h b/scripting/javascript/spidermonkey-android/include/mozilla/RangedPtr.h index 7c8d58147d..adecf7c1a0 100644 --- a/scripting/javascript/spidermonkey-android/include/mozilla/RangedPtr.h +++ b/scripting/javascript/spidermonkey-android/include/mozilla/RangedPtr.h @@ -128,13 +128,13 @@ class RangedPtr RangedPtr operator+(size_t inc) { MOZ_ASSERT(inc <= size_t(-1) / sizeof(T)); - MOZ_ASSERT(ptr + inc > ptr); + MOZ_ASSERT(ptr + inc >= ptr); return create(ptr + inc); } RangedPtr operator-(size_t dec) { MOZ_ASSERT(dec <= size_t(-1) / sizeof(T)); - MOZ_ASSERT(ptr - dec < ptr); + MOZ_ASSERT(ptr - dec <= ptr); return create(ptr - dec); } diff --git a/scripting/javascript/spidermonkey-android/include/mozilla/SHA1.h b/scripting/javascript/spidermonkey-android/include/mozilla/SHA1.h index 510ef75f0f..a6604e699f 100644 --- a/scripting/javascript/spidermonkey-android/include/mozilla/SHA1.h +++ b/scripting/javascript/spidermonkey-android/include/mozilla/SHA1.h @@ -5,44 +5,57 @@ /* Simple class for computing SHA1. */ -/* - * To compute the SHA1 of a buffer using this class you should write something - * like: - * void SHA1(const uint8_t* buf, unsigned size, uint8_t hash[20]) - * { - * SHA1Sum S; - * S.update(buf, size); - * S.finish(hash); - * } - * If there are multiple buffers or chunks, the update method can be called - * multiple times and the SHA1 is computed on the concatenation of all the - * buffers passed to it. - * The finish method may only be called once and cannot be followed by calls - * to update. - */ - #ifndef mozilla_SHA1_h_ #define mozilla_SHA1_h_ #include "mozilla/StandardInteger.h" #include "mozilla/Types.h" -namespace mozilla { -class SHA1Sum { - union { - uint32_t w[16]; /* input buffer */ - uint8_t b[64]; - } u; - uint64_t size; /* count of hashed bytes. */ - unsigned H[22]; /* 5 state variables, 16 tmp values, 1 extra */ - bool mDone; +#include -public: - static const unsigned int HashSize = 20; - MFBT_API() SHA1Sum(); - MFBT_API(void) update(const void* dataIn, uint32_t len); - MFBT_API(void) finish(uint8_t hashout[20]); +namespace mozilla { + +/** + * This class computes the SHA1 hash of a byte sequence, or of the concatenation + * of multiple sequences. For example, computing the SHA1 of two sequences of + * bytes could be done as follows: + * + * void SHA1(const uint8_t* buf1, uint32_t size1, + * const uint8_t* buf2, uint32_t size2, + * SHA1Sum::Hash& hash) + * { + * SHA1Sum s; + * s.update(buf1, size1); + * s.update(buf2, size2); + * s.finish(hash); + * } + * + * The finish method may only be called once and cannot be followed by calls + * to update. + */ +class SHA1Sum +{ + union { + uint32_t w[16]; /* input buffer */ + uint8_t b[64]; + } u; + uint64_t size; /* count of hashed bytes. */ + unsigned H[22]; /* 5 state variables, 16 tmp values, 1 extra */ + bool mDone; + + public: + MFBT_API SHA1Sum(); + + static const size_t HashSize = 20; + typedef uint8_t Hash[HashSize]; + + /* Add len bytes of dataIn to the data sequence being hashed. */ + MFBT_API void update(const void* dataIn, uint32_t len); + + /* Compute the final hash of all data into hashOut. */ + MFBT_API void finish(SHA1Sum::Hash& hashOut); }; -} + +} /* namespace mozilla */ #endif /* mozilla_SHA1_h_ */ diff --git a/scripting/javascript/spidermonkey-android/include/mozilla/TypeTraits.h b/scripting/javascript/spidermonkey-android/include/mozilla/TypeTraits.h index 8f04e7a4ac..fda156496e 100644 --- a/scripting/javascript/spidermonkey-android/include/mozilla/TypeTraits.h +++ b/scripting/javascript/spidermonkey-android/include/mozilla/TypeTraits.h @@ -9,6 +9,25 @@ namespace mozilla { +namespace detail { + +/** + * The trickery used to implement IsBaseOf here makes it possible to use it for + * the cases of private and multiple inheritance. This code was inspired by the + * sample code here: + * + * http://stackoverflow.com/questions/2910979/how-is-base-of-works + */ +template +class IsBaseOfHelper +{ + public: + operator Base*() const; + operator Derived*(); +}; + +} /* namespace detail */ + /* * IsBaseOf allows to know whether a given class is derived from another. * @@ -25,12 +44,47 @@ template class IsBaseOf { private: - static char test(Base* b); - static int test(...); + template + static char test(Derived*, T); + static int test(Base*, int); public: static const bool value = - sizeof(test(static_cast(0))) == sizeof(char); + sizeof(test(detail::IsBaseOfHelper(), int())) == sizeof(char); +}; + +template +class IsBaseOf +{ + private: + template + static char test(Derived*, T); + static int test(Base*, int); + + public: + static const bool value = + sizeof(test(detail::IsBaseOfHelper(), int())) == sizeof(char); +}; + +template +class IsBaseOf +{ + public: + static const bool value = false; +}; + +template +class IsBaseOf +{ + public: + static const bool value = true; +}; + +template +class IsBaseOf +{ + public: + static const bool value = true; }; /* diff --git a/scripting/javascript/spidermonkey-android/include/mozilla/Types.h b/scripting/javascript/spidermonkey-android/include/mozilla/Types.h index f803586ec1..56e5cb82fb 100644 --- a/scripting/javascript/spidermonkey-android/include/mozilla/Types.h +++ b/scripting/javascript/spidermonkey-android/include/mozilla/Types.h @@ -27,63 +27,60 @@ /* Implement compiler and linker macros needed for APIs. */ /* - * MOZ_EXPORT_API is used to declare and define a method which is externally + * MOZ_EXPORT is used to declare and define a symbol or type which is externally * visible to users of the current library. It encapsulates various decorations - * needed to properly export the method's symbol. MOZ_EXPORT_DATA serves the - * same purpose for data. + * needed to properly export the method's symbol. * * api.h: - * extern MOZ_EXPORT_API(int) MeaningOfLife(void); - * extern MOZ_EXPORT_DATA(int) LuggageCombination; + * extern MOZ_EXPORT int MeaningOfLife(void); + * extern MOZ_EXPORT int LuggageCombination; * * api.c: - * MOZ_EXPORT_API(int) MeaningOfLife(void) { return 42; } - * MOZ_EXPORT_DATA(int) LuggageCombination = 12345; + * int MeaningOfLife(void) { return 42; } + * int LuggageCombination = 12345; * * If you are merely sharing a method across files, just use plain |extern|. * These macros are designed for use by library interfaces -- not for normal * methods or data used cross-file. */ #if defined(WIN32) || defined(XP_OS2) -# define MOZ_EXPORT_API(type) __declspec(dllexport) type -# define MOZ_EXPORT_DATA(type) __declspec(dllexport) type +# define MOZ_EXPORT __declspec(dllexport) #else /* Unix */ # ifdef HAVE_VISIBILITY_ATTRIBUTE -# define MOZ_EXTERNAL_VIS __attribute__((visibility("default"))) +# define MOZ_EXPORT __attribute__((visibility("default"))) # elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) -# define MOZ_EXTERNAL_VIS __global +# define MOZ_EXPORT __global # else -# define MOZ_EXTERNAL_VIS +# define MOZ_EXPORT /* nothing */ # endif -# define MOZ_EXPORT_API(type) MOZ_EXTERNAL_VIS type -# define MOZ_EXPORT_DATA(type) MOZ_EXTERNAL_VIS type #endif + /* - * Whereas implementers use MOZ_EXPORT_API and MOZ_EXPORT_DATA to declare and - * define library symbols, users use MOZ_IMPORT_API and MOZ_IMPORT_DATA to - * access them. Most often the implementer of the library will expose an API - * macro which expands to either the export or import version of the macro, - * depending upon the compilation mode. + * Whereas implementers use MOZ_EXPORT to declare and define library symbols, + * users use MOZ_IMPORT_API and MOZ_IMPORT_DATA to access them. Most often the + * implementer of the library will expose an API macro which expands to either + * the export or import version of the macro, depending upon the compilation + * mode. */ #ifdef _WIN32 # if defined(__MWERKS__) -# define MOZ_IMPORT_API(x) x +# define MOZ_IMPORT_API /* nothing */ # else -# define MOZ_IMPORT_API(x) __declspec(dllimport) x +# define MOZ_IMPORT_API __declspec(dllimport) # endif #elif defined(XP_OS2) -# define MOZ_IMPORT_API(x) __declspec(dllimport) x +# define MOZ_IMPORT_API __declspec(dllimport) #else -# define MOZ_IMPORT_API(x) MOZ_EXPORT_API(x) +# define MOZ_IMPORT_API MOZ_EXPORT #endif #if defined(_WIN32) && !defined(__MWERKS__) -# define MOZ_IMPORT_DATA(x) __declspec(dllimport) x +# define MOZ_IMPORT_DATA __declspec(dllimport) #elif defined(XP_OS2) -# define MOZ_IMPORT_DATA(x) __declspec(dllimport) x +# define MOZ_IMPORT_DATA __declspec(dllimport) #else -# define MOZ_IMPORT_DATA(x) MOZ_EXPORT_DATA(x) +# define MOZ_IMPORT_DATA MOZ_EXPORT #endif /* @@ -92,19 +89,22 @@ * declarations when using mfbt. */ #if defined(IMPL_MFBT) -# define MFBT_API(type) MOZ_EXPORT_API(type) -# define MFBT_DATA(type) MOZ_EXPORT_DATA(type) +# define MFBT_API MOZ_EXPORT +# define MFBT_DATA MOZ_EXPORT #else /* - * When mozglue is linked in the program, we need the MFBT API symbols - * to be weak. + * On linux mozglue is linked in the program and we link libxul.so with + * -z,defs. Normally that causes the linker to reject undefined references in + * libxul.so, but as a loophole it allows undefined references to weak + * symbols. We add the weak attribute to the import version of the MFBT API + * macros to exploit this. */ # if defined(MOZ_GLUE_IN_PROGRAM) -# define MFBT_API(type) __attribute__((weak)) MOZ_IMPORT_API(type) -# define MFBT_DATA(type) __attribute__((weak)) MOZ_IMPORT_DATA(type) +# define MFBT_API __attribute__((weak)) MOZ_IMPORT_API +# define MFBT_DATA __attribute__((weak)) MOZ_IMPORT_DATA # else -# define MFBT_API(type) MOZ_IMPORT_API(type) -# define MFBT_DATA(type) MOZ_IMPORT_DATA(type) +# define MFBT_API MOZ_IMPORT_API +# define MFBT_DATA MOZ_IMPORT_DATA # endif #endif @@ -117,7 +117,7 @@ * * MOZ_BEGIN_EXTERN_C * - * extern MOZ_EXPORT_API(int) MostRandomNumber(void); + * extern MOZ_EXPORT int MostRandomNumber(void); * ...other declarations... * * MOZ_END_EXTERN_C diff --git a/scripting/javascript/spidermonkey-android/include/mozilla/WeakPtr.h b/scripting/javascript/spidermonkey-android/include/mozilla/WeakPtr.h index e20767141e..721c28e48d 100644 --- a/scripting/javascript/spidermonkey-android/include/mozilla/WeakPtr.h +++ b/scripting/javascript/spidermonkey-android/include/mozilla/WeakPtr.h @@ -126,6 +126,10 @@ class WeakPtr return ref->get(); } + T* get() const { + return ref->get(); + } + private: friend class SupportsWeakPtr; diff --git a/scripting/javascript/spidermonkey-android/lib/armeabi-v7a/libjs_static.a.REMOVED.git-id b/scripting/javascript/spidermonkey-android/lib/armeabi-v7a/libjs_static.a.REMOVED.git-id index 6c0b5ca55c..0383ac1365 100644 --- a/scripting/javascript/spidermonkey-android/lib/armeabi-v7a/libjs_static.a.REMOVED.git-id +++ b/scripting/javascript/spidermonkey-android/lib/armeabi-v7a/libjs_static.a.REMOVED.git-id @@ -1 +1 @@ -0b43ca82a2e3c648187f17aa0ea261a8d98ac2a5 \ No newline at end of file +50e1e403ec323943f3ab7274127546140d2d5d0a \ No newline at end of file diff --git a/scripting/javascript/spidermonkey-android/lib/armeabi/libjs_static.a.REMOVED.git-id b/scripting/javascript/spidermonkey-android/lib/armeabi/libjs_static.a.REMOVED.git-id index 2131b3d26c..351f603f35 100644 --- a/scripting/javascript/spidermonkey-android/lib/armeabi/libjs_static.a.REMOVED.git-id +++ b/scripting/javascript/spidermonkey-android/lib/armeabi/libjs_static.a.REMOVED.git-id @@ -1 +1 @@ -04eff728bf10911147107d4237d102d9b96067e7 \ No newline at end of file +60e397be69b5e1699754e996d4392c0e1b8a154c \ No newline at end of file diff --git a/scripting/javascript/spidermonkey-ios/include/gc/Barrier.h b/scripting/javascript/spidermonkey-ios/include/gc/Barrier.h deleted file mode 100644 index 443cddb7a3..0000000000 --- a/scripting/javascript/spidermonkey-ios/include/gc/Barrier.h +++ /dev/null @@ -1,659 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef jsgc_barrier_h___ -#define jsgc_barrier_h___ - -#include "jsapi.h" - -#include "gc/Heap.h" -#include "gc/Root.h" -#include "js/HashTable.h" - -/* - * A write barrier is a mechanism used by incremental or generation GCs to - * ensure that every value that needs to be marked is marked. In general, the - * write barrier should be invoked whenever a write can cause the set of things - * traced through by the GC to change. This includes: - * - writes to object properties - * - writes to array slots - * - writes to fields like JSObject::shape_ that we trace through - * - writes to fields in private data, like JSGenerator::obj - * - writes to non-markable fields like JSObject::private that point to - * markable data - * The last category is the trickiest. Even though the private pointers does not - * point to a GC thing, changing the private pointer may change the set of - * objects that are traced by the GC. Therefore it needs a write barrier. - * - * Every barriered write should have the following form: - * - * obj->field = value; // do the actual write - * - * The pre-barrier is used for incremental GC and the post-barrier is for - * generational GC. - * - * PRE-BARRIER - * - * To understand the pre-barrier, let's consider how incremental GC works. The - * GC itself is divided into "slices". Between each slice, JS code is allowed to - * run. Each slice should be short so that the user doesn't notice the - * interruptions. In our GC, the structure of the slices is as follows: - * - * 1. ... JS work, which leads to a request to do GC ... - * 2. [first GC slice, which performs all root marking and possibly more marking] - * 3. ... more JS work is allowed to run ... - * 4. [GC mark slice, which runs entirely in drainMarkStack] - * 5. ... more JS work ... - * 6. [GC mark slice, which runs entirely in drainMarkStack] - * 7. ... more JS work ... - * 8. [GC marking finishes; sweeping done non-incrementally; GC is done] - * 9. ... JS continues uninterrupted now that GC is finishes ... - * - * Of course, there may be a different number of slices depending on how much - * marking is to be done. - * - * The danger inherent in this scheme is that the JS code in steps 3, 5, and 7 - * might change the heap in a way that causes the GC to collect an object that - * is actually reachable. The write barrier prevents this from happening. We use - * a variant of incremental GC called "snapshot at the beginning." This approach - * guarantees the invariant that if an object is reachable in step 2, then we - * will mark it eventually. The name comes from the idea that we take a - * theoretical "snapshot" of all reachable objects in step 2; all objects in - * that snapshot should eventually be marked. (Note that the write barrier - * verifier code takes an actual snapshot.) - * - * The basic correctness invariant of a snapshot-at-the-beginning collector is - * that any object reachable at the end of the GC (step 9) must either: - * (1) have been reachable at the beginning (step 2) and thus in the snapshot - * (2) or must have been newly allocated, in steps 3, 5, or 7. - * To deal with case (2), any objects allocated during an incremental GC are - * automatically marked black. - * - * This strategy is actually somewhat conservative: if an object becomes - * unreachable between steps 2 and 8, it would be safe to collect it. We won't, - * mainly for simplicity. (Also, note that the snapshot is entirely - * theoretical. We don't actually do anything special in step 2 that we wouldn't - * do in a non-incremental GC. - * - * It's the pre-barrier's job to maintain the snapshot invariant. Consider the - * write "obj->field = value". Let the prior value of obj->field be - * value0. Since it's possible that value0 may have been what obj->field - * contained in step 2, when the snapshot was taken, the barrier marks - * value0. Note that it only does this if we're in the middle of an incremental - * GC. Since this is rare, the cost of the write barrier is usually just an - * extra branch. - * - * In practice, we implement the pre-barrier differently based on the type of - * value0. E.g., see JSObject::writeBarrierPre, which is used if obj->field is - * a JSObject*. It takes value0 as a parameter. - * - * POST-BARRIER - * - * These are not yet implemented. Once we get generational GC, they will allow - * us to keep track of pointers from non-nursery space into the nursery. - * - * IMPLEMENTATION DETAILS - * - * Since it would be awkward to change every write to memory into a function - * call, this file contains a bunch of C++ classes and templates that use - * operator overloading to take care of barriers automatically. In many cases, - * all that's necessary to make some field be barriered is to replace - * Type *field; - * with - * HeapPtr field; - * There are also special classes HeapValue and HeapId, which barrier js::Value - * and jsid, respectively. - * - * One additional note: not all object writes need to be barriered. Writes to - * newly allocated objects do not need a pre-barrier. In these cases, we use - * the "obj->field.init(value)" method instead of "obj->field = value". We use - * the init naming idiom in many places to signify that a field is being - * assigned for the first time. - */ - -struct JSXML; - -namespace js { - -template -class EncapsulatedPtr -{ - protected: - union { - T *value; - Unioned other; - }; - - public: - EncapsulatedPtr() : value(NULL) {} - EncapsulatedPtr(T *v) : value(v) {} - explicit EncapsulatedPtr(const EncapsulatedPtr &v) : value(v.value) {} - - ~EncapsulatedPtr() { pre(); } - - /* Use to set the pointer to NULL. */ - void clear() { - pre(); - value = NULL; - } - - EncapsulatedPtr &operator=(T *v) { - pre(); - JS_ASSERT(!IsPoisonedPtr(v)); - value = v; - return *this; - } - - EncapsulatedPtr &operator=(const EncapsulatedPtr &v) { - pre(); - JS_ASSERT(!IsPoisonedPtr(v.value)); - value = v.value; - return *this; - } - - /* Use this if the automatic coercion to T* isn't working. */ - T *get() const { return value; } - - /* - * Use these if you want to change the value without invoking the barrier. - * Obviously this is dangerous unless you know the barrier is not needed. - */ - T **unsafeGet() { return &value; } - void unsafeSet(T *v) { value = v; } - - Unioned *unsafeGetUnioned() { return &other; } - - T &operator*() const { return *value; } - T *operator->() const { return value; } - - operator T*() const { return value; } - - protected: - void pre(); -}; - -template -class HeapPtr : public EncapsulatedPtr -{ - public: - HeapPtr() : EncapsulatedPtr(NULL) {} - explicit HeapPtr(T *v) : EncapsulatedPtr(v) { post(); } - explicit HeapPtr(const HeapPtr &v) - : EncapsulatedPtr(v) { post(); } - - void init(T *v) { - JS_ASSERT(!IsPoisonedPtr(v)); - this->value = v; - post(); - } - - HeapPtr &operator=(T *v) { - this->pre(); - JS_ASSERT(!IsPoisonedPtr(v)); - this->value = v; - post(); - return *this; - } - - HeapPtr &operator=(const HeapPtr &v) { - this->pre(); - JS_ASSERT(!IsPoisonedPtr(v.value)); - this->value = v.value; - post(); - return *this; - } - - protected: - void post() { T::writeBarrierPost(this->value, (void *)&this->value); } - - /* Make this friend so it can access pre() and post(). */ - template - friend inline void - BarrieredSetPair(JSCompartment *comp, - HeapPtr &v1, T1 *val1, - HeapPtr &v2, T2 *val2); -}; - -/* - * FixedHeapPtr is designed for one very narrow case: replacing immutable raw - * pointers to GC-managed things, implicitly converting to a handle type for - * ease of use. Pointers encapsulated by this type must: - * - * be immutable (no incremental write barriers), - * never point into the nursery (no generational write barriers), and - * be traced via MarkRuntime (we use fromMarkedLocation). - * - * In short: you *really* need to know what you're doing before you use this - * class! - */ -template -class FixedHeapPtr -{ - T *value; - - public: - operator T*() const { return value; } - T * operator->() const { return value; } - - operator Handle() const { - return Handle::fromMarkedLocation(&value); - } - - void init(T *ptr) { - value = ptr; - } -}; - -template -class RelocatablePtr : public EncapsulatedPtr -{ - public: - RelocatablePtr() : EncapsulatedPtr(NULL) {} - explicit RelocatablePtr(T *v) : EncapsulatedPtr(v) { - if (v) - post(); - } - explicit RelocatablePtr(const RelocatablePtr &v) : EncapsulatedPtr(v) { - if (this->value) - post(); - } - - ~RelocatablePtr() { - if (this->value) - relocate(this->value->compartment()); - } - - RelocatablePtr &operator=(T *v) { - this->pre(); - JS_ASSERT(!IsPoisonedPtr(v)); - if (v) { - this->value = v; - post(); - } else if (this->value) { - JSCompartment *comp = this->value->compartment(); - this->value = v; - relocate(comp); - } - return *this; - } - - RelocatablePtr &operator=(const RelocatablePtr &v) { - this->pre(); - JS_ASSERT(!IsPoisonedPtr(v.value)); - if (v.value) { - this->value = v.value; - post(); - } else if (this->value) { - JSCompartment *comp = this->value->compartment(); - this->value = v; - relocate(comp); - } - return *this; - } - - protected: - inline void post(); - inline void relocate(JSCompartment *comp); -}; - -/* - * This is a hack for RegExpStatics::updateFromMatch. It allows us to do two - * barriers with only one branch to check if we're in an incremental GC. - */ -template -static inline void -BarrieredSetPair(JSCompartment *comp, - HeapPtr &v1, T1 *val1, - HeapPtr &v2, T2 *val2) -{ - if (T1::needWriteBarrierPre(comp)) { - v1.pre(); - v2.pre(); - } - v1.unsafeSet(val1); - v2.unsafeSet(val2); - v1.post(); - v2.post(); -} - -struct Shape; -class BaseShape; -namespace types { struct TypeObject; } - -typedef EncapsulatedPtr EncapsulatedPtrObject; -typedef EncapsulatedPtr EncapsulatedPtrScript; - -typedef RelocatablePtr RelocatablePtrObject; -typedef RelocatablePtr RelocatablePtrScript; - -typedef HeapPtr HeapPtrObject; -typedef HeapPtr HeapPtrFunction; -typedef HeapPtr HeapPtrString; -typedef HeapPtr HeapPtrScript; -typedef HeapPtr HeapPtrShape; -typedef HeapPtr HeapPtrBaseShape; -typedef HeapPtr HeapPtrTypeObject; -typedef HeapPtr HeapPtrXML; - -/* Useful for hashtables with a HeapPtr as key. */ -template -struct HeapPtrHasher -{ - typedef HeapPtr Key; - typedef T *Lookup; - - static HashNumber hash(Lookup obj) { return DefaultHasher::hash(obj); } - static bool match(const Key &k, Lookup l) { return k.get() == l; } -}; - -/* Specialized hashing policy for HeapPtrs. */ -template -struct DefaultHasher< HeapPtr > : HeapPtrHasher { }; - -template -struct EncapsulatedPtrHasher -{ - typedef EncapsulatedPtr Key; - typedef T *Lookup; - - static HashNumber hash(Lookup obj) { return DefaultHasher::hash(obj); } - static bool match(const Key &k, Lookup l) { return k.get() == l; } -}; - -template -struct DefaultHasher< EncapsulatedPtr > : EncapsulatedPtrHasher { }; - -class EncapsulatedValue : public ValueOperations -{ - protected: - Value value; - - /* - * Ensure that EncapsulatedValue is not constructable, except by our - * implementations. - */ - EncapsulatedValue() MOZ_DELETE; - EncapsulatedValue(const EncapsulatedValue &v) MOZ_DELETE; - EncapsulatedValue &operator=(const Value &v) MOZ_DELETE; - EncapsulatedValue &operator=(const EncapsulatedValue &v) MOZ_DELETE; - - EncapsulatedValue(const Value &v) : value(v) {} - ~EncapsulatedValue() {} - - public: - bool operator==(const EncapsulatedValue &v) const { return value == v.value; } - bool operator!=(const EncapsulatedValue &v) const { return value != v.value; } - - const Value &get() const { return value; } - Value *unsafeGet() { return &value; } - operator const Value &() const { return value; } - - JSGCTraceKind gcKind() const { return value.gcKind(); } - - uint64_t asRawBits() const { return value.asRawBits(); } - - static inline void writeBarrierPre(const Value &v); - static inline void writeBarrierPre(JSCompartment *comp, const Value &v); - - protected: - inline void pre(); - inline void pre(JSCompartment *comp); - - private: - friend class ValueOperations; - const Value * extract() const { return &value; } -}; - -class HeapValue : public EncapsulatedValue -{ - public: - explicit inline HeapValue(); - explicit inline HeapValue(const Value &v); - explicit inline HeapValue(const HeapValue &v); - inline ~HeapValue(); - - inline void init(const Value &v); - inline void init(JSCompartment *comp, const Value &v); - - inline HeapValue &operator=(const Value &v); - inline HeapValue &operator=(const HeapValue &v); - - /* - * This is a faster version of operator=. Normally, operator= has to - * determine the compartment of the value before it can decide whether to do - * the barrier. If you already know the compartment, it's faster to pass it - * in. - */ - inline void set(JSCompartment *comp, const Value &v); - - static inline void writeBarrierPost(const Value &v, Value *addr); - static inline void writeBarrierPost(JSCompartment *comp, const Value &v, Value *addr); - - private: - inline void post(); - inline void post(JSCompartment *comp); -}; - -class RelocatableValue : public EncapsulatedValue -{ - public: - explicit inline RelocatableValue(); - explicit inline RelocatableValue(const Value &v); - inline RelocatableValue(const RelocatableValue &v); - inline ~RelocatableValue(); - - inline RelocatableValue &operator=(const Value &v); - inline RelocatableValue &operator=(const RelocatableValue &v); - - private: - inline void post(); - inline void post(JSCompartment *comp); - inline void relocate(); -}; - -class HeapSlot : public EncapsulatedValue -{ - /* - * Operator= is not valid for HeapSlot because is must take the object and - * slot offset to provide to the post/generational barrier. - */ - inline HeapSlot &operator=(const Value &v) MOZ_DELETE; - inline HeapSlot &operator=(const HeapValue &v) MOZ_DELETE; - inline HeapSlot &operator=(const HeapSlot &v) MOZ_DELETE; - - public: - explicit inline HeapSlot() MOZ_DELETE; - explicit inline HeapSlot(JSObject *obj, uint32_t slot, const Value &v); - explicit inline HeapSlot(JSObject *obj, uint32_t slot, const HeapSlot &v); - inline ~HeapSlot(); - - inline void init(JSObject *owner, uint32_t slot, const Value &v); - inline void init(JSCompartment *comp, JSObject *owner, uint32_t slot, const Value &v); - - inline void set(JSObject *owner, uint32_t slot, const Value &v); - inline void set(JSCompartment *comp, JSObject *owner, uint32_t slot, const Value &v); - - static inline void writeBarrierPost(JSObject *obj, uint32_t slot); - static inline void writeBarrierPost(JSCompartment *comp, JSObject *obj, uint32_t slot); - - private: - inline void post(JSObject *owner, uint32_t slot); - inline void post(JSCompartment *comp, JSObject *owner, uint32_t slot); -}; - -/* - * NOTE: This is a placeholder for bug 619558. - * - * Run a post write barrier that encompasses multiple contiguous slots in a - * single step. - */ -inline void -SlotRangeWriteBarrierPost(JSCompartment *comp, JSObject *obj, uint32_t start, uint32_t count); - -/* - * This is a post barrier for HashTables whose key can be moved during a GC. - */ -template -inline void -HashTableWriteBarrierPost(JSCompartment *comp, const Map *map, const Key &key) -{ -#ifdef JS_GCGENERATIONAL - if (key && comp->gcNursery.isInside(key)) - comp->gcStoreBuffer.putGeneric(HashKeyRef(map, key)); -#endif -} - -static inline const Value * -Valueify(const EncapsulatedValue *array) -{ - JS_STATIC_ASSERT(sizeof(HeapValue) == sizeof(Value)); - JS_STATIC_ASSERT(sizeof(HeapSlot) == sizeof(Value)); - return (const Value *)array; -} - -static inline HeapValue * -HeapValueify(Value *v) -{ - JS_STATIC_ASSERT(sizeof(HeapValue) == sizeof(Value)); - JS_STATIC_ASSERT(sizeof(HeapSlot) == sizeof(Value)); - return (HeapValue *)v; -} - -class HeapSlotArray -{ - HeapSlot *array; - - public: - HeapSlotArray(HeapSlot *array) : array(array) {} - - operator const Value *() const { return Valueify(array); } - operator HeapSlot *() const { return array; } - - HeapSlotArray operator +(int offset) const { return HeapSlotArray(array + offset); } - HeapSlotArray operator +(uint32_t offset) const { return HeapSlotArray(array + offset); } -}; - -class EncapsulatedId -{ - protected: - jsid value; - - private: - EncapsulatedId(const EncapsulatedId &v) MOZ_DELETE; - - public: - explicit EncapsulatedId() : value(JSID_VOID) {} - explicit EncapsulatedId(jsid id) : value(id) {} - ~EncapsulatedId(); - - inline EncapsulatedId &operator=(const EncapsulatedId &v); - - bool operator==(jsid id) const { return value == id; } - bool operator!=(jsid id) const { return value != id; } - - jsid get() const { return value; } - jsid *unsafeGet() { return &value; } - operator jsid() const { return value; } - - protected: - inline void pre(); -}; - -class RelocatableId : public EncapsulatedId -{ - public: - explicit RelocatableId() : EncapsulatedId() {} - explicit inline RelocatableId(jsid id) : EncapsulatedId(id) {} - inline ~RelocatableId(); - - inline RelocatableId &operator=(jsid id); - inline RelocatableId &operator=(const RelocatableId &v); -}; - -class HeapId : public EncapsulatedId -{ - public: - explicit HeapId() : EncapsulatedId() {} - explicit inline HeapId(jsid id); - inline ~HeapId(); - - inline void init(jsid id); - - inline HeapId &operator=(jsid id); - inline HeapId &operator=(const HeapId &v); - - private: - inline void post(); - - HeapId(const HeapId &v) MOZ_DELETE; -}; - -/* - * Incremental GC requires that weak pointers have read barriers. This is mostly - * an issue for empty shapes stored in JSCompartment. The problem happens when, - * during an incremental GC, some JS code stores one of the compartment's empty - * shapes into an object already marked black. Normally, this would not be a - * problem, because the empty shape would have been part of the initial snapshot - * when the GC started. However, since this is a weak pointer, it isn't. So we - * may collect the empty shape even though a live object points to it. To fix - * this, we mark these empty shapes black whenever they get read out. - */ -template -class ReadBarriered -{ - T *value; - - public: - ReadBarriered() : value(NULL) {} - ReadBarriered(T *value) : value(value) {} - - T *get() const { - if (!value) - return NULL; - T::readBarrier(value); - return value; - } - - operator T*() const { return get(); } - - T &operator*() const { return *get(); } - T *operator->() const { return get(); } - - T **unsafeGet() { return &value; } - - void set(T *v) { value = v; } - - operator bool() { return !!value; } -}; - -class ReadBarrieredValue -{ - Value value; - - public: - ReadBarrieredValue() : value(UndefinedValue()) {} - ReadBarrieredValue(const Value &value) : value(value) {} - - inline const Value &get() const; - Value *unsafeGet() { return &value; } - inline operator const Value &() const; - - inline JSObject &toObject() const; -}; - -namespace tl { - -template struct IsRelocatableHeapType > - { static const bool result = false; }; -template <> struct IsRelocatableHeapType { static const bool result = false; }; -template <> struct IsRelocatableHeapType { static const bool result = false; }; -template <> struct IsRelocatableHeapType { static const bool result = false; }; - -} /* namespace tl */ -} /* namespace js */ - -#endif /* jsgc_barrier_h___ */ diff --git a/scripting/javascript/spidermonkey-ios/include/gc/Heap.h b/scripting/javascript/spidermonkey-ios/include/gc/Heap.h deleted file mode 100644 index 9a53deb24b..0000000000 --- a/scripting/javascript/spidermonkey-ios/include/gc/Heap.h +++ /dev/null @@ -1,1032 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef gc_heap_h___ -#define gc_heap_h___ - -#include "mozilla/Attributes.h" -#include "mozilla/StandardInteger.h" - -#include - -#include "jstypes.h" -#include "jsutil.h" - -#include "ds/BitArray.h" - -struct JSCompartment; - -extern "C" { -struct JSRuntime; -} - -namespace js { - -class FreeOp; - -namespace gc { - -struct Arena; -struct ArenaHeader; -struct Chunk; - -/* - * Live objects are marked black. How many other additional colors are available - * depends on the size of the GCThing. Objects marked gray are eligible for - * cycle collection. - */ -static const uint32_t BLACK = 0; -static const uint32_t GRAY = 1; - -/* The GC allocation kinds. */ -enum AllocKind { - FINALIZE_OBJECT0, - FINALIZE_OBJECT0_BACKGROUND, - FINALIZE_OBJECT2, - FINALIZE_OBJECT2_BACKGROUND, - FINALIZE_OBJECT4, - FINALIZE_OBJECT4_BACKGROUND, - FINALIZE_OBJECT8, - FINALIZE_OBJECT8_BACKGROUND, - FINALIZE_OBJECT12, - FINALIZE_OBJECT12_BACKGROUND, - FINALIZE_OBJECT16, - FINALIZE_OBJECT16_BACKGROUND, - FINALIZE_OBJECT_LAST = FINALIZE_OBJECT16_BACKGROUND, - FINALIZE_SCRIPT, - FINALIZE_SHAPE, - FINALIZE_BASE_SHAPE, - FINALIZE_TYPE_OBJECT, -#if JS_HAS_XML_SUPPORT - FINALIZE_XML, -#endif - FINALIZE_SHORT_STRING, - FINALIZE_STRING, - FINALIZE_EXTERNAL_STRING, - FINALIZE_IONCODE, - FINALIZE_LAST = FINALIZE_IONCODE -}; - -static const unsigned FINALIZE_LIMIT = FINALIZE_LAST + 1; -static const unsigned FINALIZE_OBJECT_LIMIT = FINALIZE_OBJECT_LAST + 1; - -/* - * This must be an upper bound, but we do not need the least upper bound, so - * we just exclude non-background objects. - */ -static const size_t MAX_BACKGROUND_FINALIZE_KINDS = FINALIZE_LIMIT - FINALIZE_OBJECT_LIMIT / 2; - -/* - * A GC cell is the base class for all GC things. - */ -struct Cell -{ - static const size_t CellShift = 3; - static const size_t CellSize = size_t(1) << CellShift; - static const size_t CellMask = CellSize - 1; - - inline uintptr_t address() const; - inline ArenaHeader *arenaHeader() const; - inline Chunk *chunk() const; - inline AllocKind getAllocKind() const; - MOZ_ALWAYS_INLINE bool isMarked(uint32_t color = BLACK) const; - MOZ_ALWAYS_INLINE bool markIfUnmarked(uint32_t color = BLACK) const; - MOZ_ALWAYS_INLINE void unmark(uint32_t color) const; - - inline JSCompartment *compartment() const; - -#ifdef DEBUG - inline bool isAligned() const; -#endif -}; - -/* - * Page size must be static to support our arena pointer optimizations, so we - * are forced to support each platform with non-4096 pages as a special case. - * Note: The freelist supports a maximum arena shift of 15. - * Note: Do not use JS_CPU_SPARC here, this header is used outside JS. - * Bug 692267: Move page size definition to gc/Memory.h and include it - * directly once jsgc.h is no longer an installed header. - */ -#if (defined(SOLARIS) || defined(__FreeBSD__)) && \ - (defined(__sparc) || defined(__sparcv9) || defined(__ia64)) -const size_t PageShift = 13; -const size_t ArenaShift = PageShift; -#elif defined(__powerpc64__) -const size_t PageShift = 16; -const size_t ArenaShift = 12; -#else -const size_t PageShift = 12; -const size_t ArenaShift = PageShift; -#endif -const size_t PageSize = size_t(1) << PageShift; -const size_t ArenaSize = size_t(1) << ArenaShift; -const size_t ArenaMask = ArenaSize - 1; - -const size_t ChunkShift = 20; -const size_t ChunkSize = size_t(1) << ChunkShift; -const size_t ChunkMask = ChunkSize - 1; - -/* - * This is the maximum number of arenas we allow in the FreeCommitted state - * before we trigger a GC_SHRINK to release free arenas to the OS. - */ -const static uint32_t FreeCommittedArenasThreshold = (32 << 20) / ArenaSize; - -/* - * The mark bitmap has one bit per each GC cell. For multi-cell GC things this - * wastes space but allows to avoid expensive devisions by thing's size when - * accessing the bitmap. In addition this allows to use some bits for colored - * marking during the cycle GC. - */ -const size_t ArenaCellCount = size_t(1) << (ArenaShift - Cell::CellShift); -const size_t ArenaBitmapBits = ArenaCellCount; -const size_t ArenaBitmapBytes = ArenaBitmapBits / 8; -const size_t ArenaBitmapWords = ArenaBitmapBits / JS_BITS_PER_WORD; - -/* - * A FreeSpan represents a contiguous sequence of free cells in an Arena. - * |first| is the address of the first free cell in the span. |last| is the - * address of the last free cell in the span. This last cell holds a FreeSpan - * data structure for the next span unless this is the last span on the list - * of spans in the arena. For this last span |last| points to the last byte of - * the last thing in the arena and no linkage is stored there, so - * |last| == arenaStart + ArenaSize - 1. If the space at the arena end is - * fully used this last span is empty and |first| == |last + 1|. - * - * Thus |first| < |last| implies that we have either the last span with at least - * one element or that the span is not the last and contains at least 2 - * elements. In both cases to allocate a thing from this span we need simply - * to increment |first| by the allocation size. - * - * |first| == |last| implies that we have a one element span that records the - * next span. So to allocate from it we need to update the span list head - * with a copy of the span stored at |last| address so the following - * allocations will use that span. - * - * |first| > |last| implies that we have an empty last span and the arena is - * fully used. - * - * Also only for the last span (|last| & 1)! = 0 as all allocation sizes are - * multiples of Cell::CellSize. - */ -struct FreeSpan -{ - uintptr_t first; - uintptr_t last; - - public: - FreeSpan() {} - - FreeSpan(uintptr_t first, uintptr_t last) - : first(first), last(last) { - checkSpan(); - } - - /* - * To minimize the size of the arena header the first span is encoded - * there as offsets from the arena start. - */ - static size_t encodeOffsets(size_t firstOffset, size_t lastOffset) { - /* Check that we can pack the offsets into uint16. */ - JS_STATIC_ASSERT(ArenaShift < 16); - JS_ASSERT(firstOffset <= ArenaSize); - JS_ASSERT(lastOffset < ArenaSize); - JS_ASSERT(firstOffset <= ((lastOffset + 1) & ~size_t(1))); - return firstOffset | (lastOffset << 16); - } - - /* - * Encoded offsets for a full arena when its first span is the last one - * and empty. - */ - static const size_t FullArenaOffsets = ArenaSize | ((ArenaSize - 1) << 16); - - static FreeSpan decodeOffsets(uintptr_t arenaAddr, size_t offsets) { - JS_ASSERT(!(arenaAddr & ArenaMask)); - - size_t firstOffset = offsets & 0xFFFF; - size_t lastOffset = offsets >> 16; - JS_ASSERT(firstOffset <= ArenaSize); - JS_ASSERT(lastOffset < ArenaSize); - - /* - * We must not use | when calculating first as firstOffset is - * ArenaMask + 1 for the empty span. - */ - return FreeSpan(arenaAddr + firstOffset, arenaAddr | lastOffset); - } - - void initAsEmpty(uintptr_t arenaAddr = 0) { - JS_ASSERT(!(arenaAddr & ArenaMask)); - first = arenaAddr + ArenaSize; - last = arenaAddr | (ArenaSize - 1); - JS_ASSERT(isEmpty()); - } - - bool isEmpty() const { - checkSpan(); - return first > last; - } - - bool hasNext() const { - checkSpan(); - return !(last & uintptr_t(1)); - } - - const FreeSpan *nextSpan() const { - JS_ASSERT(hasNext()); - return reinterpret_cast(last); - } - - FreeSpan *nextSpanUnchecked(size_t thingSize) const { -#ifdef DEBUG - uintptr_t lastOffset = last & ArenaMask; - JS_ASSERT(!(lastOffset & 1)); - JS_ASSERT((ArenaSize - lastOffset) % thingSize == 0); -#endif - return reinterpret_cast(last); - } - - uintptr_t arenaAddressUnchecked() const { - return last & ~ArenaMask; - } - - uintptr_t arenaAddress() const { - checkSpan(); - return arenaAddressUnchecked(); - } - - ArenaHeader *arenaHeader() const { - return reinterpret_cast(arenaAddress()); - } - - bool isSameNonEmptySpan(const FreeSpan *another) const { - JS_ASSERT(!isEmpty()); - JS_ASSERT(!another->isEmpty()); - return first == another->first && last == another->last; - } - - bool isWithinArena(uintptr_t arenaAddr) const { - JS_ASSERT(!(arenaAddr & ArenaMask)); - - /* Return true for the last empty span as well. */ - return arenaAddress() == arenaAddr; - } - - size_t encodeAsOffsets() const { - /* - * We must use first - arenaAddress(), not first & ArenaMask as - * first == ArenaMask + 1 for an empty span. - */ - uintptr_t arenaAddr = arenaAddress(); - return encodeOffsets(first - arenaAddr, last & ArenaMask); - } - - /* See comments before FreeSpan for details. */ - MOZ_ALWAYS_INLINE void *allocate(size_t thingSize) { - JS_ASSERT(thingSize % Cell::CellSize == 0); - checkSpan(); - uintptr_t thing = first; - if (thing < last) { - /* Bump-allocate from the current span. */ - first = thing + thingSize; - } else if (JS_LIKELY(thing == last)) { - /* - * Move to the next span. We use JS_LIKELY as without PGO - * compilers mis-predict == here as unlikely to succeed. - */ - *this = *reinterpret_cast(thing); - } else { - return NULL; - } - checkSpan(); - return reinterpret_cast(thing); - } - - /* A version of allocate when we know that the span is not empty. */ - MOZ_ALWAYS_INLINE void *infallibleAllocate(size_t thingSize) { - JS_ASSERT(thingSize % Cell::CellSize == 0); - checkSpan(); - uintptr_t thing = first; - if (thing < last) { - first = thing + thingSize; - } else { - JS_ASSERT(thing == last); - *this = *reinterpret_cast(thing); - } - checkSpan(); - return reinterpret_cast(thing); - } - - /* - * Allocate from a newly allocated arena. We do not move the free list - * from the arena. Rather we set the arena up as fully used during the - * initialization so to allocate we simply return the first thing in the - * arena and set the free list to point to the second. - */ - MOZ_ALWAYS_INLINE void *allocateFromNewArena(uintptr_t arenaAddr, size_t firstThingOffset, - size_t thingSize) { - JS_ASSERT(!(arenaAddr & ArenaMask)); - uintptr_t thing = arenaAddr | firstThingOffset; - first = thing + thingSize; - last = arenaAddr | ArenaMask; - checkSpan(); - return reinterpret_cast(thing); - } - - void checkSpan() const { -#ifdef DEBUG - /* We do not allow spans at the end of the address space. */ - JS_ASSERT(last != uintptr_t(-1)); - JS_ASSERT(first); - JS_ASSERT(last); - JS_ASSERT(first - 1 <= last); - uintptr_t arenaAddr = arenaAddressUnchecked(); - if (last & 1) { - /* The span is the last. */ - JS_ASSERT((last & ArenaMask) == ArenaMask); - - if (first - 1 == last) { - /* The span is last and empty. The above start != 0 check - * implies that we are not at the end of the address space. - */ - return; - } - size_t spanLength = last - first + 1; - JS_ASSERT(spanLength % Cell::CellSize == 0); - - /* Start and end must belong to the same arena. */ - JS_ASSERT((first & ~ArenaMask) == arenaAddr); - return; - } - - /* The span is not the last and we have more spans to follow. */ - JS_ASSERT(first <= last); - size_t spanLengthWithoutOneThing = last - first; - JS_ASSERT(spanLengthWithoutOneThing % Cell::CellSize == 0); - - JS_ASSERT((first & ~ArenaMask) == arenaAddr); - - /* - * If there is not enough space before the arena end to allocate one - * more thing, then the span must be marked as the last one to avoid - * storing useless empty span reference. - */ - size_t beforeTail = ArenaSize - (last & ArenaMask); - JS_ASSERT(beforeTail >= sizeof(FreeSpan) + Cell::CellSize); - - FreeSpan *next = reinterpret_cast(last); - - /* - * The GC things on the list of free spans come from one arena - * and the spans are linked in ascending address order with - * at least one non-free thing between spans. - */ - JS_ASSERT(last < next->first); - JS_ASSERT(arenaAddr == next->arenaAddressUnchecked()); - - if (next->first > next->last) { - /* - * The next span is the empty span that terminates the list for - * arenas that do not have any free things at the end. - */ - JS_ASSERT(next->first - 1 == next->last); - JS_ASSERT(arenaAddr + ArenaSize == next->first); - } -#endif - } - -}; - -/* Every arena has a header. */ -struct ArenaHeader -{ - friend struct FreeLists; - - JSCompartment *compartment; - - /* - * ArenaHeader::next has two purposes: when unallocated, it points to the - * next available Arena's header. When allocated, it points to the next - * arena of the same size class and compartment. - */ - ArenaHeader *next; - - private: - /* - * The first span of free things in the arena. We encode it as the start - * and end offsets within the arena, not as FreeSpan structure, to - * minimize the header size. - */ - size_t firstFreeSpanOffsets; - - /* - * One of AllocKind constants or FINALIZE_LIMIT when the arena does not - * contain any GC things and is on the list of empty arenas in the GC - * chunk. The latter allows to quickly check if the arena is allocated - * during the conservative GC scanning without searching the arena in the - * list. - * - * We use 8 bits for the allocKind so the compiler can use byte-level memory - * instructions to access it. - */ - size_t allocKind : 8; - - /* - * When collecting we sometimes need to keep an auxillary list of arenas, - * for which we use the following fields. This happens for several reasons: - * - * When recursive marking uses too much stack the marking is delayed and the - * corresponding arenas are put into a stack. To distinguish the bottom of - * the stack from the arenas not present in the stack we use the - * markOverflow flag to tag arenas on the stack. - * - * Delayed marking is also used for arenas that we allocate into during an - * incremental GC. In this case, we intend to mark all the objects in the - * arena, and it's faster to do this marking in bulk. - * - * When sweeping we keep track of which arenas have been allocated since the - * end of the mark phase. This allows us to tell whether a pointer to an - * unmarked object is yet to be finalized or has already been reallocated. - * We set the allocatedDuringIncremental flag for this and clear it at the - * end of the sweep phase. - * - * To minimize the ArenaHeader size we record the next linkage as - * arenaAddress() >> ArenaShift and pack it with the allocKind field and the - * flags. - */ - public: - size_t hasDelayedMarking : 1; - size_t allocatedDuringIncremental : 1; - size_t markOverflow : 1; - size_t auxNextLink : JS_BITS_PER_WORD - 8 - 1 - 1 - 1; - - static void staticAsserts() { - /* We must be able to fit the allockind into uint8_t. */ - JS_STATIC_ASSERT(FINALIZE_LIMIT <= 255); - - /* - * auxNextLink packing assumes that ArenaShift has enough bits - * to cover allocKind and hasDelayedMarking. - */ - JS_STATIC_ASSERT(ArenaShift >= 8 + 1 + 1 + 1); - } - - inline uintptr_t address() const; - inline Chunk *chunk() const; - - bool allocated() const { - JS_ASSERT(allocKind <= size_t(FINALIZE_LIMIT)); - return allocKind < size_t(FINALIZE_LIMIT); - } - - void init(JSCompartment *comp, AllocKind kind) { - JS_ASSERT(!allocated()); - JS_ASSERT(!markOverflow); - JS_ASSERT(!allocatedDuringIncremental); - JS_ASSERT(!hasDelayedMarking); - compartment = comp; - - JS_STATIC_ASSERT(FINALIZE_LIMIT <= 255); - allocKind = size_t(kind); - - /* See comments in FreeSpan::allocateFromNewArena. */ - firstFreeSpanOffsets = FreeSpan::FullArenaOffsets; - } - - void setAsNotAllocated() { - allocKind = size_t(FINALIZE_LIMIT); - markOverflow = 0; - allocatedDuringIncremental = 0; - hasDelayedMarking = 0; - auxNextLink = 0; - } - - inline uintptr_t arenaAddress() const; - inline Arena *getArena(); - - AllocKind getAllocKind() const { - JS_ASSERT(allocated()); - return AllocKind(allocKind); - } - - inline size_t getThingSize() const; - - bool hasFreeThings() const { - return firstFreeSpanOffsets != FreeSpan::FullArenaOffsets; - } - - inline bool isEmpty() const; - - void setAsFullyUsed() { - firstFreeSpanOffsets = FreeSpan::FullArenaOffsets; - } - - inline FreeSpan getFirstFreeSpan() const; - inline void setFirstFreeSpan(const FreeSpan *span); - -#ifdef DEBUG - void checkSynchronizedWithFreeList() const; -#endif - - inline ArenaHeader *getNextDelayedMarking() const; - inline void setNextDelayedMarking(ArenaHeader *aheader); - inline void unsetDelayedMarking(); - - inline ArenaHeader *getNextAllocDuringSweep() const; - inline void setNextAllocDuringSweep(ArenaHeader *aheader); - inline void unsetAllocDuringSweep(); -}; - -struct Arena -{ - /* - * Layout of an arena: - * An arena is 4K in size and 4K-aligned. It starts with the ArenaHeader - * descriptor followed by some pad bytes. The remainder of the arena is - * filled with the array of T things. The pad bytes ensure that the thing - * array ends exactly at the end of the arena. - * - * +-------------+-----+----+----+-----+----+ - * | ArenaHeader | pad | T0 | T1 | ... | Tn | - * +-------------+-----+----+----+-----+----+ - * - * <----------------------------------------> = ArenaSize bytes - * <-------------------> = first thing offset - */ - ArenaHeader aheader; - uint8_t data[ArenaSize - sizeof(ArenaHeader)]; - - private: - static JS_FRIEND_DATA(const uint32_t) ThingSizes[]; - static JS_FRIEND_DATA(const uint32_t) FirstThingOffsets[]; - - public: - static void staticAsserts(); - - static size_t thingSize(AllocKind kind) { - return ThingSizes[kind]; - } - - static size_t firstThingOffset(AllocKind kind) { - return FirstThingOffsets[kind]; - } - - static size_t thingsPerArena(size_t thingSize) { - JS_ASSERT(thingSize % Cell::CellSize == 0); - - /* We should be able to fit FreeSpan in any GC thing. */ - JS_ASSERT(thingSize >= sizeof(FreeSpan)); - - return (ArenaSize - sizeof(ArenaHeader)) / thingSize; - } - - static size_t thingsSpan(size_t thingSize) { - return thingsPerArena(thingSize) * thingSize; - } - - static bool isAligned(uintptr_t thing, size_t thingSize) { - /* Things ends at the arena end. */ - uintptr_t tailOffset = (ArenaSize - thing) & ArenaMask; - return tailOffset % thingSize == 0; - } - - uintptr_t address() const { - return aheader.address(); - } - - uintptr_t thingsStart(AllocKind thingKind) { - return address() | firstThingOffset(thingKind); - } - - uintptr_t thingsEnd() { - return address() + ArenaSize; - } - - template - bool finalize(FreeOp *fop, AllocKind thingKind, size_t thingSize); -}; - -inline size_t -ArenaHeader::getThingSize() const -{ - JS_ASSERT(allocated()); - return Arena::thingSize(getAllocKind()); -} - -/* The chunk header (located at the end of the chunk to preserve arena alignment). */ -struct ChunkInfo -{ - Chunk *next; - Chunk **prevp; - - /* Free arenas are linked together with aheader.next. */ - ArenaHeader *freeArenasHead; - - /* - * Decommitted arenas are tracked by a bitmap in the chunk header. We use - * this offset to start our search iteration close to a decommitted arena - * that we can allocate. - */ - uint32_t lastDecommittedArenaOffset; - - /* Number of free arenas, either committed or decommitted. */ - uint32_t numArenasFree; - - /* Number of free, committed arenas. */ - uint32_t numArenasFreeCommitted; - - /* Number of GC cycles this chunk has survived. */ - uint32_t age; -}; - -/* - * Calculating ArenasPerChunk: - * - * In order to figure out how many Arenas will fit in a chunk, we need to know - * how much extra space is available after we allocate the header data. This - * is a problem because the header size depends on the number of arenas in the - * chunk. The two dependent fields are bitmap and decommittedArenas. - * - * For the mark bitmap, we know that each arena will use a fixed number of full - * bytes: ArenaBitmapBytes. The full size of the header data is this number - * multiplied by the eventual number of arenas we have in the header. We, - * conceptually, distribute this header data among the individual arenas and do - * not include it in the header. This way we do not have to worry about its - * variable size: it gets attached to the variable number we are computing. - * - * For the decommitted arena bitmap, we only have 1 bit per arena, so this - * technique will not work. Instead, we observe that we do not have enough - * header info to fill 8 full arenas: it is currently 4 on 64bit, less on - * 32bit. Thus, with current numbers, we need 64 bytes for decommittedArenas. - * This will not become 63 bytes unless we double the data required in the - * header. Therefore, we just compute the number of bytes required to track - * every possible arena and do not worry about slop bits, since there are too - * few to usefully allocate. - * - * To actually compute the number of arenas we can allocate in a chunk, we - * divide the amount of available space less the header info (not including - * the mark bitmap which is distributed into the arena size) by the size of - * the arena (with the mark bitmap bytes it uses). - */ -const size_t BytesPerArenaWithHeader = ArenaSize + ArenaBitmapBytes; -const size_t ChunkDecommitBitmapBytes = ChunkSize / ArenaSize / JS_BITS_PER_BYTE; -const size_t ChunkBytesAvailable = ChunkSize - sizeof(ChunkInfo) - ChunkDecommitBitmapBytes; -const size_t ArenasPerChunk = ChunkBytesAvailable / BytesPerArenaWithHeader; - -/* A chunk bitmap contains enough mark bits for all the cells in a chunk. */ -struct ChunkBitmap -{ - uintptr_t bitmap[ArenaBitmapWords * ArenasPerChunk]; - - MOZ_ALWAYS_INLINE void getMarkWordAndMask(const Cell *cell, uint32_t color, - uintptr_t **wordp, uintptr_t *maskp); - - MOZ_ALWAYS_INLINE bool isMarked(const Cell *cell, uint32_t color) { - uintptr_t *word, mask; - getMarkWordAndMask(cell, color, &word, &mask); - return *word & mask; - } - - MOZ_ALWAYS_INLINE bool markIfUnmarked(const Cell *cell, uint32_t color) { - uintptr_t *word, mask; - getMarkWordAndMask(cell, BLACK, &word, &mask); - if (*word & mask) - return false; - *word |= mask; - if (color != BLACK) { - /* - * We use getMarkWordAndMask to recalculate both mask and word as - * doing just mask << color may overflow the mask. - */ - getMarkWordAndMask(cell, color, &word, &mask); - if (*word & mask) - return false; - *word |= mask; - } - return true; - } - - MOZ_ALWAYS_INLINE void unmark(const Cell *cell, uint32_t color) { - uintptr_t *word, mask; - getMarkWordAndMask(cell, color, &word, &mask); - *word &= ~mask; - } - - void clear() { - PodArrayZero(bitmap); - } - - uintptr_t *arenaBits(ArenaHeader *aheader) { - /* - * We assume that the part of the bitmap corresponding to the arena - * has the exact number of words so we do not need to deal with a word - * that covers bits from two arenas. - */ - JS_STATIC_ASSERT(ArenaBitmapBits == ArenaBitmapWords * JS_BITS_PER_WORD); - - uintptr_t *word, unused; - getMarkWordAndMask(reinterpret_cast(aheader->address()), BLACK, &word, &unused); - return word; - } -}; - -JS_STATIC_ASSERT(ArenaBitmapBytes * ArenasPerChunk == sizeof(ChunkBitmap)); - -typedef BitArray PerArenaBitmap; - -const size_t ChunkPadSize = ChunkSize - - (sizeof(Arena) * ArenasPerChunk) - - sizeof(ChunkBitmap) - - sizeof(PerArenaBitmap) - - sizeof(ChunkInfo); -JS_STATIC_ASSERT(ChunkPadSize < BytesPerArenaWithHeader); - -/* - * Chunks contain arenas and associated data structures (mark bitmap, delayed - * marking state). - */ -struct Chunk -{ - Arena arenas[ArenasPerChunk]; - - /* Pad to full size to ensure cache alignment of ChunkInfo. */ - uint8_t padding[ChunkPadSize]; - - ChunkBitmap bitmap; - PerArenaBitmap decommittedArenas; - ChunkInfo info; - - static Chunk *fromAddress(uintptr_t addr) { - addr &= ~ChunkMask; - return reinterpret_cast(addr); - } - - static bool withinArenasRange(uintptr_t addr) { - uintptr_t offset = addr & ChunkMask; - return offset < ArenasPerChunk * ArenaSize; - } - - static size_t arenaIndex(uintptr_t addr) { - JS_ASSERT(withinArenasRange(addr)); - return (addr & ChunkMask) >> ArenaShift; - } - - uintptr_t address() const { - uintptr_t addr = reinterpret_cast(this); - JS_ASSERT(!(addr & ChunkMask)); - return addr; - } - - bool unused() const { - return info.numArenasFree == ArenasPerChunk; - } - - bool hasAvailableArenas() const { - return info.numArenasFree != 0; - } - - inline void addToAvailableList(JSCompartment *compartment); - inline void insertToAvailableList(Chunk **insertPoint); - inline void removeFromAvailableList(); - - ArenaHeader *allocateArena(JSCompartment *comp, AllocKind kind); - - void releaseArena(ArenaHeader *aheader); - - static Chunk *allocate(JSRuntime *rt); - - /* Must be called with the GC lock taken. */ - static inline void release(JSRuntime *rt, Chunk *chunk); - static inline void releaseList(JSRuntime *rt, Chunk *chunkListHead); - - /* Must be called with the GC lock taken. */ - inline void prepareToBeFreed(JSRuntime *rt); - - /* - * Assuming that the info.prevp points to the next field of the previous - * chunk in a doubly-linked list, get that chunk. - */ - Chunk *getPrevious() { - JS_ASSERT(info.prevp); - return fromPointerToNext(info.prevp); - } - - /* Get the chunk from a pointer to its info.next field. */ - static Chunk *fromPointerToNext(Chunk **nextFieldPtr) { - uintptr_t addr = reinterpret_cast(nextFieldPtr); - JS_ASSERT((addr & ChunkMask) == offsetof(Chunk, info.next)); - return reinterpret_cast(addr - offsetof(Chunk, info.next)); - } - - private: - inline void init(); - - /* Search for a decommitted arena to allocate. */ - unsigned findDecommittedArenaOffset(); - ArenaHeader* fetchNextDecommittedArena(); - - public: - /* Unlink and return the freeArenasHead. */ - inline ArenaHeader* fetchNextFreeArena(JSRuntime *rt); - - inline void addArenaToFreeList(JSRuntime *rt, ArenaHeader *aheader); -}; - -JS_STATIC_ASSERT(sizeof(Chunk) == ChunkSize); - -inline uintptr_t -Cell::address() const -{ - uintptr_t addr = uintptr_t(this); - JS_ASSERT(addr % Cell::CellSize == 0); - JS_ASSERT(Chunk::withinArenasRange(addr)); - return addr; -} - -inline uintptr_t -ArenaHeader::address() const -{ - uintptr_t addr = reinterpret_cast(this); - JS_ASSERT(!(addr & ArenaMask)); - JS_ASSERT(Chunk::withinArenasRange(addr)); - return addr; -} - -inline Chunk * -ArenaHeader::chunk() const -{ - return Chunk::fromAddress(address()); -} - -inline uintptr_t -ArenaHeader::arenaAddress() const -{ - return address(); -} - -inline Arena * -ArenaHeader::getArena() -{ - return reinterpret_cast(arenaAddress()); -} - -inline bool -ArenaHeader::isEmpty() const -{ - /* Arena is empty if its first span covers the whole arena. */ - JS_ASSERT(allocated()); - size_t firstThingOffset = Arena::firstThingOffset(getAllocKind()); - return firstFreeSpanOffsets == FreeSpan::encodeOffsets(firstThingOffset, ArenaMask); -} - -FreeSpan -ArenaHeader::getFirstFreeSpan() const -{ -#ifdef DEBUG - checkSynchronizedWithFreeList(); -#endif - return FreeSpan::decodeOffsets(arenaAddress(), firstFreeSpanOffsets); -} - -void -ArenaHeader::setFirstFreeSpan(const FreeSpan *span) -{ - JS_ASSERT(span->isWithinArena(arenaAddress())); - firstFreeSpanOffsets = span->encodeAsOffsets(); -} - -inline ArenaHeader * -ArenaHeader::getNextDelayedMarking() const -{ - JS_ASSERT(hasDelayedMarking); - return &reinterpret_cast(auxNextLink << ArenaShift)->aheader; -} - -inline void -ArenaHeader::setNextDelayedMarking(ArenaHeader *aheader) -{ - JS_ASSERT(!(uintptr_t(aheader) & ArenaMask)); - JS_ASSERT(!auxNextLink && !hasDelayedMarking); - hasDelayedMarking = 1; - auxNextLink = aheader->arenaAddress() >> ArenaShift; -} - -inline void -ArenaHeader::unsetDelayedMarking() -{ - JS_ASSERT(hasDelayedMarking); - hasDelayedMarking = 0; - auxNextLink = 0; -} - -inline ArenaHeader * -ArenaHeader::getNextAllocDuringSweep() const -{ - JS_ASSERT(allocatedDuringIncremental); - return &reinterpret_cast(auxNextLink << ArenaShift)->aheader; -} - -inline void -ArenaHeader::setNextAllocDuringSweep(ArenaHeader *aheader) -{ - JS_ASSERT(!auxNextLink && !allocatedDuringIncremental); - allocatedDuringIncremental = 1; - auxNextLink = aheader->arenaAddress() >> ArenaShift; -} - -inline void -ArenaHeader::unsetAllocDuringSweep() -{ - JS_ASSERT(allocatedDuringIncremental); - allocatedDuringIncremental = 0; - auxNextLink = 0; -} - -JS_ALWAYS_INLINE void -ChunkBitmap::getMarkWordAndMask(const Cell *cell, uint32_t color, - uintptr_t **wordp, uintptr_t *maskp) -{ - size_t bit = (cell->address() & ChunkMask) / Cell::CellSize + color; - JS_ASSERT(bit < ArenaBitmapBits * ArenasPerChunk); - *maskp = uintptr_t(1) << (bit % JS_BITS_PER_WORD); - *wordp = &bitmap[bit / JS_BITS_PER_WORD]; -} - -static void -AssertValidColor(const void *thing, uint32_t color) -{ -#ifdef DEBUG - ArenaHeader *aheader = reinterpret_cast(thing)->arenaHeader(); - JS_ASSERT_IF(color, color < aheader->getThingSize() / Cell::CellSize); -#endif -} - -inline ArenaHeader * -Cell::arenaHeader() const -{ - uintptr_t addr = address(); - addr &= ~ArenaMask; - return reinterpret_cast(addr); -} - -Chunk * -Cell::chunk() const -{ - uintptr_t addr = uintptr_t(this); - JS_ASSERT(addr % Cell::CellSize == 0); - addr &= ~(ChunkSize - 1); - return reinterpret_cast(addr); -} - -AllocKind -Cell::getAllocKind() const -{ - return arenaHeader()->getAllocKind(); -} - -bool -Cell::isMarked(uint32_t color /* = BLACK */) const -{ - AssertValidColor(this, color); - return chunk()->bitmap.isMarked(this, color); -} - -bool -Cell::markIfUnmarked(uint32_t color /* = BLACK */) const -{ - AssertValidColor(this, color); - return chunk()->bitmap.markIfUnmarked(this, color); -} - -void -Cell::unmark(uint32_t color) const -{ - JS_ASSERT(color != BLACK); - AssertValidColor(this, color); - chunk()->bitmap.unmark(this, color); -} - -JSCompartment * -Cell::compartment() const -{ - return arenaHeader()->compartment; -} - -#ifdef DEBUG -bool -Cell::isAligned() const -{ - return Arena::isAligned(address(), arenaHeader()->getThingSize()); -} -#endif - -} /* namespace gc */ - -} /* namespace js */ - -#endif /* gc_heap_h___ */ diff --git a/scripting/javascript/spidermonkey-ios/include/gc/Root.h b/scripting/javascript/spidermonkey-ios/include/gc/Root.h index d83e21856f..33c648c7b5 100644 --- a/scripting/javascript/spidermonkey-ios/include/gc/Root.h +++ b/scripting/javascript/spidermonkey-ios/include/gc/Root.h @@ -77,8 +77,14 @@ class MutableHandleBase {}; namespace JS { +class AutoAssertNoGC; + template class MutableHandle; +JS_FRIEND_API(void) EnterAssertNoGCScope(); +JS_FRIEND_API(void) LeaveAssertNoGCScope(); +JS_FRIEND_API(bool) InNoGCScope(); + /* * Handle provides an implicit constructor for NullPtr so that, given: * foo(Handle h); @@ -316,6 +322,169 @@ class InternalHandle } }; +#ifdef DEBUG +template +class IntermediateNoGC +{ + T t_; + + public: + IntermediateNoGC(const T &t) : t_(t) { + EnterAssertNoGCScope(); + } + IntermediateNoGC(const IntermediateNoGC &) { + EnterAssertNoGCScope(); + } + ~IntermediateNoGC() { + LeaveAssertNoGCScope(); + } + + const T &operator->() { return t_; } + operator const T &() { return t_; } +}; +#endif + +/* + * Return wraps GC things that are returned from accessor methods. The + * wrapper helps to ensure correct rooting of the returned pointer and safe + * access while unrooted. + * + * Example usage in a method declaration: + * + * class Foo { + * HeapPtrScript script_; + * ... + * public: + * Return script() { return script_; } + * }; + * + * Example usage of method (1): + * + * Foo foo(...); + * RootedScript script(cx, foo->script()); + * + * Example usage of method (2): + * + * Foo foo(...); + * foo->script()->needsArgsObj(); + * + * The purpose of this class is to assert eagerly on incorrect use of GC thing + * pointers. For example: + * + * RootedShape shape(cx, ...); + * shape->parent.init(js_NewGCThing(cx, ...)); + * + * In this expression, C++ is allowed to order these calls as follows: + * + * Call Effect + * ---- ------ + * 1) RootedShape::operator-> Stores shape::ptr_ to stack. + * 2) js_NewGCThing Triggers GC and compaction of shapes. This + * moves shape::ptr_ to a new location. + * 3) HeapPtrObject::init This call takes the relocated shape::ptr_ + * as |this|, crashing or, worse, corrupting + * the program's state on the first access + * to a member variable. + * + * If Shape::parent were an accessor function returning a Return, this + * could not happen: Return ensures either immediate rooting or no GC within + * the same expression. + */ +template +class Return +{ + friend class Rooted; + + const T ptr_; + + public: + template + Return(const S &ptr, + typename mozilla::EnableIf::value, int>::Type dummy = 0) + : ptr_(ptr) + {} + + Return(NullPtr) : ptr_(NULL) {} + + /* + * |get(AutoAssertNoGC &)| is the safest way to access a Return without + * rooting it first: it is impossible to call this method without an + * AutoAssertNoGC in scope, so the compiler will automatically catch any + * incorrect usage. + * + * Example: + * AutoAssertNoGC nogc; + * RawScript script = fun->script().get(nogc); + */ + const T &get(AutoAssertNoGC &) const { + return ptr_; + } + + /* + * |operator->|'s result cannot be stored in a local variable, so it is safe + * to use in a CanGC context iff no GC can occur anywhere within the same + * expression (generally from one |;| to the next). |operator->| uses a + * temporary object as a guard and will assert if a CanGC context is + * encountered before the next C++ Sequence Point. + * + * INCORRECT: + * fun->script()->bindings = myBindings->clone(cx, ...); + * + * The compiler is allowed to reorder |fun->script()::operator->()| above + * the call to |clone(cx, ...)|. In this case, the RawScript C++ stores on + * the stack may be corrupted by a GC under |clone|. The subsequent + * dereference of this pointer to get |bindings| will result in an invalid + * access. This wrapper ensures that such usage asserts in DEBUG builds when + * it encounters this situation. Without this assertion, it is possible for + * such access to corrupt program state instead of crashing immediately. + * + * CORRECT: + * RootedScript clone(cx, myBindings->clone(cx, ...)); + * fun->script()->bindings = clone; + */ +#ifdef DEBUG + IntermediateNoGC operator->() const { + return IntermediateNoGC(ptr_); + } +#else + const T &operator->() const { + return ptr_; + } +#endif + + /* + * |unsafeGet()| is unsafe for most uses. Although it performs similar + * checking to |operator->|, its result can be stored to a local variable. + * For this reason, it should only be used when it would be incorrect or + * absurd to create a new Rooted for its use: e.g. for assertions. + */ +#ifdef DEBUG + IntermediateNoGC unsafeGet() const { + return IntermediateNoGC(ptr_); + } +#else + const T &unsafeGet() const { + return ptr_; + } +#endif + + /* + * |operator==| is safe to use in any context. It is present to allow: + * JS_ASSERT(myScript == fun->script().unsafeGet()); + * + * To be rewritten as: + * JS_ASSERT(fun->script() == myScript); + * + * Note: the new order tells C++ to use |Return::operator=| + * instead of direct pointer comparison. + */ + bool operator==(const T &other) { return ptr_ == other; } + bool operator!=(const T &other) { return ptr_ != other; } + bool operator==(const Return &other) { return ptr_ == other.ptr_; } + bool operator==(const JS::Handle &other) { return ptr_ == other.get(); } + inline bool operator==(const Rooted &other); +}; + /* * By default, pointers should use the inheritance hierarchy to find their * ThingRootKind. Some pointer types are explicitly set in jspubtd.h so that @@ -332,12 +501,6 @@ struct RootMethods static bool poisoned(T *v) { return IsPoisonedPtr(v); } }; -#if !(defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)) -// Defined in vm/String.h. -template <> -class Rooted; -#endif - template class RootedBase {}; @@ -356,27 +519,23 @@ class Rooted : public RootedBase { #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) ContextFriendFields *cx = ContextFriendFields::get(cxArg); - - ThingRootKind kind = RootMethods::kind(); - this->stack = reinterpret_cast**>(&cx->thingGCRooters[kind]); - this->prev = *stack; - *stack = this; - - JS_ASSERT(!RootMethods::poisoned(ptr)); + commonInit(cx->thingGCRooters); #endif } void init(JSRuntime *rtArg) { #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) - RuntimeFriendFields *rt = const_cast(RuntimeFriendFields::get(rtArg)); + PerThreadDataFriendFields *pt = PerThreadDataFriendFields::getMainThread(rtArg); + commonInit(pt->thingGCRooters); +#endif + } - ThingRootKind kind = RootMethods::kind(); - this->stack = reinterpret_cast**>(&rt->thingGCRooters[kind]); - this->prev = *stack; - *stack = this; - - JS_ASSERT(!RootMethods::poisoned(ptr)); + void init(js::PerThreadData *ptArg) + { +#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) + PerThreadDataFriendFields *pt = PerThreadDataFriendFields::get(ptArg); + commonInit(pt->thingGCRooters); #endif } @@ -413,6 +572,40 @@ class Rooted : public RootedBase init(cx); } + Rooted(js::PerThreadData *pt + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : ptr(RootMethods::initial()) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + init(pt); + } + + Rooted(js::PerThreadData *pt, T initial + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : ptr(initial) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + init(pt); + } + + template + Rooted(JSContext *cx, const Return &initial + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : ptr(initial.ptr_) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + init(cx); + } + + template + Rooted(js::PerThreadData *pt, const Return &initial + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : ptr(initial.ptr_) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + init(pt); + } + ~Rooted() { #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) @@ -445,7 +638,25 @@ class Rooted : public RootedBase return ptr; } + template + T & operator =(const Return &value) + { + ptr = value.ptr_; + return ptr; + } + private: + void commonInit(Rooted **thingGCRooters) { +#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) + ThingRootKind kind = RootMethods::kind(); + this->stack = reinterpret_cast**>(&thingGCRooters[kind]); + this->prev = *stack; + *stack = this; + + JS_ASSERT(!RootMethods::poisoned(ptr)); +#endif + } + #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) Rooted **stack, *prev; #endif @@ -455,6 +666,19 @@ class Rooted : public RootedBase Rooted(const Rooted &) MOZ_DELETE; }; +#if !(defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)) +// Defined in vm/String.h. +template <> +class Rooted; +#endif + +template +bool +Return::operator==(const Rooted &other) +{ + return ptr_ == other.get(); +} + typedef Rooted RootedObject; typedef Rooted RootedFunction; typedef Rooted RootedScript; @@ -550,10 +774,6 @@ MutableHandle::MutableHandle(js::Rooted *root, ptr = root->address(); } -JS_FRIEND_API(void) EnterAssertNoGCScope(); -JS_FRIEND_API(void) LeaveAssertNoGCScope(); -JS_FRIEND_API(bool) InNoGCScope(); - /* * The scoped guard object AutoAssertNoGC forces the GC to assert if a GC is * attempted while the guard object is live. If you have a GC-unsafe operation @@ -581,15 +801,11 @@ public: /* * AssertCanGC will assert if it is called inside of an AutoAssertNoGC region. */ -#ifdef DEBUG JS_ALWAYS_INLINE void AssertCanGC() { JS_ASSERT(!InNoGCScope()); } -#else -# define AssertCanGC() -#endif #if defined(DEBUG) && defined(JS_GC_ZEAL) && defined(JSGC_ROOT_ANALYSIS) && !defined(JS_THREADSAFE) extern void diff --git a/scripting/javascript/spidermonkey-ios/include/gc/Statistics.h b/scripting/javascript/spidermonkey-ios/include/gc/Statistics.h deleted file mode 100644 index e0434b4d61..0000000000 --- a/scripting/javascript/spidermonkey-ios/include/gc/Statistics.h +++ /dev/null @@ -1,193 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef jsgc_statistics_h___ -#define jsgc_statistics_h___ - -#include - -#include "jsfriendapi.h" -#include "jspubtd.h" -#include "jsutil.h" - -struct JSCompartment; - -namespace js { -namespace gcstats { - -enum Phase { - PHASE_GC_BEGIN, - PHASE_WAIT_BACKGROUND_THREAD, - PHASE_PURGE, - PHASE_MARK, - PHASE_MARK_DISCARD_CODE, - PHASE_MARK_ROOTS, - PHASE_MARK_TYPES, - PHASE_MARK_DELAYED, - PHASE_MARK_WEAK, - PHASE_MARK_GRAY, - PHASE_MARK_GRAY_WEAK, - PHASE_FINALIZE_START, - PHASE_SWEEP, - PHASE_SWEEP_ATOMS, - PHASE_SWEEP_COMPARTMENTS, - PHASE_SWEEP_TABLES, - PHASE_SWEEP_OBJECT, - PHASE_SWEEP_STRING, - PHASE_SWEEP_SCRIPT, - PHASE_SWEEP_SHAPE, - PHASE_SWEEP_IONCODE, - PHASE_SWEEP_DISCARD_CODE, - PHASE_DISCARD_ANALYSIS, - PHASE_DISCARD_TI, - PHASE_FREE_TI_ARENA, - PHASE_SWEEP_TYPES, - PHASE_CLEAR_SCRIPT_ANALYSIS, - PHASE_FINALIZE_END, - PHASE_DESTROY, - PHASE_GC_END, - - PHASE_LIMIT -}; - -enum Stat { - STAT_NEW_CHUNK, - STAT_DESTROY_CHUNK, - - STAT_LIMIT -}; - -class StatisticsSerializer; - -struct Statistics { - Statistics(JSRuntime *rt); - ~Statistics(); - - void beginPhase(Phase phase); - void endPhase(Phase phase); - - void beginSlice(int collectedCount, int compartmentCount, gcreason::Reason reason); - void endSlice(); - - void reset(const char *reason) { slices.back().resetReason = reason; } - void nonincremental(const char *reason) { nonincrementalReason = reason; } - - void count(Stat s) { - JS_ASSERT(s < STAT_LIMIT); - counts[s]++; - } - - int64_t beginSCC(); - void endSCC(unsigned scc, int64_t start); - - jschar *formatMessage(); - jschar *formatJSON(uint64_t timestamp); - - private: - JSRuntime *runtime; - - int64_t startupTime; - - FILE *fp; - bool fullFormat; - - /* - * GCs can't really nest, but a second GC can be triggered from within the - * JSGC_END callback. - */ - int gcDepth; - - int collectedCount; - int compartmentCount; - const char *nonincrementalReason; - - struct SliceData { - SliceData(gcreason::Reason reason, int64_t start, size_t startFaults) - : reason(reason), resetReason(NULL), start(start), startFaults(startFaults) - { - PodArrayZero(phaseTimes); - } - - gcreason::Reason reason; - const char *resetReason; - int64_t start, end; - size_t startFaults, endFaults; - int64_t phaseTimes[PHASE_LIMIT]; - - int64_t duration() const { return end - start; } - }; - - Vector slices; - - /* Most recent time when the given phase started. */ - int64_t phaseStartTimes[PHASE_LIMIT]; - - /* Total time in a given phase for this GC. */ - int64_t phaseTimes[PHASE_LIMIT]; - - /* Total time in a given phase over all GCs. */ - int64_t phaseTotals[PHASE_LIMIT]; - - /* Number of events of this type for this GC. */ - unsigned int counts[STAT_LIMIT]; - - /* Allocated space before the GC started. */ - size_t preBytes; - - /* Sweep times for SCCs of compartments. */ - Vector sccTimes; - - void beginGC(); - void endGC(); - - void gcDuration(int64_t *total, int64_t *maxPause); - void sccDurations(int64_t *total, int64_t *maxPause); - void printStats(); - bool formatData(StatisticsSerializer &ss, uint64_t timestamp); - - double computeMMU(int64_t resolution); -}; - -struct AutoGCSlice { - AutoGCSlice(Statistics &stats, int collectedCount, int compartmentCount, gcreason::Reason reason - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : stats(stats) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; - stats.beginSlice(collectedCount, compartmentCount, reason); - } - ~AutoGCSlice() { stats.endSlice(); } - - Statistics &stats; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - -struct AutoPhase { - AutoPhase(Statistics &stats, Phase phase JS_GUARD_OBJECT_NOTIFIER_PARAM) - : stats(stats), phase(phase) { JS_GUARD_OBJECT_NOTIFIER_INIT; stats.beginPhase(phase); } - ~AutoPhase() { stats.endPhase(phase); } - - Statistics &stats; - Phase phase; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - -struct AutoSCC { - AutoSCC(Statistics &stats, unsigned scc JS_GUARD_OBJECT_NOTIFIER_PARAM) - : stats(stats), scc(scc) { JS_GUARD_OBJECT_NOTIFIER_INIT; start = stats.beginSCC(); } - ~AutoSCC() { stats.endSCC(scc, start); } - - Statistics &stats; - unsigned scc; - int64_t start; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - -} /* namespace gcstats */ -} /* namespace js */ - -#endif /* jsgc_statistics_h___ */ diff --git a/scripting/javascript/spidermonkey-ios/include/gc/StoreBuffer.h b/scripting/javascript/spidermonkey-ios/include/gc/StoreBuffer.h deleted file mode 100644 index 9240e93f10..0000000000 --- a/scripting/javascript/spidermonkey-ios/include/gc/StoreBuffer.h +++ /dev/null @@ -1,398 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifdef JSGC_GENERATIONAL -#ifndef jsgc_storebuffer_h___ -#define jsgc_storebuffer_h___ - -#include "jsgc.h" -#include "jsalloc.h" - -#include "gc/Marking.h" - -namespace js { -namespace gc { - -/* - * Note: this is a stub Nursery that does not actually contain a heap, just a - * set of pointers which are "inside" the nursery to implement verification. - */ -class Nursery -{ - HashSet, SystemAllocPolicy> nursery; - - public: - Nursery() : nursery() {} - - bool enable() { - if (!nursery.initialized()) - return nursery.init(); - return true; - } - - void disable() { - if (!nursery.initialized()) - return; - nursery.finish(); - } - - bool isInside(void *cell) const { - JS_ASSERT((uintptr_t(cell) & 0x3) == 0); - return nursery.initialized() && nursery.has(cell); - } - - void insertPointer(void *cell) { - nursery.putNew(cell); - } -}; - -/* - * BufferableRef represents an abstract reference for use in the generational - * GC's remembered set. Entries in the store buffer that cannot be represented - * with the simple pointer-to-a-pointer scheme must derive from this class and - * use the generic store buffer interface. - */ -class BufferableRef -{ - public: - virtual bool match(void *location) = 0; - virtual void mark(JSTracer *trc) = 0; -}; - -/* - * HashKeyRef represents a reference to a HashTable key. Manual HashTable - * barriers should should instantiate this template with their own table/key - * type to insert into the generic buffer with putGeneric. - */ -template -class HashKeyRef : public BufferableRef -{ - Map *map; - Key key; - - typedef typename Map::Ptr Ptr; - - public: - HashKeyRef(Map *m, const Key &k) : map(m), key(k) {} - - bool match(void *location) { - Ptr p = map->lookup(key); - if (!p) - return false; - return &p->key == location; - } - - void mark(JSTracer *trc) {} -}; - -/* - * The StoreBuffer observes all writes that occur in the system and performs - * efficient filtering of them to derive a remembered set for nursery GC. - */ -class StoreBuffer -{ - /* TODO: profile to find the ideal size for these. */ - static const size_t ValueBufferSize = 1 * 1024 * sizeof(Value *); - static const size_t CellBufferSize = 2 * 1024 * sizeof(Cell **); - static const size_t SlotBufferSize = 2 * 1024 * (sizeof(JSObject *) + sizeof(uint32_t)); - static const size_t RelocValueBufferSize = 1 * 1024 * sizeof(Value *); - static const size_t RelocCellBufferSize = 1 * 1024 * sizeof(Cell **); - static const size_t GenericBufferSize = 1 * 1024 * sizeof(int); - static const size_t TotalSize = ValueBufferSize + CellBufferSize + - SlotBufferSize + RelocValueBufferSize + RelocCellBufferSize + - GenericBufferSize; - - typedef HashSet, SystemAllocPolicy> EdgeSet; - - /* - * This buffer holds only a single type of edge. Using this buffer is more - * efficient than the generic buffer when many writes will be to the same - * type of edge: e.g. Value or Cell*. - */ - template - class MonoTypeBuffer - { - friend class StoreBuffer; - - StoreBuffer *owner; - Nursery *nursery; - - T *base; /* Pointer to the start of the buffer. */ - T *pos; /* Pointer to the current insertion position. */ - T *top; /* Pointer to one element after the end. */ - - MonoTypeBuffer(StoreBuffer *owner, Nursery *nursery) - : owner(owner), nursery(nursery), base(NULL), pos(NULL), top(NULL) - {} - - MonoTypeBuffer &operator=(const MonoTypeBuffer& other) MOZ_DELETE; - - bool enable(uint8_t *region, size_t len); - void disable(); - - bool isEmpty() const { return pos == base; } - bool isFull() const { JS_ASSERT(pos <= top); return pos == top; } - - /* Compaction algorithms. */ - void compactNotInSet(); - - /* - * Attempts to reduce the usage of the buffer by removing unnecessary - * entries. - */ - virtual void compact(); - - /* Add one item to the buffer. */ - void put(const T &v); - - /* For verification. */ - bool accumulateEdges(EdgeSet &edges); - }; - - /* - * Overrides the MonoTypeBuffer to support pointers that may be moved in - * memory outside of the GC's control. - */ - template - class RelocatableMonoTypeBuffer : public MonoTypeBuffer - { - friend class StoreBuffer; - - RelocatableMonoTypeBuffer(StoreBuffer *owner, Nursery *nursery) - : MonoTypeBuffer(owner, nursery) - {} - - /* Override compaction to filter out removed items. */ - void compactMoved(); - virtual void compact(); - - /* Record a removal from the buffer. */ - void unput(const T &v); - }; - - class GenericBuffer - { - friend class StoreBuffer; - - StoreBuffer *owner; - Nursery *nursery; - - uint8_t *base; /* Pointer to start of buffer. */ - uint8_t *pos; /* Pointer to current buffer position. */ - uint8_t *top; /* Pointer to one past the last entry. */ - - GenericBuffer(StoreBuffer *owner, Nursery *nursery) - : owner(owner), nursery(nursery) - {} - - GenericBuffer &operator=(const GenericBuffer& other) MOZ_DELETE; - - bool enable(uint8_t *region, size_t len); - void disable(); - - /* Check if a pointer is present in the buffer. */ - bool containsEdge(void *location) const; - - template - void put(const T &t) { - /* Check if we have been enabled. */ - if (!pos) - return; - - /* Check for overflow. */ - if (top - pos < (unsigned)(sizeof(unsigned) + sizeof(T))) { - owner->setOverflowed(); - return; - } - - *((unsigned *)pos) = sizeof(T); - pos += sizeof(unsigned); - - T *p = (T *)pos; - new (p) T(t); - pos += sizeof(T); - } - }; - - class CellPtrEdge - { - friend class StoreBuffer; - friend class StoreBuffer::MonoTypeBuffer; - friend class StoreBuffer::RelocatableMonoTypeBuffer; - - Cell **edge; - - CellPtrEdge(Cell **v) : edge(v) {} - bool operator==(const CellPtrEdge &other) const { return edge == other.edge; } - bool operator!=(const CellPtrEdge &other) const { return edge != other.edge; } - - void *location() const { return (void *)edge; } - - bool inRememberedSet(Nursery *n) { - return !n->isInside(edge) && n->isInside(*edge); - } - - bool isNullEdge() const { - return !*edge; - } - - CellPtrEdge tagged() const { return CellPtrEdge((Cell **)(uintptr_t(edge) | 1)); } - CellPtrEdge untagged() const { return CellPtrEdge((Cell **)(uintptr_t(edge) & ~1)); } - bool isTagged() const { return bool(uintptr_t(edge) & 1); } - }; - - class ValueEdge - { - friend class StoreBuffer; - friend class StoreBuffer::MonoTypeBuffer; - friend class StoreBuffer::RelocatableMonoTypeBuffer; - - Value *edge; - - ValueEdge(Value *v) : edge(v) {} - bool operator==(const ValueEdge &other) const { return edge == other.edge; } - bool operator!=(const ValueEdge &other) const { return edge != other.edge; } - - void *deref() const { return edge->isGCThing() ? edge->toGCThing() : NULL; } - void *location() const { return (void *)edge; } - - bool inRememberedSet(Nursery *n) { - return !n->isInside(edge) && n->isInside(deref()); - } - - bool isNullEdge() const { - return !deref(); - } - - ValueEdge tagged() const { return ValueEdge((Value *)(uintptr_t(edge) | 1)); } - ValueEdge untagged() const { return ValueEdge((Value *)(uintptr_t(edge) & ~1)); } - bool isTagged() const { return bool(uintptr_t(edge) & 1); } - }; - - struct SlotEdge - { - friend class StoreBuffer; - friend class StoreBuffer::MonoTypeBuffer; - - JSObject *object; - uint32_t offset; - - SlotEdge(JSObject *object, uint32_t offset) : object(object), offset(offset) {} - - bool operator==(const SlotEdge &other) const { - return object == other.object && offset == other.offset; - } - - bool operator!=(const SlotEdge &other) const { - return object != other.object || offset != other.offset; - } - - HeapSlot *slotLocation() const { - if (object->isDenseArray()) { - if (offset >= object->getDenseArrayInitializedLength()) - return NULL; - return (HeapSlot *)&object->getDenseArrayElement(offset); - } - if (offset >= object->slotSpan()) - return NULL; - return &object->getSlotRef(offset); - } - - void *deref() const { - HeapSlot *loc = slotLocation(); - return (loc && loc->isGCThing()) ? loc->toGCThing() : NULL; - } - - void *location() const { - return (void *)slotLocation(); - } - - bool inRememberedSet(Nursery *n) { - return !n->isInside(object) && n->isInside(deref()); - } - - bool isNullEdge() const { - return !deref(); - } - }; - - MonoTypeBuffer bufferVal; - MonoTypeBuffer bufferCell; - MonoTypeBuffer bufferSlot; - RelocatableMonoTypeBuffer bufferRelocVal; - RelocatableMonoTypeBuffer bufferRelocCell; - GenericBuffer bufferGeneric; - - Nursery *nursery; - - void *buffer; - - bool overflowed; - bool enabled; - - /* For the verifier. */ - EdgeSet edgeSet; - - /* For use by our owned buffers. */ - void setOverflowed() { overflowed = true; } - - public: - StoreBuffer(Nursery *n) - : bufferVal(this, n), bufferCell(this, n), bufferSlot(this, n), - bufferRelocVal(this, n), bufferRelocCell(this, n), bufferGeneric(this, n), - nursery(n), buffer(NULL), overflowed(false), enabled(false) - {} - - bool enable(); - void disable(); - bool isEnabled() { return enabled; } - - /* Get the overflowed status. */ - bool hasOverflowed() const { return overflowed; } - - /* Insert a single edge into the buffer/remembered set. */ - void putValue(Value *v) { - bufferVal.put(v); - } - void putCell(Cell **o) { - bufferCell.put(o); - } - void putSlot(JSObject *obj, uint32_t slot) { - bufferSlot.put(SlotEdge(obj, slot)); - } - - /* Insert or update a single edge in the Relocatable buffer. */ - void putRelocatableValue(Value *v) { - bufferRelocVal.put(v); - } - void putRelocatableCell(Cell **c) { - bufferRelocCell.put(c); - } - void removeRelocatableValue(Value *v) { - bufferRelocVal.unput(v); - } - void removeRelocatableCell(Cell **c) { - bufferRelocCell.unput(c); - } - - /* Insert an entry into the generic buffer. */ - template - void putGeneric(const T &t) { - bufferGeneric.put(t); - } - - /* For the verifier. */ - bool coalesceForVerification(); - void releaseVerificationData(); - bool containsEdgeAt(void *loc) const; -}; - -} /* namespace gc */ -} /* namespace js */ - -#endif /* jsgc_storebuffer_h___ */ -#endif /* JSGC_GENERATIONAL */ diff --git a/scripting/javascript/spidermonkey-ios/include/js-config.h b/scripting/javascript/spidermonkey-ios/include/js-config.h index c98a0830d8..b406044d90 100644 --- a/scripting/javascript/spidermonkey-ios/include/js-config.h +++ b/scripting/javascript/spidermonkey-ios/include/js-config.h @@ -60,7 +60,7 @@ /* Some mozilla code uses JS-friend APIs that depend on JS_METHODJIT being correct. */ -#define JS_METHODJIT 1 +/* #undef JS_METHODJIT */ /* Define to 1 to enable support for E4X (ECMA-357), 0 to disable it. */ #define JS_HAS_XML_SUPPORT 1 diff --git a/scripting/javascript/spidermonkey-ios/include/js.msg b/scripting/javascript/spidermonkey-ios/include/js.msg index 92d4519c6f..bb2f65627c 100644 --- a/scripting/javascript/spidermonkey-ios/include/js.msg +++ b/scripting/javascript/spidermonkey-ios/include/js.msg @@ -375,3 +375,5 @@ MSG_DEF(JSMSG_MUST_REPORT_UNDEFINED, 321, 0, JSEXN_TYPEERR, "proxy must report MSG_DEF(JSMSG_CANT_SET_NW_NC, 322, 0, JSEXN_TYPEERR, "proxy can't successfully set a non-writable, non-configurable property") MSG_DEF(JSMSG_CANT_SET_WO_SETTER, 323, 0, JSEXN_TYPEERR, "proxy can't succesfully set an accessor property without a setter") MSG_DEF(JSMSG_DEBUG_BAD_REFERENT, 324, 2, JSEXN_TYPEERR, "{0} does not refer to {1}") +MSG_DEF(JSMSG_DEBUG_WRAPPER_IN_WAY, 325, 2, JSEXN_TYPEERR, "{0} is a wrapper around {1}, but a direct reference is required") +MSG_DEF(JSMSG_UNWRAP_DENIED, 326, 0, JSEXN_ERR, "permission denied to unwrap object") diff --git a/scripting/javascript/spidermonkey-ios/include/js/HashTable.h b/scripting/javascript/spidermonkey-ios/include/js/HashTable.h index bef5851548..d5710aed71 100644 --- a/scripting/javascript/spidermonkey-ios/include/js/HashTable.h +++ b/scripting/javascript/spidermonkey-ios/include/js/HashTable.h @@ -5,74 +5,621 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef jshashtable_h_ -#define jshashtable_h_ +#ifndef js_HashTable_h__ +#define js_HashTable_h__ +#include "js/TemplateLib.h" +#include "js/Utility.h" #include "mozilla/Attributes.h" -#include "TemplateLib.h" -#include "Utility.h" - namespace js { class TempAllocPolicy; +template struct DefaultHasher; +template class HashMapEntry; +namespace detail { + template class HashTableEntry; + template class HashTable; +} /*****************************************************************************/ +// A JS-friendly, STL-like container providing a hash-based map from keys to +// values. In particular, HashMap calls constructors and destructors of all +// objects added so non-PODs may be used safely. +// +// Key/Value requirements: +// - movable, destructible, assignable +// HashPolicy requirements: +// - see Hash Policy section below +// AllocPolicy: +// - see jsalloc.h +// +// Note: +// - HashMap is not reentrant: Key/Value/HashPolicy/AllocPolicy members +// called by HashMap must not call back into the same HashMap object. +// - Due to the lack of exception handling, the user must call |init()|. +template , + class AllocPolicy = TempAllocPolicy> +class HashMap +{ + typedef HashMapEntry TableEntry; + + struct MapHashPolicy : HashPolicy + { + typedef Key KeyType; + static const Key &getKey(TableEntry &e) { return e.key; } + static void setKey(TableEntry &e, Key &k) { const_cast(e.key) = k; } + }; + + typedef detail::HashTable Impl; + Impl impl; + + public: + typedef typename HashPolicy::Lookup Lookup; + typedef TableEntry Entry; + + // HashMap construction is fallible (due to OOM); thus the user must call + // init after constructing a HashMap and check the return value. + HashMap(AllocPolicy a = AllocPolicy()) : impl(a) {} + bool init(uint32_t len = 16) { return impl.init(len); } + bool initialized() const { return impl.initialized(); } + + // Return whether the given lookup value is present in the map. E.g.: + // + // typedef HashMap HM; + // HM h; + // if (HM::Ptr p = h.lookup(3)) { + // const HM::Entry &e = *p; // p acts like a pointer to Entry + // assert(p->key == 3); // Entry contains the key + // char val = p->value; // and value + // } + // + // Also see the definition of Ptr in HashTable above (with T = Entry). + typedef typename Impl::Ptr Ptr; + Ptr lookup(const Lookup &l) const { return impl.lookup(l); } + + // Assuming |p.found()|, remove |*p|. + void remove(Ptr p) { impl.remove(p); } + + // Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient + // insertion of Key |k| (where |HashPolicy::match(k,l) == true|) using + // |add(p,k,v)|. After |add(p,k,v)|, |p| points to the new Entry. E.g.: + // + // typedef HashMap HM; + // HM h; + // HM::AddPtr p = h.lookupForAdd(3); + // if (!p) { + // if (!h.add(p, 3, 'a')) + // return false; + // } + // const HM::Entry &e = *p; // p acts like a pointer to Entry + // assert(p->key == 3); // Entry contains the key + // char val = p->value; // and value + // + // Also see the definition of AddPtr in HashTable above (with T = Entry). + // + // N.B. The caller must ensure that no mutating hash table operations + // occur between a pair of |lookupForAdd| and |add| calls. To avoid + // looking up the key a second time, the caller may use the more efficient + // relookupOrAdd method. This method reuses part of the hashing computation + // to more efficiently insert the key if it has not been added. For + // example, a mutation-handling version of the previous example: + // + // HM::AddPtr p = h.lookupForAdd(3); + // if (!p) { + // call_that_may_mutate_h(); + // if (!h.relookupOrAdd(p, 3, 'a')) + // return false; + // } + // const HM::Entry &e = *p; + // assert(p->key == 3); + // char val = p->value; + typedef typename Impl::AddPtr AddPtr; + AddPtr lookupForAdd(const Lookup &l) const { + return impl.lookupForAdd(l); + } + + template + bool add(AddPtr &p, const KeyInput &k, const ValueInput &v) { + Entry e(k, v); + return impl.add(p, Move(e)); + } + + bool add(AddPtr &p, const Key &k) { + Entry e(k, Value()); + return impl.add(p, Move(e)); + } + + template + bool relookupOrAdd(AddPtr &p, const KeyInput &k, const ValueInput &v) { + Entry e(k, v); + return impl.relookupOrAdd(p, k, Move(e)); + } + + // |all()| returns a Range containing |count()| elements. E.g.: + // + // typedef HashMap HM; + // HM h; + // for (HM::Range r = h.all(); !r.empty(); r.popFront()) + // char c = r.front().value; + // + // Also see the definition of Range in HashTable above (with T = Entry). + typedef typename Impl::Range Range; + Range all() const { return impl.all(); } + + // Typedef for the enumeration class. An Enum may be used to examine and + // remove table entries: + // + // typedef HashMap HM; + // HM s; + // for (HM::Enum e(s); !e.empty(); e.popFront()) + // if (e.front().value == 'l') + // e.removeFront(); + // + // Table resize may occur in Enum's destructor. Also see the definition of + // Enum in HashTable above (with T = Entry). + typedef typename Impl::Enum Enum; + + // Remove all entries. This does not shrink the table. For that consider + // using the finish() method. + void clear() { impl.clear(); } + + // Remove all the entries and release all internal buffers. The map must + // be initialized again before any use. + void finish() { impl.finish(); } + + // Does the table contain any entries? + bool empty() const { return impl.empty(); } + + // Number of live elements in the map. + uint32_t count() const { return impl.count(); } + + // Total number of allocation in the dynamic table. Note: resize will + // happen well before count() == capacity(). + size_t capacity() const { return impl.capacity(); } + + // Don't just call |impl.sizeOfExcludingThis()| because there's no + // guarantee that |impl| is the first field in HashMap. + size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const { + return impl.sizeOfExcludingThis(mallocSizeOf); + } + size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const { + return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf); + } + + // If |generation()| is the same before and after a HashMap operation, + // pointers into the table remain valid. + unsigned generation() const { return impl.generation(); } + + /************************************************** Shorthand operations */ + + bool has(const Lookup &l) const { + return impl.lookup(l) != NULL; + } + + // Overwrite existing value with v. Return false on oom. + template + bool put(const KeyInput &k, const ValueInput &v) { + AddPtr p = lookupForAdd(k); + if (p) { + p->value = v; + return true; + } + return add(p, k, v); + } + + // Like put, but assert that the given key is not already present. + template + bool putNew(const KeyInput &k, const ValueInput &v) { + Entry e(k, v); + return impl.putNew(k, Move(e)); + } + + // Add (k,defaultValue) if |k| is not found. Return a false-y Ptr on oom. + Ptr lookupWithDefault(const Key &k, const Value &defaultValue) { + AddPtr p = lookupForAdd(k); + if (p) + return p; + (void)add(p, k, defaultValue); // p is left false-y on oom. + return p; + } + + // Remove if present. + void remove(const Lookup &l) { + if (Ptr p = lookup(l)) + remove(p); + } + + private: + // Not implicitly copyable (expensive). May add explicit |clone| later. + HashMap(const HashMap &hm) MOZ_DELETE; + HashMap &operator=(const HashMap &hm) MOZ_DELETE; + + friend class Impl::Enum; + + typedef typename tl::StaticAssert::result>::result keyAssert; + typedef typename tl::StaticAssert::result>::result valAssert; +}; + +/*****************************************************************************/ + +// A JS-friendly, STL-like container providing a hash-based set of values. In +// particular, HashSet calls constructors and destructors of all objects added +// so non-PODs may be used safely. +// +// T requirements: +// - movable, destructible, assignable +// HashPolicy requirements: +// - see Hash Policy section below +// AllocPolicy: +// - see jsalloc.h +// +// Note: +// - HashSet is not reentrant: T/HashPolicy/AllocPolicy members called by +// HashSet must not call back into the same HashSet object. +// - Due to the lack of exception handling, the user must call |init()|. +template , + class AllocPolicy = TempAllocPolicy> +class HashSet +{ + struct SetOps : HashPolicy + { + typedef T KeyType; + static const KeyType &getKey(const T &t) { return t; } + static void setKey(T &t, KeyType &k) { t = k; } + }; + + typedef detail::HashTable Impl; + Impl impl; + + public: + typedef typename HashPolicy::Lookup Lookup; + typedef T Entry; + + // HashSet construction is fallible (due to OOM); thus the user must call + // init after constructing a HashSet and check the return value. + HashSet(AllocPolicy a = AllocPolicy()) : impl(a) {} + bool init(uint32_t len = 16) { return impl.init(len); } + bool initialized() const { return impl.initialized(); } + + // Return whether the given lookup value is present in the map. E.g.: + // + // typedef HashSet HS; + // HS h; + // if (HS::Ptr p = h.lookup(3)) { + // assert(*p == 3); // p acts like a pointer to int + // } + // + // Also see the definition of Ptr in HashTable above. + typedef typename Impl::Ptr Ptr; + Ptr lookup(const Lookup &l) const { return impl.lookup(l); } + + // Assuming |p.found()|, remove |*p|. + void remove(Ptr p) { impl.remove(p); } + + // Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient + // insertion of T value |t| (where |HashPolicy::match(t,l) == true|) using + // |add(p,t)|. After |add(p,t)|, |p| points to the new element. E.g.: + // + // typedef HashSet HS; + // HS h; + // HS::AddPtr p = h.lookupForAdd(3); + // if (!p) { + // if (!h.add(p, 3)) + // return false; + // } + // assert(*p == 3); // p acts like a pointer to int + // + // Also see the definition of AddPtr in HashTable above. + // + // N.B. The caller must ensure that no mutating hash table operations + // occur between a pair of |lookupForAdd| and |add| calls. To avoid + // looking up the key a second time, the caller may use the more efficient + // relookupOrAdd method. This method reuses part of the hashing computation + // to more efficiently insert the key if it has not been added. For + // example, a mutation-handling version of the previous example: + // + // HS::AddPtr p = h.lookupForAdd(3); + // if (!p) { + // call_that_may_mutate_h(); + // if (!h.relookupOrAdd(p, 3, 3)) + // return false; + // } + // assert(*p == 3); + // + // Note that relookupOrAdd(p,l,t) performs Lookup using |l| and adds the + // entry |t|, where the caller ensures match(l,t). + typedef typename Impl::AddPtr AddPtr; + AddPtr lookupForAdd(const Lookup &l) const { return impl.lookupForAdd(l); } + + bool add(AddPtr &p, const T &t) { return impl.add(p, t); } + + bool relookupOrAdd(AddPtr &p, const Lookup &l, const T &t) { + return impl.relookupOrAdd(p, l, t); + } + + // |all()| returns a Range containing |count()| elements: + // + // typedef HashSet HS; + // HS h; + // for (HS::Range r = h.all(); !r.empty(); r.popFront()) + // int i = r.front(); + // + // Also see the definition of Range in HashTable above. + typedef typename Impl::Range Range; + Range all() const { return impl.all(); } + + // Typedef for the enumeration class. An Enum may be used to examine and + // remove table entries: + // + // typedef HashSet HS; + // HS s; + // for (HS::Enum e(s); !e.empty(); e.popFront()) + // if (e.front() == 42) + // e.removeFront(); + // + // Table resize may occur in Enum's destructor. Also see the definition of + // Enum in HashTable above. + typedef typename Impl::Enum Enum; + + // Remove all entries. This does not shrink the table. For that consider + // using the finish() method. + void clear() { impl.clear(); } + + // Remove all the entries and release all internal buffers. The set must + // be initialized again before any use. + void finish() { impl.finish(); } + + // Does the table contain any entries? + bool empty() const { return impl.empty(); } + + // Number of live elements in the map. + uint32_t count() const { return impl.count(); } + + // Total number of allocation in the dynamic table. Note: resize will + // happen well before count() == capacity(). + size_t capacity() const { return impl.capacity(); } + + // Don't just call |impl.sizeOfExcludingThis()| because there's no + // guarantee that |impl| is the first field in HashSet. + size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const { + return impl.sizeOfExcludingThis(mallocSizeOf); + } + size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const { + return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf); + } + + // If |generation()| is the same before and after a HashSet operation, + // pointers into the table remain valid. + unsigned generation() const { return impl.generation(); } + + /************************************************** Shorthand operations */ + + bool has(const Lookup &l) const { + return impl.lookup(l) != NULL; + } + + // Overwrite existing value with v. Return false on oom. + bool put(const T &t) { + AddPtr p = lookupForAdd(t); + return p ? true : add(p, t); + } + + // Like put, but assert that the given key is not already present. + bool putNew(const T &t) { + return impl.putNew(t, t); + } + + bool putNew(const Lookup &l, const T &t) { + return impl.putNew(l, t); + } + + void remove(const Lookup &l) { + if (Ptr p = lookup(l)) + remove(p); + } + + private: + // Not implicitly copyable (expensive). May add explicit |clone| later. + HashSet(const HashSet &hs) MOZ_DELETE; + HashSet &operator=(const HashSet &hs) MOZ_DELETE; + + friend class Impl::Enum; + + typedef typename tl::StaticAssert::result>::result _; +}; + +/*****************************************************************************/ + +// Hash Policy +// +// A hash policy P for a hash table with key-type Key must provide: +// - a type |P::Lookup| to use to lookup table entries; +// - a static member function |P::hash| with signature +// +// static js::HashNumber hash(Lookup) +// +// to use to hash the lookup type; and +// - a static member function |P::match| with signature +// +// static bool match(Key, Lookup) +// +// to use to test equality of key and lookup values. +// +// Normally, Lookup = Key. In general, though, different values and types of +// values can be used to lookup and store. If a Lookup value |l| is != to the +// added Key value |k|, the user must ensure that |P::match(k,l)|. E.g.: +// +// js::HashSet::AddPtr p = h.lookup(l); +// if (!p) { +// assert(P::match(k, l)); // must hold +// h.add(p, k); +// } + +// Pointer hashing policy that strips the lowest zeroBits when calculating the +// hash to improve key distribution. +template +struct PointerHasher +{ + typedef Key Lookup; + static HashNumber hash(const Lookup &l) { + size_t word = reinterpret_cast(l) >> zeroBits; + JS_STATIC_ASSERT(sizeof(HashNumber) == 4); +#if JS_BYTES_PER_WORD == 4 + return HashNumber(word); +#else + JS_STATIC_ASSERT(sizeof word == 8); + return HashNumber((word >> 32) ^ word); +#endif + } + static bool match(const Key &k, const Lookup &l) { + return k == l; + } +}; + +// Default hash policy: just use the 'lookup' value. This of course only +// works if the lookup value is integral. HashTable applies ScrambleHashCode to +// the result of the 'hash' which means that it is 'ok' if the lookup value is +// not well distributed over the HashNumber domain. +template +struct DefaultHasher +{ + typedef Key Lookup; + static HashNumber hash(const Lookup &l) { + // Hash if can implicitly cast to hash number type. + return l; + } + static bool match(const Key &k, const Lookup &l) { + // Use builtin or overloaded operator==. + return k == l; + } +}; + +// Specialize hashing policy for pointer types. It assumes that the type is +// at least word-aligned. For types with smaller size use PointerHasher. +template +struct DefaultHasher : PointerHasher::result> +{}; + +/*****************************************************************************/ + +// Both HashMap and HashSet are implemented by a single HashTable that is even +// more heavily parameterized than the other two. This leaves HashTable gnarly +// and extremely coupled to HashMap and HashSet; thus code should not use +// HashTable directly. + +template +class HashMapEntry +{ + template friend class detail::HashTable; + template friend class detail::HashTableEntry; + + HashMapEntry(const HashMapEntry &) MOZ_DELETE; + void operator=(const HashMapEntry &) MOZ_DELETE; + + public: + template + HashMapEntry(const KeyInput &k, const ValueInput &v) : key(k), value(v) {} + + HashMapEntry(MoveRef rhs) + : key(Move(rhs->key)), value(Move(rhs->value)) { } + + const Key key; + Value value; +}; + +namespace tl { + +template +struct IsPodType > { + static const bool result = IsPodType::result; +}; + +template +struct IsPodType > +{ + static const bool result = IsPodType::result && IsPodType::result; +}; + +} // namespace tl + namespace detail { template class HashTable; template -class HashTableEntry { - HashNumber keyHash; - +class HashTableEntry +{ + template friend class HashTable; typedef typename tl::StripConst::result NonConstT; + HashNumber keyHash; + mozilla::AlignedStorage2 mem; + static const HashNumber sFreeKey = 0; static const HashNumber sRemovedKey = 1; static const HashNumber sCollisionBit = 1; - template friend class HashTable; + // Assumed by calloc in createTable. + JS_STATIC_ASSERT(sFreeKey == 0); static bool isLiveHash(HashNumber hash) { return hash > sRemovedKey; } + HashTableEntry(const HashTableEntry &) MOZ_DELETE; + void operator=(const HashTableEntry &) MOZ_DELETE; + ~HashTableEntry() MOZ_DELETE; + public: - HashTableEntry() : keyHash(0), t() {} - HashTableEntry(MoveRef rhs) : keyHash(rhs->keyHash), t(Move(rhs->t)) { } - void operator=(const HashTableEntry &rhs) { keyHash = rhs.keyHash; t = rhs.t; } - void operator=(MoveRef rhs) { keyHash = rhs->keyHash; t = Move(rhs->t); } + // NB: HashTableEntry is treated as a POD: no constructor or destructor calls. - NonConstT t; - - bool isFree() const { return keyHash == sFreeKey; } - void setFree() { keyHash = sFreeKey; t = T(); } - bool isRemoved() const { return keyHash == sRemovedKey; } - void setRemoved() { keyHash = sRemovedKey; t = T(); } - bool isLive() const { return isLiveHash(keyHash); } - void setLive(HashNumber hn) { JS_ASSERT(isLiveHash(hn)); keyHash = hn; } - - void setCollision() { JS_ASSERT(isLive()); keyHash |= sCollisionBit; } - void setCollision(HashNumber collisionBit) { - JS_ASSERT(isLive()); keyHash |= collisionBit; + void destroyIfLive() { + if (isLive()) + mem.addr()->~T(); + } + + void destroy() { + JS_ASSERT(isLive()); + mem.addr()->~T(); + } + + void swap(HashTableEntry *other) { + Swap(keyHash, other->keyHash); + Swap(mem, other->mem); + } + + T &get() { JS_ASSERT(isLive()); return *mem.addr(); } + + bool isFree() const { return keyHash == sFreeKey; } + void clearLive() { JS_ASSERT(isLive()); keyHash = sFreeKey; mem.addr()->~T(); } + void clear() { if (isLive()) mem.addr()->~T(); keyHash = sFreeKey; } + bool isRemoved() const { return keyHash == sRemovedKey; } + void removeLive() { JS_ASSERT(isLive()); keyHash = sRemovedKey; mem.addr()->~T(); } + bool isLive() const { return isLiveHash(keyHash); } + void setCollision() { JS_ASSERT(isLive()); keyHash |= sCollisionBit; } + void setCollision(HashNumber bit) { JS_ASSERT(isLive()); keyHash |= bit; } + void unsetCollision() { keyHash &= ~sCollisionBit; } + bool hasCollision() const { return keyHash & sCollisionBit; } + bool matchHash(HashNumber hn) { return (keyHash & ~sCollisionBit) == hn; } + HashNumber getKeyHash() const { return keyHash & ~sCollisionBit; } + + template + void setLive(HashNumber hn, const U &u) + { + JS_ASSERT(!isLive()); + keyHash = hn; + new(mem.addr()) T(u); + JS_ASSERT(isLive()); } - void unsetCollision() { keyHash &= ~sCollisionBit; } - bool hasCollision() const { return keyHash & sCollisionBit; } - bool matchHash(HashNumber hn) { return (keyHash & ~sCollisionBit) == hn; } - HashNumber getKeyHash() const { JS_ASSERT(!hasCollision()); return keyHash; } }; -/* - * js::detail::HashTable is an implementation detail of the js::HashMap and - * js::HashSet templates. For js::Hash{Map,Set} API documentation and examples, - * skip to the end of the detail namespace. - */ - -/* Reusable implementation of HashMap and HashSet. */ template class HashTable : private AllocPolicy { @@ -83,12 +630,10 @@ class HashTable : private AllocPolicy public: typedef HashTableEntry Entry; - /* - * A nullable pointer to a hash table element. A Ptr |p| can be tested - * either explicitly |if (p.found()) p->...| or using boolean conversion - * |if (p) p->...|. Ptr objects must not be used after any mutating hash - * table operations unless |generation()| is tested. - */ + // A nullable pointer to a hash table element. A Ptr |p| can be tested + // either explicitly |if (p.found()) p->...| or using boolean conversion + // |if (p) p->...|. Ptr objects must not be used after any mutating hash + // table operations unless |generation()| is tested. class Ptr { friend class HashTable; @@ -101,7 +646,7 @@ class HashTable : private AllocPolicy Ptr(Entry &entry) : entry(&entry) {} public: - /* Leaves Ptr uninitialized. */ + // Leaves Ptr uninitialized. Ptr() { #ifdef DEBUG entry = (Entry *)0xbad; @@ -113,29 +658,27 @@ class HashTable : private AllocPolicy bool operator==(const Ptr &rhs) const { JS_ASSERT(found() && rhs.found()); return entry == rhs.entry; } bool operator!=(const Ptr &rhs) const { return !(*this == rhs); } - T &operator*() const { return entry->t; } - T *operator->() const { return &entry->t; } + T &operator*() const { return entry->get(); } + T *operator->() const { return &entry->get(); } }; - /* A Ptr that can be used to add a key after a failed lookup. */ + // A Ptr that can be used to add a key after a failed lookup. class AddPtr : public Ptr { friend class HashTable; HashNumber keyHash; - DebugOnly mutationCount; + mozilla::DebugOnly mutationCount; AddPtr(Entry &entry, HashNumber hn) : Ptr(entry), keyHash(hn) {} public: - /* Leaves AddPtr uninitialized. */ + // Leaves AddPtr uninitialized. AddPtr() {} }; - /* - * A collection of hash table entries. The collection is enumerated by - * calling |front()| followed by |popFront()| as long as |!empty()|. As - * with Ptr/AddPtr, Range objects must not be used after any mutating hash - * table operation unless the |generation()| is tested. - */ + // A collection of hash table entries. The collection is enumerated by + // calling |front()| followed by |popFront()| as long as |!empty()|. As + // with Ptr/AddPtr, Range objects must not be used after any mutating hash + // table operation unless the |generation()| is tested. class Range { protected: @@ -147,7 +690,7 @@ class HashTable : private AllocPolicy } Entry *cur, *end; - DebugOnly validEntry; + mozilla::DebugOnly validEntry; public: Range() : cur(NULL), end(NULL), validEntry(false) {} @@ -159,7 +702,7 @@ class HashTable : private AllocPolicy T &front() const { JS_ASSERT(validEntry); JS_ASSERT(!empty()); - return cur->t; + return cur->get(); } void popFront() { @@ -170,15 +713,13 @@ class HashTable : private AllocPolicy } }; - /* - * A Range whose lifetime delimits a mutating enumeration of a hash table. - * Since rehashing when elements were removed during enumeration would be - * bad, it is postponed until |endEnumeration()| is called. If - * |endEnumeration()| is not called before an Enum's constructor, it will - * be called automatically. Since |endEnumeration()| touches the hash - * table, the user must ensure that the hash table is still alive when this - * happens. - */ + // A Range whose lifetime delimits a mutating enumeration of a hash table. + // Since rehashing when elements were removed during enumeration would be + // bad, it is postponed until |endEnumeration()| is called. If + // |endEnumeration()| is not called before an Enum's constructor, it will + // be called automatically. Since |endEnumeration()| touches the hash + // table, the user must ensure that the hash table is still alive when this + // happens. class Enum : public Range { friend class HashTable; @@ -195,31 +736,27 @@ class HashTable : private AllocPolicy template explicit Enum(Map &map) : Range(map.all()), table(map.impl), rekeyed(false), removed(false) {} - /* - * Removes the |front()| element from the table, leaving |front()| - * invalid until the next call to |popFront()|. For example: - * - * HashSet s; - * for (HashSet::Enum e(s); !e.empty(); e.popFront()) - * if (e.front() == 42) - * e.removeFront(); - */ + // Removes the |front()| element from the table, leaving |front()| + // invalid until the next call to |popFront()|. For example: + // + // HashSet s; + // for (HashSet::Enum e(s); !e.empty(); e.popFront()) + // if (e.front() == 42) + // e.removeFront(); void removeFront() { table.remove(*this->cur); removed = true; this->validEntry = false; } - /* - * Removes the |front()| element and re-inserts it into the table with - * a new key at the new Lookup position. |front()| is invalid after - * this operation until the next call to |popFront()|. - */ + // Removes the |front()| element and re-inserts it into the table with + // a new key at the new Lookup position. |front()| is invalid after + // this operation until the next call to |popFront()|. void rekeyFront(const Lookup &l, const Key &k) { - typename HashTableEntry::NonConstT t = this->cur->t; + typename HashTableEntry::NonConstT t(Move(this->cur->get())); HashPolicy::setKey(t, const_cast(k)); table.remove(*this->cur); - table.putNewInfallible(l, t); + table.putNewInfallible(l, Move(t)); rekeyed = true; this->validEntry = false; } @@ -228,7 +765,7 @@ class HashTable : private AllocPolicy rekeyFront(k, k); } - /* Potentially rehashes the table. */ + // Potentially rehashes the table. ~Enum() { if (rekeyed) table.checkOverRemoved(); @@ -238,29 +775,31 @@ class HashTable : private AllocPolicy }; private: - uint32_t hashShift; /* multiplicative hash shift */ - uint32_t entryCount; /* number of entries in table */ - uint32_t gen; /* entry storage generation number */ - uint32_t removedCount; /* removed entry sentinels in table */ - Entry *table; /* entry storage */ + uint32_t hashShift; // multiplicative hash shift + uint32_t entryCount; // number of entries in table + uint32_t gen; // entry storage generation number + uint32_t removedCount; // removed entry sentinels in table + Entry *table; // entry storage - void setTableSizeLog2(unsigned sizeLog2) { + void setTableSizeLog2(unsigned sizeLog2) + { hashShift = sHashBits - sizeLog2; } #ifdef DEBUG - mutable struct Stats { - uint32_t searches; /* total number of table searches */ - uint32_t steps; /* hash chain links traversed */ - uint32_t hits; /* searches that found key */ - uint32_t misses; /* searches that didn't find key */ - uint32_t addOverRemoved; /* adds that recycled a removed entry */ - uint32_t removes; /* calls to remove */ - uint32_t removeFrees; /* calls to remove that freed the entry */ - uint32_t grows; /* table expansions */ - uint32_t shrinks; /* table contractions */ - uint32_t compresses; /* table compressions */ - uint32_t rehashes; /* tombstone decontaminations */ + mutable struct Stats + { + uint32_t searches; // total number of table searches + uint32_t steps; // hash chain links traversed + uint32_t hits; // searches that found key + uint32_t misses; // searches that didn't find key + uint32_t addOverRemoved; // adds that recycled a removed entry + uint32_t removes; // calls to remove + uint32_t removeFrees; // calls to remove that freed the entry + uint32_t grows; // table expansions + uint32_t shrinks; // table contractions + uint32_t compresses; // table compressions + uint32_t rehashes; // tombstone decontaminations } stats; # define METER(x) x #else @@ -268,29 +807,25 @@ class HashTable : private AllocPolicy #endif friend class js::ReentrancyGuard; - mutable DebugOnly entered; - DebugOnly mutationCount; + mutable mozilla::DebugOnly entered; + mozilla::DebugOnly mutationCount; - /* The default initial capacity is 16, but you can ask for as small as 4. */ + // The default initial capacity is 16, but you can ask for as small as 4. static const unsigned sMinSizeLog2 = 2; static const unsigned sMinSize = 1 << sMinSizeLog2; - static const unsigned sDefaultInitSizeLog2 = 4; - public: - static const unsigned sDefaultInitSize = 1 << sDefaultInitSizeLog2; - private: static const unsigned sMaxInit = JS_BIT(23); static const unsigned sMaxCapacity = JS_BIT(24); static const unsigned sHashBits = tl::BitSize::result; - static const uint8_t sMinAlphaFrac = 64; /* (0x100 * .25) taken from jsdhash.h */ - static const uint8_t sMaxAlphaFrac = 192; /* (0x100 * .75) taken from jsdhash.h */ - static const uint8_t sInvMaxAlpha = 171; /* (ceil(0x100 / .75) >> 1) */ + static const uint8_t sMinAlphaFrac = 64; // (0x100 * .25) + static const uint8_t sMaxAlphaFrac = 192; // (0x100 * .75) + static const uint8_t sInvMaxAlpha = 171; // (ceil(0x100 / .75) >> 1) static const HashNumber sFreeKey = Entry::sFreeKey; static const HashNumber sRemovedKey = Entry::sRemovedKey; static const HashNumber sCollisionBit = Entry::sCollisionBit; static void staticAsserts() { - /* Rely on compiler "constant overflow warnings". */ + // Rely on compiler "constant overflow warnings". JS_STATIC_ASSERT(((sMaxInit * sInvMaxAlpha) >> 7) < sMaxCapacity); JS_STATIC_ASSERT((sMaxCapacity * sInvMaxAlpha) <= UINT32_MAX); JS_STATIC_ASSERT((sMaxCapacity * sizeof(Entry)) <= UINT32_MAX); @@ -305,7 +840,7 @@ class HashTable : private AllocPolicy { HashNumber keyHash = ScrambleHashCode(HashPolicy::hash(l)); - /* Avoid reserved hash codes. */ + // Avoid reserved hash codes. if (!isLiveHash(keyHash)) keyHash -= (sRemovedKey + 1); return keyHash & ~sCollisionBit; @@ -313,18 +848,14 @@ class HashTable : private AllocPolicy static Entry *createTable(AllocPolicy &alloc, uint32_t capacity) { - Entry *newTable = (Entry *)alloc.malloc_(capacity * sizeof(Entry)); - if (!newTable) - return NULL; - for (Entry *e = newTable, *end = e + capacity; e < end; ++e) - new(e) Entry(); - return newTable; + // See JS_STATIC_ASSERT(sFreeKey == 0) in HashTableEntry. + return (Entry *)alloc.calloc_(capacity * sizeof(Entry)); } static void destroyTable(AllocPolicy &alloc, Entry *oldTable, uint32_t capacity) { for (Entry *e = oldTable, *end = e + capacity; e < end; ++e) - e->~Entry(); + e->destroyIfLive(); alloc.free_(oldTable); } @@ -344,10 +875,8 @@ class HashTable : private AllocPolicy { JS_ASSERT(!initialized()); - /* - * Correct for sMaxAlphaFrac such that the table will not resize - * when adding 'length' entries. - */ + // Correct for sMaxAlphaFrac such that the table will not resize + // when adding 'length' entries. if (length > sMaxInit) { this->reportAllocOverflow(); return false; @@ -357,7 +886,7 @@ class HashTable : private AllocPolicy if (capacity < sMinSize) capacity = sMinSize; - /* FIXME: use JS_CEILING_LOG2 when PGO stops crashing (bug 543034). */ + // FIXME: use JS_CEILING_LOG2 when PGO stops crashing (bug 543034). uint32_t roundUp = sMinSize, roundUpLog2 = sMinSizeLog2; while (roundUp < capacity) { roundUp <<= 1; @@ -388,16 +917,19 @@ class HashTable : private AllocPolicy } private: - static HashNumber hash1(HashNumber hash0, uint32_t shift) { - return hash0 >> shift; + HashNumber hash1(HashNumber hash0) const + { + return hash0 >> hashShift; } - struct DoubleHash { + struct DoubleHash + { HashNumber h2; HashNumber sizeMask; }; - DoubleHash hash2(HashNumber curKeyHash, uint32_t hashShift) const { + DoubleHash hash2(HashNumber curKeyHash) const + { unsigned sizeLog2 = sHashBits - hashShift; DoubleHash dh = { ((curKeyHash << sizeLog2) >> hashShift) | 1, @@ -406,22 +938,26 @@ class HashTable : private AllocPolicy return dh; } - static HashNumber applyDoubleHash(HashNumber h1, const DoubleHash &dh) { + static HashNumber applyDoubleHash(HashNumber h1, const DoubleHash &dh) + { return (h1 - dh.h2) & dh.sizeMask; } - bool overloaded() { + bool overloaded() + { return entryCount + removedCount >= ((sMaxAlphaFrac * capacity()) >> 8); } - bool underloaded() { + bool underloaded() + { uint32_t tableCapacity = capacity(); return tableCapacity > sMinSize && entryCount <= ((sMinAlphaFrac * tableCapacity) >> 8); } - static bool match(Entry &e, const Lookup &l) { - return HashPolicy::match(HashPolicy::getKey(e.t), l); + static bool match(Entry &e, const Lookup &l) + { + return HashPolicy::match(HashPolicy::getKey(e.get()), l); } Entry &lookup(const Lookup &l, HashNumber keyHash, unsigned collisionBit) const @@ -432,26 +968,26 @@ class HashTable : private AllocPolicy JS_ASSERT(table); METER(stats.searches++); - /* Compute the primary hash address. */ - HashNumber h1 = hash1(keyHash, hashShift); + // Compute the primary hash address. + HashNumber h1 = hash1(keyHash); Entry *entry = &table[h1]; - /* Miss: return space for a new entry. */ + // Miss: return space for a new entry. if (entry->isFree()) { METER(stats.misses++); return *entry; } - /* Hit: return entry. */ + // Hit: return entry. if (entry->matchHash(keyHash) && match(*entry, l)) { METER(stats.hits++); return *entry; } - /* Collision: double hash. */ - DoubleHash dh = hash2(keyHash, hashShift); + // Collision: double hash. + DoubleHash dh = hash2(keyHash); - /* Save the first removed entry pointer so we can recycle later. */ + // Save the first removed entry pointer so we can recycle later. Entry *firstRemoved = NULL; while(true) { @@ -478,34 +1014,32 @@ class HashTable : private AllocPolicy } } - /* - * This is a copy of lookup hardcoded to the assumptions: - * 1. the lookup is a lookupForAdd - * 2. the key, whose |keyHash| has been passed is not in the table, - * 3. no entries have been removed from the table. - * This specialized search avoids the need for recovering lookup values - * from entries, which allows more flexible Lookup/Key types. - */ + // This is a copy of lookup hardcoded to the assumptions: + // 1. the lookup is a lookupForAdd + // 2. the key, whose |keyHash| has been passed is not in the table, + // 3. no entries have been removed from the table. + // This specialized search avoids the need for recovering lookup values + // from entries, which allows more flexible Lookup/Key types. Entry &findFreeEntry(HashNumber keyHash) { JS_ASSERT(!(keyHash & sCollisionBit)); JS_ASSERT(table); METER(stats.searches++); - /* N.B. the |keyHash| has already been distributed. */ + // We assume 'keyHash' has already been distributed. - /* Compute the primary hash address. */ - HashNumber h1 = hash1(keyHash, hashShift); + // Compute the primary hash address. + HashNumber h1 = hash1(keyHash); Entry *entry = &table[h1]; - /* Miss: return space for a new entry. */ + // Miss: return space for a new entry. if (!entry->isLive()) { METER(stats.misses++); return *entry; } - /* Collision: double hash. */ - DoubleHash dh = hash2(keyHash, hashShift); + // Collision: double hash. + DoubleHash dh = hash2(keyHash); while(true) { JS_ASSERT(!entry->isRemoved()); @@ -526,7 +1060,7 @@ class HashTable : private AllocPolicy RebuildStatus changeTableSize(int deltaLog2) { - /* Look, but don't touch, until we succeed in getting new entry store. */ + // Look, but don't touch, until we succeed in getting new entry store. Entry *oldTable = table; uint32_t oldCap = capacity(); uint32_t newLog2 = sHashBits - hashShift + deltaLog2; @@ -540,21 +1074,23 @@ class HashTable : private AllocPolicy if (!newTable) return RehashFailed; - /* We can't fail from here on, so update table parameters. */ + // We can't fail from here on, so update table parameters. setTableSizeLog2(newLog2); removedCount = 0; gen++; table = newTable; - /* Copy only live entries, leaving removed ones behind. */ + // Copy only live entries, leaving removed ones behind. for (Entry *src = oldTable, *end = src + oldCap; src < end; ++src) { if (src->isLive()) { - src->unsetCollision(); - findFreeEntry(src->getKeyHash()) = Move(*src); + HashNumber hn = src->getKeyHash(); + findFreeEntry(hn).setLive(hn, Move(src->get())); + src->destroy(); } } - destroyTable(*this, oldTable, oldCap); + // All entries have been destroyed, no need to destroyTable. + this->free_(oldTable); return Rehashed; } @@ -563,7 +1099,7 @@ class HashTable : private AllocPolicy if (!overloaded()) return NotOverloaded; - /* Compress if a quarter or more of all entries are removed. */ + // Compress if a quarter or more of all entries are removed. int deltaLog2; if (removedCount >= (capacity() >> 2)) { METER(stats.compresses++); @@ -576,7 +1112,7 @@ class HashTable : private AllocPolicy return changeTableSize(deltaLog2); } - /* Infallibly rehash the table if we are overloaded with removals. */ + // Infallibly rehash the table if we are overloaded with removals. void checkOverRemoved() { if (overloaded()) { @@ -592,11 +1128,11 @@ class HashTable : private AllocPolicy METER(stats.removes++); if (e.hasCollision()) { - e.setRemoved(); + e.removeLive(); removedCount++; } else { METER(stats.removeFrees++); - e.setFree(); + e.clearLive(); } entryCount--; mutationCount++; @@ -610,13 +1146,11 @@ class HashTable : private AllocPolicy } } - /* - * This is identical to changeTableSize(currentSize), but without requiring - * a second table. We do this by recycling the collision bits to tell us if - * the element is already inserted or still waiting to be inserted. Since - * already-inserted elements win any conflicts, we get the same table as we - * would have gotten through random insertion order. - */ + // This is identical to changeTableSize(currentSize), but without requiring + // a second table. We do this by recycling the collision bits to tell us if + // the element is already inserted or still waiting to be inserted. Since + // already-inserted elements win any conflicts, we get the same table as we + // would have gotten through random insertion order. void rehashTable() { removedCount = 0; @@ -632,12 +1166,12 @@ class HashTable : private AllocPolicy } HashNumber keyHash = src->getKeyHash(); - HashNumber h1 = hash1(keyHash, hashShift); - DoubleHash dh = hash2(keyHash, hashShift); + HashNumber h1 = hash1(keyHash); + DoubleHash dh = hash2(keyHash); Entry *tgt = &table[h1]; while (true) { if (!tgt->hasCollision()) { - Swap(*src, *tgt); + src->swap(tgt); tgt->setCollision(); break; } @@ -647,13 +1181,11 @@ class HashTable : private AllocPolicy } } - /* - * TODO: this algorithm leaves collision bits on *all* elements, even if - * they are on no collision path. We have the option of setting the - * collision bits correctly on a subsequent pass or skipping the rehash - * unless we are totally filled with tombstones: benchmark to find out - * which approach is best. - */ + // TODO: this algorithm leaves collision bits on *all* elements, even if + // they are on no collision path. We have the option of setting the + // collision bits correctly on a subsequent pass or skipping the rehash + // unless we are totally filled with tombstones: benchmark to find out + // which approach is best. } public: @@ -664,7 +1196,7 @@ class HashTable : private AllocPolicy } else { uint32_t tableCapacity = capacity(); for (Entry *e = table, *end = table + tableCapacity; e < end; ++e) - *e = Move(Entry()); + e->clear(); } removedCount = 0; entryCount = 0; @@ -686,46 +1218,55 @@ class HashTable : private AllocPolicy mutationCount++; } - Range all() const { + Range all() const + { JS_ASSERT(table); return Range(table, table + capacity()); } - bool empty() const { + bool empty() const + { JS_ASSERT(table); return !entryCount; } - uint32_t count() const { + uint32_t count() const + { JS_ASSERT(table); return entryCount; } - uint32_t capacity() const { + uint32_t capacity() const + { JS_ASSERT(table); return JS_BIT(sHashBits - hashShift); } - uint32_t generation() const { + uint32_t generation() const + { JS_ASSERT(table); return gen; } - size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const { + size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const + { return mallocSizeOf(table); } - size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const { + size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const + { return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf); } - Ptr lookup(const Lookup &l) const { + Ptr lookup(const Lookup &l) const + { ReentrancyGuard g(*this); HashNumber keyHash = prepareHash(l); return Ptr(lookup(l, keyHash, 0)); } - AddPtr lookupForAdd(const Lookup &l) const { + AddPtr lookupForAdd(const Lookup &l) const + { ReentrancyGuard g(*this); HashNumber keyHash = prepareHash(l); Entry &entry = lookup(l, keyHash, sCollisionBit); @@ -734,7 +1275,8 @@ class HashTable : private AllocPolicy return p; } - bool add(AddPtr &p) + template + bool add(AddPtr &p, const U &rhs) { ReentrancyGuard g(*this); JS_ASSERT(mutationCount == p.mutationCount); @@ -742,16 +1284,14 @@ class HashTable : private AllocPolicy JS_ASSERT(!p.found()); JS_ASSERT(!(p.keyHash & sCollisionBit)); - /* - * Changing an entry from removed to live does not affect whether we - * are overloaded and can be handled separately. - */ + // Changing an entry from removed to live does not affect whether we + // are overloaded and can be handled separately. if (p.entry->isRemoved()) { METER(stats.addOverRemoved++); removedCount--; p.keyHash |= sCollisionBit; } else { - /* Preserve the validity of |p.entry|. */ + // Preserve the validity of |p.entry|. RebuildStatus status = checkOverloaded(); if (status == RehashFailed) return false; @@ -759,34 +1299,14 @@ class HashTable : private AllocPolicy p.entry = &findFreeEntry(p.keyHash); } - p.entry->setLive(p.keyHash); + p.entry->setLive(p.keyHash, rhs); entryCount++; mutationCount++; return true; } - /* - * There is an important contract between the caller and callee for this - * function: if add() returns true, the caller must assign the T value - * which produced p before using the hashtable again. - */ - bool add(AddPtr &p, T** pentry) - { - if (!add(p)) - return false; - *pentry = &p.entry->t; - return true; - } - - bool add(AddPtr &p, const T &t) - { - if (!add(p)) - return false; - p.entry->t = t; - return true; - } - - void putNewInfallible(const Lookup &l, const T &t) + template + void putNewInfallible(const Lookup &l, const U &u) { JS_ASSERT(table); @@ -799,29 +1319,30 @@ class HashTable : private AllocPolicy keyHash |= sCollisionBit; } - entry->t = t; - entry->setLive(keyHash); + entry->setLive(keyHash, u); entryCount++; mutationCount++; } - bool putNew(const Lookup &l, const T &t) + template + bool putNew(const Lookup &l, const U &u) { if (checkOverloaded() == RehashFailed) return false; - putNewInfallible(l, t); + putNewInfallible(l, u); return true; } - bool relookupOrAdd(AddPtr& p, const Lookup &l, const T& t) + template + bool relookupOrAdd(AddPtr& p, const Lookup &l, const U &u) { p.mutationCount = mutationCount; { ReentrancyGuard g(*this); p.entry = &lookup(l, p.keyHash, sCollisionBit); } - return p.found() || add(p, t); + return p.found() || add(p, u); } void remove(Ptr p) @@ -836,593 +1357,8 @@ class HashTable : private AllocPolicy #undef METER }; -} /* namespace detail */ +} // namespace detail +} // namespace js -/*****************************************************************************/ +#endif // js_HashTable_h__ -/* - * Hash policy - * - * A hash policy P for a hash table with key-type Key must provide: - * - a type |P::Lookup| to use to lookup table entries; - * - a static member function |P::hash| with signature - * - * static js::HashNumber hash(Lookup) - * - * to use to hash the lookup type; and - * - a static member function |P::match| with signature - * - * static bool match(Key, Lookup) - * - * to use to test equality of key and lookup values. - * - * Normally, Lookup = Key. In general, though, different values and types of - * values can be used to lookup and store. If a Lookup value |l| is != to the - * added Key value |k|, the user must ensure that |P::match(k,l)|. E.g.: - * - * js::HashSet::AddPtr p = h.lookup(l); - * if (!p) { - * assert(P::match(k, l)); // must hold - * h.add(p, k); - * } - */ - -/* Default hashing policies. */ -template -struct DefaultHasher -{ - typedef Key Lookup; - static HashNumber hash(const Lookup &l) { - /* Hash if can implicitly cast to hash number type. */ - return l; - } - static bool match(const Key &k, const Lookup &l) { - /* Use builtin or overloaded operator==. */ - return k == l; - } -}; - -/* - * Pointer hashing policy that strips the lowest zeroBits when calculating the - * hash to improve key distribution. - */ -template -struct PointerHasher -{ - typedef Key Lookup; - static HashNumber hash(const Lookup &l) { - size_t word = reinterpret_cast(l) >> zeroBits; - JS_STATIC_ASSERT(sizeof(HashNumber) == 4); -#if JS_BYTES_PER_WORD == 4 - return HashNumber(word); -#else - JS_STATIC_ASSERT(sizeof word == 8); - return HashNumber((word >> 32) ^ word); -#endif - } - static bool match(const Key &k, const Lookup &l) { - return k == l; - } -}; - -template -struct TaggedPointerHasher -{ - typedef Key Lookup; - - static HashNumber hash(const Lookup &l) { - return PointerHasher::hash(l); - } - - static const uintptr_t COMPARE_MASK = uintptr_t(-1) - 1; - - static bool match(const Key &k, const Lookup &l) { - return (uintptr_t(k) & COMPARE_MASK) == uintptr_t(l); - } -}; - -/* - * Specialized hashing policy for pointer types. It assumes that the type is - * at least word-aligned. For types with smaller size use PointerHasher. - */ -template -struct DefaultHasher: PointerHasher::result> { }; - -/* Looking for a hasher for jsid? Try the DefaultHasher in jsatom.h. */ - -template -class HashMapEntry -{ - template friend class detail::HashTable; - template friend class detail::HashTableEntry; - void operator=(const HashMapEntry &rhs) { - const_cast(key) = rhs.key; - value = rhs.value; - } - - public: - HashMapEntry() : key(), value() {} - - template - HashMapEntry(const KeyInput &k, const ValueInput &v) : key(k), value(v) {} - - HashMapEntry(MoveRef rhs) - : key(Move(rhs->key)), value(Move(rhs->value)) { } - void operator=(MoveRef rhs) { - const_cast(key) = Move(rhs->key); - value = Move(rhs->value); - } - - const Key key; - Value value; -}; - -namespace tl { - -template -struct IsPodType > { - static const bool result = IsPodType::result; -}; - -template -struct IsPodType > -{ - static const bool result = IsPodType::result && IsPodType::result; -}; - -} /* namespace tl */ - -/* - * JS-friendly, STL-like container providing a hash-based map from keys to - * values. In particular, HashMap calls constructors and destructors of all - * objects added so non-PODs may be used safely. - * - * Key/Value requirements: - * - default constructible, copyable, destructible, assignable - * HashPolicy requirements: - * - see "Hash policy" above (default js::DefaultHasher) - * AllocPolicy: - * - see "Allocation policies" in jsalloc.h - * - * N.B: HashMap is not reentrant: Key/Value/HashPolicy/AllocPolicy members - * called by HashMap must not call back into the same HashMap object. - * N.B: Due to the lack of exception handling, the user must call |init()|. - */ -template , - class AllocPolicy = TempAllocPolicy> -class HashMap -{ - typedef typename tl::StaticAssert::result>::result keyAssert; - typedef typename tl::StaticAssert::result>::result valAssert; - - public: - typedef typename HashPolicy::Lookup Lookup; - - typedef HashMapEntry Entry; - - private: - /* Implement HashMap using HashTable. Lift |Key| operations to |Entry|. */ - struct MapHashPolicy : HashPolicy - { - typedef Key KeyType; - static const Key &getKey(Entry &e) { return e.key; } - static void setKey(Entry &e, Key &k) { const_cast(e.key) = k; } - }; - typedef detail::HashTable Impl; - - friend class Impl::Enum; - - Impl impl; - - public: - const static unsigned sDefaultInitSize = Impl::sDefaultInitSize; - - /* - * HashMap construction is fallible (due to OOM); thus the user must call - * init after constructing a HashMap and check the return value. - */ - HashMap(AllocPolicy a = AllocPolicy()) : impl(a) {} - bool init(uint32_t len = sDefaultInitSize) { return impl.init(len); } - bool initialized() const { return impl.initialized(); } - - /* - * Return whether the given lookup value is present in the map. E.g.: - * - * typedef HashMap HM; - * HM h; - * if (HM::Ptr p = h.lookup(3)) { - * const HM::Entry &e = *p; // p acts like a pointer to Entry - * assert(p->key == 3); // Entry contains the key - * char val = p->value; // and value - * } - * - * Also see the definition of Ptr in HashTable above (with T = Entry). - */ - typedef typename Impl::Ptr Ptr; - Ptr lookup(const Lookup &l) const { return impl.lookup(l); } - - /* Assuming |p.found()|, remove |*p|. */ - void remove(Ptr p) { impl.remove(p); } - - /* - * Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient - * insertion of Key |k| (where |HashPolicy::match(k,l) == true|) using - * |add(p,k,v)|. After |add(p,k,v)|, |p| points to the new Entry. E.g.: - * - * typedef HashMap HM; - * HM h; - * HM::AddPtr p = h.lookupForAdd(3); - * if (!p) { - * if (!h.add(p, 3, 'a')) - * return false; - * } - * const HM::Entry &e = *p; // p acts like a pointer to Entry - * assert(p->key == 3); // Entry contains the key - * char val = p->value; // and value - * - * Also see the definition of AddPtr in HashTable above (with T = Entry). - * - * N.B. The caller must ensure that no mutating hash table operations - * occur between a pair of |lookupForAdd| and |add| calls. To avoid - * looking up the key a second time, the caller may use the more efficient - * relookupOrAdd method. This method reuses part of the hashing computation - * to more efficiently insert the key if it has not been added. For - * example, a mutation-handling version of the previous example: - * - * HM::AddPtr p = h.lookupForAdd(3); - * if (!p) { - * call_that_may_mutate_h(); - * if (!h.relookupOrAdd(p, 3, 'a')) - * return false; - * } - * const HM::Entry &e = *p; - * assert(p->key == 3); - * char val = p->value; - */ - typedef typename Impl::AddPtr AddPtr; - AddPtr lookupForAdd(const Lookup &l) const { - return impl.lookupForAdd(l); - } - - template - bool add(AddPtr &p, const KeyInput &k, const ValueInput &v) { - Entry *pentry; - if (!impl.add(p, &pentry)) - return false; - const_cast(pentry->key) = k; - pentry->value = v; - return true; - } - - bool add(AddPtr &p, const Key &k, MoveRef v) { - Entry *pentry; - if (!impl.add(p, &pentry)) - return false; - const_cast(pentry->key) = k; - pentry->value = v; - return true; - } - - bool add(AddPtr &p, const Key &k) { - Entry *pentry; - if (!impl.add(p, &pentry)) - return false; - const_cast(pentry->key) = k; - return true; - } - - template - bool relookupOrAdd(AddPtr &p, const KeyInput &k, const ValueInput &v) { - return impl.relookupOrAdd(p, k, Entry(k, v)); - } - - /* - * |all()| returns a Range containing |count()| elements. E.g.: - * - * typedef HashMap HM; - * HM h; - * for (HM::Range r = h.all(); !r.empty(); r.popFront()) - * char c = r.front().value; - * - * Also see the definition of Range in HashTable above (with T = Entry). - */ - typedef typename Impl::Range Range; - Range all() const { return impl.all(); } - uint32_t count() const { return impl.count(); } - size_t capacity() const { return impl.capacity(); } - size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const { - return impl.sizeOfExcludingThis(mallocSizeOf); - } - size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const { - /* - * Don't just call |impl.sizeOfExcludingThis()| because there's no - * guarantee that |impl| is the first field in HashMap. - */ - return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf); - } - - /* - * Typedef for the enumeration class. An Enum may be used to examine and - * remove table entries: - * - * typedef HashMap HM; - * HM s; - * for (HM::Enum e(s); !e.empty(); e.popFront()) - * if (e.front().value == 'l') - * e.removeFront(); - * - * Table resize may occur in Enum's destructor. Also see the definition of - * Enum in HashTable above (with T = Entry). - */ - typedef typename Impl::Enum Enum; - - /* - * Remove all entries. This does not shrink the table. For that consider - * using the finish() method. - */ - void clear() { impl.clear(); } - - /* - * Remove all the entries and release all internal buffers. The map must - * be initialized again before any use. - */ - void finish() { impl.finish(); } - - /* Does the table contain any entries? */ - bool empty() const { return impl.empty(); } - - /* - * If |generation()| is the same before and after a HashMap operation, - * pointers into the table remain valid. - */ - unsigned generation() const { return impl.generation(); } - - /* Shorthand operations: */ - - bool has(const Lookup &l) const { - return impl.lookup(l) != NULL; - } - - /* Overwrite existing value with v. Return false on oom. */ - template - bool put(const KeyInput &k, const ValueInput &v) { - AddPtr p = lookupForAdd(k); - if (p) { - p->value = v; - return true; - } - return add(p, k, v); - } - - /* Like put, but assert that the given key is not already present. */ - bool putNew(const Key &k, const Value &v) { - return impl.putNew(k, Entry(k, v)); - } - - /* Add (k,defaultValue) if k no found. Return false-y Ptr on oom. */ - Ptr lookupWithDefault(const Key &k, const Value &defaultValue) { - AddPtr p = lookupForAdd(k); - if (p) - return p; - (void)add(p, k, defaultValue); /* p is left false-y on oom. */ - return p; - } - - /* Remove if present. */ - void remove(const Lookup &l) { - if (Ptr p = lookup(l)) - remove(p); - } - - private: - /* Not implicitly copyable (expensive). May add explicit |clone| later. */ - HashMap(const HashMap &hm) MOZ_DELETE; - HashMap &operator=(const HashMap &hm) MOZ_DELETE; -}; - -/* - * JS-friendly, STL-like container providing a hash-based set of values. In - * particular, HashSet calls constructors and destructors of all objects added - * so non-PODs may be used safely. - * - * T requirements: - * - default constructible, copyable, destructible, assignable - * HashPolicy requirements: - * - see "Hash policy" above (default js::DefaultHasher) - * AllocPolicy: - * - see "Allocation policies" in jsalloc.h - * - * N.B: HashSet is not reentrant: T/HashPolicy/AllocPolicy members called by - * HashSet must not call back into the same HashSet object. - * N.B: Due to the lack of exception handling, the user must call |init()|. - */ -template , class AllocPolicy = TempAllocPolicy> -class HashSet -{ - typedef typename HashPolicy::Lookup Lookup; - - /* Implement HashSet in terms of HashTable. */ - struct SetOps : HashPolicy { - typedef T KeyType; - static const KeyType &getKey(const T &t) { return t; } - static void setKey(T &t, KeyType &k) { t = k; } - }; - typedef detail::HashTable Impl; - - friend class Impl::Enum; - - Impl impl; - - public: - const static unsigned sDefaultInitSize = Impl::sDefaultInitSize; - - /* - * HashSet construction is fallible (due to OOM); thus the user must call - * init after constructing a HashSet and check the return value. - */ - HashSet(AllocPolicy a = AllocPolicy()) : impl(a) {} - bool init(uint32_t len = sDefaultInitSize) { return impl.init(len); } - bool initialized() const { return impl.initialized(); } - - /* - * Return whether the given lookup value is present in the map. E.g.: - * - * typedef HashSet HS; - * HS h; - * if (HS::Ptr p = h.lookup(3)) { - * assert(*p == 3); // p acts like a pointer to int - * } - * - * Also see the definition of Ptr in HashTable above. - */ - typedef typename Impl::Ptr Ptr; - Ptr lookup(const Lookup &l) const { return impl.lookup(l); } - - /* Assuming |p.found()|, remove |*p|. */ - void remove(Ptr p) { impl.remove(p); } - - /* - * Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient - * insertion of T value |t| (where |HashPolicy::match(t,l) == true|) using - * |add(p,t)|. After |add(p,t)|, |p| points to the new element. E.g.: - * - * typedef HashSet HS; - * HS h; - * HS::AddPtr p = h.lookupForAdd(3); - * if (!p) { - * if (!h.add(p, 3)) - * return false; - * } - * assert(*p == 3); // p acts like a pointer to int - * - * Also see the definition of AddPtr in HashTable above. - * - * N.B. The caller must ensure that no mutating hash table operations - * occur between a pair of |lookupForAdd| and |add| calls. To avoid - * looking up the key a second time, the caller may use the more efficient - * relookupOrAdd method. This method reuses part of the hashing computation - * to more efficiently insert the key if it has not been added. For - * example, a mutation-handling version of the previous example: - * - * HS::AddPtr p = h.lookupForAdd(3); - * if (!p) { - * call_that_may_mutate_h(); - * if (!h.relookupOrAdd(p, 3, 3)) - * return false; - * } - * assert(*p == 3); - * - * Note that relookupOrAdd(p,l,t) performs Lookup using l and adds the - * entry t, where the caller ensures match(l,t). - */ - typedef typename Impl::AddPtr AddPtr; - AddPtr lookupForAdd(const Lookup &l) const { - return impl.lookupForAdd(l); - } - - bool add(AddPtr &p, const T &t) { - return impl.add(p, t); - } - - bool relookupOrAdd(AddPtr &p, const Lookup &l, const T &t) { - return impl.relookupOrAdd(p, l, t); - } - - /* - * |all()| returns a Range containing |count()| elements: - * - * typedef HashSet HS; - * HS h; - * for (HS::Range r = h.all(); !r.empty(); r.popFront()) - * int i = r.front(); - * - * Also see the definition of Range in HashTable above. - */ - typedef typename Impl::Range Range; - Range all() const { return impl.all(); } - uint32_t count() const { return impl.count(); } - size_t capacity() const { return impl.capacity(); } - size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const { - return impl.sizeOfExcludingThis(mallocSizeOf); - } - size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const { - /* - * Don't just call |impl.sizeOfExcludingThis()| because there's no - * guarantee that |impl| is the first field in HashSet. - */ - return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf); - } - - /* - * Typedef for the enumeration class. An Enum may be used to examine and - * remove table entries: - * - * typedef HashSet HS; - * HS s; - * for (HS::Enum e(s); !e.empty(); e.popFront()) - * if (e.front() == 42) - * e.removeFront(); - * - * Table resize may occur in Enum's destructor. Also see the definition of - * Enum in HashTable above. - */ - typedef typename Impl::Enum Enum; - - /* - * Remove all entries. This does not shrink the table. For that consider - * using the finish() method. - */ - void clear() { impl.clear(); } - - /* - * Remove all the entries and release all internal buffers. The set must - * be initialized again before any use. - */ - void finish() { impl.finish(); } - - /* Does the table contain any entries? */ - bool empty() const { return impl.empty(); } - - /* - * If |generation()| is the same before and after a HashSet operation, - * pointers into the table remain valid. - */ - unsigned generation() const { return impl.generation(); } - - /* Shorthand operations: */ - - bool has(const Lookup &l) const { - return impl.lookup(l) != NULL; - } - - /* Overwrite existing value with v. Return false on oom. */ - bool put(const T &t) { - AddPtr p = lookupForAdd(t); - return p ? true : add(p, t); - } - - /* Like put, but assert that the given key is not already present. */ - bool putNew(const T &t) { - return impl.putNew(t, t); - } - - bool putNew(const Lookup &l, const T &t) { - return impl.putNew(l, t); - } - - void remove(const Lookup &l) { - if (Ptr p = lookup(l)) - remove(p); - } - - private: - /* Not implicitly copyable (expensive). May add explicit |clone| later. */ - HashSet(const HashSet &hs) MOZ_DELETE; - HashSet &operator=(const HashSet &hs) MOZ_DELETE; -}; - -} /* namespace js */ - -#endif diff --git a/scripting/javascript/spidermonkey-ios/include/js/HeapAPI.h b/scripting/javascript/spidermonkey-ios/include/js/HeapAPI.h new file mode 100644 index 0000000000..9e83bec84a --- /dev/null +++ b/scripting/javascript/spidermonkey-ios/include/js/HeapAPI.h @@ -0,0 +1,76 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef js_heap_api_h___ +#define js_heap_api_h___ + +/* These values are private to the JS engine. */ +namespace js { +namespace gc { + +/* + * Page size must be static to support our arena pointer optimizations, so we + * are forced to support each platform with non-4096 pages as a special case. + * Note: The freelist supports a maximum arena shift of 15. + * Note: Do not use JS_CPU_SPARC here, this header is used outside JS. + */ +#if (defined(SOLARIS) || defined(__FreeBSD__)) && \ + (defined(__sparc) || defined(__sparcv9) || defined(__ia64)) +const size_t PageShift = 13; +const size_t ArenaShift = PageShift; +#elif defined(__powerpc64__) +const size_t PageShift = 16; +const size_t ArenaShift = 12; +#else +const size_t PageShift = 12; +const size_t ArenaShift = PageShift; +#endif +const size_t PageSize = size_t(1) << PageShift; +const size_t ArenaSize = size_t(1) << ArenaShift; +const size_t ArenaMask = ArenaSize - 1; + +const size_t ChunkShift = 20; +const size_t ChunkSize = size_t(1) << ChunkShift; +const size_t ChunkMask = ChunkSize - 1; + +} /* namespace gc */ +} /* namespace js */ + +namespace JS { + +namespace shadow { + +struct ArenaHeader +{ + JSCompartment *compartment; +}; + +} /* namespace shadow */ + +static inline shadow::ArenaHeader * +GetGCThingArena(void *thing) +{ + uintptr_t addr = uintptr_t(thing); + addr &= ~js::gc::ArenaMask; + return reinterpret_cast(addr); +} + +static inline JSCompartment * +GetGCThingCompartment(void *thing) +{ + JS_ASSERT(thing); + return GetGCThingArena(thing)->compartment; +} + +static inline JSCompartment * +GetObjectCompartment(JSObject *obj) +{ + return GetGCThingCompartment(obj); +} + +} /* namespace JS */ + +#endif /* js_heap_api_h___ */ diff --git a/scripting/javascript/spidermonkey-ios/include/js/MemoryMetrics.h b/scripting/javascript/spidermonkey-ios/include/js/MemoryMetrics.h index 823e491c30..2f7b78505f 100644 --- a/scripting/javascript/spidermonkey-ios/include/js/MemoryMetrics.h +++ b/scripting/javascript/spidermonkey-ios/include/js/MemoryMetrics.h @@ -38,22 +38,37 @@ namespace JS { struct TypeInferenceSizes { TypeInferenceSizes() - : scripts(0) - , objects(0) - , tables(0) - , temporary(0) + : typeScripts(0) + , typeResults(0) + , analysisPool(0) + , typePool(0) + , pendingArrays(0) + , allocationSiteTables(0) + , arrayTypeTables(0) + , objectTypeTables(0) + , typeObjects(0) {} - size_t scripts; - size_t objects; - size_t tables; - size_t temporary; + size_t typeScripts; + size_t typeResults; + size_t analysisPool; + size_t typePool; + size_t pendingArrays; + size_t allocationSiteTables; + size_t arrayTypeTables; + size_t objectTypeTables; + size_t typeObjects; void add(TypeInferenceSizes &sizes) { - this->scripts += sizes.scripts; - this->objects += sizes.objects; - this->tables += sizes.tables; - this->temporary += sizes.temporary; + this->typeScripts += sizes.typeScripts; + this->typeResults += sizes.typeResults; + this->analysisPool += sizes.analysisPool; + this->typePool += sizes.typePool; + this->pendingArrays += sizes.pendingArrays; + this->allocationSiteTables += sizes.allocationSiteTables; + this->arrayTypeTables += sizes.arrayTypeTables; + this->objectTypeTables += sizes.objectTypeTables; + this->typeObjects += sizes.typeObjects; } }; @@ -129,10 +144,15 @@ struct CompartmentStats , extra2(0) , gcHeapArenaAdmin(0) , gcHeapUnusedGcThings(0) - , gcHeapObjectsNonFunction(0) + , gcHeapObjectsOrdinary(0) , gcHeapObjectsFunction(0) - , gcHeapStrings(0) - , gcHeapShapesTree(0) + , gcHeapObjectsDenseArray(0) + , gcHeapObjectsSlowArray(0) + , gcHeapObjectsCrossCompartmentWrapper(0) + , gcHeapStringsNormal(0) + , gcHeapStringsShort(0) + , gcHeapShapesTreeGlobalParented(0) + , gcHeapShapesTreeNonGlobalParented(0) , gcHeapShapesDict(0) , gcHeapShapesBase(0) , gcHeapScripts(0) @@ -147,7 +167,7 @@ struct CompartmentStats , objectsExtraRegExpStatics(0) , objectsExtraPropertyIteratorData(0) , objectsExtraPrivate(0) - , nonHugeStringChars(0) + , stringCharsNonHuge(0) , shapesExtraTreeTables(0) , shapesExtraDictTables(0) , shapesExtraTreeShapeKids(0) @@ -156,7 +176,7 @@ struct CompartmentStats , jaegerData(0) , ionData(0) , compartmentObject(0) - , crossCompartmentWrappers(0) + , crossCompartmentWrappersTable(0) , regexpCompartment(0) , debuggeesSet(0) {} @@ -166,10 +186,15 @@ struct CompartmentStats , extra2(other.extra2) , gcHeapArenaAdmin(other.gcHeapArenaAdmin) , gcHeapUnusedGcThings(other.gcHeapUnusedGcThings) - , gcHeapObjectsNonFunction(other.gcHeapObjectsNonFunction) + , gcHeapObjectsOrdinary(other.gcHeapObjectsOrdinary) , gcHeapObjectsFunction(other.gcHeapObjectsFunction) - , gcHeapStrings(other.gcHeapStrings) - , gcHeapShapesTree(other.gcHeapShapesTree) + , gcHeapObjectsDenseArray(other.gcHeapObjectsDenseArray) + , gcHeapObjectsSlowArray(other.gcHeapObjectsSlowArray) + , gcHeapObjectsCrossCompartmentWrapper(other.gcHeapObjectsCrossCompartmentWrapper) + , gcHeapStringsNormal(other.gcHeapStringsNormal) + , gcHeapStringsShort(other.gcHeapStringsShort) + , gcHeapShapesTreeGlobalParented(other.gcHeapShapesTreeGlobalParented) + , gcHeapShapesTreeNonGlobalParented(other.gcHeapShapesTreeNonGlobalParented) , gcHeapShapesDict(other.gcHeapShapesDict) , gcHeapShapesBase(other.gcHeapShapesBase) , gcHeapScripts(other.gcHeapScripts) @@ -184,7 +209,7 @@ struct CompartmentStats , objectsExtraRegExpStatics(other.objectsExtraRegExpStatics) , objectsExtraPropertyIteratorData(other.objectsExtraPropertyIteratorData) , objectsExtraPrivate(other.objectsExtraPrivate) - , nonHugeStringChars(other.nonHugeStringChars) + , stringCharsNonHuge(other.stringCharsNonHuge) , shapesExtraTreeTables(other.shapesExtraTreeTables) , shapesExtraDictTables(other.shapesExtraDictTables) , shapesExtraTreeShapeKids(other.shapesExtraTreeShapeKids) @@ -193,7 +218,7 @@ struct CompartmentStats , jaegerData(other.jaegerData) , ionData(other.ionData) , compartmentObject(other.compartmentObject) - , crossCompartmentWrappers(other.crossCompartmentWrappers) + , crossCompartmentWrappersTable(other.crossCompartmentWrappersTable) , regexpCompartment(other.regexpCompartment) , debuggeesSet(other.debuggeesSet) , typeInferenceSizes(other.typeInferenceSizes) @@ -210,10 +235,15 @@ struct CompartmentStats size_t gcHeapArenaAdmin; size_t gcHeapUnusedGcThings; - size_t gcHeapObjectsNonFunction; + size_t gcHeapObjectsOrdinary; size_t gcHeapObjectsFunction; - size_t gcHeapStrings; - size_t gcHeapShapesTree; + size_t gcHeapObjectsDenseArray; + size_t gcHeapObjectsSlowArray; + size_t gcHeapObjectsCrossCompartmentWrapper; + size_t gcHeapStringsNormal; + size_t gcHeapStringsShort; + size_t gcHeapShapesTreeGlobalParented; + size_t gcHeapShapesTreeNonGlobalParented; size_t gcHeapShapesDict; size_t gcHeapShapesBase; size_t gcHeapScripts; @@ -229,7 +259,7 @@ struct CompartmentStats size_t objectsExtraRegExpStatics; size_t objectsExtraPropertyIteratorData; size_t objectsExtraPrivate; - size_t nonHugeStringChars; + size_t stringCharsNonHuge; size_t shapesExtraTreeTables; size_t shapesExtraDictTables; size_t shapesExtraTreeShapeKids; @@ -238,7 +268,7 @@ struct CompartmentStats size_t jaegerData; size_t ionData; size_t compartmentObject; - size_t crossCompartmentWrappers; + size_t crossCompartmentWrappersTable; size_t regexpCompartment; size_t debuggeesSet; @@ -253,10 +283,15 @@ struct CompartmentStats ADD(gcHeapArenaAdmin); ADD(gcHeapUnusedGcThings); - ADD(gcHeapObjectsNonFunction); + ADD(gcHeapObjectsOrdinary); ADD(gcHeapObjectsFunction); - ADD(gcHeapStrings); - ADD(gcHeapShapesTree); + ADD(gcHeapObjectsDenseArray); + ADD(gcHeapObjectsSlowArray); + ADD(gcHeapObjectsCrossCompartmentWrapper); + ADD(gcHeapStringsNormal); + ADD(gcHeapStringsShort); + ADD(gcHeapShapesTreeGlobalParented); + ADD(gcHeapShapesTreeNonGlobalParented); ADD(gcHeapShapesDict); ADD(gcHeapShapesBase); ADD(gcHeapScripts); @@ -272,7 +307,7 @@ struct CompartmentStats ADD(objectsExtraRegExpStatics); ADD(objectsExtraPropertyIteratorData); ADD(objectsExtraPrivate); - ADD(nonHugeStringChars); + ADD(stringCharsNonHuge); ADD(shapesExtraTreeTables); ADD(shapesExtraDictTables); ADD(shapesExtraTreeShapeKids); @@ -281,7 +316,7 @@ struct CompartmentStats ADD(jaegerData); ADD(ionData); ADD(compartmentObject); - ADD(crossCompartmentWrappers); + ADD(crossCompartmentWrappersTable); ADD(regexpCompartment); ADD(debuggeesSet); diff --git a/scripting/javascript/spidermonkey-ios/include/js/Utility.h b/scripting/javascript/spidermonkey-ios/include/js/Utility.h index 3b03615515..7e4d4d5beb 100644 --- a/scripting/javascript/spidermonkey-ios/include/js/Utility.h +++ b/scripting/javascript/spidermonkey-ios/include/js/Utility.h @@ -21,7 +21,6 @@ #include "jstypes.h" -#ifdef __cplusplus # include "js/TemplateLib.h" # include "mozilla/Scoped.h" @@ -36,12 +35,8 @@ namespace js { /* The private namespace is a superset of the public/shared namespaces. */ using namespace JS; -using namespace mozilla; } /* namespace js */ -#endif /* __cplusplus */ - -JS_BEGIN_EXTERN_C /* * Pattern used to overwrite freed memory. If you are accessing an object with @@ -103,7 +98,7 @@ PrintBacktrace() int32_t OOM_traceIdx = 0; OOM_traceSize = backtrace(OOM_trace, JS_OOM_BACKTRACE_SIZE); OOM_traceSymbols = backtrace_symbols(OOM_trace, OOM_traceSize); - + if (!OOM_traceSymbols) return; @@ -171,6 +166,8 @@ static JS_INLINE void js_free(void* p) } #endif/* JS_USE_CUSTOM_ALLOCATOR */ +JS_BEGIN_EXTERN_C + /* * Replace bit-scanning code sequences with CPU-specific instructions to * speedup calculations of ceiling/floor log2. @@ -330,6 +327,8 @@ JS_PUBLIC_API(size_t) js_FloorLog2wImpl(size_t n); # error "NOT SUPPORTED" #endif +JS_END_EXTERN_C + /* * Internal function. * Compute the log of the least power of 2 greater than or equal to n. This is @@ -371,9 +370,6 @@ JS_FLOOR_LOG2W(size_t n) #define JS_ROTATE_LEFT32(a, bits) (((a) << (bits)) | ((a) >> (32 - (bits)))) #endif -JS_END_EXTERN_C - -#ifdef __cplusplus #include /* @@ -903,8 +899,6 @@ inline bool IsPoisonedPtr(T *v) } -#endif /* defined(__cplusplus) */ - /* * This is SpiderMonkey's equivalent to |nsMallocSizeOfFun|. */ diff --git a/scripting/javascript/spidermonkey-ios/include/js/Vector.h b/scripting/javascript/spidermonkey-ios/include/js/Vector.h index 05480632be..9f2377fbe6 100644 --- a/scripting/javascript/spidermonkey-ios/include/js/Vector.h +++ b/scripting/javascript/spidermonkey-ios/include/js/Vector.h @@ -242,7 +242,7 @@ class Vector : private AllocPolicy size_t mReserved; /* Max elements of reserved or used space in this vector. */ #endif - AlignedStorage storage; + mozilla::AlignedStorage storage; #ifdef DEBUG friend class ReentrancyGuard; @@ -255,7 +255,11 @@ class Vector : private AllocPolicy /* private accessors */ bool usingInlineStorage() const { - return mBegin == (T *)storage.addr(); + return mBegin == inlineStorage(); + } + + T *inlineStorage() const { + return (T *)storage.addr(); } T *beginNoCheck() const { @@ -427,7 +431,7 @@ class Vector : private AllocPolicy internalAppendN(t, n); } template void infallibleAppend(const U *begin, const U *end) { - internalAppend(begin, PointerRangeSize(begin, end)); + internalAppend(begin, mozilla::PointerRangeSize(begin, end)); } template void infallibleAppend(const U *begin, size_t length) { internalAppend(begin, length); @@ -479,6 +483,8 @@ class Vector : private AllocPolicy * object (which must be heap-allocated) itself. */ size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const; + + void swap(Vector &other); }; /* This does the re-entrancy check plus several other sanity checks. */ @@ -506,6 +512,9 @@ template JS_ALWAYS_INLINE Vector::Vector(MoveRef rhs) : AllocPolicy(rhs) +#ifdef DEBUG + , entered(false) +#endif { mLength = rhs->mLength; mCapacity = rhs->mCapacity; @@ -856,7 +865,7 @@ JS_ALWAYS_INLINE bool Vector::append(const U *insBegin, const U *insEnd) { REENTRANCY_GUARD_ET_AL; - size_t needed = PointerRangeSize(insBegin, insEnd); + size_t needed = mozilla::PointerRangeSize(insBegin, insEnd); if (mLength + needed > mCapacity && !growStorageBy(needed)) return false; @@ -995,6 +1004,33 @@ Vector::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf); } +template +inline void +Vector::swap(Vector &other) +{ + // TODO Implement N != 0 + JS_STATIC_ASSERT(N == 0); + + // This only works when inline storage is always empty. + if (!usingInlineStorage() && other.usingInlineStorage()) { + other.mBegin = mBegin; + mBegin = inlineStorage(); + } else if (usingInlineStorage() && !other.usingInlineStorage()) { + mBegin = other.mBegin; + other.mBegin = other.inlineStorage(); + } else if (!usingInlineStorage() && !other.usingInlineStorage()) { + Swap(mBegin, other.mBegin); + } else { + // This case is a no-op, since we'd set both to use their inline storage. + } + + Swap(mLength, other.mLength); + Swap(mCapacity, other.mCapacity); +#ifdef DEBUG + Swap(mReserved, other.mReserved); +#endif +} + } /* namespace js */ #ifdef _MSC_VER diff --git a/scripting/javascript/spidermonkey-ios/include/jsalloc.h b/scripting/javascript/spidermonkey-ios/include/jsalloc.h index a5eeca7534..259456ff7e 100644 --- a/scripting/javascript/spidermonkey-ios/include/jsalloc.h +++ b/scripting/javascript/spidermonkey-ios/include/jsalloc.h @@ -18,6 +18,8 @@ namespace js { * - public copy constructor, assignment, destructor * - void *malloc_(size_t) * Responsible for OOM reporting on NULL return value. + * - void *calloc_(size_t) + * Responsible for OOM reporting on NULL return value. * - void *realloc_(size_t) * Responsible for OOM reporting on NULL return value. * The *used* bytes of the previous buffer is passed in @@ -33,6 +35,7 @@ class SystemAllocPolicy { public: void *malloc_(size_t bytes) { return js_malloc(bytes); } + void *calloc_(size_t bytes) { return js_calloc(bytes); } void *realloc_(void *p, size_t oldBytes, size_t bytes) { return js_realloc(p, bytes); } void free_(void *p) { js_free(p); } void reportAllocOverflow() const {} @@ -71,6 +74,13 @@ class TempAllocPolicy return p; } + void *calloc_(size_t bytes) { + void *p = js_calloc(bytes); + if (JS_UNLIKELY(!p)) + p = onOutOfMemory(NULL, bytes); + return p; + } + void *realloc_(void *p, size_t oldBytes, size_t bytes) { void *p2 = js_realloc(p, bytes); if (JS_UNLIKELY(!p2)) diff --git a/scripting/javascript/spidermonkey-ios/include/jsapi.h.REMOVED.git-id b/scripting/javascript/spidermonkey-ios/include/jsapi.h.REMOVED.git-id index 80ca60e448..4ed0fd8565 100644 --- a/scripting/javascript/spidermonkey-ios/include/jsapi.h.REMOVED.git-id +++ b/scripting/javascript/spidermonkey-ios/include/jsapi.h.REMOVED.git-id @@ -1 +1 @@ -4f78a759104eea6e7790c03ce0130299eeaa3968 \ No newline at end of file +ba4ed5550f4042bc9963d0e15cce943b9f0be17a \ No newline at end of file diff --git a/scripting/javascript/spidermonkey-ios/include/jsdbgapi.h b/scripting/javascript/spidermonkey-ios/include/jsdbgapi.h index 4fb4ee1ecf..5f4d68dd46 100644 --- a/scripting/javascript/spidermonkey-ios/include/jsdbgapi.h +++ b/scripting/javascript/spidermonkey-ios/include/jsdbgapi.h @@ -13,7 +13,6 @@ #include "jsapi.h" #include "jsprvtd.h" -#if defined(__cplusplus) namespace JS { struct FrameDescription @@ -47,9 +46,6 @@ JS_FRIEND_API(void) js_DumpValue(const js::Value &val); JS_FRIEND_API(void) js_DumpId(jsid id); JS_FRIEND_API(void) js_DumpStackFrame(JSContext *cx, js::StackFrame *start = NULL); # endif -#endif - -JS_BEGIN_EXTERN_C JS_FRIEND_API(void) js_DumpBacktrace(JSContext *cx); @@ -432,6 +428,4 @@ JS_UnwrapObjectAndInnerize(JSObject *obj); extern JS_FRIEND_API(JSBool) js_CallContextDebugHandler(JSContext *cx); -JS_END_EXTERN_C - #endif /* jsdbgapi_h___ */ diff --git a/scripting/javascript/spidermonkey-ios/include/jsdhash.h b/scripting/javascript/spidermonkey-ios/include/jsdhash.h index 7af17046b0..9553658b57 100644 --- a/scripting/javascript/spidermonkey-ios/include/jsdhash.h +++ b/scripting/javascript/spidermonkey-ios/include/jsdhash.h @@ -14,8 +14,6 @@ #include "jstypes.h" #include "jsutil.h" -JS_BEGIN_EXTERN_C - #if defined(__GNUC__) && defined(__i386__) && (__GNUC__ >= 3) && !defined(XP_OS2) #define JS_DHASH_FASTCALL __attribute__ ((regparm (3),stdcall)) #elif defined(XP_WIN) @@ -598,6 +596,4 @@ extern JS_PUBLIC_API(void) JS_DHashTableDumpMeter(JSDHashTable *table, JSDHashEnumerator dump, FILE *fp); #endif -JS_END_EXTERN_C - #endif /* jsdhash_h___ */ diff --git a/scripting/javascript/spidermonkey-ios/include/jsfriendapi.h b/scripting/javascript/spidermonkey-ios/include/jsfriendapi.h index 63e79aeb14..51e7b90533 100644 --- a/scripting/javascript/spidermonkey-ios/include/jsfriendapi.h +++ b/scripting/javascript/spidermonkey-ios/include/jsfriendapi.h @@ -8,12 +8,28 @@ #define jsfriendapi_h___ #include "jsclass.h" +#include "jscpucfg.h" #include "jspubtd.h" #include "jsprvtd.h" +#include "js/HeapAPI.h" + #include "mozilla/GuardObjects.h" -JS_BEGIN_EXTERN_C +/* + * This macro checks if the stack pointer has exceeded a given limit. If + * |tolerance| is non-zero, it returns true only if the stack pointer has + * exceeded the limit by more than |tolerance| bytes. + */ +#if JS_STACK_GROWTH_DIRECTION > 0 +# define JS_CHECK_STACK_SIZE_WITH_TOLERANCE(limit, sp, tolerance) \ + ((uintptr_t)(sp) < (limit)+(tolerance)) +#else +# define JS_CHECK_STACK_SIZE_WITH_TOLERANCE(limit, sp, tolerance) \ + ((uintptr_t)(sp) > (limit)-(tolerance)) +#endif + +#define JS_CHECK_STACK_SIZE(limit, lval) JS_CHECK_STACK_SIZE_WITH_TOLERANCE(limit, lval, 0) extern JS_FRIEND_API(void) JS_SetGrayGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data); @@ -139,8 +155,6 @@ extern JS_FRIEND_API(void) js_DumpChars(const jschar *s, size_t n); #endif -#ifdef __cplusplus - extern JS_FRIEND_API(bool) JS_CopyPropertiesFrom(JSContext *cx, JSObject *target, JSObject *obj); @@ -171,12 +185,6 @@ struct JSFunctionSpecWithHelp { extern JS_FRIEND_API(bool) JS_DefineFunctionsWithHelp(JSContext *cx, JSObject *obj, const JSFunctionSpecWithHelp *fs); -#endif - -JS_END_EXTERN_C - -#ifdef __cplusplus - typedef bool (* JS_SourceHook)(JSContext *cx, JSScript *script, jschar **src, uint32_t *length); extern JS_FRIEND_API(void) @@ -184,6 +192,8 @@ JS_SetSourceHook(JSRuntime *rt, JS_SourceHook hook); namespace js { +extern mozilla::ThreadLocal TlsPerThreadData; + inline JSRuntime * GetRuntime(const JSContext *cx) { @@ -259,15 +269,34 @@ TraceWeakMaps(WeakMapTracer *trc); extern JS_FRIEND_API(bool) GCThingIsMarkedGray(void *thing); +extern JS_FRIEND_API(bool) +AreGCGrayBitsValid(JSRuntime *rt); + +/* + * Unsets the gray bit for anything reachable from |thing|. |kind| should not be + * JSTRACE_SHAPE. |thing| should be non-null. + */ +extern JS_FRIEND_API(void) +UnmarkGrayGCThingRecursively(void *thing, JSGCTraceKind kind); + typedef void -(GCThingCallback)(void *closure, void *gcthing); +(*GCThingCallback)(void *closure, void *gcthing); extern JS_FRIEND_API(void) -VisitGrayWrapperTargets(JSCompartment *comp, GCThingCallback *callback, void *closure); +VisitGrayWrapperTargets(JSCompartment *comp, GCThingCallback callback, void *closure); extern JS_FRIEND_API(JSObject *) GetWeakmapKeyDelegate(JSObject *key); +JS_FRIEND_API(JSGCTraceKind) +GCThingTraceKind(void *thing); + +/* + * Invoke cellCallback on every gray JS_OBJECT in the given compartment. + */ +extern JS_FRIEND_API(void) +IterateGrayObjects(JSCompartment *compartment, GCThingCallback cellCallback, void *data); + /* * Shadow declarations of JS internal structures, for access by inline access * functions below. Do not use these structures in any other way. When adding @@ -527,7 +556,13 @@ GetNativeStackLimit(const JSRuntime *rt) return RuntimeFriendFields::get(rt)->nativeStackLimit; } -#define JS_CHECK_RECURSION(cx, onerror) \ +/* + * These macros report a stack overflow and run |onerror| if we are close to + * using up the C stack. The JS_CHECK_CHROME_RECURSION variant gives us a little + * extra space so that we can ensure that crucial code is able to run. + */ + +#define JS_CHECK_RECURSION(cx, onerror) \ JS_BEGIN_MACRO \ int stackDummy_; \ if (!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(js::GetRuntime(cx)), &stackDummy_)) { \ @@ -536,6 +571,18 @@ GetNativeStackLimit(const JSRuntime *rt) } \ JS_END_MACRO +#define JS_CHECK_CHROME_RECURSION(cx, onerror) \ + JS_BEGIN_MACRO \ + int stackDummy_; \ + if (!JS_CHECK_STACK_SIZE_WITH_TOLERANCE(js::GetNativeStackLimit(js::GetRuntime(cx)), \ + &stackDummy_, \ + 1024 * sizeof(size_t))) \ + { \ + js_ReportOverRecursed(cx); \ + onerror; \ + } \ + JS_END_MACRO + JS_FRIEND_API(void) StartPCCountProfiling(JSContext *cx); @@ -979,8 +1026,6 @@ uint32_t GetListBaseExpandoSlot(); } /* namespace js */ -#endif - /* Implemented in jsdate.cpp. */ /* @@ -1017,8 +1062,6 @@ js_GetSCOffset(JSStructuredCloneWriter* writer); /* Typed Array functions, implemented in jstypedarray.cpp */ -#ifdef __cplusplus - namespace js { namespace ArrayBufferView { @@ -1038,6 +1081,12 @@ enum ViewType { */ TYPE_UINT8_CLAMPED, + /* + * Type returned for a DataView. Note that there is no single element type + * in this case. + */ + TYPE_DATAVIEW, + TYPE_MAX }; @@ -1045,9 +1094,6 @@ enum ViewType { } /* namespace js */ typedef js::ArrayBufferView::ViewType JSArrayBufferViewType; -#else -typedef uint32_t JSArrayBufferViewType; -#endif /* __cplusplus */ /* * Create a new typed array with nelements elements. @@ -1150,41 +1196,40 @@ JS_NewArrayBuffer(JSContext *cx, uint32_t nbytes); * the various accessor JSAPI calls defined below. */ extern JS_FRIEND_API(JSBool) -JS_IsTypedArrayObject(JSObject *obj, JSContext *cx); +JS_IsTypedArrayObject(JSObject *obj); /* * Check whether obj supports JS_GetArrayBufferView* APIs. Note that this may * return false if a security wrapper is encountered that denies the * unwrapping. If this test or one of the more specific tests succeeds, then it * is safe to call the various ArrayBufferView accessor JSAPI calls defined - * below. cx MUST be non-NULL and valid. + * below. */ extern JS_FRIEND_API(JSBool) -JS_IsArrayBufferViewObject(JSObject *obj, JSContext *cx); +JS_IsArrayBufferViewObject(JSObject *obj); /* * Test for specific typed array types (ArrayBufferView subtypes) */ extern JS_FRIEND_API(JSBool) -JS_IsInt8Array(JSObject *obj, JSContext *cx); +JS_IsInt8Array(JSObject *obj); extern JS_FRIEND_API(JSBool) -JS_IsUint8Array(JSObject *obj, JSContext *cx); +JS_IsUint8Array(JSObject *obj); extern JS_FRIEND_API(JSBool) -JS_IsUint8ClampedArray(JSObject *obj, JSContext *cx); +JS_IsUint8ClampedArray(JSObject *obj); extern JS_FRIEND_API(JSBool) -JS_IsInt16Array(JSObject *obj, JSContext *cx); +JS_IsInt16Array(JSObject *obj); extern JS_FRIEND_API(JSBool) -JS_IsUint16Array(JSObject *obj, JSContext *cx); +JS_IsUint16Array(JSObject *obj); extern JS_FRIEND_API(JSBool) -JS_IsInt32Array(JSObject *obj, JSContext *cx); +JS_IsInt32Array(JSObject *obj); extern JS_FRIEND_API(JSBool) -JS_IsUint32Array(JSObject *obj, JSContext *cx); +JS_IsUint32Array(JSObject *obj); extern JS_FRIEND_API(JSBool) -JS_IsFloat32Array(JSObject *obj, JSContext *cx); +JS_IsFloat32Array(JSObject *obj); extern JS_FRIEND_API(JSBool) -JS_IsFloat64Array(JSObject *obj, JSContext *cx); - +JS_IsFloat64Array(JSObject *obj); /* * Unwrap Typed arrays all at once. Return NULL without throwing if the object @@ -1192,38 +1237,37 @@ JS_IsFloat64Array(JSObject *obj, JSContext *cx); * success, filling both outparameters. */ extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsInt8Array(JSContext *cx, JSObject *obj, uint32_t *length, int8_t **data); +JS_GetObjectAsInt8Array(JSObject *obj, uint32_t *length, int8_t **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsUint8Array(JSContext *cx, JSObject *obj, uint32_t *length, uint8_t **data); +JS_GetObjectAsUint8Array(JSObject *obj, uint32_t *length, uint8_t **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsUint8ClampedArray(JSContext *cx, JSObject *obj, uint32_t *length, uint8_t **data); +JS_GetObjectAsUint8ClampedArray(JSObject *obj, uint32_t *length, uint8_t **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsInt16Array(JSContext *cx, JSObject *obj, uint32_t *length, int16_t **data); +JS_GetObjectAsInt16Array(JSObject *obj, uint32_t *length, int16_t **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsUint16Array(JSContext *cx, JSObject *obj, uint32_t *length, uint16_t **data); +JS_GetObjectAsUint16Array(JSObject *obj, uint32_t *length, uint16_t **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsInt32Array(JSContext *cx, JSObject *obj, uint32_t *length, int32_t **data); +JS_GetObjectAsInt32Array(JSObject *obj, uint32_t *length, int32_t **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsUint32Array(JSContext *cx, JSObject *obj, uint32_t *length, uint32_t **data); +JS_GetObjectAsUint32Array(JSObject *obj, uint32_t *length, uint32_t **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsFloat32Array(JSContext *cx, JSObject *obj, uint32_t *length, float **data); +JS_GetObjectAsFloat32Array(JSObject *obj, uint32_t *length, float **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsFloat64Array(JSContext *cx, JSObject *obj, uint32_t *length, double **data); +JS_GetObjectAsFloat64Array(JSObject *obj, uint32_t *length, double **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsArrayBufferView(JSContext *cx, JSObject *obj, uint32_t *length, uint8_t **data); +JS_GetObjectAsArrayBufferView(JSObject *obj, uint32_t *length, uint8_t **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsArrayBuffer(JSContext *cx, JSObject *obj, uint32_t *length, uint8_t **data); +JS_GetObjectAsArrayBuffer(JSObject *obj, uint32_t *length, uint8_t **data); /* - * Get the type of elements in a typed array. + * Get the type of elements in a typed array, or TYPE_DATAVIEW if a DataView. * - * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow - * be known that it would pass such a test: it is a typed array or a wrapper of - * a typed array, and the unwrapping will succeed. If cx is NULL, then DEBUG - * builds may be unable to assert when unwrapping should be disallowed. + * |obj| must have passed a JS_IsArrayBufferView/JS_Is*Array test, or somehow + * be known that it would pass such a test: it is an ArrayBufferView or a + * wrapper of an ArrayBufferView, and the unwrapping will succeed. */ extern JS_FRIEND_API(JSArrayBufferViewType) -JS_GetTypedArrayType(JSObject *obj, JSContext *maybecx); +JS_GetArrayBufferViewType(JSObject *obj); /* * Check whether obj supports the JS_GetArrayBuffer* APIs. Note that this may @@ -1232,18 +1276,17 @@ JS_GetTypedArrayType(JSObject *obj, JSContext *maybecx); * accessor JSAPI calls defined below. */ extern JS_FRIEND_API(JSBool) -JS_IsArrayBufferObject(JSObject *obj, JSContext *maybecx); +JS_IsArrayBufferObject(JSObject *obj); /* * Return the available byte length of an array buffer. * * |obj| must have passed a JS_IsArrayBufferObject test, or somehow be known * that it would pass such a test: it is an ArrayBuffer or a wrapper of an - * ArrayBuffer, and the unwrapping will succeed. If cx is NULL, then DEBUG - * builds may be unable to assert when unwrapping should be disallowed. + * ArrayBuffer, and the unwrapping will succeed. */ extern JS_FRIEND_API(uint32_t) -JS_GetArrayBufferByteLength(JSObject *obj, JSContext *maybecx); +JS_GetArrayBufferByteLength(JSObject *obj); /* * Return a pointer to an array buffer's data. The buffer is still owned by the @@ -1252,22 +1295,20 @@ JS_GetArrayBufferByteLength(JSObject *obj, JSContext *maybecx); * * |obj| must have passed a JS_IsArrayBufferObject test, or somehow be known * that it would pass such a test: it is an ArrayBuffer or a wrapper of an - * ArrayBuffer, and the unwrapping will succeed. If cx is NULL, then DEBUG - * builds may be unable to assert when unwrapping should be disallowed. + * ArrayBuffer, and the unwrapping will succeed. */ extern JS_FRIEND_API(uint8_t *) -JS_GetArrayBufferData(JSObject *obj, JSContext *maybecx); +JS_GetArrayBufferData(JSObject *obj); /* * Return the number of elements in a typed array. * * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow * be known that it would pass such a test: it is a typed array or a wrapper of - * a typed array, and the unwrapping will succeed. If cx is NULL, then DEBUG - * builds may be unable to assert when unwrapping should be disallowed. + * a typed array, and the unwrapping will succeed. */ extern JS_FRIEND_API(uint32_t) -JS_GetTypedArrayLength(JSObject *obj, JSContext *cx); +JS_GetTypedArrayLength(JSObject *obj); /* * Return the byte offset from the start of an array buffer to the start of a @@ -1275,22 +1316,20 @@ JS_GetTypedArrayLength(JSObject *obj, JSContext *cx); * * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow * be known that it would pass such a test: it is a typed array or a wrapper of - * a typed array, and the unwrapping will succeed. If cx is NULL, then DEBUG - * builds may be unable to assert when unwrapping should be disallowed. + * a typed array, and the unwrapping will succeed. */ extern JS_FRIEND_API(uint32_t) -JS_GetTypedArrayByteOffset(JSObject *obj, JSContext *cx); +JS_GetTypedArrayByteOffset(JSObject *obj); /* * Return the byte length of a typed array. * * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow * be known that it would pass such a test: it is a typed array or a wrapper of - * a typed array, and the unwrapping will succeed. If cx is NULL, then DEBUG - * builds may be unable to assert when unwrapping should be disallowed. + * a typed array, and the unwrapping will succeed. */ extern JS_FRIEND_API(uint32_t) -JS_GetTypedArrayByteLength(JSObject *obj, JSContext *cx); +JS_GetTypedArrayByteLength(JSObject *obj); /* * Check whether obj supports JS_ArrayBufferView* APIs. Note that this may @@ -1298,13 +1337,13 @@ JS_GetTypedArrayByteLength(JSObject *obj, JSContext *cx); * unwrapping. */ extern JS_FRIEND_API(JSBool) -JS_IsArrayBufferViewObject(JSObject *obj, JSContext *cx); +JS_IsArrayBufferViewObject(JSObject *obj); /* * More generic name for JS_GetTypedArrayByteLength to cover DataViews as well */ extern JS_FRIEND_API(uint32_t) -JS_GetArrayBufferViewByteLength(JSObject *obj, JSContext *cx); +JS_GetArrayBufferViewByteLength(JSObject *obj); /* * Return a pointer to the start of the data referenced by a typed array. The @@ -1313,43 +1352,48 @@ JS_GetArrayBufferViewByteLength(JSObject *obj, JSContext *cx); * * |obj| must have passed a JS_Is*Array test, or somehow be known that it would * pass such a test: it is a typed array or a wrapper of a typed array, and the - * unwrapping will succeed. If cx is NULL, then DEBUG builds may be unable to - * assert when unwrapping should be disallowed. + * unwrapping will succeed. */ extern JS_FRIEND_API(int8_t *) -JS_GetInt8ArrayData(JSObject *obj, JSContext *maybecx); +JS_GetInt8ArrayData(JSObject *obj); extern JS_FRIEND_API(uint8_t *) -JS_GetUint8ArrayData(JSObject *obj, JSContext *maybecx); +JS_GetUint8ArrayData(JSObject *obj); extern JS_FRIEND_API(uint8_t *) -JS_GetUint8ClampedArrayData(JSObject *obj, JSContext *maybecx); +JS_GetUint8ClampedArrayData(JSObject *obj); extern JS_FRIEND_API(int16_t *) -JS_GetInt16ArrayData(JSObject *obj, JSContext *maybecx); +JS_GetInt16ArrayData(JSObject *obj); extern JS_FRIEND_API(uint16_t *) -JS_GetUint16ArrayData(JSObject *obj, JSContext *maybecx); +JS_GetUint16ArrayData(JSObject *obj); extern JS_FRIEND_API(int32_t *) -JS_GetInt32ArrayData(JSObject *obj, JSContext *maybecx); +JS_GetInt32ArrayData(JSObject *obj); extern JS_FRIEND_API(uint32_t *) -JS_GetUint32ArrayData(JSObject *obj, JSContext *maybecx); +JS_GetUint32ArrayData(JSObject *obj); extern JS_FRIEND_API(float *) -JS_GetFloat32ArrayData(JSObject *obj, JSContext *maybecx); +JS_GetFloat32ArrayData(JSObject *obj); extern JS_FRIEND_API(double *) -JS_GetFloat64ArrayData(JSObject *obj, JSContext *maybecx); +JS_GetFloat64ArrayData(JSObject *obj); /* * Same as above, but for any kind of ArrayBufferView. Prefer the type-specific * versions when possible. */ extern JS_FRIEND_API(void *) -JS_GetArrayBufferViewData(JSObject *obj, JSContext *maybecx); +JS_GetArrayBufferViewData(JSObject *obj); /* - * Check whether obj supports JS_GetDataView* APIs. Note that this may fail and - * throw an exception if a security wrapper is encountered that denies the - * operation. + * Return the ArrayBuffer underlying an ArrayBufferView. If the buffer has been + * neutered, this will still return the neutered buffer. |obj| must be an + * object that would return true for JS_IsArrayBufferViewObject(). + */ +extern JS_FRIEND_API(JSObject *) +JS_GetArrayBufferViewBuffer(JSObject *obj); + +/* + * Check whether obj supports JS_GetDataView* APIs. */ JS_FRIEND_API(JSBool) -JS_IsDataViewObject(JSContext *cx, JSObject *obj, JSBool *isDataView); +JS_IsDataViewObject(JSObject *obj); /* * Return the byte offset of a data view into its array buffer. |obj| must be a @@ -1357,11 +1401,10 @@ JS_IsDataViewObject(JSContext *cx, JSObject *obj, JSBool *isDataView); * * |obj| must have passed a JS_IsDataViewObject test, or somehow be known that * it would pass such a test: it is a data view or a wrapper of a data view, - * and the unwrapping will succeed. If cx is NULL, then DEBUG builds may be - * unable to assert when unwrapping should be disallowed. + * and the unwrapping will succeed. */ JS_FRIEND_API(uint32_t) -JS_GetDataViewByteOffset(JSObject *obj, JSContext *maybecx); +JS_GetDataViewByteOffset(JSObject *obj); /* * Return the byte length of a data view. @@ -1372,7 +1415,7 @@ JS_GetDataViewByteOffset(JSObject *obj, JSContext *maybecx); * unable to assert when unwrapping should be disallowed. */ JS_FRIEND_API(uint32_t) -JS_GetDataViewByteLength(JSObject *obj, JSContext *maybecx); +JS_GetDataViewByteLength(JSObject *obj); /* * Return a pointer to the beginning of the data referenced by a DataView. @@ -1383,9 +1426,8 @@ JS_GetDataViewByteLength(JSObject *obj, JSContext *maybecx); * unable to assert when unwrapping should be disallowed. */ JS_FRIEND_API(void *) -JS_GetDataViewData(JSObject *obj, JSContext *maybecx); +JS_GetDataViewData(JSObject *obj); -#ifdef __cplusplus /* * This struct contains metadata passed from the DOM to the JS Engine for JIT * optimizations on DOM property accessors. Eventually, this should be made @@ -1420,15 +1462,16 @@ FUNCTION_VALUE_TO_JITINFO(const JS::Value& v) return reinterpret_cast(&v.toObject())->jitinfo; } +/* Statically asserted in jsfun.h. */ +static const unsigned JS_FUNCTION_INTERPRETED_BIT = 0x1; + static JS_ALWAYS_INLINE void SET_JITINFO(JSFunction * func, const JSJitInfo *info) { js::shadow::Function *fun = reinterpret_cast(func); - /* JS_ASSERT(func->isNative()). 0x4000 is JSFUN_INTERPRETED */ - JS_ASSERT(!(fun->flags & 0x4000)); + JS_ASSERT(!(fun->flags & JS_FUNCTION_INTERPRETED_BIT)); fun->jitinfo = info; } -#endif /* __cplusplus */ /* * Engine-internal extensions of jsid. This code is here only until we @@ -1494,8 +1537,6 @@ JSID_TO_ATOM(jsid id) JS_STATIC_ASSERT(sizeof(jsid) == JS_BYTES_PER_WORD); -#ifdef __cplusplus - namespace js { static JS_ALWAYS_INLINE Value @@ -1517,8 +1558,12 @@ IdToJsval(jsid id) return IdToValue(id); } +extern JS_FRIEND_API(bool) +IsReadOnlyDateMethod(JS::IsAcceptableThis test, JS::NativeImpl method); + +extern JS_FRIEND_API(bool) +IsTypedArrayThisCheck(JS::IsAcceptableThis test); + } /* namespace js */ -#endif /* __cplusplus */ - #endif /* jsfriendapi_h___ */ diff --git a/scripting/javascript/spidermonkey-ios/include/jsgc.h b/scripting/javascript/spidermonkey-ios/include/jsgc.h deleted file mode 100644 index 97e4610a44..0000000000 --- a/scripting/javascript/spidermonkey-ios/include/jsgc.h +++ /dev/null @@ -1,1230 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef jsgc_h___ -#define jsgc_h___ - -/* - * JS Garbage Collector. - */ -#include - -#include "mozilla/Util.h" - -#include "jsalloc.h" -#include "jstypes.h" -#include "jsprvtd.h" -#include "jspubtd.h" -#include "jslock.h" -#include "jsutil.h" -#include "jsversion.h" - -#include "ds/BitArray.h" -#include "gc/Heap.h" -#include "gc/Statistics.h" -#include "js/HashTable.h" -#include "js/Vector.h" -#include "js/TemplateLib.h" - -struct JSCompartment; - -#if JS_STACK_GROWTH_DIRECTION > 0 -# define JS_CHECK_STACK_SIZE(limit, lval) ((uintptr_t)(lval) < limit) -#else -# define JS_CHECK_STACK_SIZE(limit, lval) ((uintptr_t)(lval) > limit) -#endif - -namespace js { - -class GCHelperThread; -struct Shape; -struct SliceBudget; - -namespace ion { - class IonCode; -} - -namespace gc { - -enum State { - NO_INCREMENTAL, - MARK_ROOTS, - MARK, - SWEEP, - SWEEP_END, - INVALID -}; - -class ChunkPool { - Chunk *emptyChunkListHead; - size_t emptyCount; - - public: - ChunkPool() - : emptyChunkListHead(NULL), - emptyCount(0) { } - - size_t getEmptyCount() const { - return emptyCount; - } - - inline bool wantBackgroundAllocation(JSRuntime *rt) const; - - /* Must be called with the GC lock taken. */ - inline Chunk *get(JSRuntime *rt); - - /* Must be called either during the GC or with the GC lock taken. */ - inline void put(Chunk *chunk); - - /* - * Return the list of chunks that can be released outside the GC lock. - * Must be called either during the GC or with the GC lock taken. - */ - Chunk *expire(JSRuntime *rt, bool releaseAll); - - /* Must be called with the GC lock taken. */ - void expireAndFree(JSRuntime *rt, bool releaseAll); -}; - -static inline JSGCTraceKind -MapAllocToTraceKind(AllocKind thingKind) -{ - static const JSGCTraceKind map[FINALIZE_LIMIT] = { - JSTRACE_OBJECT, /* FINALIZE_OBJECT0 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT0_BACKGROUND */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT2 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT2_BACKGROUND */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT4 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT4_BACKGROUND */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT8 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT8_BACKGROUND */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT12 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT12_BACKGROUND */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT16 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT16_BACKGROUND */ - JSTRACE_SCRIPT, /* FINALIZE_SCRIPT */ - JSTRACE_SHAPE, /* FINALIZE_SHAPE */ - JSTRACE_BASE_SHAPE, /* FINALIZE_BASE_SHAPE */ - JSTRACE_TYPE_OBJECT,/* FINALIZE_TYPE_OBJECT */ -#if JS_HAS_XML_SUPPORT /* FINALIZE_XML */ - JSTRACE_XML, -#endif - JSTRACE_STRING, /* FINALIZE_SHORT_STRING */ - JSTRACE_STRING, /* FINALIZE_STRING */ - JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING */ - JSTRACE_IONCODE, /* FINALIZE_IONCODE */ - }; - return map[thingKind]; -} - -static inline bool -IsNurseryAllocable(AllocKind kind) -{ - JS_ASSERT(kind >= 0 && unsigned(kind) < FINALIZE_LIMIT); - static const bool map[FINALIZE_LIMIT] = { - false, /* FINALIZE_OBJECT0 */ - true, /* FINALIZE_OBJECT0_BACKGROUND */ - false, /* FINALIZE_OBJECT2 */ - true, /* FINALIZE_OBJECT2_BACKGROUND */ - false, /* FINALIZE_OBJECT4 */ - true, /* FINALIZE_OBJECT4_BACKGROUND */ - false, /* FINALIZE_OBJECT8 */ - true, /* FINALIZE_OBJECT8_BACKGROUND */ - false, /* FINALIZE_OBJECT12 */ - true, /* FINALIZE_OBJECT12_BACKGROUND */ - false, /* FINALIZE_OBJECT16 */ - true, /* FINALIZE_OBJECT16_BACKGROUND */ - false, /* FINALIZE_SCRIPT */ - false, /* FINALIZE_SHAPE */ - false, /* FINALIZE_BASE_SHAPE */ - false, /* FINALIZE_TYPE_OBJECT */ -#if JS_HAS_XML_SUPPORT - false, /* FINALIZE_XML */ -#endif - true, /* FINALIZE_SHORT_STRING */ - true, /* FINALIZE_STRING */ - false /* FINALIZE_EXTERNAL_STRING */ - }; - return map[kind]; -} - -static inline bool -IsBackgroundFinalized(AllocKind kind) -{ - JS_ASSERT(kind >= 0 && unsigned(kind) < FINALIZE_LIMIT); - static const bool map[FINALIZE_LIMIT] = { - false, /* FINALIZE_OBJECT0 */ - true, /* FINALIZE_OBJECT0_BACKGROUND */ - false, /* FINALIZE_OBJECT2 */ - true, /* FINALIZE_OBJECT2_BACKGROUND */ - false, /* FINALIZE_OBJECT4 */ - true, /* FINALIZE_OBJECT4_BACKGROUND */ - false, /* FINALIZE_OBJECT8 */ - true, /* FINALIZE_OBJECT8_BACKGROUND */ - false, /* FINALIZE_OBJECT12 */ - true, /* FINALIZE_OBJECT12_BACKGROUND */ - false, /* FINALIZE_OBJECT16 */ - true, /* FINALIZE_OBJECT16_BACKGROUND */ - false, /* FINALIZE_SCRIPT */ - false, /* FINALIZE_SHAPE */ - false, /* FINALIZE_BASE_SHAPE */ - false, /* FINALIZE_TYPE_OBJECT */ -#if JS_HAS_XML_SUPPORT - false, /* FINALIZE_XML */ -#endif - true, /* FINALIZE_SHORT_STRING */ - true, /* FINALIZE_STRING */ - false /* FINALIZE_EXTERNAL_STRING */ - }; - return map[kind]; -} - -inline JSGCTraceKind -GetGCThingTraceKind(const void *thing); - -/* - * ArenaList::head points to the start of the list. Normally cursor points - * to the first arena in the list with some free things and all arenas - * before cursor are fully allocated. However, as the arena currently being - * allocated from is considered full while its list of free spans is moved - * into the freeList, during the GC or cell enumeration, when an - * unallocated freeList is moved back to the arena, we can see an arena - * with some free cells before the cursor. The cursor is an indirect - * pointer to allow for efficient list insertion at the cursor point and - * other list manipulations. - */ -struct ArenaList { - ArenaHeader *head; - ArenaHeader **cursor; - - ArenaList() { - clear(); - } - - void clear() { - head = NULL; - cursor = &head; - } - - void insert(ArenaHeader *arena); -}; - -struct ArenaLists { - - private: - /* - * For each arena kind its free list is represented as the first span with - * free things. Initially all the spans are initialized as empty. After we - * find a new arena with available things we move its first free span into - * the list and set the arena as fully allocated. way we do not need to - * update the arena header after the initial allocation. When starting the - * GC we only move the head of the of the list of spans back to the arena - * only for the arena that was not fully allocated. - */ - FreeSpan freeLists[FINALIZE_LIMIT]; - - ArenaList arenaLists[FINALIZE_LIMIT]; - - /* - * The background finalization adds the finalized arenas to the list at - * the *cursor position. backgroundFinalizeState controls the interaction - * between the GC lock and the access to the list from the allocation - * thread. - * - * BFS_DONE indicates that the finalizations is not running or cannot - * affect this arena list. The allocation thread can access the list - * outside the GC lock. - * - * In BFS_RUN and BFS_JUST_FINISHED the allocation thread must take the - * lock. The former indicates that the finalization still runs. The latter - * signals that finalization just added to the list finalized arenas. In - * that case the lock effectively serves as a read barrier to ensure that - * the allocation thread see all the writes done during finalization. - */ - enum BackgroundFinalizeState { - BFS_DONE, - BFS_RUN, - BFS_JUST_FINISHED - }; - - volatile uintptr_t backgroundFinalizeState[FINALIZE_LIMIT]; - - public: - /* For each arena kind, a list of arenas remaining to be swept. */ - ArenaHeader *arenaListsToSweep[FINALIZE_LIMIT]; - - public: - ArenaLists() { - for (size_t i = 0; i != FINALIZE_LIMIT; ++i) - freeLists[i].initAsEmpty(); - for (size_t i = 0; i != FINALIZE_LIMIT; ++i) - backgroundFinalizeState[i] = BFS_DONE; - for (size_t i = 0; i != FINALIZE_LIMIT; ++i) - arenaListsToSweep[i] = NULL; - } - - ~ArenaLists() { - for (size_t i = 0; i != FINALIZE_LIMIT; ++i) { - /* - * We can only call this during the shutdown after the last GC when - * the background finalization is disabled. - */ - JS_ASSERT(backgroundFinalizeState[i] == BFS_DONE); - ArenaHeader **headp = &arenaLists[i].head; - while (ArenaHeader *aheader = *headp) { - *headp = aheader->next; - aheader->chunk()->releaseArena(aheader); - } - } - } - - const FreeSpan *getFreeList(AllocKind thingKind) const { - return &freeLists[thingKind]; - } - - ArenaHeader *getFirstArena(AllocKind thingKind) const { - return arenaLists[thingKind].head; - } - - ArenaHeader *getFirstArenaToSweep(AllocKind thingKind) const { - return arenaListsToSweep[thingKind]; - } - - bool arenaListsAreEmpty() const { - for (size_t i = 0; i != FINALIZE_LIMIT; ++i) { - /* - * The arena cannot be empty if the background finalization is not yet - * done. - */ - if (backgroundFinalizeState[i] != BFS_DONE) - return false; - if (arenaLists[i].head) - return false; - } - return true; - } - - bool arenasAreFull(AllocKind thingKind) const { - return !*arenaLists[thingKind].cursor; - } - - void unmarkAll() { - for (size_t i = 0; i != FINALIZE_LIMIT; ++i) { - /* The background finalization must have stopped at this point. */ - JS_ASSERT(backgroundFinalizeState[i] == BFS_DONE || - backgroundFinalizeState[i] == BFS_JUST_FINISHED); - for (ArenaHeader *aheader = arenaLists[i].head; aheader; aheader = aheader->next) { - uintptr_t *word = aheader->chunk()->bitmap.arenaBits(aheader); - memset(word, 0, ArenaBitmapWords * sizeof(uintptr_t)); - } - } - } - - bool doneBackgroundFinalize(AllocKind kind) const { - return backgroundFinalizeState[kind] == BFS_DONE || - backgroundFinalizeState[kind] == BFS_JUST_FINISHED; - } - - /* - * Return the free list back to the arena so the GC finalization will not - * run the finalizers over unitialized bytes from free things. - */ - void purge() { - for (size_t i = 0; i != FINALIZE_LIMIT; ++i) { - FreeSpan *headSpan = &freeLists[i]; - if (!headSpan->isEmpty()) { - ArenaHeader *aheader = headSpan->arenaHeader(); - aheader->setFirstFreeSpan(headSpan); - headSpan->initAsEmpty(); - } - } - } - - inline void prepareForIncrementalGC(JSRuntime *rt); - - /* - * Temporarily copy the free list heads to the arenas so the code can see - * the proper value in ArenaHeader::freeList when accessing the latter - * outside the GC. - */ - void copyFreeListsToArenas() { - for (size_t i = 0; i != FINALIZE_LIMIT; ++i) - copyFreeListToArena(AllocKind(i)); - } - - void copyFreeListToArena(AllocKind thingKind) { - FreeSpan *headSpan = &freeLists[thingKind]; - if (!headSpan->isEmpty()) { - ArenaHeader *aheader = headSpan->arenaHeader(); - JS_ASSERT(!aheader->hasFreeThings()); - aheader->setFirstFreeSpan(headSpan); - } - } - - /* - * Clear the free lists in arenas that were temporarily set there using - * copyToArenas. - */ - void clearFreeListsInArenas() { - for (size_t i = 0; i != FINALIZE_LIMIT; ++i) - clearFreeListInArena(AllocKind(i)); - } - - - void clearFreeListInArena(AllocKind kind) { - FreeSpan *headSpan = &freeLists[kind]; - if (!headSpan->isEmpty()) { - ArenaHeader *aheader = headSpan->arenaHeader(); - JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(headSpan)); - aheader->setAsFullyUsed(); - } - } - - /* - * Check that the free list is either empty or were synchronized with the - * arena using copyToArena(). - */ - bool isSynchronizedFreeList(AllocKind kind) { - FreeSpan *headSpan = &freeLists[kind]; - if (headSpan->isEmpty()) - return true; - ArenaHeader *aheader = headSpan->arenaHeader(); - if (aheader->hasFreeThings()) { - /* - * If the arena has a free list, it must be the same as one in - * lists. - */ - JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(headSpan)); - return true; - } - return false; - } - - JS_ALWAYS_INLINE void *allocateFromFreeList(AllocKind thingKind, size_t thingSize) { - return freeLists[thingKind].allocate(thingSize); - } - - static void *refillFreeList(JSContext *cx, AllocKind thingKind); - - void checkEmptyFreeLists() { -#ifdef DEBUG - for (size_t i = 0; i < mozilla::ArrayLength(freeLists); ++i) - JS_ASSERT(freeLists[i].isEmpty()); -#endif - } - - void checkEmptyFreeList(AllocKind kind) { - JS_ASSERT(freeLists[kind].isEmpty()); - } - - void queueObjectsForSweep(FreeOp *fop); - void queueStringsForSweep(FreeOp *fop); - void queueShapesForSweep(FreeOp *fop); - void queueScriptsForSweep(FreeOp *fop); - void queueIonCodeForSweep(FreeOp *fop); - - bool foregroundFinalize(FreeOp *fop, AllocKind thingKind, SliceBudget &sliceBudget); - static void backgroundFinalize(FreeOp *fop, ArenaHeader *listHead, bool onBackgroundThread); - - private: - inline void finalizeNow(FreeOp *fop, AllocKind thingKind); - inline void queueForForegroundSweep(FreeOp *fop, AllocKind thingKind); - inline void queueForBackgroundSweep(FreeOp *fop, AllocKind thingKind); - - inline void *allocateFromArena(JSCompartment *comp, AllocKind thingKind); -}; - -/* - * Initial allocation size for data structures holding chunks is set to hold - * chunks with total capacity of 16MB to avoid buffer resizes during browser - * startup. - */ -const size_t INITIAL_CHUNK_CAPACITY = 16 * 1024 * 1024 / ChunkSize; - -/* The number of GC cycles an empty chunk can survive before been released. */ -const size_t MAX_EMPTY_CHUNK_AGE = 4; - -inline Cell * -AsCell(JSObject *obj) -{ - return reinterpret_cast(obj); -} - -} /* namespace gc */ - -struct GCPtrHasher -{ - typedef void *Lookup; - - static HashNumber hash(void *key) { - return HashNumber(uintptr_t(key) >> JS_GCTHING_ZEROBITS); - } - - static bool match(void *l, void *k) { return l == k; } -}; - -typedef HashMap GCLocks; - -struct RootInfo { - RootInfo() {} - RootInfo(const char *name, JSGCRootType type) : name(name), type(type) {} - const char *name; - JSGCRootType type; -}; - -typedef js::HashMap, - js::SystemAllocPolicy> RootedValueMap; - -} /* namespace js */ - -extern JS_FRIEND_API(JSGCTraceKind) -js_GetGCThingTraceKind(void *thing); - -extern JSBool -js_InitGC(JSRuntime *rt, uint32_t maxbytes); - -extern void -js_FinishGC(JSRuntime *rt); - -extern JSBool -js_AddRoot(JSContext *cx, js::Value *vp, const char *name); - -extern JSBool -js_AddGCThingRoot(JSContext *cx, void **rp, const char *name); - -#ifdef DEBUG -extern void -js_DumpNamedRoots(JSRuntime *rt, - void (*dump)(const char *name, void *rp, JSGCRootType type, void *data), - void *data); -#endif - -extern uint32_t -js_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data); - -/* Table of pointers with count valid members. */ -typedef struct JSPtrTable { - size_t count; - void **array; -} JSPtrTable; - -extern JSBool -js_LockGCThingRT(JSRuntime *rt, void *thing); - -extern void -js_UnlockGCThingRT(JSRuntime *rt, void *thing); - -extern bool -js_IsAddressableGCThing(JSRuntime *rt, uintptr_t w, js::gc::AllocKind *thingKind, void **thing); - -namespace js { - -extern void -MarkCompartmentActive(js::StackFrame *fp); - -extern void -TraceRuntime(JSTracer *trc); - -extern JS_FRIEND_API(void) -MarkContext(JSTracer *trc, JSContext *acx); - -/* Must be called with GC lock taken. */ -extern void -TriggerGC(JSRuntime *rt, js::gcreason::Reason reason); - -/* Must be called with GC lock taken. */ -extern void -TriggerCompartmentGC(JSCompartment *comp, js::gcreason::Reason reason); - -extern void -MaybeGC(JSContext *cx); - -extern void -ShrinkGCBuffers(JSRuntime *rt); - -extern void -ReleaseAllJITCode(FreeOp *op); - -extern JS_FRIEND_API(void) -PrepareForFullGC(JSRuntime *rt); - -/* - * Kinds of js_GC invocation. - */ -typedef enum JSGCInvocationKind { - /* Normal invocation. */ - GC_NORMAL = 0, - - /* Minimize GC triggers and release empty GC chunks right away. */ - GC_SHRINK = 1 -} JSGCInvocationKind; - -extern void -GC(JSRuntime *rt, JSGCInvocationKind gckind, js::gcreason::Reason reason); - -extern void -GCSlice(JSRuntime *rt, JSGCInvocationKind gckind, js::gcreason::Reason reason, int64_t millis = 0); - -extern void -GCFinalSlice(JSRuntime *rt, JSGCInvocationKind gckind, js::gcreason::Reason reason); - -extern void -GCDebugSlice(JSRuntime *rt, bool limit, int64_t objCount); - -extern void -PrepareForDebugGC(JSRuntime *rt); - -} /* namespace js */ - -namespace js { - -void -InitTracer(JSTracer *trc, JSRuntime *rt, JSTraceCallback callback); - -/* - * Helper that implements sweeping and allocation for kinds that can be swept - * and allocated off the main thread. - * - * In non-threadsafe builds, all actual sweeping and allocation is performed - * on the main thread, but GCHelperThread encapsulates this from clients as - * much as possible. - */ -class GCHelperThread { - enum State { - IDLE, - SWEEPING, - ALLOCATING, - CANCEL_ALLOCATION, - SHUTDOWN - }; - - /* - * During the finalization we do not free immediately. Rather we add the - * corresponding pointers to a buffer which we later release on a - * separated thread. - * - * The buffer is implemented as a vector of 64K arrays of pointers, not as - * a simple vector, to avoid realloc calls during the vector growth and to - * not bloat the binary size of the inlined freeLater method. Any OOM - * during buffer growth results in the pointer being freed immediately. - */ - static const size_t FREE_ARRAY_SIZE = size_t(1) << 16; - static const size_t FREE_ARRAY_LENGTH = FREE_ARRAY_SIZE / sizeof(void *); - - JSRuntime *const rt; - PRThread *thread; - PRCondVar *wakeup; - PRCondVar *done; - volatile State state; - - bool sweepFlag; - bool shrinkFlag; - - Vector freeVector; - void **freeCursor; - void **freeCursorEnd; - - bool backgroundAllocation; - - friend struct js::gc::ArenaLists; - - JS_FRIEND_API(void) - replenishAndFreeLater(void *ptr); - - static void freeElementsAndArray(void **array, void **end) { - JS_ASSERT(array <= end); - for (void **p = array; p != end; ++p) - js_free(*p); - js_free(array); - } - - static void threadMain(void* arg); - void threadLoop(); - - /* Must be called with the GC lock taken. */ - void doSweep(); - - public: - GCHelperThread(JSRuntime *rt) - : rt(rt), - thread(NULL), - wakeup(NULL), - done(NULL), - state(IDLE), - sweepFlag(false), - shrinkFlag(false), - freeCursor(NULL), - freeCursorEnd(NULL), - backgroundAllocation(true) - { } - - bool init(); - void finish(); - - /* Must be called with the GC lock taken. */ - void startBackgroundSweep(bool shouldShrink); - - /* Must be called with the GC lock taken. */ - void startBackgroundShrink(); - - /* Must be called without the GC lock taken. */ - void waitBackgroundSweepEnd(); - - /* Must be called without the GC lock taken. */ - void waitBackgroundSweepOrAllocEnd(); - - /* Must be called with the GC lock taken. */ - inline void startBackgroundAllocationIfIdle(); - - bool canBackgroundAllocate() const { - return backgroundAllocation; - } - - void disableBackgroundAllocation() { - backgroundAllocation = false; - } - - PRThread *getThread() const { - return thread; - } - - /* - * Outside the GC lock may give true answer when in fact the sweeping has - * been done. - */ - bool sweeping() const { - return state == SWEEPING; - } - - bool shouldShrink() const { - JS_ASSERT(sweeping()); - return shrinkFlag; - } - - void freeLater(void *ptr) { - JS_ASSERT(!sweeping()); - if (freeCursor != freeCursorEnd) - *freeCursor++ = ptr; - else - replenishAndFreeLater(ptr); - } -}; - - -struct GCChunkHasher { - typedef gc::Chunk *Lookup; - - /* - * Strip zeros for better distribution after multiplying by the golden - * ratio. - */ - static HashNumber hash(gc::Chunk *chunk) { - JS_ASSERT(!(uintptr_t(chunk) & gc::ChunkMask)); - return HashNumber(uintptr_t(chunk) >> gc::ChunkShift); - } - - static bool match(gc::Chunk *k, gc::Chunk *l) { - JS_ASSERT(!(uintptr_t(k) & gc::ChunkMask)); - JS_ASSERT(!(uintptr_t(l) & gc::ChunkMask)); - return k == l; - } -}; - -typedef HashSet GCChunkSet; - -template -struct MarkStack { - T *stack; - T *tos; - T *limit; - - T *ballast; - T *ballastLimit; - - size_t sizeLimit; - - MarkStack(size_t sizeLimit) - : stack(NULL), - tos(NULL), - limit(NULL), - ballast(NULL), - ballastLimit(NULL), - sizeLimit(sizeLimit) { } - - ~MarkStack() { - if (stack != ballast) - js_free(stack); - js_free(ballast); - } - - bool init(size_t ballastcap) { - JS_ASSERT(!stack); - - if (ballastcap == 0) - return true; - - ballast = js_pod_malloc(ballastcap); - if (!ballast) - return false; - ballastLimit = ballast + ballastcap; - initFromBallast(); - return true; - } - - void initFromBallast() { - stack = ballast; - limit = ballastLimit; - if (size_t(limit - stack) > sizeLimit) - limit = stack + sizeLimit; - tos = stack; - } - - void setSizeLimit(size_t size) { - JS_ASSERT(isEmpty()); - - sizeLimit = size; - reset(); - } - - bool push(T item) { - if (tos == limit) { - if (!enlarge()) - return false; - } - JS_ASSERT(tos < limit); - *tos++ = item; - return true; - } - - bool push(T item1, T item2, T item3) { - T *nextTos = tos + 3; - if (nextTos > limit) { - if (!enlarge()) - return false; - nextTos = tos + 3; - } - JS_ASSERT(nextTos <= limit); - tos[0] = item1; - tos[1] = item2; - tos[2] = item3; - tos = nextTos; - return true; - } - - bool isEmpty() const { - return tos == stack; - } - - T pop() { - JS_ASSERT(!isEmpty()); - return *--tos; - } - - ptrdiff_t position() const { - return tos - stack; - } - - void reset() { - if (stack != ballast) - js_free(stack); - initFromBallast(); - JS_ASSERT(stack == ballast); - } - - bool enlarge() { - size_t tosIndex = tos - stack; - size_t cap = limit - stack; - if (cap == sizeLimit) - return false; - size_t newcap = cap * 2; - if (newcap == 0) - newcap = 32; - if (newcap > sizeLimit) - newcap = sizeLimit; - - T *newStack; - if (stack == ballast) { - newStack = js_pod_malloc(newcap); - if (!newStack) - return false; - for (T *src = stack, *dst = newStack; src < tos; ) - *dst++ = *src++; - } else { - newStack = (T *)js_realloc(stack, sizeof(T) * newcap); - if (!newStack) - return false; - } - stack = newStack; - tos = stack + tosIndex; - limit = newStack + newcap; - return true; - } - - size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const { - size_t n = 0; - if (stack != ballast) - n += mallocSizeOf(stack); - n += mallocSizeOf(ballast); - return n; - } -}; - -/* - * This class records how much work has been done in a given GC slice, so that - * we can return before pausing for too long. Some slices are allowed to run for - * unlimited time, and others are bounded. To reduce the number of gettimeofday - * calls, we only check the time every 1000 operations. - */ -struct SliceBudget { - int64_t deadline; /* in microseconds */ - intptr_t counter; - - static const intptr_t CounterReset = 1000; - - static const int64_t Unlimited = 0; - static int64_t TimeBudget(int64_t millis); - static int64_t WorkBudget(int64_t work); - - /* Equivalent to SliceBudget(UnlimitedBudget). */ - SliceBudget(); - - /* Instantiate as SliceBudget(Time/WorkBudget(n)). */ - SliceBudget(int64_t budget); - - void reset() { - deadline = INT64_MAX; - counter = INTPTR_MAX; - } - - void step(intptr_t amt = 1) { - counter -= amt; - } - - bool checkOverBudget(); - - bool isOverBudget() { - if (counter >= 0) - return false; - return checkOverBudget(); - } -}; - -static const size_t MARK_STACK_LENGTH = 32768; - -struct GCMarker : public JSTracer { - private: - /* - * We use a common mark stack to mark GC things of different types and use - * the explicit tags to distinguish them when it cannot be deduced from - * the context of push or pop operation. - */ - enum StackTag { - ValueArrayTag, - ObjectTag, - TypeTag, - XmlTag, - ArenaTag, - SavedValueArrayTag, - IonCodeTag, - LastTag = IonCodeTag - }; - - static const uintptr_t StackTagMask = 7; - - static void staticAsserts() { - JS_STATIC_ASSERT(StackTagMask >= uintptr_t(LastTag)); - JS_STATIC_ASSERT(StackTagMask <= gc::Cell::CellMask); - } - - public: - explicit GCMarker(); - bool init(); - - void setSizeLimit(size_t size) { stack.setSizeLimit(size); } - size_t sizeLimit() const { return stack.sizeLimit; } - - void start(JSRuntime *rt); - void stop(); - void reset(); - - void pushObject(JSObject *obj) { - pushTaggedPtr(ObjectTag, obj); - } - - void pushArenaList(gc::ArenaHeader *firstArena) { - pushTaggedPtr(ArenaTag, firstArena); - } - - void pushType(types::TypeObject *type) { - pushTaggedPtr(TypeTag, type); - } - -#if JS_HAS_XML_SUPPORT - void pushXML(JSXML *xml) { - pushTaggedPtr(XmlTag, xml); - } - -#endif - - void pushIonCode(ion::IonCode *code) { - pushTaggedPtr(IonCodeTag, code); - } - - uint32_t getMarkColor() const { - return color; - } - - /* - * The only valid color transition during a GC is from black to gray. It is - * wrong to switch the mark color from gray to black. The reason is that the - * cycle collector depends on the invariant that there are no black to gray - * edges in the GC heap. This invariant lets the CC not trace through black - * objects. If this invariant is violated, the cycle collector may free - * objects that are still reachable. - */ - void setMarkColorGray() { - JS_ASSERT(isDrained()); - JS_ASSERT(color == gc::BLACK); - color = gc::GRAY; - } - - inline void delayMarkingArena(gc::ArenaHeader *aheader); - void delayMarkingChildren(const void *thing); - void markDelayedChildren(gc::ArenaHeader *aheader); - bool markDelayedChildren(SliceBudget &budget); - bool hasDelayedChildren() const { - return !!unmarkedArenaStackTop; - } - - bool isDrained() { - return isMarkStackEmpty() && !unmarkedArenaStackTop; - } - - bool drainMarkStack(SliceBudget &budget); - - /* - * Gray marking must be done after all black marking is complete. However, - * we do not have write barriers on XPConnect roots. Therefore, XPConnect - * roots must be accumulated in the first slice of incremental GC. We - * accumulate these roots in the GrayRootMarker and then mark them later, - * after black marking is complete. This accumulation can fail, but in that - * case we switch to non-incremental GC. - */ - bool hasBufferedGrayRoots() const; - void startBufferingGrayRoots(); - void endBufferingGrayRoots(); - void markBufferedGrayRoots(); - - static void GrayCallback(JSTracer *trc, void **thing, JSGCTraceKind kind); - - size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const; - - MarkStack stack; - - private: -#ifdef DEBUG - void checkCompartment(void *p); -#else - void checkCompartment(void *p) {} -#endif - - void pushTaggedPtr(StackTag tag, void *ptr) { - checkCompartment(ptr); - uintptr_t addr = reinterpret_cast(ptr); - JS_ASSERT(!(addr & StackTagMask)); - if (!stack.push(addr | uintptr_t(tag))) - delayMarkingChildren(ptr); - } - - void pushValueArray(JSObject *obj, void *start, void *end) { - checkCompartment(obj); - - JS_ASSERT(start <= end); - uintptr_t tagged = reinterpret_cast(obj) | GCMarker::ValueArrayTag; - uintptr_t startAddr = reinterpret_cast(start); - uintptr_t endAddr = reinterpret_cast(end); - - /* - * Push in the reverse order so obj will be on top. If we cannot push - * the array, we trigger delay marking for the whole object. - */ - if (!stack.push(endAddr, startAddr, tagged)) - delayMarkingChildren(obj); - } - - bool isMarkStackEmpty() { - return stack.isEmpty(); - } - - bool restoreValueArray(JSObject *obj, void **vpp, void **endp); - void saveValueRanges(); - inline void processMarkStackTop(SliceBudget &budget); - void processMarkStackOther(SliceBudget &budget, uintptr_t tag, uintptr_t addr); - - void appendGrayRoot(void *thing, JSGCTraceKind kind); - - /* The color is only applied to objects, functions and xml. */ - uint32_t color; - - DebugOnly started; - - /* Pointer to the top of the stack of arenas we are delaying marking on. */ - js::gc::ArenaHeader *unmarkedArenaStackTop; - /* Count of arenas that are currently in the stack. */ - DebugOnly markLaterArenas; - - struct GrayRoot { - void *thing; - JSGCTraceKind kind; -#ifdef DEBUG - JSTraceNamePrinter debugPrinter; - const void *debugPrintArg; - size_t debugPrintIndex; -#endif - - GrayRoot(void *thing, JSGCTraceKind kind) - : thing(thing), kind(kind) {} - }; - - bool grayFailed; - Vector grayRoots; -}; - -void -SetMarkStackLimit(JSRuntime *rt, size_t limit); - -void -MarkStackRangeConservatively(JSTracer *trc, Value *begin, Value *end); - -typedef void (*IterateChunkCallback)(JSRuntime *rt, void *data, gc::Chunk *chunk); -typedef void (*IterateArenaCallback)(JSRuntime *rt, void *data, gc::Arena *arena, - JSGCTraceKind traceKind, size_t thingSize); -typedef void (*IterateCellCallback)(JSRuntime *rt, void *data, void *thing, - JSGCTraceKind traceKind, size_t thingSize); - -/* - * This function calls |compartmentCallback| on every compartment, - * |arenaCallback| on every in-use arena, and |cellCallback| on every in-use - * cell in the GC heap. - */ -extern JS_FRIEND_API(void) -IterateCompartmentsArenasCells(JSRuntime *rt, void *data, - JSIterateCompartmentCallback compartmentCallback, - IterateArenaCallback arenaCallback, - IterateCellCallback cellCallback); - -/* - * Invoke chunkCallback on every in-use chunk. - */ -extern JS_FRIEND_API(void) -IterateChunks(JSRuntime *rt, void *data, IterateChunkCallback chunkCallback); - -/* - * Invoke cellCallback on every in-use object of the specified thing kind for - * the given compartment or for all compartments if it is null. - */ -extern JS_FRIEND_API(void) -IterateCells(JSRuntime *rt, JSCompartment *compartment, gc::AllocKind thingKind, - void *data, IterateCellCallback cellCallback); - -/* - * Invoke cellCallback on every gray JS_OBJECT in the given compartment. - */ -extern JS_FRIEND_API(void) -IterateGrayObjects(JSCompartment *compartment, GCThingCallback *cellCallback, void *data); - -} /* namespace js */ - -extern void -js_FinalizeStringRT(JSRuntime *rt, JSString *str); - -/* - * Macro to test if a traversal is the marking phase of the GC. - */ -#define IS_GC_MARKING_TRACER(trc) \ - ((trc)->callback == NULL || (trc)->callback == GCMarker::GrayCallback) - -namespace js { -namespace gc { - -JSCompartment * -NewCompartment(JSContext *cx, JSPrincipals *principals); - -/* Tries to run a GC no matter what (used for GC zeal). */ -void -RunDebugGC(JSContext *cx); - -void -SetDeterministicGC(JSContext *cx, bool enabled); - -void -SetValidateGC(JSContext *cx, bool enabled); - -const int ZealPokeValue = 1; -const int ZealAllocValue = 2; -const int ZealFrameGCValue = 3; -const int ZealVerifierPreValue = 4; -const int ZealFrameVerifierPreValue = 5; -const int ZealStackRootingSafeValue = 6; -const int ZealStackRootingValue = 7; -const int ZealIncrementalRootsThenFinish = 8; -const int ZealIncrementalMarkAllThenFinish = 9; -const int ZealIncrementalMultipleSlices = 10; -const int ZealVerifierPostValue = 11; -const int ZealFrameVerifierPostValue = 12; -const int ZealPurgeAnalysisValue = 13; - -enum VerifierType { - PreBarrierVerifier, - PostBarrierVerifier -}; - -#ifdef JS_GC_ZEAL - -/* Check that write barriers have been used correctly. See jsgc.cpp. */ -void -VerifyBarriers(JSRuntime *rt, VerifierType type); - -void -MaybeVerifyBarriers(JSContext *cx, bool always = false); - -#else - -static inline void -VerifyBarriers(JSRuntime *rt, VerifierType type) -{ -} - -static inline void -MaybeVerifyBarriers(JSContext *cx, bool always = false) -{ -} - -#endif - -} /* namespace gc */ - -static inline JSCompartment * -GetGCThingCompartment(void *thing) -{ - JS_ASSERT(thing); - return reinterpret_cast(thing)->compartment(); -} - -static inline JSCompartment * -GetObjectCompartment(JSObject *obj) -{ - return GetGCThingCompartment(obj); -} - -void -PurgeJITCaches(JSCompartment *c); - -} /* namespace js */ - -#endif /* jsgc_h___ */ diff --git a/scripting/javascript/spidermonkey-ios/include/jslock.h b/scripting/javascript/spidermonkey-ios/include/jslock.h index 11c2893c14..aa5e1c83e7 100644 --- a/scripting/javascript/spidermonkey-ios/include/jslock.h +++ b/scripting/javascript/spidermonkey-ios/include/jslock.h @@ -38,25 +38,4 @@ typedef struct PRLock PRLock; #endif /* JS_THREADSAFE */ -namespace js { - -class AutoAtomicIncrement -{ - int32_t *p; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER - - public: - AutoAtomicIncrement(int32_t *p JS_GUARD_OBJECT_NOTIFIER_PARAM) - : p(p) { - JS_GUARD_OBJECT_NOTIFIER_INIT; - JS_ATOMIC_INCREMENT(p); - } - - ~AutoAtomicIncrement() { - JS_ATOMIC_DECREMENT(p); - } -}; - -} /* namespace js */ - #endif /* jslock_h___ */ diff --git a/scripting/javascript/spidermonkey-ios/include/json.h b/scripting/javascript/spidermonkey-ios/include/json.h index 4794d64e65..bb21f91451 100644 --- a/scripting/javascript/spidermonkey-ios/include/json.h +++ b/scripting/javascript/spidermonkey-ios/include/json.h @@ -38,7 +38,7 @@ enum DecodingMode { STRICT, LEGACY }; namespace js { extern JS_FRIEND_API(JSBool) -ParseJSONWithReviver(JSContext *cx, const jschar *chars, size_t length, HandleValue filter, +ParseJSONWithReviver(JSContext *cx, JS::StableCharPtr chars, size_t length, HandleValue filter, MutableHandleValue vp, DecodingMode decodingMode = STRICT); } /* namespace js */ diff --git a/scripting/javascript/spidermonkey-ios/include/jsprf.h b/scripting/javascript/spidermonkey-ios/include/jsprf.h index 4c72b691f8..4c2bef91d2 100644 --- a/scripting/javascript/spidermonkey-ios/include/jsprf.h +++ b/scripting/javascript/spidermonkey-ios/include/jsprf.h @@ -16,10 +16,9 @@ ** %hd, %hu, %hx, %hX, %ho - 16-bit versions of above ** %ld, %lu, %lx, %lX, %lo - 32-bit versions of above ** %lld, %llu, %llx, %llX, %llo - 64 bit versions of above -** %s - string -** %hs - 16-bit version of above (only available if js_CStringsAreUTF8) +** %s - ascii string +** %hs - ucs2 string ** %c - character -** %hc - 16-bit version of above (only available if js_CStringsAreUTF8) ** %p - pointer (deals with machine dependent pointer size) ** %f - float ** %g - float @@ -28,8 +27,6 @@ #include #include -JS_BEGIN_EXTERN_C - /* ** sprintf into a fixed size buffer. Guarantees that a NUL is at the end ** of the buffer. Returns the length of the written output, NOT including @@ -77,6 +74,4 @@ extern JS_PUBLIC_API(char*) JS_vsmprintf(const char *fmt, va_list ap); extern JS_PUBLIC_API(char*) JS_vsprintf_append(char *last, const char *fmt, va_list ap); extern JS_PUBLIC_API(uint32_t) JS_vsxprintf(JSStuffFunc f, void *arg, const char *fmt, va_list ap); -JS_END_EXTERN_C - #endif /* jsprf_h___ */ diff --git a/scripting/javascript/spidermonkey-ios/include/jsprototypes.h b/scripting/javascript/spidermonkey-ios/include/jsprototypes.h index 5c65eb25cd..1abbc831a9 100644 --- a/scripting/javascript/spidermonkey-ios/include/jsprototypes.h +++ b/scripting/javascript/spidermonkey-ios/include/jsprototypes.h @@ -60,5 +60,6 @@ macro(Set, 38, js_InitSetClass) \ macro(DataView, 39, js_InitTypedArrayClasses) \ macro(ParallelArray, 40, js_InitParallelArrayClass) \ + macro(Intl, 41, js_InitIntlClass) \ #endif /* jsprototypes_h___ */ diff --git a/scripting/javascript/spidermonkey-ios/include/jsproxy.h b/scripting/javascript/spidermonkey-ios/include/jsproxy.h index 075abc7123..73ea5c8c70 100644 --- a/scripting/javascript/spidermonkey-ios/include/jsproxy.h +++ b/scripting/javascript/spidermonkey-ios/include/jsproxy.h @@ -26,7 +26,9 @@ class JS_FRIEND_API(Wrapper); * * Proxy traps are grouped into fundamental and derived traps. Every proxy has * to at least provide implementations for the fundamental traps, but the - * derived traps can be implemented in terms of the fundamental ones. + * derived traps can be implemented in terms of the fundamental ones + * BaseProxyHandler provides implementations of the derived traps in terms of + * the (pure virtual) fundamental traps. * * To minimize code duplication, a set of abstract proxy handler classes is * provided, from which other handlers may inherit. These abstract classes @@ -34,9 +36,9 @@ class JS_FRIEND_API(Wrapper); * * BaseProxyHandler * | - * IndirectProxyHandler - * | * DirectProxyHandler + * | + * Wrapper */ /* @@ -69,20 +71,6 @@ class JS_FRIEND_API(BaseProxyHandler) { return false; } - /* - * The function Wrapper::wrapperHandler takes a pointer to a - * BaseProxyHandler and returns a pointer to a Wrapper if and only if the - * BaseProxyHandler is a wrapper handler (otherwise, it returns NULL). - * - * Unfortunately, we can't inherit Wrapper from BaseProxyHandler, since that - * would create a dreaded diamond, and we can't use dynamic_cast to cast - * BaseProxyHandler to Wrapper, since that would require us to compile with - * run-time type information. Hence the need for this virtual function. - */ - virtual Wrapper *toWrapper() { - return NULL; - } - /* ES5 Harmony fundamental proxy traps. */ virtual bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, PropertyDescriptor *desc) = 0; @@ -130,17 +118,14 @@ class JS_FRIEND_API(BaseProxyHandler) { }; /* - * IndirectProxyHandler assumes that a target exists. Moreover, it assumes the - * target is a JSObject. Consequently, it provides default implementations for - * the fundamental traps that forward their behavior to the target. The derived - * traps, however, are inherited from BaseProxyHandler, and therefore still - * implemented in terms of the fundamental ones. This allows consumers of this - * class to define custom behavior without implementing the entire gamut of - * proxy traps. + * DirectProxyHandler includes a notion of a target object. All traps are + * reimplemented such that they forward their behavior to the target. This + * allows consumers of this class to forward to another object as transparently + * and efficiently as possible. */ -class JS_PUBLIC_API(IndirectProxyHandler) : public BaseProxyHandler { - public: - explicit IndirectProxyHandler(void *family); +class JS_PUBLIC_API(DirectProxyHandler) : public BaseProxyHandler { +public: + explicit DirectProxyHandler(void *family); /* ES5 Harmony fundamental proxy traps. */ virtual bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, @@ -158,11 +143,21 @@ class JS_PUBLIC_API(IndirectProxyHandler) : public BaseProxyHandler { virtual bool enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props) MOZ_OVERRIDE; + /* ES5 Harmony derived proxy traps. */ + virtual bool has(JSContext *cx, JSObject *proxy, jsid id, + bool *bp) MOZ_OVERRIDE; + virtual bool hasOwn(JSContext *cx, JSObject *proxy, jsid id, + bool *bp) MOZ_OVERRIDE; + virtual bool get(JSContext *cx, JSObject *proxy, JSObject *receiver, + jsid id, Value *vp) MOZ_OVERRIDE; + virtual bool set(JSContext *cx, JSObject *proxy, JSObject *receiver, + jsid id, bool strict, Value *vp) MOZ_OVERRIDE; + virtual bool keys(JSContext *cx, JSObject *proxy, + AutoIdVector &props) MOZ_OVERRIDE; + virtual bool iterate(JSContext *cx, JSObject *proxy, unsigned flags, + Value *vp) MOZ_OVERRIDE; + /* Spidermonkey extensions. */ - virtual bool call(JSContext *cx, JSObject *proxy, unsigned argc, - Value *vp) MOZ_OVERRIDE; - virtual bool construct(JSContext *cx, JSObject *proxy, unsigned argc, - Value *argv, Value *rval) MOZ_OVERRIDE; virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args) MOZ_OVERRIDE; virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, @@ -182,33 +177,6 @@ class JS_PUBLIC_API(IndirectProxyHandler) : public BaseProxyHandler { virtual JSObject *weakmapKeyDelegate(JSObject *proxy); }; -/* - * DirectProxyHandler has the same assumptions about the target as its base, - * IndirectProxyHandler. Its fundamental traps are inherited from this class, - * and therefore forward their behavior to the target. The derived traps, - * however, are overrided so that, they too, forward their behavior to the - * target. This allows consumers of this class to forward to another object as - * transparently as possible. - */ -class JS_PUBLIC_API(DirectProxyHandler) : public IndirectProxyHandler { -public: - explicit DirectProxyHandler(void *family); - - /* ES5 Harmony derived proxy traps. */ - virtual bool has(JSContext *cx, JSObject *proxy, jsid id, - bool *bp) MOZ_OVERRIDE; - virtual bool hasOwn(JSContext *cx, JSObject *proxy, jsid id, - bool *bp) MOZ_OVERRIDE; - virtual bool get(JSContext *cx, JSObject *proxy, JSObject *receiver, - jsid id, Value *vp) MOZ_OVERRIDE; - virtual bool set(JSContext *cx, JSObject *proxy, JSObject *receiver, - jsid id, bool strict, Value *vp) MOZ_OVERRIDE; - virtual bool keys(JSContext *cx, JSObject *proxy, - AutoIdVector &props) MOZ_OVERRIDE; - virtual bool iterate(JSContext *cx, JSObject *proxy, unsigned flags, - Value *vp) MOZ_OVERRIDE; -}; - /* Dispatch point for handlers that executes the appropriate C++ or scripted traps. */ class Proxy { public: @@ -350,13 +318,12 @@ NewProxyObject(JSContext *cx, BaseProxyHandler *handler, const Value &priv, JSObject *proto, JSObject *parent, JSObject *call = NULL, JSObject *construct = NULL); -} /* namespace js */ +JSObject * +RenewProxyObject(JSContext *cx, JSObject *obj, BaseProxyHandler *handler, Value priv); -JS_BEGIN_EXTERN_C +} /* namespace js */ extern JS_FRIEND_API(JSObject *) js_InitProxyClass(JSContext *cx, JSHandleObject obj); -JS_END_EXTERN_C - #endif diff --git a/scripting/javascript/spidermonkey-ios/include/jsprvtd.h b/scripting/javascript/spidermonkey-ios/include/jsprvtd.h index 2fbfc54aa1..d399b3fd93 100644 --- a/scripting/javascript/spidermonkey-ios/include/jsprvtd.h +++ b/scripting/javascript/spidermonkey-ios/include/jsprvtd.h @@ -29,8 +29,6 @@ #include "js/Vector.h" #endif -JS_BEGIN_EXTERN_C - /* * Convenience constants. */ @@ -129,7 +127,7 @@ class ScriptFrameIter; class Proxy; class JS_FRIEND_API(BaseProxyHandler); -class JS_FRIEND_API(DirectWrapper); +class JS_FRIEND_API(Wrapper); class JS_FRIEND_API(CrossCompartmentWrapper); class TempAllocPolicy; @@ -379,16 +377,5 @@ typedef JSObject * typedef JSObject * (* JSIteratorOp)(JSContext *cx, JSHandleObject obj, JSBool keysonly); -/* - * The following determines whether JS_EncodeCharacters and JS_DecodeBytes - * treat char[] as utf-8 or simply as bytes that need to be inflated/deflated. - */ -#ifdef JS_C_STRINGS_ARE_UTF8 -# define js_CStringsAreUTF8 JS_TRUE -#else -extern JSBool js_CStringsAreUTF8; -#endif - -JS_END_EXTERN_C #endif /* jsprvtd_h___ */ diff --git a/scripting/javascript/spidermonkey-ios/include/jspubtd.h b/scripting/javascript/spidermonkey-ios/include/jspubtd.h index c14ef29c24..caf75de0f6 100644 --- a/scripting/javascript/spidermonkey-ios/include/jspubtd.h +++ b/scripting/javascript/spidermonkey-ios/include/jspubtd.h @@ -39,6 +39,8 @@ namespace JS { class Value; } */ #ifdef __cplusplus +#define JS_NO_JSVAL_JSID_STRUCT_TYPES + # if defined(DEBUG) && !defined(JS_NO_JSVAL_JSID_STRUCT_TYPES) # define JS_USE_JSID_STRUCT_TYPES # endif @@ -60,8 +62,6 @@ typedef ptrdiff_t jsid; # define JSID_BITS(id) (id) #endif -JS_BEGIN_EXTERN_C - #ifdef WIN32 typedef wchar_t jschar; #else @@ -217,8 +217,6 @@ typedef JSBool JSCallOnceType; #endif typedef JSBool (*JSInitCallback)(void); -JS_END_EXTERN_C - #ifdef __cplusplus namespace js { @@ -311,14 +309,6 @@ struct RuntimeFriendFields { /* Limit pointer for checking native stack consumption. */ uintptr_t nativeStackLimit; -#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) - /* - * Stack allocated GC roots for stack GC heap pointers, which may be - * overwritten if moved during a GC. - */ - Rooted *thingGCRooters[THING_ROOT_LIMIT]; -#endif - RuntimeFriendFields() : interrupt(0), nativeStackLimit(0) { } @@ -328,6 +318,32 @@ struct RuntimeFriendFields { } }; +class PerThreadData; + +struct PerThreadDataFriendFields +{ + PerThreadDataFriendFields(); + +#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) + /* + * Stack allocated GC roots for stack GC heap pointers, which may be + * overwritten if moved during a GC. + */ + Rooted *thingGCRooters[THING_ROOT_LIMIT]; +#endif + + static PerThreadDataFriendFields *get(js::PerThreadData *pt) { + return reinterpret_cast(pt); + } + + static PerThreadDataFriendFields *getMainThread(JSRuntime *rt) { + // mainThread must always appear directly after |RuntimeFriendFields|. + // Tested by a JS_STATIC_ASSERT in |jsfriendapi.cpp| + return reinterpret_cast( + reinterpret_cast(rt) + sizeof(RuntimeFriendFields)); + } +}; + } /* namespace js */ #endif /* __cplusplus */ diff --git a/scripting/javascript/spidermonkey-ios/include/jstypes.h b/scripting/javascript/spidermonkey-ios/include/jstypes.h index d0cf183e0f..90f5d44a64 100644 --- a/scripting/javascript/spidermonkey-ios/include/jstypes.h +++ b/scripting/javascript/spidermonkey-ios/include/jstypes.h @@ -46,11 +46,11 @@ ** ***********************************************************************/ -#define JS_EXTERN_API(type) extern MOZ_EXPORT_API(type) -#define JS_EXPORT_API(type) MOZ_EXPORT_API(type) -#define JS_EXPORT_DATA(type) MOZ_EXPORT_DATA(type) -#define JS_IMPORT_API(type) MOZ_IMPORT_API(type) -#define JS_IMPORT_DATA(type) MOZ_IMPORT_DATA(type) +#define JS_EXTERN_API(type) extern MOZ_EXPORT type +#define JS_EXPORT_API(type) MOZ_EXPORT type +#define JS_EXPORT_DATA(type) MOZ_EXPORT type +#define JS_IMPORT_API(type) MOZ_IMPORT_API type +#define JS_IMPORT_DATA(type) MOZ_IMPORT_DATA type /* * The linkage of JS API functions differs depending on whether the file is @@ -62,11 +62,11 @@ # define JS_PUBLIC_API(t) t # define JS_PUBLIC_DATA(t) t #elif defined(EXPORT_JS_API) || defined(STATIC_EXPORTABLE_JS_API) -# define JS_PUBLIC_API(t) MOZ_EXPORT_API(t) -# define JS_PUBLIC_DATA(t) MOZ_EXPORT_DATA(t) +# define JS_PUBLIC_API(t) MOZ_EXPORT t +# define JS_PUBLIC_DATA(t) MOZ_EXPORT t #else -# define JS_PUBLIC_API(t) MOZ_IMPORT_API(t) -# define JS_PUBLIC_DATA(t) MOZ_IMPORT_DATA(t) +# define JS_PUBLIC_API(t) MOZ_IMPORT_API t +# define JS_PUBLIC_DATA(t) MOZ_IMPORT_DATA t #endif #define JS_FRIEND_API(t) JS_PUBLIC_API(t) @@ -181,8 +181,6 @@ #endif -JS_BEGIN_EXTERN_C - /************************************************************************ ** TYPES: JSBool ** DESCRIPTION: @@ -282,6 +280,4 @@ typedef int JSBool; # define JS_EXTENSION_(s) s #endif -JS_END_EXTERN_C - #endif /* jstypes_h___ */ diff --git a/scripting/javascript/spidermonkey-ios/include/jsutil.h b/scripting/javascript/spidermonkey-ios/include/jsutil.h index 9f0c922a23..ef7f94a824 100644 --- a/scripting/javascript/spidermonkey-ios/include/jsutil.h +++ b/scripting/javascript/spidermonkey-ios/include/jsutil.h @@ -369,23 +369,23 @@ class Compressor z_stream zs; const unsigned char *inp; size_t inplen; + size_t outbytes; + public: - Compressor(const unsigned char *inp, size_t inplen, unsigned char *out) - : inp(inp), - inplen(inplen) - { - JS_ASSERT(inplen > 0); - zs.opaque = NULL; - zs.next_in = (Bytef *)inp; - zs.avail_in = 0; - zs.next_out = out; - zs.avail_out = inplen; - } + enum Status { + MOREOUTPUT, + DONE, + CONTINUE, + OOM + }; + + Compressor(const unsigned char *inp, size_t inplen); + ~Compressor(); bool init(); + void setOutput(unsigned char *out, size_t outlen); + size_t outWritten() const { return outbytes; } /* Compress some of the input. Return true if it should be called again. */ - bool compressMore(); - /* Finalize compression. Return the length of the compressed input. */ - size_t finish(); + Status compressMore(); }; /* diff --git a/scripting/javascript/spidermonkey-ios/include/jsval.h b/scripting/javascript/spidermonkey-ios/include/jsval.h index 67c8be4252..949127aa0d 100644 --- a/scripting/javascript/spidermonkey-ios/include/jsval.h +++ b/scripting/javascript/spidermonkey-ios/include/jsval.h @@ -14,8 +14,6 @@ #include "js/Utility.h" -JS_BEGIN_EXTERN_C - /* * Try to get jsvals 64-bit aligned. We could almost assert that all values are * aligned, but MSVC and GCC occasionally break alignment. @@ -835,8 +833,6 @@ JS_CANONICALIZE_NAN(double d) return d; } -JS_END_EXTERN_C - #ifdef __cplusplus static jsval_layout JSVAL_TO_IMPL(JS::Value); static JS::Value IMPL_TO_JSVAL(jsval_layout); diff --git a/scripting/javascript/spidermonkey-ios/include/jsversion.h b/scripting/javascript/spidermonkey-ios/include/jsversion.h index 5a620c8fcc..475525177a 100644 --- a/scripting/javascript/spidermonkey-ios/include/jsversion.h +++ b/scripting/javascript/spidermonkey-ios/include/jsversion.h @@ -170,4 +170,7 @@ MOZ_NOT_REACHED("don't call this! to be used in the new object representation") #endif +/* ECMAScript Internationalization API isn't fully implemented yet. */ +#define ENABLE_INTL_API 0 + #endif /* jsversion_h___ */ diff --git a/scripting/javascript/spidermonkey-ios/include/jswrapper.h b/scripting/javascript/spidermonkey-ios/include/jswrapper.h index 1ec99cb4f8..e5ef59c6c9 100644 --- a/scripting/javascript/spidermonkey-ios/include/jswrapper.h +++ b/scripting/javascript/spidermonkey-ios/include/jswrapper.h @@ -18,36 +18,24 @@ namespace js { class DummyFrameGuard; /* - * A wrapper is essentially a proxy that restricts access to certain traps. The - * way in which a wrapper restricts access to its traps depends on the - * particular policy for that wrapper. To allow a wrapper's policy to be - * customized, the Wrapper base class contains two functions, enter/leave, which - * are called as a policy enforcement check before/after each trap is forwarded. - * - * To minimize code duplication, a set of abstract wrapper classes is - * provided, from which other wrappers may inherit. These abstract classes are - * organized in the following hierarchy: - * - * BaseProxyHandler Wrapper - * | | | - * IndirectProxyHandler | | - * | | | | - * | IndirectWrapper | - * | | - * DirectProxyHandler | - * | | - * DirectWrapper + * A wrapper is a proxy with a target object to which it generally forwards + * operations, but may restrict access to certain operations or instrument + * the trap operations in various ways. A wrapper is distinct from a Direct Proxy + * Handler in the sense that it can be "unwrapped" in C++, exposing the underlying + * object (Direct Proxy Handlers have an underlying target object, but don't + * expect to expose this object via any kind of unwrapping operation). Callers + * should be careful to avoid unwrapping security wrappers in the wrong context. */ -class JS_FRIEND_API(Wrapper) +class JS_FRIEND_API(Wrapper) : public DirectProxyHandler { unsigned mFlags; + bool mSafeToUnwrap; public: enum Action { GET, SET, - CALL, - PUNCTURE + CALL }; enum Flags { @@ -55,128 +43,42 @@ class JS_FRIEND_API(Wrapper) LAST_USED_FLAG = CROSS_COMPARTMENT }; - typedef enum { - PermitObjectAccess, - PermitPropertyAccess, - DenyAccess - } Permission; + /* + * Wrappers can explicitly specify that they are unsafe to unwrap from a + * security perspective (as is the case for SecurityWrappers). If a wrapper + * is not safe to unwrap, operations requiring full access to the underlying + * object (via UnwrapObjectChecked) will throw. Otherwise, they will succeed. + */ + void setSafeToUnwrap(bool safe) { mSafeToUnwrap = safe; }; + bool isSafeToUnwrap() { return mSafeToUnwrap; }; static JSObject *New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, Wrapper *handler); + static JSObject *Renew(JSContext *cx, JSObject *existing, JSObject *obj, Wrapper *handler); + static Wrapper *wrapperHandler(RawObject wrapper); static JSObject *wrappedObject(RawObject wrapper); - explicit Wrapper(unsigned flags); - unsigned flags() const { return mFlags; } - /* - * The function Wrapper::New takes a pointer to a Wrapper as the handler - * object. It then passes it on to the function NewProxyObject, which - * expects a pointer to a BaseProxyHandler as the handler object. We don't - * want to change Wrapper::New to take a pointer to a BaseProxyHandler, - * because that would allow the creation of wrappers with non-wrapper - * handlers. Unfortunately, we can't inherit Wrapper from BaseProxyHandler, - * since that would create a dreaded diamond, and we can't use dynamic_cast - * to cast Wrapper to BaseProxyHandler, since that would require us to - * compile with run time type information. Hence the need for this virtual - * function. - */ - virtual BaseProxyHandler *toBaseProxyHandler() = 0; - /* Policy enforcement traps. * * enter() allows the policy to specify whether the caller may perform |act| * on the underlying object's |id| property. In the case when |act| is CALL, * |id| is generally JSID_VOID. * - * The |act| parameter to enter() specifies the action being performed. GET, - * SET, and CALL are self-explanatory, but PUNCTURE requires more - * explanation: - * - * GET and SET allow for a very fine-grained security membrane, through - * which access can be granted or denied on a per-property, per-object, and - * per-action basis. Sometimes though, we just want to asks if we can access - * _everything_ behind the wrapper barrier. For example, when the structured - * clone algorithm runs up against a cross-compartment wrapper, it needs to - * know whether it can enter the compartment and keep cloning, or whether it - * should throw. This is the role of PUNCTURE. - * - * PUNCTURE allows the policy to specify whether the wrapper barrier may - * be lifted - that is to say, whether the caller is allowed to access - * anything that the wrapped object could access. This is a very powerful - * permission, and thus should generally be denied for security wrappers - * except under very special circumstances. When |act| is PUNCTURE, |id| - * should be JSID_VOID. + * The |act| parameter to enter() specifies the action being performed. */ virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, Action act, bool *bp); -}; -/* - * IndirectWrapper forwards its traps by forwarding them to - * IndirectProxyHandler. In effect, IndirectWrapper behaves the same as - * IndirectProxyHandler, except that it adds policy enforcement checks to each - * fundamental trap. - */ -class JS_FRIEND_API(IndirectWrapper) : public Wrapper, - public IndirectProxyHandler -{ - public: - explicit IndirectWrapper(unsigned flags); + explicit Wrapper(unsigned flags, bool hasPrototype = false); - virtual BaseProxyHandler* toBaseProxyHandler() { - return this; - } - - virtual Wrapper *toWrapper() { - return this; - } - - /* ES5 Harmony fundamental wrapper traps. */ - virtual bool getPropertyDescriptor(JSContext *cx, JSObject *wrapper, - jsid id, bool set, - PropertyDescriptor *desc) MOZ_OVERRIDE; - virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, - jsid id, bool set, - PropertyDescriptor *desc) MOZ_OVERRIDE; - virtual bool defineProperty(JSContext *cx, JSObject *wrapper, jsid id, - PropertyDescriptor *desc) MOZ_OVERRIDE; - virtual bool getOwnPropertyNames(JSContext *cx, JSObject *wrapper, - AutoIdVector &props) MOZ_OVERRIDE; - virtual bool delete_(JSContext *cx, JSObject *wrapper, jsid id, - bool *bp) MOZ_OVERRIDE; - virtual bool enumerate(JSContext *cx, JSObject *wrapper, - AutoIdVector &props) MOZ_OVERRIDE; - - /* Spidermonkey extensions. */ - virtual bool defaultValue(JSContext *cx, JSObject *wrapper_, JSType hint, - Value *vp) MOZ_OVERRIDE; -}; - -/* - * DirectWrapper forwards its traps by forwarding them to DirectProxyHandler. - * In effect, DirectWrapper behaves the same as DirectProxyHandler, except that - * it adds policy enforcement checks to each trap. - */ -class JS_FRIEND_API(DirectWrapper) : public Wrapper, public DirectProxyHandler -{ - public: - explicit DirectWrapper(unsigned flags, bool hasPrototype = false); - - virtual ~DirectWrapper(); - - virtual BaseProxyHandler* toBaseProxyHandler() { - return this; - } - - virtual Wrapper *toWrapper() { - return this; - } + virtual ~Wrapper(); /* ES5 Harmony fundamental wrapper traps. */ virtual bool getPropertyDescriptor(JSContext *cx, JSObject *wrapper, @@ -214,14 +116,14 @@ class JS_FRIEND_API(DirectWrapper) : public Wrapper, public DirectProxyHandler virtual bool defaultValue(JSContext *cx, JSObject *wrapper_, JSType hint, Value *vp) MOZ_OVERRIDE; - static DirectWrapper singleton; - static DirectWrapper singletonWithPrototype; + static Wrapper singleton; + static Wrapper singletonWithPrototype; static void *getWrapperFamily(); }; /* Base class for all cross compartment wrapper handlers. */ -class JS_FRIEND_API(CrossCompartmentWrapper) : public DirectWrapper +class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper { public: CrossCompartmentWrapper(unsigned flags, bool hasPrototype = false); @@ -280,13 +182,22 @@ class JS_FRIEND_API(SecurityWrapper) : public Base public: SecurityWrapper(unsigned flags); + virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, Wrapper::Action act, + bool *bp) MOZ_OVERRIDE; virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args) MOZ_OVERRIDE; virtual bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx) MOZ_OVERRIDE; virtual bool regexp_toShared(JSContext *cx, JSObject *proxy, RegExpGuard *g) MOZ_OVERRIDE; + + /* + * Allow our subclasses to select the superclass behavior they want without + * needing to specify an exact superclass. + */ + typedef Base Permissive; + typedef SecurityWrapper Restrictive; }; -typedef SecurityWrapper SameCompartmentSecurityWrapper; +typedef SecurityWrapper SameCompartmentSecurityWrapper; typedef SecurityWrapper CrossCompartmentSecurityWrapper; class JS_FRIEND_API(DeadObjectProxy) : public BaseProxyHandler @@ -327,7 +238,8 @@ class JS_FRIEND_API(DeadObjectProxy) : public BaseProxyHandler }; extern JSObject * -TransparentObjectWrapper(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSObject *parent, +TransparentObjectWrapper(JSContext *cx, JSObject *existing, JSObject *obj, + JSObject *wrappedProto, JSObject *parent, unsigned flags); // Proxy family for wrappers. Public so that IsWrapper() can be fully inlined by @@ -352,16 +264,19 @@ UnwrapObject(JSObject *obj, bool stopAtOuter = true, unsigned *flagsp = NULL); // code should never be unwrapping outer window wrappers, we always stop at // outer windows. JS_FRIEND_API(JSObject *) -UnwrapObjectChecked(JSContext *cx, RawObject obj); +UnwrapObjectChecked(RawObject obj); // Unwrap only the outermost security wrapper, with the same semantics as // above. This is the checked version of Wrapper::wrappedObject. JS_FRIEND_API(JSObject *) -UnwrapOneChecked(JSContext *cx, HandleObject obj); +UnwrapOneChecked(RawObject obj); JS_FRIEND_API(bool) IsCrossCompartmentWrapper(RawObject obj); +bool +IsDeadProxyObject(RawObject obj); + JSObject * NewDeadProxyObject(JSContext *cx, JSObject *parent); @@ -381,6 +296,34 @@ JS_FRIEND_API(bool) RecomputeWrappers(JSContext *cx, const CompartmentFilter &sourceFilter, const CompartmentFilter &targetFilter); +/* + * This auto class should be used around any code, such as brain transplants, + * that may touch dead compartments. Brain transplants can cause problems + * because they operate on all compartments, whether live or dead. A brain + * transplant can cause a formerly dead object to be "reanimated" by causing a + * read or write barrier to be invoked on it during the transplant. In this way, + * a compartment becomes a zombie, kept alive by repeatedly consuming + * (transplanted) brains. + * + * To work around this issue, we observe when mark bits are set on objects in + * dead compartments. If this happens during a brain transplant, we do a full, + * non-incremental GC at the end of the brain transplant. This will clean up any + * objects that were improperly marked. + */ +struct JS_FRIEND_API(AutoMaybeTouchDeadCompartments) +{ + // The version that takes an object just uses it for its runtime. + AutoMaybeTouchDeadCompartments(JSContext *cx); + AutoMaybeTouchDeadCompartments(JSObject *obj); + ~AutoMaybeTouchDeadCompartments(); + + private: + JSRuntime *runtime; + unsigned markCount; + bool inIncremental; + bool manipulatingDeadCompartments; +}; + } /* namespace js */ #endif diff --git a/scripting/javascript/spidermonkey-ios/include/mozilla/Attributes.h b/scripting/javascript/spidermonkey-ios/include/mozilla/Attributes.h index 6b4e81612c..ca990be15a 100644 --- a/scripting/javascript/spidermonkey-ios/include/mozilla/Attributes.h +++ b/scripting/javascript/spidermonkey-ios/include/mozilla/Attributes.h @@ -93,6 +93,8 @@ # endif # if __GNUC_MINOR__ >= 4 # define MOZ_HAVE_CXX11_DELETE +# endif +# if __GNUC_MINOR__ >= 5 # define MOZ_HAVE_CXX11_ENUM_TYPE # define MOZ_HAVE_CXX11_STRONG_ENUMS # endif @@ -356,6 +358,9 @@ * supported, as with MOZ_ENUM_TYPE(). For simplicity, it is currently * mandatory. As with MOZ_ENUM_TYPE(), it will do nothing on compilers that do * not support it. + * + * Note that the workaround implemented here is not compatible with enums + * nested inside a class. */ #if defined(MOZ_HAVE_CXX11_STRONG_ENUMS) /* All compilers that support strong enums also support an explicit diff --git a/scripting/javascript/spidermonkey-ios/include/mozilla/BloomFilter.h b/scripting/javascript/spidermonkey-ios/include/mozilla/BloomFilter.h index 9effa1756c..8680ef2907 100644 --- a/scripting/javascript/spidermonkey-ios/include/mozilla/BloomFilter.h +++ b/scripting/javascript/spidermonkey-ios/include/mozilla/BloomFilter.h @@ -13,6 +13,7 @@ #ifndef mozilla_BloomFilter_h_ #define mozilla_BloomFilter_h_ +#include "mozilla/Assertions.h" #include "mozilla/Likely.h" #include "mozilla/StandardInteger.h" #include "mozilla/Util.h" diff --git a/scripting/javascript/spidermonkey-ios/include/mozilla/CheckedInt.h b/scripting/javascript/spidermonkey-ios/include/mozilla/CheckedInt.h index 790fc6eabe..b56ac42b19 100644 --- a/scripting/javascript/spidermonkey-ios/include/mozilla/CheckedInt.h +++ b/scripting/javascript/spidermonkey-ios/include/mozilla/CheckedInt.h @@ -385,13 +385,13 @@ IsSubValid(T x, T y) } template::value, + bool IsTSigned = IsSigned::value, bool TwiceBiggerTypeIsSupported = IsSupported::Type>::value> struct IsMulValidImpl {}; -template -struct IsMulValidImpl +template +struct IsMulValidImpl { static bool run(T x, T y) { @@ -451,7 +451,7 @@ IsDivValid(T x, T y) } // This is just to shut up msvc warnings about negating unsigned ints. -template::value> +template::value> struct OppositeIfSignedImpl { static T run(T x) { return -x; } diff --git a/scripting/javascript/spidermonkey-ios/include/mozilla/EnumSet.h b/scripting/javascript/spidermonkey-ios/include/mozilla/EnumSet.h new file mode 100644 index 0000000000..b18b005669 --- /dev/null +++ b/scripting/javascript/spidermonkey-ios/include/mozilla/EnumSet.h @@ -0,0 +1,175 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* A set abstraction for enumeration values. */ + +#ifndef mozilla_EnumSet_h +#define mozilla_EnumSet_h + +#include "mozilla/Assertions.h" +#include "mozilla/StandardInteger.h" + +namespace mozilla { + +/** + * EnumSet is a set of values defined by an enumeration. It is implemented + * using a 32 bit mask for each value so it will only work for enums with an int + * representation less than 32. It works both for enum and enum class types. + */ +template +class EnumSet +{ + public: + EnumSet() + : mBitField(0) + { } + + EnumSet(T aEnum) + : mBitField(aEnum) + { } + + EnumSet(T aEnum1, T aEnum2) + : mBitField(bitFor(aEnum1) | + bitFor(aEnum2)) + { } + + EnumSet(T aEnum1, T aEnum2, T aEnum3) + : mBitField(bitFor(aEnum1) | + bitFor(aEnum2) | + bitFor(aEnum3)) + { } + + EnumSet(T aEnum1, T aEnum2, T aEnum3, T aEnum4) + : mBitField(bitFor(aEnum1) | + bitFor(aEnum2) | + bitFor(aEnum3) | + bitFor(aEnum4)) + { } + + EnumSet(const EnumSet& aEnumSet) + : mBitField(aEnumSet.mBitField) + { } + + /** + * Add an element + */ + void operator+=(T aEnum) { + mBitField |= bitFor(aEnum); + } + + /** + * Add an element + */ + EnumSet operator+(T aEnum) const { + EnumSet result(*this); + result += aEnum; + return result; + } + + /** + * Union + */ + void operator+=(const EnumSet aEnumSet) { + mBitField |= aEnumSet.mBitField; + } + + /** + * Union + */ + EnumSet operator+(const EnumSet aEnumSet) const { + EnumSet result(*this); + result += aEnumSet; + return result; + } + + /** + * Remove an element + */ + void operator-=(T aEnum) { + mBitField &= ~(bitFor(aEnum)); + } + + /** + * Remove an element + */ + EnumSet operator-(T aEnum) const { + EnumSet result(*this); + result -= aEnum; + return result; + } + + /** + * Remove a set of elements + */ + void operator-=(const EnumSet aEnumSet) { + mBitField &= ~(aEnumSet.mBitField); + } + + /** + * Remove a set of elements + */ + EnumSet operator-(const EnumSet aEnumSet) const { + EnumSet result(*this); + result -= aEnumSet; + return result; + } + + /** + * Intersection + */ + void operator&=(const EnumSet aEnumSet) { + mBitField &= aEnumSet.mBitField; + } + + /** + * Intersection + */ + EnumSet operator&(const EnumSet aEnumSet) const { + EnumSet result(*this); + result &= aEnumSet; + return result; + } + + /** + * Equality + */ + + bool operator==(const EnumSet aEnumSet) const { + return mBitField == aEnumSet.mBitField; + } + + /** + * Test is an element is contained in the set + */ + bool contains(T aEnum) const { + return mBitField & bitFor(aEnum); + } + + /** + * Return the number of elements in the set + */ + + uint8_t size() { + uint8_t count = 0; + for (uint32_t bitField = mBitField; bitField; bitField >>= 1) { + if (bitField & 1) + count++; + } + return count; + } + + private: + static uint32_t bitFor(T aEnum) { + uint32_t bitNumber(aEnum); + MOZ_ASSERT(bitNumber < 32); + return 1U << bitNumber; + } + + uint32_t mBitField; +}; + +} // namespace mozilla + +#endif // mozilla_EnumSet_h_ diff --git a/scripting/javascript/spidermonkey-ios/include/mozilla/GuardObjects.h b/scripting/javascript/spidermonkey-ios/include/mozilla/GuardObjects.h index 95aa37a19d..e3297c8fcf 100644 --- a/scripting/javascript/spidermonkey-ios/include/mozilla/GuardObjects.h +++ b/scripting/javascript/spidermonkey-ios/include/mozilla/GuardObjects.h @@ -66,7 +66,7 @@ namespace detail { * For more details, and examples of using these macros, see * https://developer.mozilla.org/en/Using_RAII_classes_in_Mozilla */ -class MOZ_EXPORT_API(GuardObjectNotifier) +class MOZ_EXPORT GuardObjectNotifier { private: bool* statementDone; @@ -83,7 +83,7 @@ class MOZ_EXPORT_API(GuardObjectNotifier) } }; -class MOZ_EXPORT_API(GuardObjectNotificationReceiver) +class MOZ_EXPORT GuardObjectNotificationReceiver { private: bool statementDone; diff --git a/scripting/javascript/spidermonkey-ios/include/mozilla/HashFunctions.h b/scripting/javascript/spidermonkey-ios/include/mozilla/HashFunctions.h index badfc3c808..96242b629a 100644 --- a/scripting/javascript/spidermonkey-ios/include/mozilla/HashFunctions.h +++ b/scripting/javascript/spidermonkey-ios/include/mozilla/HashFunctions.h @@ -351,7 +351,7 @@ HashString(const wchar_t* str, size_t length) * same result out of HashBytes as you would out of HashString. */ MOZ_WARN_UNUSED_RESULT -extern MFBT_API(uint32_t) +extern MFBT_API uint32_t HashBytes(const void* bytes, size_t length); } /* namespace mozilla */ diff --git a/scripting/javascript/spidermonkey-ios/include/mozilla/LinkedList.h b/scripting/javascript/spidermonkey-ios/include/mozilla/LinkedList.h index d7d3b23607..5cfd60e4ac 100644 --- a/scripting/javascript/spidermonkey-ios/include/mozilla/LinkedList.h +++ b/scripting/javascript/spidermonkey-ios/include/mozilla/LinkedList.h @@ -13,6 +13,10 @@ * LinkedListElement. A given object may be in only one linked list at a * time. * + * A LinkedListElement automatically removes itself from the list upon + * destruction, and a LinkedList will fatally assert in debug builds if it's + * non-empty when it's destructed. + * * For example, you might use LinkedList in a simple observer list class as * follows. * @@ -36,6 +40,8 @@ * void removeObserver(Observer* observer) { * // Will assert if |observer| is not part of some list. * observer.remove(); + * // Or, will assert if |observer| is not part of |list| specifically. + * // observer.removeFrom(list); * } * * void notifyObservers(char* topic) { @@ -101,8 +107,19 @@ class LinkedListElement LinkedListElement* prev; const bool isSentinel; + LinkedListElement* thisDuringConstruction() { return this; } + public: - LinkedListElement() : next(this), prev(this), isSentinel(false) { } + LinkedListElement() + : next(thisDuringConstruction()), + prev(thisDuringConstruction()), + isSentinel(false) + { } + + ~LinkedListElement() { + if (!isSentinel && isInList()) + remove(); + } /* * Get the next element in the list, or NULL if this is the last element in @@ -158,6 +175,15 @@ class LinkedListElement prev = this; } + /* + * Identical to remove(), but also asserts in debug builds that this element + * is in list. + */ + void removeFrom(const LinkedList& list) { + list.assertContains(asT()); + remove(); + } + /* * Return true if |this| part is of a linked list, and false otherwise. */ @@ -175,11 +201,10 @@ class LinkedListElement }; LinkedListElement(NodeKind nodeKind) - : next(this), - prev(this), + : next(thisDuringConstruction()), + prev(thisDuringConstruction()), isSentinel(nodeKind == NODE_KIND_SENTINEL) - { - } + { } /* * Return |this| cast to T* if we're a normal node, or return NULL if we're @@ -240,6 +265,10 @@ class LinkedList public: LinkedList() : sentinel(LinkedListElement::NODE_KIND_SENTINEL) { } + ~LinkedList() { + MOZ_ASSERT(isEmpty()); + } + /* * Add elem to the front of the list. */ @@ -375,6 +404,21 @@ class LinkedList } private: + friend class LinkedListElement; + + void assertContains(const T* t) const { +#ifdef DEBUG + for (const T* elem = getFirst(); + elem; + elem = elem->getNext()) + { + if (elem == t) + return; + } + MOZ_NOT_REACHED("element wasn't found in this list!"); +#endif + } + LinkedList& operator=(const LinkedList& other) MOZ_DELETE; LinkedList(const LinkedList& other) MOZ_DELETE; }; diff --git a/scripting/javascript/spidermonkey-ios/include/mozilla/NullPtr.h b/scripting/javascript/spidermonkey-ios/include/mozilla/NullPtr.h index e6fc892759..c2ea7dd91f 100644 --- a/scripting/javascript/spidermonkey-ios/include/mozilla/NullPtr.h +++ b/scripting/javascript/spidermonkey-ios/include/mozilla/NullPtr.h @@ -20,7 +20,7 @@ # endif #elif defined(__GNUC__) # if defined(_GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L -# if (__GNUC__ * 1000 + __GNU_MINOR__) >= 4006 +# if (__GNUC__ * 1000 + __GNUC_MINOR__) >= 4006 # define MOZ_HAVE_CXX11_NULLPTR # endif # endif diff --git a/scripting/javascript/spidermonkey-ios/include/mozilla/RangedPtr.h b/scripting/javascript/spidermonkey-ios/include/mozilla/RangedPtr.h index 7c8d58147d..adecf7c1a0 100644 --- a/scripting/javascript/spidermonkey-ios/include/mozilla/RangedPtr.h +++ b/scripting/javascript/spidermonkey-ios/include/mozilla/RangedPtr.h @@ -128,13 +128,13 @@ class RangedPtr RangedPtr operator+(size_t inc) { MOZ_ASSERT(inc <= size_t(-1) / sizeof(T)); - MOZ_ASSERT(ptr + inc > ptr); + MOZ_ASSERT(ptr + inc >= ptr); return create(ptr + inc); } RangedPtr operator-(size_t dec) { MOZ_ASSERT(dec <= size_t(-1) / sizeof(T)); - MOZ_ASSERT(ptr - dec < ptr); + MOZ_ASSERT(ptr - dec <= ptr); return create(ptr - dec); } diff --git a/scripting/javascript/spidermonkey-ios/include/mozilla/SHA1.h b/scripting/javascript/spidermonkey-ios/include/mozilla/SHA1.h index 510ef75f0f..a6604e699f 100644 --- a/scripting/javascript/spidermonkey-ios/include/mozilla/SHA1.h +++ b/scripting/javascript/spidermonkey-ios/include/mozilla/SHA1.h @@ -5,44 +5,57 @@ /* Simple class for computing SHA1. */ -/* - * To compute the SHA1 of a buffer using this class you should write something - * like: - * void SHA1(const uint8_t* buf, unsigned size, uint8_t hash[20]) - * { - * SHA1Sum S; - * S.update(buf, size); - * S.finish(hash); - * } - * If there are multiple buffers or chunks, the update method can be called - * multiple times and the SHA1 is computed on the concatenation of all the - * buffers passed to it. - * The finish method may only be called once and cannot be followed by calls - * to update. - */ - #ifndef mozilla_SHA1_h_ #define mozilla_SHA1_h_ #include "mozilla/StandardInteger.h" #include "mozilla/Types.h" -namespace mozilla { -class SHA1Sum { - union { - uint32_t w[16]; /* input buffer */ - uint8_t b[64]; - } u; - uint64_t size; /* count of hashed bytes. */ - unsigned H[22]; /* 5 state variables, 16 tmp values, 1 extra */ - bool mDone; +#include -public: - static const unsigned int HashSize = 20; - MFBT_API() SHA1Sum(); - MFBT_API(void) update(const void* dataIn, uint32_t len); - MFBT_API(void) finish(uint8_t hashout[20]); +namespace mozilla { + +/** + * This class computes the SHA1 hash of a byte sequence, or of the concatenation + * of multiple sequences. For example, computing the SHA1 of two sequences of + * bytes could be done as follows: + * + * void SHA1(const uint8_t* buf1, uint32_t size1, + * const uint8_t* buf2, uint32_t size2, + * SHA1Sum::Hash& hash) + * { + * SHA1Sum s; + * s.update(buf1, size1); + * s.update(buf2, size2); + * s.finish(hash); + * } + * + * The finish method may only be called once and cannot be followed by calls + * to update. + */ +class SHA1Sum +{ + union { + uint32_t w[16]; /* input buffer */ + uint8_t b[64]; + } u; + uint64_t size; /* count of hashed bytes. */ + unsigned H[22]; /* 5 state variables, 16 tmp values, 1 extra */ + bool mDone; + + public: + MFBT_API SHA1Sum(); + + static const size_t HashSize = 20; + typedef uint8_t Hash[HashSize]; + + /* Add len bytes of dataIn to the data sequence being hashed. */ + MFBT_API void update(const void* dataIn, uint32_t len); + + /* Compute the final hash of all data into hashOut. */ + MFBT_API void finish(SHA1Sum::Hash& hashOut); }; -} + +} /* namespace mozilla */ #endif /* mozilla_SHA1_h_ */ diff --git a/scripting/javascript/spidermonkey-ios/include/mozilla/TypeTraits.h b/scripting/javascript/spidermonkey-ios/include/mozilla/TypeTraits.h index 8f04e7a4ac..fda156496e 100644 --- a/scripting/javascript/spidermonkey-ios/include/mozilla/TypeTraits.h +++ b/scripting/javascript/spidermonkey-ios/include/mozilla/TypeTraits.h @@ -9,6 +9,25 @@ namespace mozilla { +namespace detail { + +/** + * The trickery used to implement IsBaseOf here makes it possible to use it for + * the cases of private and multiple inheritance. This code was inspired by the + * sample code here: + * + * http://stackoverflow.com/questions/2910979/how-is-base-of-works + */ +template +class IsBaseOfHelper +{ + public: + operator Base*() const; + operator Derived*(); +}; + +} /* namespace detail */ + /* * IsBaseOf allows to know whether a given class is derived from another. * @@ -25,12 +44,47 @@ template class IsBaseOf { private: - static char test(Base* b); - static int test(...); + template + static char test(Derived*, T); + static int test(Base*, int); public: static const bool value = - sizeof(test(static_cast(0))) == sizeof(char); + sizeof(test(detail::IsBaseOfHelper(), int())) == sizeof(char); +}; + +template +class IsBaseOf +{ + private: + template + static char test(Derived*, T); + static int test(Base*, int); + + public: + static const bool value = + sizeof(test(detail::IsBaseOfHelper(), int())) == sizeof(char); +}; + +template +class IsBaseOf +{ + public: + static const bool value = false; +}; + +template +class IsBaseOf +{ + public: + static const bool value = true; +}; + +template +class IsBaseOf +{ + public: + static const bool value = true; }; /* diff --git a/scripting/javascript/spidermonkey-ios/include/mozilla/Types.h b/scripting/javascript/spidermonkey-ios/include/mozilla/Types.h index f803586ec1..56e5cb82fb 100644 --- a/scripting/javascript/spidermonkey-ios/include/mozilla/Types.h +++ b/scripting/javascript/spidermonkey-ios/include/mozilla/Types.h @@ -27,63 +27,60 @@ /* Implement compiler and linker macros needed for APIs. */ /* - * MOZ_EXPORT_API is used to declare and define a method which is externally + * MOZ_EXPORT is used to declare and define a symbol or type which is externally * visible to users of the current library. It encapsulates various decorations - * needed to properly export the method's symbol. MOZ_EXPORT_DATA serves the - * same purpose for data. + * needed to properly export the method's symbol. * * api.h: - * extern MOZ_EXPORT_API(int) MeaningOfLife(void); - * extern MOZ_EXPORT_DATA(int) LuggageCombination; + * extern MOZ_EXPORT int MeaningOfLife(void); + * extern MOZ_EXPORT int LuggageCombination; * * api.c: - * MOZ_EXPORT_API(int) MeaningOfLife(void) { return 42; } - * MOZ_EXPORT_DATA(int) LuggageCombination = 12345; + * int MeaningOfLife(void) { return 42; } + * int LuggageCombination = 12345; * * If you are merely sharing a method across files, just use plain |extern|. * These macros are designed for use by library interfaces -- not for normal * methods or data used cross-file. */ #if defined(WIN32) || defined(XP_OS2) -# define MOZ_EXPORT_API(type) __declspec(dllexport) type -# define MOZ_EXPORT_DATA(type) __declspec(dllexport) type +# define MOZ_EXPORT __declspec(dllexport) #else /* Unix */ # ifdef HAVE_VISIBILITY_ATTRIBUTE -# define MOZ_EXTERNAL_VIS __attribute__((visibility("default"))) +# define MOZ_EXPORT __attribute__((visibility("default"))) # elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) -# define MOZ_EXTERNAL_VIS __global +# define MOZ_EXPORT __global # else -# define MOZ_EXTERNAL_VIS +# define MOZ_EXPORT /* nothing */ # endif -# define MOZ_EXPORT_API(type) MOZ_EXTERNAL_VIS type -# define MOZ_EXPORT_DATA(type) MOZ_EXTERNAL_VIS type #endif + /* - * Whereas implementers use MOZ_EXPORT_API and MOZ_EXPORT_DATA to declare and - * define library symbols, users use MOZ_IMPORT_API and MOZ_IMPORT_DATA to - * access them. Most often the implementer of the library will expose an API - * macro which expands to either the export or import version of the macro, - * depending upon the compilation mode. + * Whereas implementers use MOZ_EXPORT to declare and define library symbols, + * users use MOZ_IMPORT_API and MOZ_IMPORT_DATA to access them. Most often the + * implementer of the library will expose an API macro which expands to either + * the export or import version of the macro, depending upon the compilation + * mode. */ #ifdef _WIN32 # if defined(__MWERKS__) -# define MOZ_IMPORT_API(x) x +# define MOZ_IMPORT_API /* nothing */ # else -# define MOZ_IMPORT_API(x) __declspec(dllimport) x +# define MOZ_IMPORT_API __declspec(dllimport) # endif #elif defined(XP_OS2) -# define MOZ_IMPORT_API(x) __declspec(dllimport) x +# define MOZ_IMPORT_API __declspec(dllimport) #else -# define MOZ_IMPORT_API(x) MOZ_EXPORT_API(x) +# define MOZ_IMPORT_API MOZ_EXPORT #endif #if defined(_WIN32) && !defined(__MWERKS__) -# define MOZ_IMPORT_DATA(x) __declspec(dllimport) x +# define MOZ_IMPORT_DATA __declspec(dllimport) #elif defined(XP_OS2) -# define MOZ_IMPORT_DATA(x) __declspec(dllimport) x +# define MOZ_IMPORT_DATA __declspec(dllimport) #else -# define MOZ_IMPORT_DATA(x) MOZ_EXPORT_DATA(x) +# define MOZ_IMPORT_DATA MOZ_EXPORT #endif /* @@ -92,19 +89,22 @@ * declarations when using mfbt. */ #if defined(IMPL_MFBT) -# define MFBT_API(type) MOZ_EXPORT_API(type) -# define MFBT_DATA(type) MOZ_EXPORT_DATA(type) +# define MFBT_API MOZ_EXPORT +# define MFBT_DATA MOZ_EXPORT #else /* - * When mozglue is linked in the program, we need the MFBT API symbols - * to be weak. + * On linux mozglue is linked in the program and we link libxul.so with + * -z,defs. Normally that causes the linker to reject undefined references in + * libxul.so, but as a loophole it allows undefined references to weak + * symbols. We add the weak attribute to the import version of the MFBT API + * macros to exploit this. */ # if defined(MOZ_GLUE_IN_PROGRAM) -# define MFBT_API(type) __attribute__((weak)) MOZ_IMPORT_API(type) -# define MFBT_DATA(type) __attribute__((weak)) MOZ_IMPORT_DATA(type) +# define MFBT_API __attribute__((weak)) MOZ_IMPORT_API +# define MFBT_DATA __attribute__((weak)) MOZ_IMPORT_DATA # else -# define MFBT_API(type) MOZ_IMPORT_API(type) -# define MFBT_DATA(type) MOZ_IMPORT_DATA(type) +# define MFBT_API MOZ_IMPORT_API +# define MFBT_DATA MOZ_IMPORT_DATA # endif #endif @@ -117,7 +117,7 @@ * * MOZ_BEGIN_EXTERN_C * - * extern MOZ_EXPORT_API(int) MostRandomNumber(void); + * extern MOZ_EXPORT int MostRandomNumber(void); * ...other declarations... * * MOZ_END_EXTERN_C diff --git a/scripting/javascript/spidermonkey-ios/include/mozilla/WeakPtr.h b/scripting/javascript/spidermonkey-ios/include/mozilla/WeakPtr.h index e20767141e..721c28e48d 100644 --- a/scripting/javascript/spidermonkey-ios/include/mozilla/WeakPtr.h +++ b/scripting/javascript/spidermonkey-ios/include/mozilla/WeakPtr.h @@ -126,6 +126,10 @@ class WeakPtr return ref->get(); } + T* get() const { + return ref->get(); + } + private: friend class SupportsWeakPtr; diff --git a/scripting/javascript/spidermonkey-ios/lib/libjs_static.a.REMOVED.git-id b/scripting/javascript/spidermonkey-ios/lib/libjs_static.a.REMOVED.git-id index 7264bd34f1..8fddb6c5bf 100644 --- a/scripting/javascript/spidermonkey-ios/lib/libjs_static.a.REMOVED.git-id +++ b/scripting/javascript/spidermonkey-ios/lib/libjs_static.a.REMOVED.git-id @@ -1 +1 @@ -5c425151bd8cd8a816c40f6d414564d333843e2c \ No newline at end of file +f94b80f69c9d07189db11fb1b0e0265862cfadda \ No newline at end of file diff --git a/scripting/javascript/spidermonkey-win32/include/gc/Barrier.h b/scripting/javascript/spidermonkey-win32/include/gc/Barrier.h deleted file mode 100644 index 443cddb7a3..0000000000 --- a/scripting/javascript/spidermonkey-win32/include/gc/Barrier.h +++ /dev/null @@ -1,659 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef jsgc_barrier_h___ -#define jsgc_barrier_h___ - -#include "jsapi.h" - -#include "gc/Heap.h" -#include "gc/Root.h" -#include "js/HashTable.h" - -/* - * A write barrier is a mechanism used by incremental or generation GCs to - * ensure that every value that needs to be marked is marked. In general, the - * write barrier should be invoked whenever a write can cause the set of things - * traced through by the GC to change. This includes: - * - writes to object properties - * - writes to array slots - * - writes to fields like JSObject::shape_ that we trace through - * - writes to fields in private data, like JSGenerator::obj - * - writes to non-markable fields like JSObject::private that point to - * markable data - * The last category is the trickiest. Even though the private pointers does not - * point to a GC thing, changing the private pointer may change the set of - * objects that are traced by the GC. Therefore it needs a write barrier. - * - * Every barriered write should have the following form: - * - * obj->field = value; // do the actual write - * - * The pre-barrier is used for incremental GC and the post-barrier is for - * generational GC. - * - * PRE-BARRIER - * - * To understand the pre-barrier, let's consider how incremental GC works. The - * GC itself is divided into "slices". Between each slice, JS code is allowed to - * run. Each slice should be short so that the user doesn't notice the - * interruptions. In our GC, the structure of the slices is as follows: - * - * 1. ... JS work, which leads to a request to do GC ... - * 2. [first GC slice, which performs all root marking and possibly more marking] - * 3. ... more JS work is allowed to run ... - * 4. [GC mark slice, which runs entirely in drainMarkStack] - * 5. ... more JS work ... - * 6. [GC mark slice, which runs entirely in drainMarkStack] - * 7. ... more JS work ... - * 8. [GC marking finishes; sweeping done non-incrementally; GC is done] - * 9. ... JS continues uninterrupted now that GC is finishes ... - * - * Of course, there may be a different number of slices depending on how much - * marking is to be done. - * - * The danger inherent in this scheme is that the JS code in steps 3, 5, and 7 - * might change the heap in a way that causes the GC to collect an object that - * is actually reachable. The write barrier prevents this from happening. We use - * a variant of incremental GC called "snapshot at the beginning." This approach - * guarantees the invariant that if an object is reachable in step 2, then we - * will mark it eventually. The name comes from the idea that we take a - * theoretical "snapshot" of all reachable objects in step 2; all objects in - * that snapshot should eventually be marked. (Note that the write barrier - * verifier code takes an actual snapshot.) - * - * The basic correctness invariant of a snapshot-at-the-beginning collector is - * that any object reachable at the end of the GC (step 9) must either: - * (1) have been reachable at the beginning (step 2) and thus in the snapshot - * (2) or must have been newly allocated, in steps 3, 5, or 7. - * To deal with case (2), any objects allocated during an incremental GC are - * automatically marked black. - * - * This strategy is actually somewhat conservative: if an object becomes - * unreachable between steps 2 and 8, it would be safe to collect it. We won't, - * mainly for simplicity. (Also, note that the snapshot is entirely - * theoretical. We don't actually do anything special in step 2 that we wouldn't - * do in a non-incremental GC. - * - * It's the pre-barrier's job to maintain the snapshot invariant. Consider the - * write "obj->field = value". Let the prior value of obj->field be - * value0. Since it's possible that value0 may have been what obj->field - * contained in step 2, when the snapshot was taken, the barrier marks - * value0. Note that it only does this if we're in the middle of an incremental - * GC. Since this is rare, the cost of the write barrier is usually just an - * extra branch. - * - * In practice, we implement the pre-barrier differently based on the type of - * value0. E.g., see JSObject::writeBarrierPre, which is used if obj->field is - * a JSObject*. It takes value0 as a parameter. - * - * POST-BARRIER - * - * These are not yet implemented. Once we get generational GC, they will allow - * us to keep track of pointers from non-nursery space into the nursery. - * - * IMPLEMENTATION DETAILS - * - * Since it would be awkward to change every write to memory into a function - * call, this file contains a bunch of C++ classes and templates that use - * operator overloading to take care of barriers automatically. In many cases, - * all that's necessary to make some field be barriered is to replace - * Type *field; - * with - * HeapPtr field; - * There are also special classes HeapValue and HeapId, which barrier js::Value - * and jsid, respectively. - * - * One additional note: not all object writes need to be barriered. Writes to - * newly allocated objects do not need a pre-barrier. In these cases, we use - * the "obj->field.init(value)" method instead of "obj->field = value". We use - * the init naming idiom in many places to signify that a field is being - * assigned for the first time. - */ - -struct JSXML; - -namespace js { - -template -class EncapsulatedPtr -{ - protected: - union { - T *value; - Unioned other; - }; - - public: - EncapsulatedPtr() : value(NULL) {} - EncapsulatedPtr(T *v) : value(v) {} - explicit EncapsulatedPtr(const EncapsulatedPtr &v) : value(v.value) {} - - ~EncapsulatedPtr() { pre(); } - - /* Use to set the pointer to NULL. */ - void clear() { - pre(); - value = NULL; - } - - EncapsulatedPtr &operator=(T *v) { - pre(); - JS_ASSERT(!IsPoisonedPtr(v)); - value = v; - return *this; - } - - EncapsulatedPtr &operator=(const EncapsulatedPtr &v) { - pre(); - JS_ASSERT(!IsPoisonedPtr(v.value)); - value = v.value; - return *this; - } - - /* Use this if the automatic coercion to T* isn't working. */ - T *get() const { return value; } - - /* - * Use these if you want to change the value without invoking the barrier. - * Obviously this is dangerous unless you know the barrier is not needed. - */ - T **unsafeGet() { return &value; } - void unsafeSet(T *v) { value = v; } - - Unioned *unsafeGetUnioned() { return &other; } - - T &operator*() const { return *value; } - T *operator->() const { return value; } - - operator T*() const { return value; } - - protected: - void pre(); -}; - -template -class HeapPtr : public EncapsulatedPtr -{ - public: - HeapPtr() : EncapsulatedPtr(NULL) {} - explicit HeapPtr(T *v) : EncapsulatedPtr(v) { post(); } - explicit HeapPtr(const HeapPtr &v) - : EncapsulatedPtr(v) { post(); } - - void init(T *v) { - JS_ASSERT(!IsPoisonedPtr(v)); - this->value = v; - post(); - } - - HeapPtr &operator=(T *v) { - this->pre(); - JS_ASSERT(!IsPoisonedPtr(v)); - this->value = v; - post(); - return *this; - } - - HeapPtr &operator=(const HeapPtr &v) { - this->pre(); - JS_ASSERT(!IsPoisonedPtr(v.value)); - this->value = v.value; - post(); - return *this; - } - - protected: - void post() { T::writeBarrierPost(this->value, (void *)&this->value); } - - /* Make this friend so it can access pre() and post(). */ - template - friend inline void - BarrieredSetPair(JSCompartment *comp, - HeapPtr &v1, T1 *val1, - HeapPtr &v2, T2 *val2); -}; - -/* - * FixedHeapPtr is designed for one very narrow case: replacing immutable raw - * pointers to GC-managed things, implicitly converting to a handle type for - * ease of use. Pointers encapsulated by this type must: - * - * be immutable (no incremental write barriers), - * never point into the nursery (no generational write barriers), and - * be traced via MarkRuntime (we use fromMarkedLocation). - * - * In short: you *really* need to know what you're doing before you use this - * class! - */ -template -class FixedHeapPtr -{ - T *value; - - public: - operator T*() const { return value; } - T * operator->() const { return value; } - - operator Handle() const { - return Handle::fromMarkedLocation(&value); - } - - void init(T *ptr) { - value = ptr; - } -}; - -template -class RelocatablePtr : public EncapsulatedPtr -{ - public: - RelocatablePtr() : EncapsulatedPtr(NULL) {} - explicit RelocatablePtr(T *v) : EncapsulatedPtr(v) { - if (v) - post(); - } - explicit RelocatablePtr(const RelocatablePtr &v) : EncapsulatedPtr(v) { - if (this->value) - post(); - } - - ~RelocatablePtr() { - if (this->value) - relocate(this->value->compartment()); - } - - RelocatablePtr &operator=(T *v) { - this->pre(); - JS_ASSERT(!IsPoisonedPtr(v)); - if (v) { - this->value = v; - post(); - } else if (this->value) { - JSCompartment *comp = this->value->compartment(); - this->value = v; - relocate(comp); - } - return *this; - } - - RelocatablePtr &operator=(const RelocatablePtr &v) { - this->pre(); - JS_ASSERT(!IsPoisonedPtr(v.value)); - if (v.value) { - this->value = v.value; - post(); - } else if (this->value) { - JSCompartment *comp = this->value->compartment(); - this->value = v; - relocate(comp); - } - return *this; - } - - protected: - inline void post(); - inline void relocate(JSCompartment *comp); -}; - -/* - * This is a hack for RegExpStatics::updateFromMatch. It allows us to do two - * barriers with only one branch to check if we're in an incremental GC. - */ -template -static inline void -BarrieredSetPair(JSCompartment *comp, - HeapPtr &v1, T1 *val1, - HeapPtr &v2, T2 *val2) -{ - if (T1::needWriteBarrierPre(comp)) { - v1.pre(); - v2.pre(); - } - v1.unsafeSet(val1); - v2.unsafeSet(val2); - v1.post(); - v2.post(); -} - -struct Shape; -class BaseShape; -namespace types { struct TypeObject; } - -typedef EncapsulatedPtr EncapsulatedPtrObject; -typedef EncapsulatedPtr EncapsulatedPtrScript; - -typedef RelocatablePtr RelocatablePtrObject; -typedef RelocatablePtr RelocatablePtrScript; - -typedef HeapPtr HeapPtrObject; -typedef HeapPtr HeapPtrFunction; -typedef HeapPtr HeapPtrString; -typedef HeapPtr HeapPtrScript; -typedef HeapPtr HeapPtrShape; -typedef HeapPtr HeapPtrBaseShape; -typedef HeapPtr HeapPtrTypeObject; -typedef HeapPtr HeapPtrXML; - -/* Useful for hashtables with a HeapPtr as key. */ -template -struct HeapPtrHasher -{ - typedef HeapPtr Key; - typedef T *Lookup; - - static HashNumber hash(Lookup obj) { return DefaultHasher::hash(obj); } - static bool match(const Key &k, Lookup l) { return k.get() == l; } -}; - -/* Specialized hashing policy for HeapPtrs. */ -template -struct DefaultHasher< HeapPtr > : HeapPtrHasher { }; - -template -struct EncapsulatedPtrHasher -{ - typedef EncapsulatedPtr Key; - typedef T *Lookup; - - static HashNumber hash(Lookup obj) { return DefaultHasher::hash(obj); } - static bool match(const Key &k, Lookup l) { return k.get() == l; } -}; - -template -struct DefaultHasher< EncapsulatedPtr > : EncapsulatedPtrHasher { }; - -class EncapsulatedValue : public ValueOperations -{ - protected: - Value value; - - /* - * Ensure that EncapsulatedValue is not constructable, except by our - * implementations. - */ - EncapsulatedValue() MOZ_DELETE; - EncapsulatedValue(const EncapsulatedValue &v) MOZ_DELETE; - EncapsulatedValue &operator=(const Value &v) MOZ_DELETE; - EncapsulatedValue &operator=(const EncapsulatedValue &v) MOZ_DELETE; - - EncapsulatedValue(const Value &v) : value(v) {} - ~EncapsulatedValue() {} - - public: - bool operator==(const EncapsulatedValue &v) const { return value == v.value; } - bool operator!=(const EncapsulatedValue &v) const { return value != v.value; } - - const Value &get() const { return value; } - Value *unsafeGet() { return &value; } - operator const Value &() const { return value; } - - JSGCTraceKind gcKind() const { return value.gcKind(); } - - uint64_t asRawBits() const { return value.asRawBits(); } - - static inline void writeBarrierPre(const Value &v); - static inline void writeBarrierPre(JSCompartment *comp, const Value &v); - - protected: - inline void pre(); - inline void pre(JSCompartment *comp); - - private: - friend class ValueOperations; - const Value * extract() const { return &value; } -}; - -class HeapValue : public EncapsulatedValue -{ - public: - explicit inline HeapValue(); - explicit inline HeapValue(const Value &v); - explicit inline HeapValue(const HeapValue &v); - inline ~HeapValue(); - - inline void init(const Value &v); - inline void init(JSCompartment *comp, const Value &v); - - inline HeapValue &operator=(const Value &v); - inline HeapValue &operator=(const HeapValue &v); - - /* - * This is a faster version of operator=. Normally, operator= has to - * determine the compartment of the value before it can decide whether to do - * the barrier. If you already know the compartment, it's faster to pass it - * in. - */ - inline void set(JSCompartment *comp, const Value &v); - - static inline void writeBarrierPost(const Value &v, Value *addr); - static inline void writeBarrierPost(JSCompartment *comp, const Value &v, Value *addr); - - private: - inline void post(); - inline void post(JSCompartment *comp); -}; - -class RelocatableValue : public EncapsulatedValue -{ - public: - explicit inline RelocatableValue(); - explicit inline RelocatableValue(const Value &v); - inline RelocatableValue(const RelocatableValue &v); - inline ~RelocatableValue(); - - inline RelocatableValue &operator=(const Value &v); - inline RelocatableValue &operator=(const RelocatableValue &v); - - private: - inline void post(); - inline void post(JSCompartment *comp); - inline void relocate(); -}; - -class HeapSlot : public EncapsulatedValue -{ - /* - * Operator= is not valid for HeapSlot because is must take the object and - * slot offset to provide to the post/generational barrier. - */ - inline HeapSlot &operator=(const Value &v) MOZ_DELETE; - inline HeapSlot &operator=(const HeapValue &v) MOZ_DELETE; - inline HeapSlot &operator=(const HeapSlot &v) MOZ_DELETE; - - public: - explicit inline HeapSlot() MOZ_DELETE; - explicit inline HeapSlot(JSObject *obj, uint32_t slot, const Value &v); - explicit inline HeapSlot(JSObject *obj, uint32_t slot, const HeapSlot &v); - inline ~HeapSlot(); - - inline void init(JSObject *owner, uint32_t slot, const Value &v); - inline void init(JSCompartment *comp, JSObject *owner, uint32_t slot, const Value &v); - - inline void set(JSObject *owner, uint32_t slot, const Value &v); - inline void set(JSCompartment *comp, JSObject *owner, uint32_t slot, const Value &v); - - static inline void writeBarrierPost(JSObject *obj, uint32_t slot); - static inline void writeBarrierPost(JSCompartment *comp, JSObject *obj, uint32_t slot); - - private: - inline void post(JSObject *owner, uint32_t slot); - inline void post(JSCompartment *comp, JSObject *owner, uint32_t slot); -}; - -/* - * NOTE: This is a placeholder for bug 619558. - * - * Run a post write barrier that encompasses multiple contiguous slots in a - * single step. - */ -inline void -SlotRangeWriteBarrierPost(JSCompartment *comp, JSObject *obj, uint32_t start, uint32_t count); - -/* - * This is a post barrier for HashTables whose key can be moved during a GC. - */ -template -inline void -HashTableWriteBarrierPost(JSCompartment *comp, const Map *map, const Key &key) -{ -#ifdef JS_GCGENERATIONAL - if (key && comp->gcNursery.isInside(key)) - comp->gcStoreBuffer.putGeneric(HashKeyRef(map, key)); -#endif -} - -static inline const Value * -Valueify(const EncapsulatedValue *array) -{ - JS_STATIC_ASSERT(sizeof(HeapValue) == sizeof(Value)); - JS_STATIC_ASSERT(sizeof(HeapSlot) == sizeof(Value)); - return (const Value *)array; -} - -static inline HeapValue * -HeapValueify(Value *v) -{ - JS_STATIC_ASSERT(sizeof(HeapValue) == sizeof(Value)); - JS_STATIC_ASSERT(sizeof(HeapSlot) == sizeof(Value)); - return (HeapValue *)v; -} - -class HeapSlotArray -{ - HeapSlot *array; - - public: - HeapSlotArray(HeapSlot *array) : array(array) {} - - operator const Value *() const { return Valueify(array); } - operator HeapSlot *() const { return array; } - - HeapSlotArray operator +(int offset) const { return HeapSlotArray(array + offset); } - HeapSlotArray operator +(uint32_t offset) const { return HeapSlotArray(array + offset); } -}; - -class EncapsulatedId -{ - protected: - jsid value; - - private: - EncapsulatedId(const EncapsulatedId &v) MOZ_DELETE; - - public: - explicit EncapsulatedId() : value(JSID_VOID) {} - explicit EncapsulatedId(jsid id) : value(id) {} - ~EncapsulatedId(); - - inline EncapsulatedId &operator=(const EncapsulatedId &v); - - bool operator==(jsid id) const { return value == id; } - bool operator!=(jsid id) const { return value != id; } - - jsid get() const { return value; } - jsid *unsafeGet() { return &value; } - operator jsid() const { return value; } - - protected: - inline void pre(); -}; - -class RelocatableId : public EncapsulatedId -{ - public: - explicit RelocatableId() : EncapsulatedId() {} - explicit inline RelocatableId(jsid id) : EncapsulatedId(id) {} - inline ~RelocatableId(); - - inline RelocatableId &operator=(jsid id); - inline RelocatableId &operator=(const RelocatableId &v); -}; - -class HeapId : public EncapsulatedId -{ - public: - explicit HeapId() : EncapsulatedId() {} - explicit inline HeapId(jsid id); - inline ~HeapId(); - - inline void init(jsid id); - - inline HeapId &operator=(jsid id); - inline HeapId &operator=(const HeapId &v); - - private: - inline void post(); - - HeapId(const HeapId &v) MOZ_DELETE; -}; - -/* - * Incremental GC requires that weak pointers have read barriers. This is mostly - * an issue for empty shapes stored in JSCompartment. The problem happens when, - * during an incremental GC, some JS code stores one of the compartment's empty - * shapes into an object already marked black. Normally, this would not be a - * problem, because the empty shape would have been part of the initial snapshot - * when the GC started. However, since this is a weak pointer, it isn't. So we - * may collect the empty shape even though a live object points to it. To fix - * this, we mark these empty shapes black whenever they get read out. - */ -template -class ReadBarriered -{ - T *value; - - public: - ReadBarriered() : value(NULL) {} - ReadBarriered(T *value) : value(value) {} - - T *get() const { - if (!value) - return NULL; - T::readBarrier(value); - return value; - } - - operator T*() const { return get(); } - - T &operator*() const { return *get(); } - T *operator->() const { return get(); } - - T **unsafeGet() { return &value; } - - void set(T *v) { value = v; } - - operator bool() { return !!value; } -}; - -class ReadBarrieredValue -{ - Value value; - - public: - ReadBarrieredValue() : value(UndefinedValue()) {} - ReadBarrieredValue(const Value &value) : value(value) {} - - inline const Value &get() const; - Value *unsafeGet() { return &value; } - inline operator const Value &() const; - - inline JSObject &toObject() const; -}; - -namespace tl { - -template struct IsRelocatableHeapType > - { static const bool result = false; }; -template <> struct IsRelocatableHeapType { static const bool result = false; }; -template <> struct IsRelocatableHeapType { static const bool result = false; }; -template <> struct IsRelocatableHeapType { static const bool result = false; }; - -} /* namespace tl */ -} /* namespace js */ - -#endif /* jsgc_barrier_h___ */ diff --git a/scripting/javascript/spidermonkey-win32/include/gc/Heap.h b/scripting/javascript/spidermonkey-win32/include/gc/Heap.h deleted file mode 100644 index 9a53deb24b..0000000000 --- a/scripting/javascript/spidermonkey-win32/include/gc/Heap.h +++ /dev/null @@ -1,1032 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef gc_heap_h___ -#define gc_heap_h___ - -#include "mozilla/Attributes.h" -#include "mozilla/StandardInteger.h" - -#include - -#include "jstypes.h" -#include "jsutil.h" - -#include "ds/BitArray.h" - -struct JSCompartment; - -extern "C" { -struct JSRuntime; -} - -namespace js { - -class FreeOp; - -namespace gc { - -struct Arena; -struct ArenaHeader; -struct Chunk; - -/* - * Live objects are marked black. How many other additional colors are available - * depends on the size of the GCThing. Objects marked gray are eligible for - * cycle collection. - */ -static const uint32_t BLACK = 0; -static const uint32_t GRAY = 1; - -/* The GC allocation kinds. */ -enum AllocKind { - FINALIZE_OBJECT0, - FINALIZE_OBJECT0_BACKGROUND, - FINALIZE_OBJECT2, - FINALIZE_OBJECT2_BACKGROUND, - FINALIZE_OBJECT4, - FINALIZE_OBJECT4_BACKGROUND, - FINALIZE_OBJECT8, - FINALIZE_OBJECT8_BACKGROUND, - FINALIZE_OBJECT12, - FINALIZE_OBJECT12_BACKGROUND, - FINALIZE_OBJECT16, - FINALIZE_OBJECT16_BACKGROUND, - FINALIZE_OBJECT_LAST = FINALIZE_OBJECT16_BACKGROUND, - FINALIZE_SCRIPT, - FINALIZE_SHAPE, - FINALIZE_BASE_SHAPE, - FINALIZE_TYPE_OBJECT, -#if JS_HAS_XML_SUPPORT - FINALIZE_XML, -#endif - FINALIZE_SHORT_STRING, - FINALIZE_STRING, - FINALIZE_EXTERNAL_STRING, - FINALIZE_IONCODE, - FINALIZE_LAST = FINALIZE_IONCODE -}; - -static const unsigned FINALIZE_LIMIT = FINALIZE_LAST + 1; -static const unsigned FINALIZE_OBJECT_LIMIT = FINALIZE_OBJECT_LAST + 1; - -/* - * This must be an upper bound, but we do not need the least upper bound, so - * we just exclude non-background objects. - */ -static const size_t MAX_BACKGROUND_FINALIZE_KINDS = FINALIZE_LIMIT - FINALIZE_OBJECT_LIMIT / 2; - -/* - * A GC cell is the base class for all GC things. - */ -struct Cell -{ - static const size_t CellShift = 3; - static const size_t CellSize = size_t(1) << CellShift; - static const size_t CellMask = CellSize - 1; - - inline uintptr_t address() const; - inline ArenaHeader *arenaHeader() const; - inline Chunk *chunk() const; - inline AllocKind getAllocKind() const; - MOZ_ALWAYS_INLINE bool isMarked(uint32_t color = BLACK) const; - MOZ_ALWAYS_INLINE bool markIfUnmarked(uint32_t color = BLACK) const; - MOZ_ALWAYS_INLINE void unmark(uint32_t color) const; - - inline JSCompartment *compartment() const; - -#ifdef DEBUG - inline bool isAligned() const; -#endif -}; - -/* - * Page size must be static to support our arena pointer optimizations, so we - * are forced to support each platform with non-4096 pages as a special case. - * Note: The freelist supports a maximum arena shift of 15. - * Note: Do not use JS_CPU_SPARC here, this header is used outside JS. - * Bug 692267: Move page size definition to gc/Memory.h and include it - * directly once jsgc.h is no longer an installed header. - */ -#if (defined(SOLARIS) || defined(__FreeBSD__)) && \ - (defined(__sparc) || defined(__sparcv9) || defined(__ia64)) -const size_t PageShift = 13; -const size_t ArenaShift = PageShift; -#elif defined(__powerpc64__) -const size_t PageShift = 16; -const size_t ArenaShift = 12; -#else -const size_t PageShift = 12; -const size_t ArenaShift = PageShift; -#endif -const size_t PageSize = size_t(1) << PageShift; -const size_t ArenaSize = size_t(1) << ArenaShift; -const size_t ArenaMask = ArenaSize - 1; - -const size_t ChunkShift = 20; -const size_t ChunkSize = size_t(1) << ChunkShift; -const size_t ChunkMask = ChunkSize - 1; - -/* - * This is the maximum number of arenas we allow in the FreeCommitted state - * before we trigger a GC_SHRINK to release free arenas to the OS. - */ -const static uint32_t FreeCommittedArenasThreshold = (32 << 20) / ArenaSize; - -/* - * The mark bitmap has one bit per each GC cell. For multi-cell GC things this - * wastes space but allows to avoid expensive devisions by thing's size when - * accessing the bitmap. In addition this allows to use some bits for colored - * marking during the cycle GC. - */ -const size_t ArenaCellCount = size_t(1) << (ArenaShift - Cell::CellShift); -const size_t ArenaBitmapBits = ArenaCellCount; -const size_t ArenaBitmapBytes = ArenaBitmapBits / 8; -const size_t ArenaBitmapWords = ArenaBitmapBits / JS_BITS_PER_WORD; - -/* - * A FreeSpan represents a contiguous sequence of free cells in an Arena. - * |first| is the address of the first free cell in the span. |last| is the - * address of the last free cell in the span. This last cell holds a FreeSpan - * data structure for the next span unless this is the last span on the list - * of spans in the arena. For this last span |last| points to the last byte of - * the last thing in the arena and no linkage is stored there, so - * |last| == arenaStart + ArenaSize - 1. If the space at the arena end is - * fully used this last span is empty and |first| == |last + 1|. - * - * Thus |first| < |last| implies that we have either the last span with at least - * one element or that the span is not the last and contains at least 2 - * elements. In both cases to allocate a thing from this span we need simply - * to increment |first| by the allocation size. - * - * |first| == |last| implies that we have a one element span that records the - * next span. So to allocate from it we need to update the span list head - * with a copy of the span stored at |last| address so the following - * allocations will use that span. - * - * |first| > |last| implies that we have an empty last span and the arena is - * fully used. - * - * Also only for the last span (|last| & 1)! = 0 as all allocation sizes are - * multiples of Cell::CellSize. - */ -struct FreeSpan -{ - uintptr_t first; - uintptr_t last; - - public: - FreeSpan() {} - - FreeSpan(uintptr_t first, uintptr_t last) - : first(first), last(last) { - checkSpan(); - } - - /* - * To minimize the size of the arena header the first span is encoded - * there as offsets from the arena start. - */ - static size_t encodeOffsets(size_t firstOffset, size_t lastOffset) { - /* Check that we can pack the offsets into uint16. */ - JS_STATIC_ASSERT(ArenaShift < 16); - JS_ASSERT(firstOffset <= ArenaSize); - JS_ASSERT(lastOffset < ArenaSize); - JS_ASSERT(firstOffset <= ((lastOffset + 1) & ~size_t(1))); - return firstOffset | (lastOffset << 16); - } - - /* - * Encoded offsets for a full arena when its first span is the last one - * and empty. - */ - static const size_t FullArenaOffsets = ArenaSize | ((ArenaSize - 1) << 16); - - static FreeSpan decodeOffsets(uintptr_t arenaAddr, size_t offsets) { - JS_ASSERT(!(arenaAddr & ArenaMask)); - - size_t firstOffset = offsets & 0xFFFF; - size_t lastOffset = offsets >> 16; - JS_ASSERT(firstOffset <= ArenaSize); - JS_ASSERT(lastOffset < ArenaSize); - - /* - * We must not use | when calculating first as firstOffset is - * ArenaMask + 1 for the empty span. - */ - return FreeSpan(arenaAddr + firstOffset, arenaAddr | lastOffset); - } - - void initAsEmpty(uintptr_t arenaAddr = 0) { - JS_ASSERT(!(arenaAddr & ArenaMask)); - first = arenaAddr + ArenaSize; - last = arenaAddr | (ArenaSize - 1); - JS_ASSERT(isEmpty()); - } - - bool isEmpty() const { - checkSpan(); - return first > last; - } - - bool hasNext() const { - checkSpan(); - return !(last & uintptr_t(1)); - } - - const FreeSpan *nextSpan() const { - JS_ASSERT(hasNext()); - return reinterpret_cast(last); - } - - FreeSpan *nextSpanUnchecked(size_t thingSize) const { -#ifdef DEBUG - uintptr_t lastOffset = last & ArenaMask; - JS_ASSERT(!(lastOffset & 1)); - JS_ASSERT((ArenaSize - lastOffset) % thingSize == 0); -#endif - return reinterpret_cast(last); - } - - uintptr_t arenaAddressUnchecked() const { - return last & ~ArenaMask; - } - - uintptr_t arenaAddress() const { - checkSpan(); - return arenaAddressUnchecked(); - } - - ArenaHeader *arenaHeader() const { - return reinterpret_cast(arenaAddress()); - } - - bool isSameNonEmptySpan(const FreeSpan *another) const { - JS_ASSERT(!isEmpty()); - JS_ASSERT(!another->isEmpty()); - return first == another->first && last == another->last; - } - - bool isWithinArena(uintptr_t arenaAddr) const { - JS_ASSERT(!(arenaAddr & ArenaMask)); - - /* Return true for the last empty span as well. */ - return arenaAddress() == arenaAddr; - } - - size_t encodeAsOffsets() const { - /* - * We must use first - arenaAddress(), not first & ArenaMask as - * first == ArenaMask + 1 for an empty span. - */ - uintptr_t arenaAddr = arenaAddress(); - return encodeOffsets(first - arenaAddr, last & ArenaMask); - } - - /* See comments before FreeSpan for details. */ - MOZ_ALWAYS_INLINE void *allocate(size_t thingSize) { - JS_ASSERT(thingSize % Cell::CellSize == 0); - checkSpan(); - uintptr_t thing = first; - if (thing < last) { - /* Bump-allocate from the current span. */ - first = thing + thingSize; - } else if (JS_LIKELY(thing == last)) { - /* - * Move to the next span. We use JS_LIKELY as without PGO - * compilers mis-predict == here as unlikely to succeed. - */ - *this = *reinterpret_cast(thing); - } else { - return NULL; - } - checkSpan(); - return reinterpret_cast(thing); - } - - /* A version of allocate when we know that the span is not empty. */ - MOZ_ALWAYS_INLINE void *infallibleAllocate(size_t thingSize) { - JS_ASSERT(thingSize % Cell::CellSize == 0); - checkSpan(); - uintptr_t thing = first; - if (thing < last) { - first = thing + thingSize; - } else { - JS_ASSERT(thing == last); - *this = *reinterpret_cast(thing); - } - checkSpan(); - return reinterpret_cast(thing); - } - - /* - * Allocate from a newly allocated arena. We do not move the free list - * from the arena. Rather we set the arena up as fully used during the - * initialization so to allocate we simply return the first thing in the - * arena and set the free list to point to the second. - */ - MOZ_ALWAYS_INLINE void *allocateFromNewArena(uintptr_t arenaAddr, size_t firstThingOffset, - size_t thingSize) { - JS_ASSERT(!(arenaAddr & ArenaMask)); - uintptr_t thing = arenaAddr | firstThingOffset; - first = thing + thingSize; - last = arenaAddr | ArenaMask; - checkSpan(); - return reinterpret_cast(thing); - } - - void checkSpan() const { -#ifdef DEBUG - /* We do not allow spans at the end of the address space. */ - JS_ASSERT(last != uintptr_t(-1)); - JS_ASSERT(first); - JS_ASSERT(last); - JS_ASSERT(first - 1 <= last); - uintptr_t arenaAddr = arenaAddressUnchecked(); - if (last & 1) { - /* The span is the last. */ - JS_ASSERT((last & ArenaMask) == ArenaMask); - - if (first - 1 == last) { - /* The span is last and empty. The above start != 0 check - * implies that we are not at the end of the address space. - */ - return; - } - size_t spanLength = last - first + 1; - JS_ASSERT(spanLength % Cell::CellSize == 0); - - /* Start and end must belong to the same arena. */ - JS_ASSERT((first & ~ArenaMask) == arenaAddr); - return; - } - - /* The span is not the last and we have more spans to follow. */ - JS_ASSERT(first <= last); - size_t spanLengthWithoutOneThing = last - first; - JS_ASSERT(spanLengthWithoutOneThing % Cell::CellSize == 0); - - JS_ASSERT((first & ~ArenaMask) == arenaAddr); - - /* - * If there is not enough space before the arena end to allocate one - * more thing, then the span must be marked as the last one to avoid - * storing useless empty span reference. - */ - size_t beforeTail = ArenaSize - (last & ArenaMask); - JS_ASSERT(beforeTail >= sizeof(FreeSpan) + Cell::CellSize); - - FreeSpan *next = reinterpret_cast(last); - - /* - * The GC things on the list of free spans come from one arena - * and the spans are linked in ascending address order with - * at least one non-free thing between spans. - */ - JS_ASSERT(last < next->first); - JS_ASSERT(arenaAddr == next->arenaAddressUnchecked()); - - if (next->first > next->last) { - /* - * The next span is the empty span that terminates the list for - * arenas that do not have any free things at the end. - */ - JS_ASSERT(next->first - 1 == next->last); - JS_ASSERT(arenaAddr + ArenaSize == next->first); - } -#endif - } - -}; - -/* Every arena has a header. */ -struct ArenaHeader -{ - friend struct FreeLists; - - JSCompartment *compartment; - - /* - * ArenaHeader::next has two purposes: when unallocated, it points to the - * next available Arena's header. When allocated, it points to the next - * arena of the same size class and compartment. - */ - ArenaHeader *next; - - private: - /* - * The first span of free things in the arena. We encode it as the start - * and end offsets within the arena, not as FreeSpan structure, to - * minimize the header size. - */ - size_t firstFreeSpanOffsets; - - /* - * One of AllocKind constants or FINALIZE_LIMIT when the arena does not - * contain any GC things and is on the list of empty arenas in the GC - * chunk. The latter allows to quickly check if the arena is allocated - * during the conservative GC scanning without searching the arena in the - * list. - * - * We use 8 bits for the allocKind so the compiler can use byte-level memory - * instructions to access it. - */ - size_t allocKind : 8; - - /* - * When collecting we sometimes need to keep an auxillary list of arenas, - * for which we use the following fields. This happens for several reasons: - * - * When recursive marking uses too much stack the marking is delayed and the - * corresponding arenas are put into a stack. To distinguish the bottom of - * the stack from the arenas not present in the stack we use the - * markOverflow flag to tag arenas on the stack. - * - * Delayed marking is also used for arenas that we allocate into during an - * incremental GC. In this case, we intend to mark all the objects in the - * arena, and it's faster to do this marking in bulk. - * - * When sweeping we keep track of which arenas have been allocated since the - * end of the mark phase. This allows us to tell whether a pointer to an - * unmarked object is yet to be finalized or has already been reallocated. - * We set the allocatedDuringIncremental flag for this and clear it at the - * end of the sweep phase. - * - * To minimize the ArenaHeader size we record the next linkage as - * arenaAddress() >> ArenaShift and pack it with the allocKind field and the - * flags. - */ - public: - size_t hasDelayedMarking : 1; - size_t allocatedDuringIncremental : 1; - size_t markOverflow : 1; - size_t auxNextLink : JS_BITS_PER_WORD - 8 - 1 - 1 - 1; - - static void staticAsserts() { - /* We must be able to fit the allockind into uint8_t. */ - JS_STATIC_ASSERT(FINALIZE_LIMIT <= 255); - - /* - * auxNextLink packing assumes that ArenaShift has enough bits - * to cover allocKind and hasDelayedMarking. - */ - JS_STATIC_ASSERT(ArenaShift >= 8 + 1 + 1 + 1); - } - - inline uintptr_t address() const; - inline Chunk *chunk() const; - - bool allocated() const { - JS_ASSERT(allocKind <= size_t(FINALIZE_LIMIT)); - return allocKind < size_t(FINALIZE_LIMIT); - } - - void init(JSCompartment *comp, AllocKind kind) { - JS_ASSERT(!allocated()); - JS_ASSERT(!markOverflow); - JS_ASSERT(!allocatedDuringIncremental); - JS_ASSERT(!hasDelayedMarking); - compartment = comp; - - JS_STATIC_ASSERT(FINALIZE_LIMIT <= 255); - allocKind = size_t(kind); - - /* See comments in FreeSpan::allocateFromNewArena. */ - firstFreeSpanOffsets = FreeSpan::FullArenaOffsets; - } - - void setAsNotAllocated() { - allocKind = size_t(FINALIZE_LIMIT); - markOverflow = 0; - allocatedDuringIncremental = 0; - hasDelayedMarking = 0; - auxNextLink = 0; - } - - inline uintptr_t arenaAddress() const; - inline Arena *getArena(); - - AllocKind getAllocKind() const { - JS_ASSERT(allocated()); - return AllocKind(allocKind); - } - - inline size_t getThingSize() const; - - bool hasFreeThings() const { - return firstFreeSpanOffsets != FreeSpan::FullArenaOffsets; - } - - inline bool isEmpty() const; - - void setAsFullyUsed() { - firstFreeSpanOffsets = FreeSpan::FullArenaOffsets; - } - - inline FreeSpan getFirstFreeSpan() const; - inline void setFirstFreeSpan(const FreeSpan *span); - -#ifdef DEBUG - void checkSynchronizedWithFreeList() const; -#endif - - inline ArenaHeader *getNextDelayedMarking() const; - inline void setNextDelayedMarking(ArenaHeader *aheader); - inline void unsetDelayedMarking(); - - inline ArenaHeader *getNextAllocDuringSweep() const; - inline void setNextAllocDuringSweep(ArenaHeader *aheader); - inline void unsetAllocDuringSweep(); -}; - -struct Arena -{ - /* - * Layout of an arena: - * An arena is 4K in size and 4K-aligned. It starts with the ArenaHeader - * descriptor followed by some pad bytes. The remainder of the arena is - * filled with the array of T things. The pad bytes ensure that the thing - * array ends exactly at the end of the arena. - * - * +-------------+-----+----+----+-----+----+ - * | ArenaHeader | pad | T0 | T1 | ... | Tn | - * +-------------+-----+----+----+-----+----+ - * - * <----------------------------------------> = ArenaSize bytes - * <-------------------> = first thing offset - */ - ArenaHeader aheader; - uint8_t data[ArenaSize - sizeof(ArenaHeader)]; - - private: - static JS_FRIEND_DATA(const uint32_t) ThingSizes[]; - static JS_FRIEND_DATA(const uint32_t) FirstThingOffsets[]; - - public: - static void staticAsserts(); - - static size_t thingSize(AllocKind kind) { - return ThingSizes[kind]; - } - - static size_t firstThingOffset(AllocKind kind) { - return FirstThingOffsets[kind]; - } - - static size_t thingsPerArena(size_t thingSize) { - JS_ASSERT(thingSize % Cell::CellSize == 0); - - /* We should be able to fit FreeSpan in any GC thing. */ - JS_ASSERT(thingSize >= sizeof(FreeSpan)); - - return (ArenaSize - sizeof(ArenaHeader)) / thingSize; - } - - static size_t thingsSpan(size_t thingSize) { - return thingsPerArena(thingSize) * thingSize; - } - - static bool isAligned(uintptr_t thing, size_t thingSize) { - /* Things ends at the arena end. */ - uintptr_t tailOffset = (ArenaSize - thing) & ArenaMask; - return tailOffset % thingSize == 0; - } - - uintptr_t address() const { - return aheader.address(); - } - - uintptr_t thingsStart(AllocKind thingKind) { - return address() | firstThingOffset(thingKind); - } - - uintptr_t thingsEnd() { - return address() + ArenaSize; - } - - template - bool finalize(FreeOp *fop, AllocKind thingKind, size_t thingSize); -}; - -inline size_t -ArenaHeader::getThingSize() const -{ - JS_ASSERT(allocated()); - return Arena::thingSize(getAllocKind()); -} - -/* The chunk header (located at the end of the chunk to preserve arena alignment). */ -struct ChunkInfo -{ - Chunk *next; - Chunk **prevp; - - /* Free arenas are linked together with aheader.next. */ - ArenaHeader *freeArenasHead; - - /* - * Decommitted arenas are tracked by a bitmap in the chunk header. We use - * this offset to start our search iteration close to a decommitted arena - * that we can allocate. - */ - uint32_t lastDecommittedArenaOffset; - - /* Number of free arenas, either committed or decommitted. */ - uint32_t numArenasFree; - - /* Number of free, committed arenas. */ - uint32_t numArenasFreeCommitted; - - /* Number of GC cycles this chunk has survived. */ - uint32_t age; -}; - -/* - * Calculating ArenasPerChunk: - * - * In order to figure out how many Arenas will fit in a chunk, we need to know - * how much extra space is available after we allocate the header data. This - * is a problem because the header size depends on the number of arenas in the - * chunk. The two dependent fields are bitmap and decommittedArenas. - * - * For the mark bitmap, we know that each arena will use a fixed number of full - * bytes: ArenaBitmapBytes. The full size of the header data is this number - * multiplied by the eventual number of arenas we have in the header. We, - * conceptually, distribute this header data among the individual arenas and do - * not include it in the header. This way we do not have to worry about its - * variable size: it gets attached to the variable number we are computing. - * - * For the decommitted arena bitmap, we only have 1 bit per arena, so this - * technique will not work. Instead, we observe that we do not have enough - * header info to fill 8 full arenas: it is currently 4 on 64bit, less on - * 32bit. Thus, with current numbers, we need 64 bytes for decommittedArenas. - * This will not become 63 bytes unless we double the data required in the - * header. Therefore, we just compute the number of bytes required to track - * every possible arena and do not worry about slop bits, since there are too - * few to usefully allocate. - * - * To actually compute the number of arenas we can allocate in a chunk, we - * divide the amount of available space less the header info (not including - * the mark bitmap which is distributed into the arena size) by the size of - * the arena (with the mark bitmap bytes it uses). - */ -const size_t BytesPerArenaWithHeader = ArenaSize + ArenaBitmapBytes; -const size_t ChunkDecommitBitmapBytes = ChunkSize / ArenaSize / JS_BITS_PER_BYTE; -const size_t ChunkBytesAvailable = ChunkSize - sizeof(ChunkInfo) - ChunkDecommitBitmapBytes; -const size_t ArenasPerChunk = ChunkBytesAvailable / BytesPerArenaWithHeader; - -/* A chunk bitmap contains enough mark bits for all the cells in a chunk. */ -struct ChunkBitmap -{ - uintptr_t bitmap[ArenaBitmapWords * ArenasPerChunk]; - - MOZ_ALWAYS_INLINE void getMarkWordAndMask(const Cell *cell, uint32_t color, - uintptr_t **wordp, uintptr_t *maskp); - - MOZ_ALWAYS_INLINE bool isMarked(const Cell *cell, uint32_t color) { - uintptr_t *word, mask; - getMarkWordAndMask(cell, color, &word, &mask); - return *word & mask; - } - - MOZ_ALWAYS_INLINE bool markIfUnmarked(const Cell *cell, uint32_t color) { - uintptr_t *word, mask; - getMarkWordAndMask(cell, BLACK, &word, &mask); - if (*word & mask) - return false; - *word |= mask; - if (color != BLACK) { - /* - * We use getMarkWordAndMask to recalculate both mask and word as - * doing just mask << color may overflow the mask. - */ - getMarkWordAndMask(cell, color, &word, &mask); - if (*word & mask) - return false; - *word |= mask; - } - return true; - } - - MOZ_ALWAYS_INLINE void unmark(const Cell *cell, uint32_t color) { - uintptr_t *word, mask; - getMarkWordAndMask(cell, color, &word, &mask); - *word &= ~mask; - } - - void clear() { - PodArrayZero(bitmap); - } - - uintptr_t *arenaBits(ArenaHeader *aheader) { - /* - * We assume that the part of the bitmap corresponding to the arena - * has the exact number of words so we do not need to deal with a word - * that covers bits from two arenas. - */ - JS_STATIC_ASSERT(ArenaBitmapBits == ArenaBitmapWords * JS_BITS_PER_WORD); - - uintptr_t *word, unused; - getMarkWordAndMask(reinterpret_cast(aheader->address()), BLACK, &word, &unused); - return word; - } -}; - -JS_STATIC_ASSERT(ArenaBitmapBytes * ArenasPerChunk == sizeof(ChunkBitmap)); - -typedef BitArray PerArenaBitmap; - -const size_t ChunkPadSize = ChunkSize - - (sizeof(Arena) * ArenasPerChunk) - - sizeof(ChunkBitmap) - - sizeof(PerArenaBitmap) - - sizeof(ChunkInfo); -JS_STATIC_ASSERT(ChunkPadSize < BytesPerArenaWithHeader); - -/* - * Chunks contain arenas and associated data structures (mark bitmap, delayed - * marking state). - */ -struct Chunk -{ - Arena arenas[ArenasPerChunk]; - - /* Pad to full size to ensure cache alignment of ChunkInfo. */ - uint8_t padding[ChunkPadSize]; - - ChunkBitmap bitmap; - PerArenaBitmap decommittedArenas; - ChunkInfo info; - - static Chunk *fromAddress(uintptr_t addr) { - addr &= ~ChunkMask; - return reinterpret_cast(addr); - } - - static bool withinArenasRange(uintptr_t addr) { - uintptr_t offset = addr & ChunkMask; - return offset < ArenasPerChunk * ArenaSize; - } - - static size_t arenaIndex(uintptr_t addr) { - JS_ASSERT(withinArenasRange(addr)); - return (addr & ChunkMask) >> ArenaShift; - } - - uintptr_t address() const { - uintptr_t addr = reinterpret_cast(this); - JS_ASSERT(!(addr & ChunkMask)); - return addr; - } - - bool unused() const { - return info.numArenasFree == ArenasPerChunk; - } - - bool hasAvailableArenas() const { - return info.numArenasFree != 0; - } - - inline void addToAvailableList(JSCompartment *compartment); - inline void insertToAvailableList(Chunk **insertPoint); - inline void removeFromAvailableList(); - - ArenaHeader *allocateArena(JSCompartment *comp, AllocKind kind); - - void releaseArena(ArenaHeader *aheader); - - static Chunk *allocate(JSRuntime *rt); - - /* Must be called with the GC lock taken. */ - static inline void release(JSRuntime *rt, Chunk *chunk); - static inline void releaseList(JSRuntime *rt, Chunk *chunkListHead); - - /* Must be called with the GC lock taken. */ - inline void prepareToBeFreed(JSRuntime *rt); - - /* - * Assuming that the info.prevp points to the next field of the previous - * chunk in a doubly-linked list, get that chunk. - */ - Chunk *getPrevious() { - JS_ASSERT(info.prevp); - return fromPointerToNext(info.prevp); - } - - /* Get the chunk from a pointer to its info.next field. */ - static Chunk *fromPointerToNext(Chunk **nextFieldPtr) { - uintptr_t addr = reinterpret_cast(nextFieldPtr); - JS_ASSERT((addr & ChunkMask) == offsetof(Chunk, info.next)); - return reinterpret_cast(addr - offsetof(Chunk, info.next)); - } - - private: - inline void init(); - - /* Search for a decommitted arena to allocate. */ - unsigned findDecommittedArenaOffset(); - ArenaHeader* fetchNextDecommittedArena(); - - public: - /* Unlink and return the freeArenasHead. */ - inline ArenaHeader* fetchNextFreeArena(JSRuntime *rt); - - inline void addArenaToFreeList(JSRuntime *rt, ArenaHeader *aheader); -}; - -JS_STATIC_ASSERT(sizeof(Chunk) == ChunkSize); - -inline uintptr_t -Cell::address() const -{ - uintptr_t addr = uintptr_t(this); - JS_ASSERT(addr % Cell::CellSize == 0); - JS_ASSERT(Chunk::withinArenasRange(addr)); - return addr; -} - -inline uintptr_t -ArenaHeader::address() const -{ - uintptr_t addr = reinterpret_cast(this); - JS_ASSERT(!(addr & ArenaMask)); - JS_ASSERT(Chunk::withinArenasRange(addr)); - return addr; -} - -inline Chunk * -ArenaHeader::chunk() const -{ - return Chunk::fromAddress(address()); -} - -inline uintptr_t -ArenaHeader::arenaAddress() const -{ - return address(); -} - -inline Arena * -ArenaHeader::getArena() -{ - return reinterpret_cast(arenaAddress()); -} - -inline bool -ArenaHeader::isEmpty() const -{ - /* Arena is empty if its first span covers the whole arena. */ - JS_ASSERT(allocated()); - size_t firstThingOffset = Arena::firstThingOffset(getAllocKind()); - return firstFreeSpanOffsets == FreeSpan::encodeOffsets(firstThingOffset, ArenaMask); -} - -FreeSpan -ArenaHeader::getFirstFreeSpan() const -{ -#ifdef DEBUG - checkSynchronizedWithFreeList(); -#endif - return FreeSpan::decodeOffsets(arenaAddress(), firstFreeSpanOffsets); -} - -void -ArenaHeader::setFirstFreeSpan(const FreeSpan *span) -{ - JS_ASSERT(span->isWithinArena(arenaAddress())); - firstFreeSpanOffsets = span->encodeAsOffsets(); -} - -inline ArenaHeader * -ArenaHeader::getNextDelayedMarking() const -{ - JS_ASSERT(hasDelayedMarking); - return &reinterpret_cast(auxNextLink << ArenaShift)->aheader; -} - -inline void -ArenaHeader::setNextDelayedMarking(ArenaHeader *aheader) -{ - JS_ASSERT(!(uintptr_t(aheader) & ArenaMask)); - JS_ASSERT(!auxNextLink && !hasDelayedMarking); - hasDelayedMarking = 1; - auxNextLink = aheader->arenaAddress() >> ArenaShift; -} - -inline void -ArenaHeader::unsetDelayedMarking() -{ - JS_ASSERT(hasDelayedMarking); - hasDelayedMarking = 0; - auxNextLink = 0; -} - -inline ArenaHeader * -ArenaHeader::getNextAllocDuringSweep() const -{ - JS_ASSERT(allocatedDuringIncremental); - return &reinterpret_cast(auxNextLink << ArenaShift)->aheader; -} - -inline void -ArenaHeader::setNextAllocDuringSweep(ArenaHeader *aheader) -{ - JS_ASSERT(!auxNextLink && !allocatedDuringIncremental); - allocatedDuringIncremental = 1; - auxNextLink = aheader->arenaAddress() >> ArenaShift; -} - -inline void -ArenaHeader::unsetAllocDuringSweep() -{ - JS_ASSERT(allocatedDuringIncremental); - allocatedDuringIncremental = 0; - auxNextLink = 0; -} - -JS_ALWAYS_INLINE void -ChunkBitmap::getMarkWordAndMask(const Cell *cell, uint32_t color, - uintptr_t **wordp, uintptr_t *maskp) -{ - size_t bit = (cell->address() & ChunkMask) / Cell::CellSize + color; - JS_ASSERT(bit < ArenaBitmapBits * ArenasPerChunk); - *maskp = uintptr_t(1) << (bit % JS_BITS_PER_WORD); - *wordp = &bitmap[bit / JS_BITS_PER_WORD]; -} - -static void -AssertValidColor(const void *thing, uint32_t color) -{ -#ifdef DEBUG - ArenaHeader *aheader = reinterpret_cast(thing)->arenaHeader(); - JS_ASSERT_IF(color, color < aheader->getThingSize() / Cell::CellSize); -#endif -} - -inline ArenaHeader * -Cell::arenaHeader() const -{ - uintptr_t addr = address(); - addr &= ~ArenaMask; - return reinterpret_cast(addr); -} - -Chunk * -Cell::chunk() const -{ - uintptr_t addr = uintptr_t(this); - JS_ASSERT(addr % Cell::CellSize == 0); - addr &= ~(ChunkSize - 1); - return reinterpret_cast(addr); -} - -AllocKind -Cell::getAllocKind() const -{ - return arenaHeader()->getAllocKind(); -} - -bool -Cell::isMarked(uint32_t color /* = BLACK */) const -{ - AssertValidColor(this, color); - return chunk()->bitmap.isMarked(this, color); -} - -bool -Cell::markIfUnmarked(uint32_t color /* = BLACK */) const -{ - AssertValidColor(this, color); - return chunk()->bitmap.markIfUnmarked(this, color); -} - -void -Cell::unmark(uint32_t color) const -{ - JS_ASSERT(color != BLACK); - AssertValidColor(this, color); - chunk()->bitmap.unmark(this, color); -} - -JSCompartment * -Cell::compartment() const -{ - return arenaHeader()->compartment; -} - -#ifdef DEBUG -bool -Cell::isAligned() const -{ - return Arena::isAligned(address(), arenaHeader()->getThingSize()); -} -#endif - -} /* namespace gc */ - -} /* namespace js */ - -#endif /* gc_heap_h___ */ diff --git a/scripting/javascript/spidermonkey-win32/include/gc/Root.h b/scripting/javascript/spidermonkey-win32/include/gc/Root.h index d83e21856f..33c648c7b5 100644 --- a/scripting/javascript/spidermonkey-win32/include/gc/Root.h +++ b/scripting/javascript/spidermonkey-win32/include/gc/Root.h @@ -77,8 +77,14 @@ class MutableHandleBase {}; namespace JS { +class AutoAssertNoGC; + template class MutableHandle; +JS_FRIEND_API(void) EnterAssertNoGCScope(); +JS_FRIEND_API(void) LeaveAssertNoGCScope(); +JS_FRIEND_API(bool) InNoGCScope(); + /* * Handle provides an implicit constructor for NullPtr so that, given: * foo(Handle h); @@ -316,6 +322,169 @@ class InternalHandle } }; +#ifdef DEBUG +template +class IntermediateNoGC +{ + T t_; + + public: + IntermediateNoGC(const T &t) : t_(t) { + EnterAssertNoGCScope(); + } + IntermediateNoGC(const IntermediateNoGC &) { + EnterAssertNoGCScope(); + } + ~IntermediateNoGC() { + LeaveAssertNoGCScope(); + } + + const T &operator->() { return t_; } + operator const T &() { return t_; } +}; +#endif + +/* + * Return wraps GC things that are returned from accessor methods. The + * wrapper helps to ensure correct rooting of the returned pointer and safe + * access while unrooted. + * + * Example usage in a method declaration: + * + * class Foo { + * HeapPtrScript script_; + * ... + * public: + * Return script() { return script_; } + * }; + * + * Example usage of method (1): + * + * Foo foo(...); + * RootedScript script(cx, foo->script()); + * + * Example usage of method (2): + * + * Foo foo(...); + * foo->script()->needsArgsObj(); + * + * The purpose of this class is to assert eagerly on incorrect use of GC thing + * pointers. For example: + * + * RootedShape shape(cx, ...); + * shape->parent.init(js_NewGCThing(cx, ...)); + * + * In this expression, C++ is allowed to order these calls as follows: + * + * Call Effect + * ---- ------ + * 1) RootedShape::operator-> Stores shape::ptr_ to stack. + * 2) js_NewGCThing Triggers GC and compaction of shapes. This + * moves shape::ptr_ to a new location. + * 3) HeapPtrObject::init This call takes the relocated shape::ptr_ + * as |this|, crashing or, worse, corrupting + * the program's state on the first access + * to a member variable. + * + * If Shape::parent were an accessor function returning a Return, this + * could not happen: Return ensures either immediate rooting or no GC within + * the same expression. + */ +template +class Return +{ + friend class Rooted; + + const T ptr_; + + public: + template + Return(const S &ptr, + typename mozilla::EnableIf::value, int>::Type dummy = 0) + : ptr_(ptr) + {} + + Return(NullPtr) : ptr_(NULL) {} + + /* + * |get(AutoAssertNoGC &)| is the safest way to access a Return without + * rooting it first: it is impossible to call this method without an + * AutoAssertNoGC in scope, so the compiler will automatically catch any + * incorrect usage. + * + * Example: + * AutoAssertNoGC nogc; + * RawScript script = fun->script().get(nogc); + */ + const T &get(AutoAssertNoGC &) const { + return ptr_; + } + + /* + * |operator->|'s result cannot be stored in a local variable, so it is safe + * to use in a CanGC context iff no GC can occur anywhere within the same + * expression (generally from one |;| to the next). |operator->| uses a + * temporary object as a guard and will assert if a CanGC context is + * encountered before the next C++ Sequence Point. + * + * INCORRECT: + * fun->script()->bindings = myBindings->clone(cx, ...); + * + * The compiler is allowed to reorder |fun->script()::operator->()| above + * the call to |clone(cx, ...)|. In this case, the RawScript C++ stores on + * the stack may be corrupted by a GC under |clone|. The subsequent + * dereference of this pointer to get |bindings| will result in an invalid + * access. This wrapper ensures that such usage asserts in DEBUG builds when + * it encounters this situation. Without this assertion, it is possible for + * such access to corrupt program state instead of crashing immediately. + * + * CORRECT: + * RootedScript clone(cx, myBindings->clone(cx, ...)); + * fun->script()->bindings = clone; + */ +#ifdef DEBUG + IntermediateNoGC operator->() const { + return IntermediateNoGC(ptr_); + } +#else + const T &operator->() const { + return ptr_; + } +#endif + + /* + * |unsafeGet()| is unsafe for most uses. Although it performs similar + * checking to |operator->|, its result can be stored to a local variable. + * For this reason, it should only be used when it would be incorrect or + * absurd to create a new Rooted for its use: e.g. for assertions. + */ +#ifdef DEBUG + IntermediateNoGC unsafeGet() const { + return IntermediateNoGC(ptr_); + } +#else + const T &unsafeGet() const { + return ptr_; + } +#endif + + /* + * |operator==| is safe to use in any context. It is present to allow: + * JS_ASSERT(myScript == fun->script().unsafeGet()); + * + * To be rewritten as: + * JS_ASSERT(fun->script() == myScript); + * + * Note: the new order tells C++ to use |Return::operator=| + * instead of direct pointer comparison. + */ + bool operator==(const T &other) { return ptr_ == other; } + bool operator!=(const T &other) { return ptr_ != other; } + bool operator==(const Return &other) { return ptr_ == other.ptr_; } + bool operator==(const JS::Handle &other) { return ptr_ == other.get(); } + inline bool operator==(const Rooted &other); +}; + /* * By default, pointers should use the inheritance hierarchy to find their * ThingRootKind. Some pointer types are explicitly set in jspubtd.h so that @@ -332,12 +501,6 @@ struct RootMethods static bool poisoned(T *v) { return IsPoisonedPtr(v); } }; -#if !(defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)) -// Defined in vm/String.h. -template <> -class Rooted; -#endif - template class RootedBase {}; @@ -356,27 +519,23 @@ class Rooted : public RootedBase { #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) ContextFriendFields *cx = ContextFriendFields::get(cxArg); - - ThingRootKind kind = RootMethods::kind(); - this->stack = reinterpret_cast**>(&cx->thingGCRooters[kind]); - this->prev = *stack; - *stack = this; - - JS_ASSERT(!RootMethods::poisoned(ptr)); + commonInit(cx->thingGCRooters); #endif } void init(JSRuntime *rtArg) { #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) - RuntimeFriendFields *rt = const_cast(RuntimeFriendFields::get(rtArg)); + PerThreadDataFriendFields *pt = PerThreadDataFriendFields::getMainThread(rtArg); + commonInit(pt->thingGCRooters); +#endif + } - ThingRootKind kind = RootMethods::kind(); - this->stack = reinterpret_cast**>(&rt->thingGCRooters[kind]); - this->prev = *stack; - *stack = this; - - JS_ASSERT(!RootMethods::poisoned(ptr)); + void init(js::PerThreadData *ptArg) + { +#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) + PerThreadDataFriendFields *pt = PerThreadDataFriendFields::get(ptArg); + commonInit(pt->thingGCRooters); #endif } @@ -413,6 +572,40 @@ class Rooted : public RootedBase init(cx); } + Rooted(js::PerThreadData *pt + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : ptr(RootMethods::initial()) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + init(pt); + } + + Rooted(js::PerThreadData *pt, T initial + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : ptr(initial) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + init(pt); + } + + template + Rooted(JSContext *cx, const Return &initial + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : ptr(initial.ptr_) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + init(cx); + } + + template + Rooted(js::PerThreadData *pt, const Return &initial + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : ptr(initial.ptr_) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + init(pt); + } + ~Rooted() { #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) @@ -445,7 +638,25 @@ class Rooted : public RootedBase return ptr; } + template + T & operator =(const Return &value) + { + ptr = value.ptr_; + return ptr; + } + private: + void commonInit(Rooted **thingGCRooters) { +#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) + ThingRootKind kind = RootMethods::kind(); + this->stack = reinterpret_cast**>(&thingGCRooters[kind]); + this->prev = *stack; + *stack = this; + + JS_ASSERT(!RootMethods::poisoned(ptr)); +#endif + } + #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) Rooted **stack, *prev; #endif @@ -455,6 +666,19 @@ class Rooted : public RootedBase Rooted(const Rooted &) MOZ_DELETE; }; +#if !(defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)) +// Defined in vm/String.h. +template <> +class Rooted; +#endif + +template +bool +Return::operator==(const Rooted &other) +{ + return ptr_ == other.get(); +} + typedef Rooted RootedObject; typedef Rooted RootedFunction; typedef Rooted RootedScript; @@ -550,10 +774,6 @@ MutableHandle::MutableHandle(js::Rooted *root, ptr = root->address(); } -JS_FRIEND_API(void) EnterAssertNoGCScope(); -JS_FRIEND_API(void) LeaveAssertNoGCScope(); -JS_FRIEND_API(bool) InNoGCScope(); - /* * The scoped guard object AutoAssertNoGC forces the GC to assert if a GC is * attempted while the guard object is live. If you have a GC-unsafe operation @@ -581,15 +801,11 @@ public: /* * AssertCanGC will assert if it is called inside of an AutoAssertNoGC region. */ -#ifdef DEBUG JS_ALWAYS_INLINE void AssertCanGC() { JS_ASSERT(!InNoGCScope()); } -#else -# define AssertCanGC() -#endif #if defined(DEBUG) && defined(JS_GC_ZEAL) && defined(JSGC_ROOT_ANALYSIS) && !defined(JS_THREADSAFE) extern void diff --git a/scripting/javascript/spidermonkey-win32/include/gc/Statistics.h b/scripting/javascript/spidermonkey-win32/include/gc/Statistics.h deleted file mode 100644 index e0434b4d61..0000000000 --- a/scripting/javascript/spidermonkey-win32/include/gc/Statistics.h +++ /dev/null @@ -1,193 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef jsgc_statistics_h___ -#define jsgc_statistics_h___ - -#include - -#include "jsfriendapi.h" -#include "jspubtd.h" -#include "jsutil.h" - -struct JSCompartment; - -namespace js { -namespace gcstats { - -enum Phase { - PHASE_GC_BEGIN, - PHASE_WAIT_BACKGROUND_THREAD, - PHASE_PURGE, - PHASE_MARK, - PHASE_MARK_DISCARD_CODE, - PHASE_MARK_ROOTS, - PHASE_MARK_TYPES, - PHASE_MARK_DELAYED, - PHASE_MARK_WEAK, - PHASE_MARK_GRAY, - PHASE_MARK_GRAY_WEAK, - PHASE_FINALIZE_START, - PHASE_SWEEP, - PHASE_SWEEP_ATOMS, - PHASE_SWEEP_COMPARTMENTS, - PHASE_SWEEP_TABLES, - PHASE_SWEEP_OBJECT, - PHASE_SWEEP_STRING, - PHASE_SWEEP_SCRIPT, - PHASE_SWEEP_SHAPE, - PHASE_SWEEP_IONCODE, - PHASE_SWEEP_DISCARD_CODE, - PHASE_DISCARD_ANALYSIS, - PHASE_DISCARD_TI, - PHASE_FREE_TI_ARENA, - PHASE_SWEEP_TYPES, - PHASE_CLEAR_SCRIPT_ANALYSIS, - PHASE_FINALIZE_END, - PHASE_DESTROY, - PHASE_GC_END, - - PHASE_LIMIT -}; - -enum Stat { - STAT_NEW_CHUNK, - STAT_DESTROY_CHUNK, - - STAT_LIMIT -}; - -class StatisticsSerializer; - -struct Statistics { - Statistics(JSRuntime *rt); - ~Statistics(); - - void beginPhase(Phase phase); - void endPhase(Phase phase); - - void beginSlice(int collectedCount, int compartmentCount, gcreason::Reason reason); - void endSlice(); - - void reset(const char *reason) { slices.back().resetReason = reason; } - void nonincremental(const char *reason) { nonincrementalReason = reason; } - - void count(Stat s) { - JS_ASSERT(s < STAT_LIMIT); - counts[s]++; - } - - int64_t beginSCC(); - void endSCC(unsigned scc, int64_t start); - - jschar *formatMessage(); - jschar *formatJSON(uint64_t timestamp); - - private: - JSRuntime *runtime; - - int64_t startupTime; - - FILE *fp; - bool fullFormat; - - /* - * GCs can't really nest, but a second GC can be triggered from within the - * JSGC_END callback. - */ - int gcDepth; - - int collectedCount; - int compartmentCount; - const char *nonincrementalReason; - - struct SliceData { - SliceData(gcreason::Reason reason, int64_t start, size_t startFaults) - : reason(reason), resetReason(NULL), start(start), startFaults(startFaults) - { - PodArrayZero(phaseTimes); - } - - gcreason::Reason reason; - const char *resetReason; - int64_t start, end; - size_t startFaults, endFaults; - int64_t phaseTimes[PHASE_LIMIT]; - - int64_t duration() const { return end - start; } - }; - - Vector slices; - - /* Most recent time when the given phase started. */ - int64_t phaseStartTimes[PHASE_LIMIT]; - - /* Total time in a given phase for this GC. */ - int64_t phaseTimes[PHASE_LIMIT]; - - /* Total time in a given phase over all GCs. */ - int64_t phaseTotals[PHASE_LIMIT]; - - /* Number of events of this type for this GC. */ - unsigned int counts[STAT_LIMIT]; - - /* Allocated space before the GC started. */ - size_t preBytes; - - /* Sweep times for SCCs of compartments. */ - Vector sccTimes; - - void beginGC(); - void endGC(); - - void gcDuration(int64_t *total, int64_t *maxPause); - void sccDurations(int64_t *total, int64_t *maxPause); - void printStats(); - bool formatData(StatisticsSerializer &ss, uint64_t timestamp); - - double computeMMU(int64_t resolution); -}; - -struct AutoGCSlice { - AutoGCSlice(Statistics &stats, int collectedCount, int compartmentCount, gcreason::Reason reason - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : stats(stats) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; - stats.beginSlice(collectedCount, compartmentCount, reason); - } - ~AutoGCSlice() { stats.endSlice(); } - - Statistics &stats; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - -struct AutoPhase { - AutoPhase(Statistics &stats, Phase phase JS_GUARD_OBJECT_NOTIFIER_PARAM) - : stats(stats), phase(phase) { JS_GUARD_OBJECT_NOTIFIER_INIT; stats.beginPhase(phase); } - ~AutoPhase() { stats.endPhase(phase); } - - Statistics &stats; - Phase phase; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - -struct AutoSCC { - AutoSCC(Statistics &stats, unsigned scc JS_GUARD_OBJECT_NOTIFIER_PARAM) - : stats(stats), scc(scc) { JS_GUARD_OBJECT_NOTIFIER_INIT; start = stats.beginSCC(); } - ~AutoSCC() { stats.endSCC(scc, start); } - - Statistics &stats; - unsigned scc; - int64_t start; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - -} /* namespace gcstats */ -} /* namespace js */ - -#endif /* jsgc_statistics_h___ */ diff --git a/scripting/javascript/spidermonkey-win32/include/gc/StoreBuffer.h b/scripting/javascript/spidermonkey-win32/include/gc/StoreBuffer.h deleted file mode 100644 index 9240e93f10..0000000000 --- a/scripting/javascript/spidermonkey-win32/include/gc/StoreBuffer.h +++ /dev/null @@ -1,398 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifdef JSGC_GENERATIONAL -#ifndef jsgc_storebuffer_h___ -#define jsgc_storebuffer_h___ - -#include "jsgc.h" -#include "jsalloc.h" - -#include "gc/Marking.h" - -namespace js { -namespace gc { - -/* - * Note: this is a stub Nursery that does not actually contain a heap, just a - * set of pointers which are "inside" the nursery to implement verification. - */ -class Nursery -{ - HashSet, SystemAllocPolicy> nursery; - - public: - Nursery() : nursery() {} - - bool enable() { - if (!nursery.initialized()) - return nursery.init(); - return true; - } - - void disable() { - if (!nursery.initialized()) - return; - nursery.finish(); - } - - bool isInside(void *cell) const { - JS_ASSERT((uintptr_t(cell) & 0x3) == 0); - return nursery.initialized() && nursery.has(cell); - } - - void insertPointer(void *cell) { - nursery.putNew(cell); - } -}; - -/* - * BufferableRef represents an abstract reference for use in the generational - * GC's remembered set. Entries in the store buffer that cannot be represented - * with the simple pointer-to-a-pointer scheme must derive from this class and - * use the generic store buffer interface. - */ -class BufferableRef -{ - public: - virtual bool match(void *location) = 0; - virtual void mark(JSTracer *trc) = 0; -}; - -/* - * HashKeyRef represents a reference to a HashTable key. Manual HashTable - * barriers should should instantiate this template with their own table/key - * type to insert into the generic buffer with putGeneric. - */ -template -class HashKeyRef : public BufferableRef -{ - Map *map; - Key key; - - typedef typename Map::Ptr Ptr; - - public: - HashKeyRef(Map *m, const Key &k) : map(m), key(k) {} - - bool match(void *location) { - Ptr p = map->lookup(key); - if (!p) - return false; - return &p->key == location; - } - - void mark(JSTracer *trc) {} -}; - -/* - * The StoreBuffer observes all writes that occur in the system and performs - * efficient filtering of them to derive a remembered set for nursery GC. - */ -class StoreBuffer -{ - /* TODO: profile to find the ideal size for these. */ - static const size_t ValueBufferSize = 1 * 1024 * sizeof(Value *); - static const size_t CellBufferSize = 2 * 1024 * sizeof(Cell **); - static const size_t SlotBufferSize = 2 * 1024 * (sizeof(JSObject *) + sizeof(uint32_t)); - static const size_t RelocValueBufferSize = 1 * 1024 * sizeof(Value *); - static const size_t RelocCellBufferSize = 1 * 1024 * sizeof(Cell **); - static const size_t GenericBufferSize = 1 * 1024 * sizeof(int); - static const size_t TotalSize = ValueBufferSize + CellBufferSize + - SlotBufferSize + RelocValueBufferSize + RelocCellBufferSize + - GenericBufferSize; - - typedef HashSet, SystemAllocPolicy> EdgeSet; - - /* - * This buffer holds only a single type of edge. Using this buffer is more - * efficient than the generic buffer when many writes will be to the same - * type of edge: e.g. Value or Cell*. - */ - template - class MonoTypeBuffer - { - friend class StoreBuffer; - - StoreBuffer *owner; - Nursery *nursery; - - T *base; /* Pointer to the start of the buffer. */ - T *pos; /* Pointer to the current insertion position. */ - T *top; /* Pointer to one element after the end. */ - - MonoTypeBuffer(StoreBuffer *owner, Nursery *nursery) - : owner(owner), nursery(nursery), base(NULL), pos(NULL), top(NULL) - {} - - MonoTypeBuffer &operator=(const MonoTypeBuffer& other) MOZ_DELETE; - - bool enable(uint8_t *region, size_t len); - void disable(); - - bool isEmpty() const { return pos == base; } - bool isFull() const { JS_ASSERT(pos <= top); return pos == top; } - - /* Compaction algorithms. */ - void compactNotInSet(); - - /* - * Attempts to reduce the usage of the buffer by removing unnecessary - * entries. - */ - virtual void compact(); - - /* Add one item to the buffer. */ - void put(const T &v); - - /* For verification. */ - bool accumulateEdges(EdgeSet &edges); - }; - - /* - * Overrides the MonoTypeBuffer to support pointers that may be moved in - * memory outside of the GC's control. - */ - template - class RelocatableMonoTypeBuffer : public MonoTypeBuffer - { - friend class StoreBuffer; - - RelocatableMonoTypeBuffer(StoreBuffer *owner, Nursery *nursery) - : MonoTypeBuffer(owner, nursery) - {} - - /* Override compaction to filter out removed items. */ - void compactMoved(); - virtual void compact(); - - /* Record a removal from the buffer. */ - void unput(const T &v); - }; - - class GenericBuffer - { - friend class StoreBuffer; - - StoreBuffer *owner; - Nursery *nursery; - - uint8_t *base; /* Pointer to start of buffer. */ - uint8_t *pos; /* Pointer to current buffer position. */ - uint8_t *top; /* Pointer to one past the last entry. */ - - GenericBuffer(StoreBuffer *owner, Nursery *nursery) - : owner(owner), nursery(nursery) - {} - - GenericBuffer &operator=(const GenericBuffer& other) MOZ_DELETE; - - bool enable(uint8_t *region, size_t len); - void disable(); - - /* Check if a pointer is present in the buffer. */ - bool containsEdge(void *location) const; - - template - void put(const T &t) { - /* Check if we have been enabled. */ - if (!pos) - return; - - /* Check for overflow. */ - if (top - pos < (unsigned)(sizeof(unsigned) + sizeof(T))) { - owner->setOverflowed(); - return; - } - - *((unsigned *)pos) = sizeof(T); - pos += sizeof(unsigned); - - T *p = (T *)pos; - new (p) T(t); - pos += sizeof(T); - } - }; - - class CellPtrEdge - { - friend class StoreBuffer; - friend class StoreBuffer::MonoTypeBuffer; - friend class StoreBuffer::RelocatableMonoTypeBuffer; - - Cell **edge; - - CellPtrEdge(Cell **v) : edge(v) {} - bool operator==(const CellPtrEdge &other) const { return edge == other.edge; } - bool operator!=(const CellPtrEdge &other) const { return edge != other.edge; } - - void *location() const { return (void *)edge; } - - bool inRememberedSet(Nursery *n) { - return !n->isInside(edge) && n->isInside(*edge); - } - - bool isNullEdge() const { - return !*edge; - } - - CellPtrEdge tagged() const { return CellPtrEdge((Cell **)(uintptr_t(edge) | 1)); } - CellPtrEdge untagged() const { return CellPtrEdge((Cell **)(uintptr_t(edge) & ~1)); } - bool isTagged() const { return bool(uintptr_t(edge) & 1); } - }; - - class ValueEdge - { - friend class StoreBuffer; - friend class StoreBuffer::MonoTypeBuffer; - friend class StoreBuffer::RelocatableMonoTypeBuffer; - - Value *edge; - - ValueEdge(Value *v) : edge(v) {} - bool operator==(const ValueEdge &other) const { return edge == other.edge; } - bool operator!=(const ValueEdge &other) const { return edge != other.edge; } - - void *deref() const { return edge->isGCThing() ? edge->toGCThing() : NULL; } - void *location() const { return (void *)edge; } - - bool inRememberedSet(Nursery *n) { - return !n->isInside(edge) && n->isInside(deref()); - } - - bool isNullEdge() const { - return !deref(); - } - - ValueEdge tagged() const { return ValueEdge((Value *)(uintptr_t(edge) | 1)); } - ValueEdge untagged() const { return ValueEdge((Value *)(uintptr_t(edge) & ~1)); } - bool isTagged() const { return bool(uintptr_t(edge) & 1); } - }; - - struct SlotEdge - { - friend class StoreBuffer; - friend class StoreBuffer::MonoTypeBuffer; - - JSObject *object; - uint32_t offset; - - SlotEdge(JSObject *object, uint32_t offset) : object(object), offset(offset) {} - - bool operator==(const SlotEdge &other) const { - return object == other.object && offset == other.offset; - } - - bool operator!=(const SlotEdge &other) const { - return object != other.object || offset != other.offset; - } - - HeapSlot *slotLocation() const { - if (object->isDenseArray()) { - if (offset >= object->getDenseArrayInitializedLength()) - return NULL; - return (HeapSlot *)&object->getDenseArrayElement(offset); - } - if (offset >= object->slotSpan()) - return NULL; - return &object->getSlotRef(offset); - } - - void *deref() const { - HeapSlot *loc = slotLocation(); - return (loc && loc->isGCThing()) ? loc->toGCThing() : NULL; - } - - void *location() const { - return (void *)slotLocation(); - } - - bool inRememberedSet(Nursery *n) { - return !n->isInside(object) && n->isInside(deref()); - } - - bool isNullEdge() const { - return !deref(); - } - }; - - MonoTypeBuffer bufferVal; - MonoTypeBuffer bufferCell; - MonoTypeBuffer bufferSlot; - RelocatableMonoTypeBuffer bufferRelocVal; - RelocatableMonoTypeBuffer bufferRelocCell; - GenericBuffer bufferGeneric; - - Nursery *nursery; - - void *buffer; - - bool overflowed; - bool enabled; - - /* For the verifier. */ - EdgeSet edgeSet; - - /* For use by our owned buffers. */ - void setOverflowed() { overflowed = true; } - - public: - StoreBuffer(Nursery *n) - : bufferVal(this, n), bufferCell(this, n), bufferSlot(this, n), - bufferRelocVal(this, n), bufferRelocCell(this, n), bufferGeneric(this, n), - nursery(n), buffer(NULL), overflowed(false), enabled(false) - {} - - bool enable(); - void disable(); - bool isEnabled() { return enabled; } - - /* Get the overflowed status. */ - bool hasOverflowed() const { return overflowed; } - - /* Insert a single edge into the buffer/remembered set. */ - void putValue(Value *v) { - bufferVal.put(v); - } - void putCell(Cell **o) { - bufferCell.put(o); - } - void putSlot(JSObject *obj, uint32_t slot) { - bufferSlot.put(SlotEdge(obj, slot)); - } - - /* Insert or update a single edge in the Relocatable buffer. */ - void putRelocatableValue(Value *v) { - bufferRelocVal.put(v); - } - void putRelocatableCell(Cell **c) { - bufferRelocCell.put(c); - } - void removeRelocatableValue(Value *v) { - bufferRelocVal.unput(v); - } - void removeRelocatableCell(Cell **c) { - bufferRelocCell.unput(c); - } - - /* Insert an entry into the generic buffer. */ - template - void putGeneric(const T &t) { - bufferGeneric.put(t); - } - - /* For the verifier. */ - bool coalesceForVerification(); - void releaseVerificationData(); - bool containsEdgeAt(void *loc) const; -}; - -} /* namespace gc */ -} /* namespace js */ - -#endif /* jsgc_storebuffer_h___ */ -#endif /* JSGC_GENERATIONAL */ diff --git a/scripting/javascript/spidermonkey-win32/include/js.msg b/scripting/javascript/spidermonkey-win32/include/js.msg index 92d4519c6f..bb2f65627c 100644 --- a/scripting/javascript/spidermonkey-win32/include/js.msg +++ b/scripting/javascript/spidermonkey-win32/include/js.msg @@ -375,3 +375,5 @@ MSG_DEF(JSMSG_MUST_REPORT_UNDEFINED, 321, 0, JSEXN_TYPEERR, "proxy must report MSG_DEF(JSMSG_CANT_SET_NW_NC, 322, 0, JSEXN_TYPEERR, "proxy can't successfully set a non-writable, non-configurable property") MSG_DEF(JSMSG_CANT_SET_WO_SETTER, 323, 0, JSEXN_TYPEERR, "proxy can't succesfully set an accessor property without a setter") MSG_DEF(JSMSG_DEBUG_BAD_REFERENT, 324, 2, JSEXN_TYPEERR, "{0} does not refer to {1}") +MSG_DEF(JSMSG_DEBUG_WRAPPER_IN_WAY, 325, 2, JSEXN_TYPEERR, "{0} is a wrapper around {1}, but a direct reference is required") +MSG_DEF(JSMSG_UNWRAP_DENIED, 326, 0, JSEXN_ERR, "permission denied to unwrap object") diff --git a/scripting/javascript/spidermonkey-win32/include/js/HashTable.h b/scripting/javascript/spidermonkey-win32/include/js/HashTable.h index bef5851548..d5710aed71 100644 --- a/scripting/javascript/spidermonkey-win32/include/js/HashTable.h +++ b/scripting/javascript/spidermonkey-win32/include/js/HashTable.h @@ -5,74 +5,621 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef jshashtable_h_ -#define jshashtable_h_ +#ifndef js_HashTable_h__ +#define js_HashTable_h__ +#include "js/TemplateLib.h" +#include "js/Utility.h" #include "mozilla/Attributes.h" -#include "TemplateLib.h" -#include "Utility.h" - namespace js { class TempAllocPolicy; +template struct DefaultHasher; +template class HashMapEntry; +namespace detail { + template class HashTableEntry; + template class HashTable; +} /*****************************************************************************/ +// A JS-friendly, STL-like container providing a hash-based map from keys to +// values. In particular, HashMap calls constructors and destructors of all +// objects added so non-PODs may be used safely. +// +// Key/Value requirements: +// - movable, destructible, assignable +// HashPolicy requirements: +// - see Hash Policy section below +// AllocPolicy: +// - see jsalloc.h +// +// Note: +// - HashMap is not reentrant: Key/Value/HashPolicy/AllocPolicy members +// called by HashMap must not call back into the same HashMap object. +// - Due to the lack of exception handling, the user must call |init()|. +template , + class AllocPolicy = TempAllocPolicy> +class HashMap +{ + typedef HashMapEntry TableEntry; + + struct MapHashPolicy : HashPolicy + { + typedef Key KeyType; + static const Key &getKey(TableEntry &e) { return e.key; } + static void setKey(TableEntry &e, Key &k) { const_cast(e.key) = k; } + }; + + typedef detail::HashTable Impl; + Impl impl; + + public: + typedef typename HashPolicy::Lookup Lookup; + typedef TableEntry Entry; + + // HashMap construction is fallible (due to OOM); thus the user must call + // init after constructing a HashMap and check the return value. + HashMap(AllocPolicy a = AllocPolicy()) : impl(a) {} + bool init(uint32_t len = 16) { return impl.init(len); } + bool initialized() const { return impl.initialized(); } + + // Return whether the given lookup value is present in the map. E.g.: + // + // typedef HashMap HM; + // HM h; + // if (HM::Ptr p = h.lookup(3)) { + // const HM::Entry &e = *p; // p acts like a pointer to Entry + // assert(p->key == 3); // Entry contains the key + // char val = p->value; // and value + // } + // + // Also see the definition of Ptr in HashTable above (with T = Entry). + typedef typename Impl::Ptr Ptr; + Ptr lookup(const Lookup &l) const { return impl.lookup(l); } + + // Assuming |p.found()|, remove |*p|. + void remove(Ptr p) { impl.remove(p); } + + // Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient + // insertion of Key |k| (where |HashPolicy::match(k,l) == true|) using + // |add(p,k,v)|. After |add(p,k,v)|, |p| points to the new Entry. E.g.: + // + // typedef HashMap HM; + // HM h; + // HM::AddPtr p = h.lookupForAdd(3); + // if (!p) { + // if (!h.add(p, 3, 'a')) + // return false; + // } + // const HM::Entry &e = *p; // p acts like a pointer to Entry + // assert(p->key == 3); // Entry contains the key + // char val = p->value; // and value + // + // Also see the definition of AddPtr in HashTable above (with T = Entry). + // + // N.B. The caller must ensure that no mutating hash table operations + // occur between a pair of |lookupForAdd| and |add| calls. To avoid + // looking up the key a second time, the caller may use the more efficient + // relookupOrAdd method. This method reuses part of the hashing computation + // to more efficiently insert the key if it has not been added. For + // example, a mutation-handling version of the previous example: + // + // HM::AddPtr p = h.lookupForAdd(3); + // if (!p) { + // call_that_may_mutate_h(); + // if (!h.relookupOrAdd(p, 3, 'a')) + // return false; + // } + // const HM::Entry &e = *p; + // assert(p->key == 3); + // char val = p->value; + typedef typename Impl::AddPtr AddPtr; + AddPtr lookupForAdd(const Lookup &l) const { + return impl.lookupForAdd(l); + } + + template + bool add(AddPtr &p, const KeyInput &k, const ValueInput &v) { + Entry e(k, v); + return impl.add(p, Move(e)); + } + + bool add(AddPtr &p, const Key &k) { + Entry e(k, Value()); + return impl.add(p, Move(e)); + } + + template + bool relookupOrAdd(AddPtr &p, const KeyInput &k, const ValueInput &v) { + Entry e(k, v); + return impl.relookupOrAdd(p, k, Move(e)); + } + + // |all()| returns a Range containing |count()| elements. E.g.: + // + // typedef HashMap HM; + // HM h; + // for (HM::Range r = h.all(); !r.empty(); r.popFront()) + // char c = r.front().value; + // + // Also see the definition of Range in HashTable above (with T = Entry). + typedef typename Impl::Range Range; + Range all() const { return impl.all(); } + + // Typedef for the enumeration class. An Enum may be used to examine and + // remove table entries: + // + // typedef HashMap HM; + // HM s; + // for (HM::Enum e(s); !e.empty(); e.popFront()) + // if (e.front().value == 'l') + // e.removeFront(); + // + // Table resize may occur in Enum's destructor. Also see the definition of + // Enum in HashTable above (with T = Entry). + typedef typename Impl::Enum Enum; + + // Remove all entries. This does not shrink the table. For that consider + // using the finish() method. + void clear() { impl.clear(); } + + // Remove all the entries and release all internal buffers. The map must + // be initialized again before any use. + void finish() { impl.finish(); } + + // Does the table contain any entries? + bool empty() const { return impl.empty(); } + + // Number of live elements in the map. + uint32_t count() const { return impl.count(); } + + // Total number of allocation in the dynamic table. Note: resize will + // happen well before count() == capacity(). + size_t capacity() const { return impl.capacity(); } + + // Don't just call |impl.sizeOfExcludingThis()| because there's no + // guarantee that |impl| is the first field in HashMap. + size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const { + return impl.sizeOfExcludingThis(mallocSizeOf); + } + size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const { + return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf); + } + + // If |generation()| is the same before and after a HashMap operation, + // pointers into the table remain valid. + unsigned generation() const { return impl.generation(); } + + /************************************************** Shorthand operations */ + + bool has(const Lookup &l) const { + return impl.lookup(l) != NULL; + } + + // Overwrite existing value with v. Return false on oom. + template + bool put(const KeyInput &k, const ValueInput &v) { + AddPtr p = lookupForAdd(k); + if (p) { + p->value = v; + return true; + } + return add(p, k, v); + } + + // Like put, but assert that the given key is not already present. + template + bool putNew(const KeyInput &k, const ValueInput &v) { + Entry e(k, v); + return impl.putNew(k, Move(e)); + } + + // Add (k,defaultValue) if |k| is not found. Return a false-y Ptr on oom. + Ptr lookupWithDefault(const Key &k, const Value &defaultValue) { + AddPtr p = lookupForAdd(k); + if (p) + return p; + (void)add(p, k, defaultValue); // p is left false-y on oom. + return p; + } + + // Remove if present. + void remove(const Lookup &l) { + if (Ptr p = lookup(l)) + remove(p); + } + + private: + // Not implicitly copyable (expensive). May add explicit |clone| later. + HashMap(const HashMap &hm) MOZ_DELETE; + HashMap &operator=(const HashMap &hm) MOZ_DELETE; + + friend class Impl::Enum; + + typedef typename tl::StaticAssert::result>::result keyAssert; + typedef typename tl::StaticAssert::result>::result valAssert; +}; + +/*****************************************************************************/ + +// A JS-friendly, STL-like container providing a hash-based set of values. In +// particular, HashSet calls constructors and destructors of all objects added +// so non-PODs may be used safely. +// +// T requirements: +// - movable, destructible, assignable +// HashPolicy requirements: +// - see Hash Policy section below +// AllocPolicy: +// - see jsalloc.h +// +// Note: +// - HashSet is not reentrant: T/HashPolicy/AllocPolicy members called by +// HashSet must not call back into the same HashSet object. +// - Due to the lack of exception handling, the user must call |init()|. +template , + class AllocPolicy = TempAllocPolicy> +class HashSet +{ + struct SetOps : HashPolicy + { + typedef T KeyType; + static const KeyType &getKey(const T &t) { return t; } + static void setKey(T &t, KeyType &k) { t = k; } + }; + + typedef detail::HashTable Impl; + Impl impl; + + public: + typedef typename HashPolicy::Lookup Lookup; + typedef T Entry; + + // HashSet construction is fallible (due to OOM); thus the user must call + // init after constructing a HashSet and check the return value. + HashSet(AllocPolicy a = AllocPolicy()) : impl(a) {} + bool init(uint32_t len = 16) { return impl.init(len); } + bool initialized() const { return impl.initialized(); } + + // Return whether the given lookup value is present in the map. E.g.: + // + // typedef HashSet HS; + // HS h; + // if (HS::Ptr p = h.lookup(3)) { + // assert(*p == 3); // p acts like a pointer to int + // } + // + // Also see the definition of Ptr in HashTable above. + typedef typename Impl::Ptr Ptr; + Ptr lookup(const Lookup &l) const { return impl.lookup(l); } + + // Assuming |p.found()|, remove |*p|. + void remove(Ptr p) { impl.remove(p); } + + // Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient + // insertion of T value |t| (where |HashPolicy::match(t,l) == true|) using + // |add(p,t)|. After |add(p,t)|, |p| points to the new element. E.g.: + // + // typedef HashSet HS; + // HS h; + // HS::AddPtr p = h.lookupForAdd(3); + // if (!p) { + // if (!h.add(p, 3)) + // return false; + // } + // assert(*p == 3); // p acts like a pointer to int + // + // Also see the definition of AddPtr in HashTable above. + // + // N.B. The caller must ensure that no mutating hash table operations + // occur between a pair of |lookupForAdd| and |add| calls. To avoid + // looking up the key a second time, the caller may use the more efficient + // relookupOrAdd method. This method reuses part of the hashing computation + // to more efficiently insert the key if it has not been added. For + // example, a mutation-handling version of the previous example: + // + // HS::AddPtr p = h.lookupForAdd(3); + // if (!p) { + // call_that_may_mutate_h(); + // if (!h.relookupOrAdd(p, 3, 3)) + // return false; + // } + // assert(*p == 3); + // + // Note that relookupOrAdd(p,l,t) performs Lookup using |l| and adds the + // entry |t|, where the caller ensures match(l,t). + typedef typename Impl::AddPtr AddPtr; + AddPtr lookupForAdd(const Lookup &l) const { return impl.lookupForAdd(l); } + + bool add(AddPtr &p, const T &t) { return impl.add(p, t); } + + bool relookupOrAdd(AddPtr &p, const Lookup &l, const T &t) { + return impl.relookupOrAdd(p, l, t); + } + + // |all()| returns a Range containing |count()| elements: + // + // typedef HashSet HS; + // HS h; + // for (HS::Range r = h.all(); !r.empty(); r.popFront()) + // int i = r.front(); + // + // Also see the definition of Range in HashTable above. + typedef typename Impl::Range Range; + Range all() const { return impl.all(); } + + // Typedef for the enumeration class. An Enum may be used to examine and + // remove table entries: + // + // typedef HashSet HS; + // HS s; + // for (HS::Enum e(s); !e.empty(); e.popFront()) + // if (e.front() == 42) + // e.removeFront(); + // + // Table resize may occur in Enum's destructor. Also see the definition of + // Enum in HashTable above. + typedef typename Impl::Enum Enum; + + // Remove all entries. This does not shrink the table. For that consider + // using the finish() method. + void clear() { impl.clear(); } + + // Remove all the entries and release all internal buffers. The set must + // be initialized again before any use. + void finish() { impl.finish(); } + + // Does the table contain any entries? + bool empty() const { return impl.empty(); } + + // Number of live elements in the map. + uint32_t count() const { return impl.count(); } + + // Total number of allocation in the dynamic table. Note: resize will + // happen well before count() == capacity(). + size_t capacity() const { return impl.capacity(); } + + // Don't just call |impl.sizeOfExcludingThis()| because there's no + // guarantee that |impl| is the first field in HashSet. + size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const { + return impl.sizeOfExcludingThis(mallocSizeOf); + } + size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const { + return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf); + } + + // If |generation()| is the same before and after a HashSet operation, + // pointers into the table remain valid. + unsigned generation() const { return impl.generation(); } + + /************************************************** Shorthand operations */ + + bool has(const Lookup &l) const { + return impl.lookup(l) != NULL; + } + + // Overwrite existing value with v. Return false on oom. + bool put(const T &t) { + AddPtr p = lookupForAdd(t); + return p ? true : add(p, t); + } + + // Like put, but assert that the given key is not already present. + bool putNew(const T &t) { + return impl.putNew(t, t); + } + + bool putNew(const Lookup &l, const T &t) { + return impl.putNew(l, t); + } + + void remove(const Lookup &l) { + if (Ptr p = lookup(l)) + remove(p); + } + + private: + // Not implicitly copyable (expensive). May add explicit |clone| later. + HashSet(const HashSet &hs) MOZ_DELETE; + HashSet &operator=(const HashSet &hs) MOZ_DELETE; + + friend class Impl::Enum; + + typedef typename tl::StaticAssert::result>::result _; +}; + +/*****************************************************************************/ + +// Hash Policy +// +// A hash policy P for a hash table with key-type Key must provide: +// - a type |P::Lookup| to use to lookup table entries; +// - a static member function |P::hash| with signature +// +// static js::HashNumber hash(Lookup) +// +// to use to hash the lookup type; and +// - a static member function |P::match| with signature +// +// static bool match(Key, Lookup) +// +// to use to test equality of key and lookup values. +// +// Normally, Lookup = Key. In general, though, different values and types of +// values can be used to lookup and store. If a Lookup value |l| is != to the +// added Key value |k|, the user must ensure that |P::match(k,l)|. E.g.: +// +// js::HashSet::AddPtr p = h.lookup(l); +// if (!p) { +// assert(P::match(k, l)); // must hold +// h.add(p, k); +// } + +// Pointer hashing policy that strips the lowest zeroBits when calculating the +// hash to improve key distribution. +template +struct PointerHasher +{ + typedef Key Lookup; + static HashNumber hash(const Lookup &l) { + size_t word = reinterpret_cast(l) >> zeroBits; + JS_STATIC_ASSERT(sizeof(HashNumber) == 4); +#if JS_BYTES_PER_WORD == 4 + return HashNumber(word); +#else + JS_STATIC_ASSERT(sizeof word == 8); + return HashNumber((word >> 32) ^ word); +#endif + } + static bool match(const Key &k, const Lookup &l) { + return k == l; + } +}; + +// Default hash policy: just use the 'lookup' value. This of course only +// works if the lookup value is integral. HashTable applies ScrambleHashCode to +// the result of the 'hash' which means that it is 'ok' if the lookup value is +// not well distributed over the HashNumber domain. +template +struct DefaultHasher +{ + typedef Key Lookup; + static HashNumber hash(const Lookup &l) { + // Hash if can implicitly cast to hash number type. + return l; + } + static bool match(const Key &k, const Lookup &l) { + // Use builtin or overloaded operator==. + return k == l; + } +}; + +// Specialize hashing policy for pointer types. It assumes that the type is +// at least word-aligned. For types with smaller size use PointerHasher. +template +struct DefaultHasher : PointerHasher::result> +{}; + +/*****************************************************************************/ + +// Both HashMap and HashSet are implemented by a single HashTable that is even +// more heavily parameterized than the other two. This leaves HashTable gnarly +// and extremely coupled to HashMap and HashSet; thus code should not use +// HashTable directly. + +template +class HashMapEntry +{ + template friend class detail::HashTable; + template friend class detail::HashTableEntry; + + HashMapEntry(const HashMapEntry &) MOZ_DELETE; + void operator=(const HashMapEntry &) MOZ_DELETE; + + public: + template + HashMapEntry(const KeyInput &k, const ValueInput &v) : key(k), value(v) {} + + HashMapEntry(MoveRef rhs) + : key(Move(rhs->key)), value(Move(rhs->value)) { } + + const Key key; + Value value; +}; + +namespace tl { + +template +struct IsPodType > { + static const bool result = IsPodType::result; +}; + +template +struct IsPodType > +{ + static const bool result = IsPodType::result && IsPodType::result; +}; + +} // namespace tl + namespace detail { template class HashTable; template -class HashTableEntry { - HashNumber keyHash; - +class HashTableEntry +{ + template friend class HashTable; typedef typename tl::StripConst::result NonConstT; + HashNumber keyHash; + mozilla::AlignedStorage2 mem; + static const HashNumber sFreeKey = 0; static const HashNumber sRemovedKey = 1; static const HashNumber sCollisionBit = 1; - template friend class HashTable; + // Assumed by calloc in createTable. + JS_STATIC_ASSERT(sFreeKey == 0); static bool isLiveHash(HashNumber hash) { return hash > sRemovedKey; } + HashTableEntry(const HashTableEntry &) MOZ_DELETE; + void operator=(const HashTableEntry &) MOZ_DELETE; + ~HashTableEntry() MOZ_DELETE; + public: - HashTableEntry() : keyHash(0), t() {} - HashTableEntry(MoveRef rhs) : keyHash(rhs->keyHash), t(Move(rhs->t)) { } - void operator=(const HashTableEntry &rhs) { keyHash = rhs.keyHash; t = rhs.t; } - void operator=(MoveRef rhs) { keyHash = rhs->keyHash; t = Move(rhs->t); } + // NB: HashTableEntry is treated as a POD: no constructor or destructor calls. - NonConstT t; - - bool isFree() const { return keyHash == sFreeKey; } - void setFree() { keyHash = sFreeKey; t = T(); } - bool isRemoved() const { return keyHash == sRemovedKey; } - void setRemoved() { keyHash = sRemovedKey; t = T(); } - bool isLive() const { return isLiveHash(keyHash); } - void setLive(HashNumber hn) { JS_ASSERT(isLiveHash(hn)); keyHash = hn; } - - void setCollision() { JS_ASSERT(isLive()); keyHash |= sCollisionBit; } - void setCollision(HashNumber collisionBit) { - JS_ASSERT(isLive()); keyHash |= collisionBit; + void destroyIfLive() { + if (isLive()) + mem.addr()->~T(); + } + + void destroy() { + JS_ASSERT(isLive()); + mem.addr()->~T(); + } + + void swap(HashTableEntry *other) { + Swap(keyHash, other->keyHash); + Swap(mem, other->mem); + } + + T &get() { JS_ASSERT(isLive()); return *mem.addr(); } + + bool isFree() const { return keyHash == sFreeKey; } + void clearLive() { JS_ASSERT(isLive()); keyHash = sFreeKey; mem.addr()->~T(); } + void clear() { if (isLive()) mem.addr()->~T(); keyHash = sFreeKey; } + bool isRemoved() const { return keyHash == sRemovedKey; } + void removeLive() { JS_ASSERT(isLive()); keyHash = sRemovedKey; mem.addr()->~T(); } + bool isLive() const { return isLiveHash(keyHash); } + void setCollision() { JS_ASSERT(isLive()); keyHash |= sCollisionBit; } + void setCollision(HashNumber bit) { JS_ASSERT(isLive()); keyHash |= bit; } + void unsetCollision() { keyHash &= ~sCollisionBit; } + bool hasCollision() const { return keyHash & sCollisionBit; } + bool matchHash(HashNumber hn) { return (keyHash & ~sCollisionBit) == hn; } + HashNumber getKeyHash() const { return keyHash & ~sCollisionBit; } + + template + void setLive(HashNumber hn, const U &u) + { + JS_ASSERT(!isLive()); + keyHash = hn; + new(mem.addr()) T(u); + JS_ASSERT(isLive()); } - void unsetCollision() { keyHash &= ~sCollisionBit; } - bool hasCollision() const { return keyHash & sCollisionBit; } - bool matchHash(HashNumber hn) { return (keyHash & ~sCollisionBit) == hn; } - HashNumber getKeyHash() const { JS_ASSERT(!hasCollision()); return keyHash; } }; -/* - * js::detail::HashTable is an implementation detail of the js::HashMap and - * js::HashSet templates. For js::Hash{Map,Set} API documentation and examples, - * skip to the end of the detail namespace. - */ - -/* Reusable implementation of HashMap and HashSet. */ template class HashTable : private AllocPolicy { @@ -83,12 +630,10 @@ class HashTable : private AllocPolicy public: typedef HashTableEntry Entry; - /* - * A nullable pointer to a hash table element. A Ptr |p| can be tested - * either explicitly |if (p.found()) p->...| or using boolean conversion - * |if (p) p->...|. Ptr objects must not be used after any mutating hash - * table operations unless |generation()| is tested. - */ + // A nullable pointer to a hash table element. A Ptr |p| can be tested + // either explicitly |if (p.found()) p->...| or using boolean conversion + // |if (p) p->...|. Ptr objects must not be used after any mutating hash + // table operations unless |generation()| is tested. class Ptr { friend class HashTable; @@ -101,7 +646,7 @@ class HashTable : private AllocPolicy Ptr(Entry &entry) : entry(&entry) {} public: - /* Leaves Ptr uninitialized. */ + // Leaves Ptr uninitialized. Ptr() { #ifdef DEBUG entry = (Entry *)0xbad; @@ -113,29 +658,27 @@ class HashTable : private AllocPolicy bool operator==(const Ptr &rhs) const { JS_ASSERT(found() && rhs.found()); return entry == rhs.entry; } bool operator!=(const Ptr &rhs) const { return !(*this == rhs); } - T &operator*() const { return entry->t; } - T *operator->() const { return &entry->t; } + T &operator*() const { return entry->get(); } + T *operator->() const { return &entry->get(); } }; - /* A Ptr that can be used to add a key after a failed lookup. */ + // A Ptr that can be used to add a key after a failed lookup. class AddPtr : public Ptr { friend class HashTable; HashNumber keyHash; - DebugOnly mutationCount; + mozilla::DebugOnly mutationCount; AddPtr(Entry &entry, HashNumber hn) : Ptr(entry), keyHash(hn) {} public: - /* Leaves AddPtr uninitialized. */ + // Leaves AddPtr uninitialized. AddPtr() {} }; - /* - * A collection of hash table entries. The collection is enumerated by - * calling |front()| followed by |popFront()| as long as |!empty()|. As - * with Ptr/AddPtr, Range objects must not be used after any mutating hash - * table operation unless the |generation()| is tested. - */ + // A collection of hash table entries. The collection is enumerated by + // calling |front()| followed by |popFront()| as long as |!empty()|. As + // with Ptr/AddPtr, Range objects must not be used after any mutating hash + // table operation unless the |generation()| is tested. class Range { protected: @@ -147,7 +690,7 @@ class HashTable : private AllocPolicy } Entry *cur, *end; - DebugOnly validEntry; + mozilla::DebugOnly validEntry; public: Range() : cur(NULL), end(NULL), validEntry(false) {} @@ -159,7 +702,7 @@ class HashTable : private AllocPolicy T &front() const { JS_ASSERT(validEntry); JS_ASSERT(!empty()); - return cur->t; + return cur->get(); } void popFront() { @@ -170,15 +713,13 @@ class HashTable : private AllocPolicy } }; - /* - * A Range whose lifetime delimits a mutating enumeration of a hash table. - * Since rehashing when elements were removed during enumeration would be - * bad, it is postponed until |endEnumeration()| is called. If - * |endEnumeration()| is not called before an Enum's constructor, it will - * be called automatically. Since |endEnumeration()| touches the hash - * table, the user must ensure that the hash table is still alive when this - * happens. - */ + // A Range whose lifetime delimits a mutating enumeration of a hash table. + // Since rehashing when elements were removed during enumeration would be + // bad, it is postponed until |endEnumeration()| is called. If + // |endEnumeration()| is not called before an Enum's constructor, it will + // be called automatically. Since |endEnumeration()| touches the hash + // table, the user must ensure that the hash table is still alive when this + // happens. class Enum : public Range { friend class HashTable; @@ -195,31 +736,27 @@ class HashTable : private AllocPolicy template explicit Enum(Map &map) : Range(map.all()), table(map.impl), rekeyed(false), removed(false) {} - /* - * Removes the |front()| element from the table, leaving |front()| - * invalid until the next call to |popFront()|. For example: - * - * HashSet s; - * for (HashSet::Enum e(s); !e.empty(); e.popFront()) - * if (e.front() == 42) - * e.removeFront(); - */ + // Removes the |front()| element from the table, leaving |front()| + // invalid until the next call to |popFront()|. For example: + // + // HashSet s; + // for (HashSet::Enum e(s); !e.empty(); e.popFront()) + // if (e.front() == 42) + // e.removeFront(); void removeFront() { table.remove(*this->cur); removed = true; this->validEntry = false; } - /* - * Removes the |front()| element and re-inserts it into the table with - * a new key at the new Lookup position. |front()| is invalid after - * this operation until the next call to |popFront()|. - */ + // Removes the |front()| element and re-inserts it into the table with + // a new key at the new Lookup position. |front()| is invalid after + // this operation until the next call to |popFront()|. void rekeyFront(const Lookup &l, const Key &k) { - typename HashTableEntry::NonConstT t = this->cur->t; + typename HashTableEntry::NonConstT t(Move(this->cur->get())); HashPolicy::setKey(t, const_cast(k)); table.remove(*this->cur); - table.putNewInfallible(l, t); + table.putNewInfallible(l, Move(t)); rekeyed = true; this->validEntry = false; } @@ -228,7 +765,7 @@ class HashTable : private AllocPolicy rekeyFront(k, k); } - /* Potentially rehashes the table. */ + // Potentially rehashes the table. ~Enum() { if (rekeyed) table.checkOverRemoved(); @@ -238,29 +775,31 @@ class HashTable : private AllocPolicy }; private: - uint32_t hashShift; /* multiplicative hash shift */ - uint32_t entryCount; /* number of entries in table */ - uint32_t gen; /* entry storage generation number */ - uint32_t removedCount; /* removed entry sentinels in table */ - Entry *table; /* entry storage */ + uint32_t hashShift; // multiplicative hash shift + uint32_t entryCount; // number of entries in table + uint32_t gen; // entry storage generation number + uint32_t removedCount; // removed entry sentinels in table + Entry *table; // entry storage - void setTableSizeLog2(unsigned sizeLog2) { + void setTableSizeLog2(unsigned sizeLog2) + { hashShift = sHashBits - sizeLog2; } #ifdef DEBUG - mutable struct Stats { - uint32_t searches; /* total number of table searches */ - uint32_t steps; /* hash chain links traversed */ - uint32_t hits; /* searches that found key */ - uint32_t misses; /* searches that didn't find key */ - uint32_t addOverRemoved; /* adds that recycled a removed entry */ - uint32_t removes; /* calls to remove */ - uint32_t removeFrees; /* calls to remove that freed the entry */ - uint32_t grows; /* table expansions */ - uint32_t shrinks; /* table contractions */ - uint32_t compresses; /* table compressions */ - uint32_t rehashes; /* tombstone decontaminations */ + mutable struct Stats + { + uint32_t searches; // total number of table searches + uint32_t steps; // hash chain links traversed + uint32_t hits; // searches that found key + uint32_t misses; // searches that didn't find key + uint32_t addOverRemoved; // adds that recycled a removed entry + uint32_t removes; // calls to remove + uint32_t removeFrees; // calls to remove that freed the entry + uint32_t grows; // table expansions + uint32_t shrinks; // table contractions + uint32_t compresses; // table compressions + uint32_t rehashes; // tombstone decontaminations } stats; # define METER(x) x #else @@ -268,29 +807,25 @@ class HashTable : private AllocPolicy #endif friend class js::ReentrancyGuard; - mutable DebugOnly entered; - DebugOnly mutationCount; + mutable mozilla::DebugOnly entered; + mozilla::DebugOnly mutationCount; - /* The default initial capacity is 16, but you can ask for as small as 4. */ + // The default initial capacity is 16, but you can ask for as small as 4. static const unsigned sMinSizeLog2 = 2; static const unsigned sMinSize = 1 << sMinSizeLog2; - static const unsigned sDefaultInitSizeLog2 = 4; - public: - static const unsigned sDefaultInitSize = 1 << sDefaultInitSizeLog2; - private: static const unsigned sMaxInit = JS_BIT(23); static const unsigned sMaxCapacity = JS_BIT(24); static const unsigned sHashBits = tl::BitSize::result; - static const uint8_t sMinAlphaFrac = 64; /* (0x100 * .25) taken from jsdhash.h */ - static const uint8_t sMaxAlphaFrac = 192; /* (0x100 * .75) taken from jsdhash.h */ - static const uint8_t sInvMaxAlpha = 171; /* (ceil(0x100 / .75) >> 1) */ + static const uint8_t sMinAlphaFrac = 64; // (0x100 * .25) + static const uint8_t sMaxAlphaFrac = 192; // (0x100 * .75) + static const uint8_t sInvMaxAlpha = 171; // (ceil(0x100 / .75) >> 1) static const HashNumber sFreeKey = Entry::sFreeKey; static const HashNumber sRemovedKey = Entry::sRemovedKey; static const HashNumber sCollisionBit = Entry::sCollisionBit; static void staticAsserts() { - /* Rely on compiler "constant overflow warnings". */ + // Rely on compiler "constant overflow warnings". JS_STATIC_ASSERT(((sMaxInit * sInvMaxAlpha) >> 7) < sMaxCapacity); JS_STATIC_ASSERT((sMaxCapacity * sInvMaxAlpha) <= UINT32_MAX); JS_STATIC_ASSERT((sMaxCapacity * sizeof(Entry)) <= UINT32_MAX); @@ -305,7 +840,7 @@ class HashTable : private AllocPolicy { HashNumber keyHash = ScrambleHashCode(HashPolicy::hash(l)); - /* Avoid reserved hash codes. */ + // Avoid reserved hash codes. if (!isLiveHash(keyHash)) keyHash -= (sRemovedKey + 1); return keyHash & ~sCollisionBit; @@ -313,18 +848,14 @@ class HashTable : private AllocPolicy static Entry *createTable(AllocPolicy &alloc, uint32_t capacity) { - Entry *newTable = (Entry *)alloc.malloc_(capacity * sizeof(Entry)); - if (!newTable) - return NULL; - for (Entry *e = newTable, *end = e + capacity; e < end; ++e) - new(e) Entry(); - return newTable; + // See JS_STATIC_ASSERT(sFreeKey == 0) in HashTableEntry. + return (Entry *)alloc.calloc_(capacity * sizeof(Entry)); } static void destroyTable(AllocPolicy &alloc, Entry *oldTable, uint32_t capacity) { for (Entry *e = oldTable, *end = e + capacity; e < end; ++e) - e->~Entry(); + e->destroyIfLive(); alloc.free_(oldTable); } @@ -344,10 +875,8 @@ class HashTable : private AllocPolicy { JS_ASSERT(!initialized()); - /* - * Correct for sMaxAlphaFrac such that the table will not resize - * when adding 'length' entries. - */ + // Correct for sMaxAlphaFrac such that the table will not resize + // when adding 'length' entries. if (length > sMaxInit) { this->reportAllocOverflow(); return false; @@ -357,7 +886,7 @@ class HashTable : private AllocPolicy if (capacity < sMinSize) capacity = sMinSize; - /* FIXME: use JS_CEILING_LOG2 when PGO stops crashing (bug 543034). */ + // FIXME: use JS_CEILING_LOG2 when PGO stops crashing (bug 543034). uint32_t roundUp = sMinSize, roundUpLog2 = sMinSizeLog2; while (roundUp < capacity) { roundUp <<= 1; @@ -388,16 +917,19 @@ class HashTable : private AllocPolicy } private: - static HashNumber hash1(HashNumber hash0, uint32_t shift) { - return hash0 >> shift; + HashNumber hash1(HashNumber hash0) const + { + return hash0 >> hashShift; } - struct DoubleHash { + struct DoubleHash + { HashNumber h2; HashNumber sizeMask; }; - DoubleHash hash2(HashNumber curKeyHash, uint32_t hashShift) const { + DoubleHash hash2(HashNumber curKeyHash) const + { unsigned sizeLog2 = sHashBits - hashShift; DoubleHash dh = { ((curKeyHash << sizeLog2) >> hashShift) | 1, @@ -406,22 +938,26 @@ class HashTable : private AllocPolicy return dh; } - static HashNumber applyDoubleHash(HashNumber h1, const DoubleHash &dh) { + static HashNumber applyDoubleHash(HashNumber h1, const DoubleHash &dh) + { return (h1 - dh.h2) & dh.sizeMask; } - bool overloaded() { + bool overloaded() + { return entryCount + removedCount >= ((sMaxAlphaFrac * capacity()) >> 8); } - bool underloaded() { + bool underloaded() + { uint32_t tableCapacity = capacity(); return tableCapacity > sMinSize && entryCount <= ((sMinAlphaFrac * tableCapacity) >> 8); } - static bool match(Entry &e, const Lookup &l) { - return HashPolicy::match(HashPolicy::getKey(e.t), l); + static bool match(Entry &e, const Lookup &l) + { + return HashPolicy::match(HashPolicy::getKey(e.get()), l); } Entry &lookup(const Lookup &l, HashNumber keyHash, unsigned collisionBit) const @@ -432,26 +968,26 @@ class HashTable : private AllocPolicy JS_ASSERT(table); METER(stats.searches++); - /* Compute the primary hash address. */ - HashNumber h1 = hash1(keyHash, hashShift); + // Compute the primary hash address. + HashNumber h1 = hash1(keyHash); Entry *entry = &table[h1]; - /* Miss: return space for a new entry. */ + // Miss: return space for a new entry. if (entry->isFree()) { METER(stats.misses++); return *entry; } - /* Hit: return entry. */ + // Hit: return entry. if (entry->matchHash(keyHash) && match(*entry, l)) { METER(stats.hits++); return *entry; } - /* Collision: double hash. */ - DoubleHash dh = hash2(keyHash, hashShift); + // Collision: double hash. + DoubleHash dh = hash2(keyHash); - /* Save the first removed entry pointer so we can recycle later. */ + // Save the first removed entry pointer so we can recycle later. Entry *firstRemoved = NULL; while(true) { @@ -478,34 +1014,32 @@ class HashTable : private AllocPolicy } } - /* - * This is a copy of lookup hardcoded to the assumptions: - * 1. the lookup is a lookupForAdd - * 2. the key, whose |keyHash| has been passed is not in the table, - * 3. no entries have been removed from the table. - * This specialized search avoids the need for recovering lookup values - * from entries, which allows more flexible Lookup/Key types. - */ + // This is a copy of lookup hardcoded to the assumptions: + // 1. the lookup is a lookupForAdd + // 2. the key, whose |keyHash| has been passed is not in the table, + // 3. no entries have been removed from the table. + // This specialized search avoids the need for recovering lookup values + // from entries, which allows more flexible Lookup/Key types. Entry &findFreeEntry(HashNumber keyHash) { JS_ASSERT(!(keyHash & sCollisionBit)); JS_ASSERT(table); METER(stats.searches++); - /* N.B. the |keyHash| has already been distributed. */ + // We assume 'keyHash' has already been distributed. - /* Compute the primary hash address. */ - HashNumber h1 = hash1(keyHash, hashShift); + // Compute the primary hash address. + HashNumber h1 = hash1(keyHash); Entry *entry = &table[h1]; - /* Miss: return space for a new entry. */ + // Miss: return space for a new entry. if (!entry->isLive()) { METER(stats.misses++); return *entry; } - /* Collision: double hash. */ - DoubleHash dh = hash2(keyHash, hashShift); + // Collision: double hash. + DoubleHash dh = hash2(keyHash); while(true) { JS_ASSERT(!entry->isRemoved()); @@ -526,7 +1060,7 @@ class HashTable : private AllocPolicy RebuildStatus changeTableSize(int deltaLog2) { - /* Look, but don't touch, until we succeed in getting new entry store. */ + // Look, but don't touch, until we succeed in getting new entry store. Entry *oldTable = table; uint32_t oldCap = capacity(); uint32_t newLog2 = sHashBits - hashShift + deltaLog2; @@ -540,21 +1074,23 @@ class HashTable : private AllocPolicy if (!newTable) return RehashFailed; - /* We can't fail from here on, so update table parameters. */ + // We can't fail from here on, so update table parameters. setTableSizeLog2(newLog2); removedCount = 0; gen++; table = newTable; - /* Copy only live entries, leaving removed ones behind. */ + // Copy only live entries, leaving removed ones behind. for (Entry *src = oldTable, *end = src + oldCap; src < end; ++src) { if (src->isLive()) { - src->unsetCollision(); - findFreeEntry(src->getKeyHash()) = Move(*src); + HashNumber hn = src->getKeyHash(); + findFreeEntry(hn).setLive(hn, Move(src->get())); + src->destroy(); } } - destroyTable(*this, oldTable, oldCap); + // All entries have been destroyed, no need to destroyTable. + this->free_(oldTable); return Rehashed; } @@ -563,7 +1099,7 @@ class HashTable : private AllocPolicy if (!overloaded()) return NotOverloaded; - /* Compress if a quarter or more of all entries are removed. */ + // Compress if a quarter or more of all entries are removed. int deltaLog2; if (removedCount >= (capacity() >> 2)) { METER(stats.compresses++); @@ -576,7 +1112,7 @@ class HashTable : private AllocPolicy return changeTableSize(deltaLog2); } - /* Infallibly rehash the table if we are overloaded with removals. */ + // Infallibly rehash the table if we are overloaded with removals. void checkOverRemoved() { if (overloaded()) { @@ -592,11 +1128,11 @@ class HashTable : private AllocPolicy METER(stats.removes++); if (e.hasCollision()) { - e.setRemoved(); + e.removeLive(); removedCount++; } else { METER(stats.removeFrees++); - e.setFree(); + e.clearLive(); } entryCount--; mutationCount++; @@ -610,13 +1146,11 @@ class HashTable : private AllocPolicy } } - /* - * This is identical to changeTableSize(currentSize), but without requiring - * a second table. We do this by recycling the collision bits to tell us if - * the element is already inserted or still waiting to be inserted. Since - * already-inserted elements win any conflicts, we get the same table as we - * would have gotten through random insertion order. - */ + // This is identical to changeTableSize(currentSize), but without requiring + // a second table. We do this by recycling the collision bits to tell us if + // the element is already inserted or still waiting to be inserted. Since + // already-inserted elements win any conflicts, we get the same table as we + // would have gotten through random insertion order. void rehashTable() { removedCount = 0; @@ -632,12 +1166,12 @@ class HashTable : private AllocPolicy } HashNumber keyHash = src->getKeyHash(); - HashNumber h1 = hash1(keyHash, hashShift); - DoubleHash dh = hash2(keyHash, hashShift); + HashNumber h1 = hash1(keyHash); + DoubleHash dh = hash2(keyHash); Entry *tgt = &table[h1]; while (true) { if (!tgt->hasCollision()) { - Swap(*src, *tgt); + src->swap(tgt); tgt->setCollision(); break; } @@ -647,13 +1181,11 @@ class HashTable : private AllocPolicy } } - /* - * TODO: this algorithm leaves collision bits on *all* elements, even if - * they are on no collision path. We have the option of setting the - * collision bits correctly on a subsequent pass or skipping the rehash - * unless we are totally filled with tombstones: benchmark to find out - * which approach is best. - */ + // TODO: this algorithm leaves collision bits on *all* elements, even if + // they are on no collision path. We have the option of setting the + // collision bits correctly on a subsequent pass or skipping the rehash + // unless we are totally filled with tombstones: benchmark to find out + // which approach is best. } public: @@ -664,7 +1196,7 @@ class HashTable : private AllocPolicy } else { uint32_t tableCapacity = capacity(); for (Entry *e = table, *end = table + tableCapacity; e < end; ++e) - *e = Move(Entry()); + e->clear(); } removedCount = 0; entryCount = 0; @@ -686,46 +1218,55 @@ class HashTable : private AllocPolicy mutationCount++; } - Range all() const { + Range all() const + { JS_ASSERT(table); return Range(table, table + capacity()); } - bool empty() const { + bool empty() const + { JS_ASSERT(table); return !entryCount; } - uint32_t count() const { + uint32_t count() const + { JS_ASSERT(table); return entryCount; } - uint32_t capacity() const { + uint32_t capacity() const + { JS_ASSERT(table); return JS_BIT(sHashBits - hashShift); } - uint32_t generation() const { + uint32_t generation() const + { JS_ASSERT(table); return gen; } - size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const { + size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const + { return mallocSizeOf(table); } - size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const { + size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const + { return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf); } - Ptr lookup(const Lookup &l) const { + Ptr lookup(const Lookup &l) const + { ReentrancyGuard g(*this); HashNumber keyHash = prepareHash(l); return Ptr(lookup(l, keyHash, 0)); } - AddPtr lookupForAdd(const Lookup &l) const { + AddPtr lookupForAdd(const Lookup &l) const + { ReentrancyGuard g(*this); HashNumber keyHash = prepareHash(l); Entry &entry = lookup(l, keyHash, sCollisionBit); @@ -734,7 +1275,8 @@ class HashTable : private AllocPolicy return p; } - bool add(AddPtr &p) + template + bool add(AddPtr &p, const U &rhs) { ReentrancyGuard g(*this); JS_ASSERT(mutationCount == p.mutationCount); @@ -742,16 +1284,14 @@ class HashTable : private AllocPolicy JS_ASSERT(!p.found()); JS_ASSERT(!(p.keyHash & sCollisionBit)); - /* - * Changing an entry from removed to live does not affect whether we - * are overloaded and can be handled separately. - */ + // Changing an entry from removed to live does not affect whether we + // are overloaded and can be handled separately. if (p.entry->isRemoved()) { METER(stats.addOverRemoved++); removedCount--; p.keyHash |= sCollisionBit; } else { - /* Preserve the validity of |p.entry|. */ + // Preserve the validity of |p.entry|. RebuildStatus status = checkOverloaded(); if (status == RehashFailed) return false; @@ -759,34 +1299,14 @@ class HashTable : private AllocPolicy p.entry = &findFreeEntry(p.keyHash); } - p.entry->setLive(p.keyHash); + p.entry->setLive(p.keyHash, rhs); entryCount++; mutationCount++; return true; } - /* - * There is an important contract between the caller and callee for this - * function: if add() returns true, the caller must assign the T value - * which produced p before using the hashtable again. - */ - bool add(AddPtr &p, T** pentry) - { - if (!add(p)) - return false; - *pentry = &p.entry->t; - return true; - } - - bool add(AddPtr &p, const T &t) - { - if (!add(p)) - return false; - p.entry->t = t; - return true; - } - - void putNewInfallible(const Lookup &l, const T &t) + template + void putNewInfallible(const Lookup &l, const U &u) { JS_ASSERT(table); @@ -799,29 +1319,30 @@ class HashTable : private AllocPolicy keyHash |= sCollisionBit; } - entry->t = t; - entry->setLive(keyHash); + entry->setLive(keyHash, u); entryCount++; mutationCount++; } - bool putNew(const Lookup &l, const T &t) + template + bool putNew(const Lookup &l, const U &u) { if (checkOverloaded() == RehashFailed) return false; - putNewInfallible(l, t); + putNewInfallible(l, u); return true; } - bool relookupOrAdd(AddPtr& p, const Lookup &l, const T& t) + template + bool relookupOrAdd(AddPtr& p, const Lookup &l, const U &u) { p.mutationCount = mutationCount; { ReentrancyGuard g(*this); p.entry = &lookup(l, p.keyHash, sCollisionBit); } - return p.found() || add(p, t); + return p.found() || add(p, u); } void remove(Ptr p) @@ -836,593 +1357,8 @@ class HashTable : private AllocPolicy #undef METER }; -} /* namespace detail */ +} // namespace detail +} // namespace js -/*****************************************************************************/ +#endif // js_HashTable_h__ -/* - * Hash policy - * - * A hash policy P for a hash table with key-type Key must provide: - * - a type |P::Lookup| to use to lookup table entries; - * - a static member function |P::hash| with signature - * - * static js::HashNumber hash(Lookup) - * - * to use to hash the lookup type; and - * - a static member function |P::match| with signature - * - * static bool match(Key, Lookup) - * - * to use to test equality of key and lookup values. - * - * Normally, Lookup = Key. In general, though, different values and types of - * values can be used to lookup and store. If a Lookup value |l| is != to the - * added Key value |k|, the user must ensure that |P::match(k,l)|. E.g.: - * - * js::HashSet::AddPtr p = h.lookup(l); - * if (!p) { - * assert(P::match(k, l)); // must hold - * h.add(p, k); - * } - */ - -/* Default hashing policies. */ -template -struct DefaultHasher -{ - typedef Key Lookup; - static HashNumber hash(const Lookup &l) { - /* Hash if can implicitly cast to hash number type. */ - return l; - } - static bool match(const Key &k, const Lookup &l) { - /* Use builtin or overloaded operator==. */ - return k == l; - } -}; - -/* - * Pointer hashing policy that strips the lowest zeroBits when calculating the - * hash to improve key distribution. - */ -template -struct PointerHasher -{ - typedef Key Lookup; - static HashNumber hash(const Lookup &l) { - size_t word = reinterpret_cast(l) >> zeroBits; - JS_STATIC_ASSERT(sizeof(HashNumber) == 4); -#if JS_BYTES_PER_WORD == 4 - return HashNumber(word); -#else - JS_STATIC_ASSERT(sizeof word == 8); - return HashNumber((word >> 32) ^ word); -#endif - } - static bool match(const Key &k, const Lookup &l) { - return k == l; - } -}; - -template -struct TaggedPointerHasher -{ - typedef Key Lookup; - - static HashNumber hash(const Lookup &l) { - return PointerHasher::hash(l); - } - - static const uintptr_t COMPARE_MASK = uintptr_t(-1) - 1; - - static bool match(const Key &k, const Lookup &l) { - return (uintptr_t(k) & COMPARE_MASK) == uintptr_t(l); - } -}; - -/* - * Specialized hashing policy for pointer types. It assumes that the type is - * at least word-aligned. For types with smaller size use PointerHasher. - */ -template -struct DefaultHasher: PointerHasher::result> { }; - -/* Looking for a hasher for jsid? Try the DefaultHasher in jsatom.h. */ - -template -class HashMapEntry -{ - template friend class detail::HashTable; - template friend class detail::HashTableEntry; - void operator=(const HashMapEntry &rhs) { - const_cast(key) = rhs.key; - value = rhs.value; - } - - public: - HashMapEntry() : key(), value() {} - - template - HashMapEntry(const KeyInput &k, const ValueInput &v) : key(k), value(v) {} - - HashMapEntry(MoveRef rhs) - : key(Move(rhs->key)), value(Move(rhs->value)) { } - void operator=(MoveRef rhs) { - const_cast(key) = Move(rhs->key); - value = Move(rhs->value); - } - - const Key key; - Value value; -}; - -namespace tl { - -template -struct IsPodType > { - static const bool result = IsPodType::result; -}; - -template -struct IsPodType > -{ - static const bool result = IsPodType::result && IsPodType::result; -}; - -} /* namespace tl */ - -/* - * JS-friendly, STL-like container providing a hash-based map from keys to - * values. In particular, HashMap calls constructors and destructors of all - * objects added so non-PODs may be used safely. - * - * Key/Value requirements: - * - default constructible, copyable, destructible, assignable - * HashPolicy requirements: - * - see "Hash policy" above (default js::DefaultHasher) - * AllocPolicy: - * - see "Allocation policies" in jsalloc.h - * - * N.B: HashMap is not reentrant: Key/Value/HashPolicy/AllocPolicy members - * called by HashMap must not call back into the same HashMap object. - * N.B: Due to the lack of exception handling, the user must call |init()|. - */ -template , - class AllocPolicy = TempAllocPolicy> -class HashMap -{ - typedef typename tl::StaticAssert::result>::result keyAssert; - typedef typename tl::StaticAssert::result>::result valAssert; - - public: - typedef typename HashPolicy::Lookup Lookup; - - typedef HashMapEntry Entry; - - private: - /* Implement HashMap using HashTable. Lift |Key| operations to |Entry|. */ - struct MapHashPolicy : HashPolicy - { - typedef Key KeyType; - static const Key &getKey(Entry &e) { return e.key; } - static void setKey(Entry &e, Key &k) { const_cast(e.key) = k; } - }; - typedef detail::HashTable Impl; - - friend class Impl::Enum; - - Impl impl; - - public: - const static unsigned sDefaultInitSize = Impl::sDefaultInitSize; - - /* - * HashMap construction is fallible (due to OOM); thus the user must call - * init after constructing a HashMap and check the return value. - */ - HashMap(AllocPolicy a = AllocPolicy()) : impl(a) {} - bool init(uint32_t len = sDefaultInitSize) { return impl.init(len); } - bool initialized() const { return impl.initialized(); } - - /* - * Return whether the given lookup value is present in the map. E.g.: - * - * typedef HashMap HM; - * HM h; - * if (HM::Ptr p = h.lookup(3)) { - * const HM::Entry &e = *p; // p acts like a pointer to Entry - * assert(p->key == 3); // Entry contains the key - * char val = p->value; // and value - * } - * - * Also see the definition of Ptr in HashTable above (with T = Entry). - */ - typedef typename Impl::Ptr Ptr; - Ptr lookup(const Lookup &l) const { return impl.lookup(l); } - - /* Assuming |p.found()|, remove |*p|. */ - void remove(Ptr p) { impl.remove(p); } - - /* - * Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient - * insertion of Key |k| (where |HashPolicy::match(k,l) == true|) using - * |add(p,k,v)|. After |add(p,k,v)|, |p| points to the new Entry. E.g.: - * - * typedef HashMap HM; - * HM h; - * HM::AddPtr p = h.lookupForAdd(3); - * if (!p) { - * if (!h.add(p, 3, 'a')) - * return false; - * } - * const HM::Entry &e = *p; // p acts like a pointer to Entry - * assert(p->key == 3); // Entry contains the key - * char val = p->value; // and value - * - * Also see the definition of AddPtr in HashTable above (with T = Entry). - * - * N.B. The caller must ensure that no mutating hash table operations - * occur between a pair of |lookupForAdd| and |add| calls. To avoid - * looking up the key a second time, the caller may use the more efficient - * relookupOrAdd method. This method reuses part of the hashing computation - * to more efficiently insert the key if it has not been added. For - * example, a mutation-handling version of the previous example: - * - * HM::AddPtr p = h.lookupForAdd(3); - * if (!p) { - * call_that_may_mutate_h(); - * if (!h.relookupOrAdd(p, 3, 'a')) - * return false; - * } - * const HM::Entry &e = *p; - * assert(p->key == 3); - * char val = p->value; - */ - typedef typename Impl::AddPtr AddPtr; - AddPtr lookupForAdd(const Lookup &l) const { - return impl.lookupForAdd(l); - } - - template - bool add(AddPtr &p, const KeyInput &k, const ValueInput &v) { - Entry *pentry; - if (!impl.add(p, &pentry)) - return false; - const_cast(pentry->key) = k; - pentry->value = v; - return true; - } - - bool add(AddPtr &p, const Key &k, MoveRef v) { - Entry *pentry; - if (!impl.add(p, &pentry)) - return false; - const_cast(pentry->key) = k; - pentry->value = v; - return true; - } - - bool add(AddPtr &p, const Key &k) { - Entry *pentry; - if (!impl.add(p, &pentry)) - return false; - const_cast(pentry->key) = k; - return true; - } - - template - bool relookupOrAdd(AddPtr &p, const KeyInput &k, const ValueInput &v) { - return impl.relookupOrAdd(p, k, Entry(k, v)); - } - - /* - * |all()| returns a Range containing |count()| elements. E.g.: - * - * typedef HashMap HM; - * HM h; - * for (HM::Range r = h.all(); !r.empty(); r.popFront()) - * char c = r.front().value; - * - * Also see the definition of Range in HashTable above (with T = Entry). - */ - typedef typename Impl::Range Range; - Range all() const { return impl.all(); } - uint32_t count() const { return impl.count(); } - size_t capacity() const { return impl.capacity(); } - size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const { - return impl.sizeOfExcludingThis(mallocSizeOf); - } - size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const { - /* - * Don't just call |impl.sizeOfExcludingThis()| because there's no - * guarantee that |impl| is the first field in HashMap. - */ - return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf); - } - - /* - * Typedef for the enumeration class. An Enum may be used to examine and - * remove table entries: - * - * typedef HashMap HM; - * HM s; - * for (HM::Enum e(s); !e.empty(); e.popFront()) - * if (e.front().value == 'l') - * e.removeFront(); - * - * Table resize may occur in Enum's destructor. Also see the definition of - * Enum in HashTable above (with T = Entry). - */ - typedef typename Impl::Enum Enum; - - /* - * Remove all entries. This does not shrink the table. For that consider - * using the finish() method. - */ - void clear() { impl.clear(); } - - /* - * Remove all the entries and release all internal buffers. The map must - * be initialized again before any use. - */ - void finish() { impl.finish(); } - - /* Does the table contain any entries? */ - bool empty() const { return impl.empty(); } - - /* - * If |generation()| is the same before and after a HashMap operation, - * pointers into the table remain valid. - */ - unsigned generation() const { return impl.generation(); } - - /* Shorthand operations: */ - - bool has(const Lookup &l) const { - return impl.lookup(l) != NULL; - } - - /* Overwrite existing value with v. Return false on oom. */ - template - bool put(const KeyInput &k, const ValueInput &v) { - AddPtr p = lookupForAdd(k); - if (p) { - p->value = v; - return true; - } - return add(p, k, v); - } - - /* Like put, but assert that the given key is not already present. */ - bool putNew(const Key &k, const Value &v) { - return impl.putNew(k, Entry(k, v)); - } - - /* Add (k,defaultValue) if k no found. Return false-y Ptr on oom. */ - Ptr lookupWithDefault(const Key &k, const Value &defaultValue) { - AddPtr p = lookupForAdd(k); - if (p) - return p; - (void)add(p, k, defaultValue); /* p is left false-y on oom. */ - return p; - } - - /* Remove if present. */ - void remove(const Lookup &l) { - if (Ptr p = lookup(l)) - remove(p); - } - - private: - /* Not implicitly copyable (expensive). May add explicit |clone| later. */ - HashMap(const HashMap &hm) MOZ_DELETE; - HashMap &operator=(const HashMap &hm) MOZ_DELETE; -}; - -/* - * JS-friendly, STL-like container providing a hash-based set of values. In - * particular, HashSet calls constructors and destructors of all objects added - * so non-PODs may be used safely. - * - * T requirements: - * - default constructible, copyable, destructible, assignable - * HashPolicy requirements: - * - see "Hash policy" above (default js::DefaultHasher) - * AllocPolicy: - * - see "Allocation policies" in jsalloc.h - * - * N.B: HashSet is not reentrant: T/HashPolicy/AllocPolicy members called by - * HashSet must not call back into the same HashSet object. - * N.B: Due to the lack of exception handling, the user must call |init()|. - */ -template , class AllocPolicy = TempAllocPolicy> -class HashSet -{ - typedef typename HashPolicy::Lookup Lookup; - - /* Implement HashSet in terms of HashTable. */ - struct SetOps : HashPolicy { - typedef T KeyType; - static const KeyType &getKey(const T &t) { return t; } - static void setKey(T &t, KeyType &k) { t = k; } - }; - typedef detail::HashTable Impl; - - friend class Impl::Enum; - - Impl impl; - - public: - const static unsigned sDefaultInitSize = Impl::sDefaultInitSize; - - /* - * HashSet construction is fallible (due to OOM); thus the user must call - * init after constructing a HashSet and check the return value. - */ - HashSet(AllocPolicy a = AllocPolicy()) : impl(a) {} - bool init(uint32_t len = sDefaultInitSize) { return impl.init(len); } - bool initialized() const { return impl.initialized(); } - - /* - * Return whether the given lookup value is present in the map. E.g.: - * - * typedef HashSet HS; - * HS h; - * if (HS::Ptr p = h.lookup(3)) { - * assert(*p == 3); // p acts like a pointer to int - * } - * - * Also see the definition of Ptr in HashTable above. - */ - typedef typename Impl::Ptr Ptr; - Ptr lookup(const Lookup &l) const { return impl.lookup(l); } - - /* Assuming |p.found()|, remove |*p|. */ - void remove(Ptr p) { impl.remove(p); } - - /* - * Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient - * insertion of T value |t| (where |HashPolicy::match(t,l) == true|) using - * |add(p,t)|. After |add(p,t)|, |p| points to the new element. E.g.: - * - * typedef HashSet HS; - * HS h; - * HS::AddPtr p = h.lookupForAdd(3); - * if (!p) { - * if (!h.add(p, 3)) - * return false; - * } - * assert(*p == 3); // p acts like a pointer to int - * - * Also see the definition of AddPtr in HashTable above. - * - * N.B. The caller must ensure that no mutating hash table operations - * occur between a pair of |lookupForAdd| and |add| calls. To avoid - * looking up the key a second time, the caller may use the more efficient - * relookupOrAdd method. This method reuses part of the hashing computation - * to more efficiently insert the key if it has not been added. For - * example, a mutation-handling version of the previous example: - * - * HS::AddPtr p = h.lookupForAdd(3); - * if (!p) { - * call_that_may_mutate_h(); - * if (!h.relookupOrAdd(p, 3, 3)) - * return false; - * } - * assert(*p == 3); - * - * Note that relookupOrAdd(p,l,t) performs Lookup using l and adds the - * entry t, where the caller ensures match(l,t). - */ - typedef typename Impl::AddPtr AddPtr; - AddPtr lookupForAdd(const Lookup &l) const { - return impl.lookupForAdd(l); - } - - bool add(AddPtr &p, const T &t) { - return impl.add(p, t); - } - - bool relookupOrAdd(AddPtr &p, const Lookup &l, const T &t) { - return impl.relookupOrAdd(p, l, t); - } - - /* - * |all()| returns a Range containing |count()| elements: - * - * typedef HashSet HS; - * HS h; - * for (HS::Range r = h.all(); !r.empty(); r.popFront()) - * int i = r.front(); - * - * Also see the definition of Range in HashTable above. - */ - typedef typename Impl::Range Range; - Range all() const { return impl.all(); } - uint32_t count() const { return impl.count(); } - size_t capacity() const { return impl.capacity(); } - size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const { - return impl.sizeOfExcludingThis(mallocSizeOf); - } - size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const { - /* - * Don't just call |impl.sizeOfExcludingThis()| because there's no - * guarantee that |impl| is the first field in HashSet. - */ - return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf); - } - - /* - * Typedef for the enumeration class. An Enum may be used to examine and - * remove table entries: - * - * typedef HashSet HS; - * HS s; - * for (HS::Enum e(s); !e.empty(); e.popFront()) - * if (e.front() == 42) - * e.removeFront(); - * - * Table resize may occur in Enum's destructor. Also see the definition of - * Enum in HashTable above. - */ - typedef typename Impl::Enum Enum; - - /* - * Remove all entries. This does not shrink the table. For that consider - * using the finish() method. - */ - void clear() { impl.clear(); } - - /* - * Remove all the entries and release all internal buffers. The set must - * be initialized again before any use. - */ - void finish() { impl.finish(); } - - /* Does the table contain any entries? */ - bool empty() const { return impl.empty(); } - - /* - * If |generation()| is the same before and after a HashSet operation, - * pointers into the table remain valid. - */ - unsigned generation() const { return impl.generation(); } - - /* Shorthand operations: */ - - bool has(const Lookup &l) const { - return impl.lookup(l) != NULL; - } - - /* Overwrite existing value with v. Return false on oom. */ - bool put(const T &t) { - AddPtr p = lookupForAdd(t); - return p ? true : add(p, t); - } - - /* Like put, but assert that the given key is not already present. */ - bool putNew(const T &t) { - return impl.putNew(t, t); - } - - bool putNew(const Lookup &l, const T &t) { - return impl.putNew(l, t); - } - - void remove(const Lookup &l) { - if (Ptr p = lookup(l)) - remove(p); - } - - private: - /* Not implicitly copyable (expensive). May add explicit |clone| later. */ - HashSet(const HashSet &hs) MOZ_DELETE; - HashSet &operator=(const HashSet &hs) MOZ_DELETE; -}; - -} /* namespace js */ - -#endif diff --git a/scripting/javascript/spidermonkey-win32/include/js/HeapAPI.h b/scripting/javascript/spidermonkey-win32/include/js/HeapAPI.h new file mode 100644 index 0000000000..9e83bec84a --- /dev/null +++ b/scripting/javascript/spidermonkey-win32/include/js/HeapAPI.h @@ -0,0 +1,76 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef js_heap_api_h___ +#define js_heap_api_h___ + +/* These values are private to the JS engine. */ +namespace js { +namespace gc { + +/* + * Page size must be static to support our arena pointer optimizations, so we + * are forced to support each platform with non-4096 pages as a special case. + * Note: The freelist supports a maximum arena shift of 15. + * Note: Do not use JS_CPU_SPARC here, this header is used outside JS. + */ +#if (defined(SOLARIS) || defined(__FreeBSD__)) && \ + (defined(__sparc) || defined(__sparcv9) || defined(__ia64)) +const size_t PageShift = 13; +const size_t ArenaShift = PageShift; +#elif defined(__powerpc64__) +const size_t PageShift = 16; +const size_t ArenaShift = 12; +#else +const size_t PageShift = 12; +const size_t ArenaShift = PageShift; +#endif +const size_t PageSize = size_t(1) << PageShift; +const size_t ArenaSize = size_t(1) << ArenaShift; +const size_t ArenaMask = ArenaSize - 1; + +const size_t ChunkShift = 20; +const size_t ChunkSize = size_t(1) << ChunkShift; +const size_t ChunkMask = ChunkSize - 1; + +} /* namespace gc */ +} /* namespace js */ + +namespace JS { + +namespace shadow { + +struct ArenaHeader +{ + JSCompartment *compartment; +}; + +} /* namespace shadow */ + +static inline shadow::ArenaHeader * +GetGCThingArena(void *thing) +{ + uintptr_t addr = uintptr_t(thing); + addr &= ~js::gc::ArenaMask; + return reinterpret_cast(addr); +} + +static inline JSCompartment * +GetGCThingCompartment(void *thing) +{ + JS_ASSERT(thing); + return GetGCThingArena(thing)->compartment; +} + +static inline JSCompartment * +GetObjectCompartment(JSObject *obj) +{ + return GetGCThingCompartment(obj); +} + +} /* namespace JS */ + +#endif /* js_heap_api_h___ */ diff --git a/scripting/javascript/spidermonkey-win32/include/js/MemoryMetrics.h b/scripting/javascript/spidermonkey-win32/include/js/MemoryMetrics.h index 823e491c30..2f7b78505f 100644 --- a/scripting/javascript/spidermonkey-win32/include/js/MemoryMetrics.h +++ b/scripting/javascript/spidermonkey-win32/include/js/MemoryMetrics.h @@ -38,22 +38,37 @@ namespace JS { struct TypeInferenceSizes { TypeInferenceSizes() - : scripts(0) - , objects(0) - , tables(0) - , temporary(0) + : typeScripts(0) + , typeResults(0) + , analysisPool(0) + , typePool(0) + , pendingArrays(0) + , allocationSiteTables(0) + , arrayTypeTables(0) + , objectTypeTables(0) + , typeObjects(0) {} - size_t scripts; - size_t objects; - size_t tables; - size_t temporary; + size_t typeScripts; + size_t typeResults; + size_t analysisPool; + size_t typePool; + size_t pendingArrays; + size_t allocationSiteTables; + size_t arrayTypeTables; + size_t objectTypeTables; + size_t typeObjects; void add(TypeInferenceSizes &sizes) { - this->scripts += sizes.scripts; - this->objects += sizes.objects; - this->tables += sizes.tables; - this->temporary += sizes.temporary; + this->typeScripts += sizes.typeScripts; + this->typeResults += sizes.typeResults; + this->analysisPool += sizes.analysisPool; + this->typePool += sizes.typePool; + this->pendingArrays += sizes.pendingArrays; + this->allocationSiteTables += sizes.allocationSiteTables; + this->arrayTypeTables += sizes.arrayTypeTables; + this->objectTypeTables += sizes.objectTypeTables; + this->typeObjects += sizes.typeObjects; } }; @@ -129,10 +144,15 @@ struct CompartmentStats , extra2(0) , gcHeapArenaAdmin(0) , gcHeapUnusedGcThings(0) - , gcHeapObjectsNonFunction(0) + , gcHeapObjectsOrdinary(0) , gcHeapObjectsFunction(0) - , gcHeapStrings(0) - , gcHeapShapesTree(0) + , gcHeapObjectsDenseArray(0) + , gcHeapObjectsSlowArray(0) + , gcHeapObjectsCrossCompartmentWrapper(0) + , gcHeapStringsNormal(0) + , gcHeapStringsShort(0) + , gcHeapShapesTreeGlobalParented(0) + , gcHeapShapesTreeNonGlobalParented(0) , gcHeapShapesDict(0) , gcHeapShapesBase(0) , gcHeapScripts(0) @@ -147,7 +167,7 @@ struct CompartmentStats , objectsExtraRegExpStatics(0) , objectsExtraPropertyIteratorData(0) , objectsExtraPrivate(0) - , nonHugeStringChars(0) + , stringCharsNonHuge(0) , shapesExtraTreeTables(0) , shapesExtraDictTables(0) , shapesExtraTreeShapeKids(0) @@ -156,7 +176,7 @@ struct CompartmentStats , jaegerData(0) , ionData(0) , compartmentObject(0) - , crossCompartmentWrappers(0) + , crossCompartmentWrappersTable(0) , regexpCompartment(0) , debuggeesSet(0) {} @@ -166,10 +186,15 @@ struct CompartmentStats , extra2(other.extra2) , gcHeapArenaAdmin(other.gcHeapArenaAdmin) , gcHeapUnusedGcThings(other.gcHeapUnusedGcThings) - , gcHeapObjectsNonFunction(other.gcHeapObjectsNonFunction) + , gcHeapObjectsOrdinary(other.gcHeapObjectsOrdinary) , gcHeapObjectsFunction(other.gcHeapObjectsFunction) - , gcHeapStrings(other.gcHeapStrings) - , gcHeapShapesTree(other.gcHeapShapesTree) + , gcHeapObjectsDenseArray(other.gcHeapObjectsDenseArray) + , gcHeapObjectsSlowArray(other.gcHeapObjectsSlowArray) + , gcHeapObjectsCrossCompartmentWrapper(other.gcHeapObjectsCrossCompartmentWrapper) + , gcHeapStringsNormal(other.gcHeapStringsNormal) + , gcHeapStringsShort(other.gcHeapStringsShort) + , gcHeapShapesTreeGlobalParented(other.gcHeapShapesTreeGlobalParented) + , gcHeapShapesTreeNonGlobalParented(other.gcHeapShapesTreeNonGlobalParented) , gcHeapShapesDict(other.gcHeapShapesDict) , gcHeapShapesBase(other.gcHeapShapesBase) , gcHeapScripts(other.gcHeapScripts) @@ -184,7 +209,7 @@ struct CompartmentStats , objectsExtraRegExpStatics(other.objectsExtraRegExpStatics) , objectsExtraPropertyIteratorData(other.objectsExtraPropertyIteratorData) , objectsExtraPrivate(other.objectsExtraPrivate) - , nonHugeStringChars(other.nonHugeStringChars) + , stringCharsNonHuge(other.stringCharsNonHuge) , shapesExtraTreeTables(other.shapesExtraTreeTables) , shapesExtraDictTables(other.shapesExtraDictTables) , shapesExtraTreeShapeKids(other.shapesExtraTreeShapeKids) @@ -193,7 +218,7 @@ struct CompartmentStats , jaegerData(other.jaegerData) , ionData(other.ionData) , compartmentObject(other.compartmentObject) - , crossCompartmentWrappers(other.crossCompartmentWrappers) + , crossCompartmentWrappersTable(other.crossCompartmentWrappersTable) , regexpCompartment(other.regexpCompartment) , debuggeesSet(other.debuggeesSet) , typeInferenceSizes(other.typeInferenceSizes) @@ -210,10 +235,15 @@ struct CompartmentStats size_t gcHeapArenaAdmin; size_t gcHeapUnusedGcThings; - size_t gcHeapObjectsNonFunction; + size_t gcHeapObjectsOrdinary; size_t gcHeapObjectsFunction; - size_t gcHeapStrings; - size_t gcHeapShapesTree; + size_t gcHeapObjectsDenseArray; + size_t gcHeapObjectsSlowArray; + size_t gcHeapObjectsCrossCompartmentWrapper; + size_t gcHeapStringsNormal; + size_t gcHeapStringsShort; + size_t gcHeapShapesTreeGlobalParented; + size_t gcHeapShapesTreeNonGlobalParented; size_t gcHeapShapesDict; size_t gcHeapShapesBase; size_t gcHeapScripts; @@ -229,7 +259,7 @@ struct CompartmentStats size_t objectsExtraRegExpStatics; size_t objectsExtraPropertyIteratorData; size_t objectsExtraPrivate; - size_t nonHugeStringChars; + size_t stringCharsNonHuge; size_t shapesExtraTreeTables; size_t shapesExtraDictTables; size_t shapesExtraTreeShapeKids; @@ -238,7 +268,7 @@ struct CompartmentStats size_t jaegerData; size_t ionData; size_t compartmentObject; - size_t crossCompartmentWrappers; + size_t crossCompartmentWrappersTable; size_t regexpCompartment; size_t debuggeesSet; @@ -253,10 +283,15 @@ struct CompartmentStats ADD(gcHeapArenaAdmin); ADD(gcHeapUnusedGcThings); - ADD(gcHeapObjectsNonFunction); + ADD(gcHeapObjectsOrdinary); ADD(gcHeapObjectsFunction); - ADD(gcHeapStrings); - ADD(gcHeapShapesTree); + ADD(gcHeapObjectsDenseArray); + ADD(gcHeapObjectsSlowArray); + ADD(gcHeapObjectsCrossCompartmentWrapper); + ADD(gcHeapStringsNormal); + ADD(gcHeapStringsShort); + ADD(gcHeapShapesTreeGlobalParented); + ADD(gcHeapShapesTreeNonGlobalParented); ADD(gcHeapShapesDict); ADD(gcHeapShapesBase); ADD(gcHeapScripts); @@ -272,7 +307,7 @@ struct CompartmentStats ADD(objectsExtraRegExpStatics); ADD(objectsExtraPropertyIteratorData); ADD(objectsExtraPrivate); - ADD(nonHugeStringChars); + ADD(stringCharsNonHuge); ADD(shapesExtraTreeTables); ADD(shapesExtraDictTables); ADD(shapesExtraTreeShapeKids); @@ -281,7 +316,7 @@ struct CompartmentStats ADD(jaegerData); ADD(ionData); ADD(compartmentObject); - ADD(crossCompartmentWrappers); + ADD(crossCompartmentWrappersTable); ADD(regexpCompartment); ADD(debuggeesSet); diff --git a/scripting/javascript/spidermonkey-win32/include/js/Utility.h b/scripting/javascript/spidermonkey-win32/include/js/Utility.h index 3b03615515..7e4d4d5beb 100644 --- a/scripting/javascript/spidermonkey-win32/include/js/Utility.h +++ b/scripting/javascript/spidermonkey-win32/include/js/Utility.h @@ -21,7 +21,6 @@ #include "jstypes.h" -#ifdef __cplusplus # include "js/TemplateLib.h" # include "mozilla/Scoped.h" @@ -36,12 +35,8 @@ namespace js { /* The private namespace is a superset of the public/shared namespaces. */ using namespace JS; -using namespace mozilla; } /* namespace js */ -#endif /* __cplusplus */ - -JS_BEGIN_EXTERN_C /* * Pattern used to overwrite freed memory. If you are accessing an object with @@ -103,7 +98,7 @@ PrintBacktrace() int32_t OOM_traceIdx = 0; OOM_traceSize = backtrace(OOM_trace, JS_OOM_BACKTRACE_SIZE); OOM_traceSymbols = backtrace_symbols(OOM_trace, OOM_traceSize); - + if (!OOM_traceSymbols) return; @@ -171,6 +166,8 @@ static JS_INLINE void js_free(void* p) } #endif/* JS_USE_CUSTOM_ALLOCATOR */ +JS_BEGIN_EXTERN_C + /* * Replace bit-scanning code sequences with CPU-specific instructions to * speedup calculations of ceiling/floor log2. @@ -330,6 +327,8 @@ JS_PUBLIC_API(size_t) js_FloorLog2wImpl(size_t n); # error "NOT SUPPORTED" #endif +JS_END_EXTERN_C + /* * Internal function. * Compute the log of the least power of 2 greater than or equal to n. This is @@ -371,9 +370,6 @@ JS_FLOOR_LOG2W(size_t n) #define JS_ROTATE_LEFT32(a, bits) (((a) << (bits)) | ((a) >> (32 - (bits)))) #endif -JS_END_EXTERN_C - -#ifdef __cplusplus #include /* @@ -903,8 +899,6 @@ inline bool IsPoisonedPtr(T *v) } -#endif /* defined(__cplusplus) */ - /* * This is SpiderMonkey's equivalent to |nsMallocSizeOfFun|. */ diff --git a/scripting/javascript/spidermonkey-win32/include/js/Vector.h b/scripting/javascript/spidermonkey-win32/include/js/Vector.h index 05480632be..9f2377fbe6 100644 --- a/scripting/javascript/spidermonkey-win32/include/js/Vector.h +++ b/scripting/javascript/spidermonkey-win32/include/js/Vector.h @@ -242,7 +242,7 @@ class Vector : private AllocPolicy size_t mReserved; /* Max elements of reserved or used space in this vector. */ #endif - AlignedStorage storage; + mozilla::AlignedStorage storage; #ifdef DEBUG friend class ReentrancyGuard; @@ -255,7 +255,11 @@ class Vector : private AllocPolicy /* private accessors */ bool usingInlineStorage() const { - return mBegin == (T *)storage.addr(); + return mBegin == inlineStorage(); + } + + T *inlineStorage() const { + return (T *)storage.addr(); } T *beginNoCheck() const { @@ -427,7 +431,7 @@ class Vector : private AllocPolicy internalAppendN(t, n); } template void infallibleAppend(const U *begin, const U *end) { - internalAppend(begin, PointerRangeSize(begin, end)); + internalAppend(begin, mozilla::PointerRangeSize(begin, end)); } template void infallibleAppend(const U *begin, size_t length) { internalAppend(begin, length); @@ -479,6 +483,8 @@ class Vector : private AllocPolicy * object (which must be heap-allocated) itself. */ size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const; + + void swap(Vector &other); }; /* This does the re-entrancy check plus several other sanity checks. */ @@ -506,6 +512,9 @@ template JS_ALWAYS_INLINE Vector::Vector(MoveRef rhs) : AllocPolicy(rhs) +#ifdef DEBUG + , entered(false) +#endif { mLength = rhs->mLength; mCapacity = rhs->mCapacity; @@ -856,7 +865,7 @@ JS_ALWAYS_INLINE bool Vector::append(const U *insBegin, const U *insEnd) { REENTRANCY_GUARD_ET_AL; - size_t needed = PointerRangeSize(insBegin, insEnd); + size_t needed = mozilla::PointerRangeSize(insBegin, insEnd); if (mLength + needed > mCapacity && !growStorageBy(needed)) return false; @@ -995,6 +1004,33 @@ Vector::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf); } +template +inline void +Vector::swap(Vector &other) +{ + // TODO Implement N != 0 + JS_STATIC_ASSERT(N == 0); + + // This only works when inline storage is always empty. + if (!usingInlineStorage() && other.usingInlineStorage()) { + other.mBegin = mBegin; + mBegin = inlineStorage(); + } else if (usingInlineStorage() && !other.usingInlineStorage()) { + mBegin = other.mBegin; + other.mBegin = other.inlineStorage(); + } else if (!usingInlineStorage() && !other.usingInlineStorage()) { + Swap(mBegin, other.mBegin); + } else { + // This case is a no-op, since we'd set both to use their inline storage. + } + + Swap(mLength, other.mLength); + Swap(mCapacity, other.mCapacity); +#ifdef DEBUG + Swap(mReserved, other.mReserved); +#endif +} + } /* namespace js */ #ifdef _MSC_VER diff --git a/scripting/javascript/spidermonkey-win32/include/jsalloc.h b/scripting/javascript/spidermonkey-win32/include/jsalloc.h index a5eeca7534..259456ff7e 100644 --- a/scripting/javascript/spidermonkey-win32/include/jsalloc.h +++ b/scripting/javascript/spidermonkey-win32/include/jsalloc.h @@ -18,6 +18,8 @@ namespace js { * - public copy constructor, assignment, destructor * - void *malloc_(size_t) * Responsible for OOM reporting on NULL return value. + * - void *calloc_(size_t) + * Responsible for OOM reporting on NULL return value. * - void *realloc_(size_t) * Responsible for OOM reporting on NULL return value. * The *used* bytes of the previous buffer is passed in @@ -33,6 +35,7 @@ class SystemAllocPolicy { public: void *malloc_(size_t bytes) { return js_malloc(bytes); } + void *calloc_(size_t bytes) { return js_calloc(bytes); } void *realloc_(void *p, size_t oldBytes, size_t bytes) { return js_realloc(p, bytes); } void free_(void *p) { js_free(p); } void reportAllocOverflow() const {} @@ -71,6 +74,13 @@ class TempAllocPolicy return p; } + void *calloc_(size_t bytes) { + void *p = js_calloc(bytes); + if (JS_UNLIKELY(!p)) + p = onOutOfMemory(NULL, bytes); + return p; + } + void *realloc_(void *p, size_t oldBytes, size_t bytes) { void *p2 = js_realloc(p, bytes); if (JS_UNLIKELY(!p2)) diff --git a/scripting/javascript/spidermonkey-win32/include/jsapi.h.REMOVED.git-id b/scripting/javascript/spidermonkey-win32/include/jsapi.h.REMOVED.git-id index 80ca60e448..4ed0fd8565 100644 --- a/scripting/javascript/spidermonkey-win32/include/jsapi.h.REMOVED.git-id +++ b/scripting/javascript/spidermonkey-win32/include/jsapi.h.REMOVED.git-id @@ -1 +1 @@ -4f78a759104eea6e7790c03ce0130299eeaa3968 \ No newline at end of file +ba4ed5550f4042bc9963d0e15cce943b9f0be17a \ No newline at end of file diff --git a/scripting/javascript/spidermonkey-win32/include/jsdbgapi.h b/scripting/javascript/spidermonkey-win32/include/jsdbgapi.h index 4fb4ee1ecf..5f4d68dd46 100644 --- a/scripting/javascript/spidermonkey-win32/include/jsdbgapi.h +++ b/scripting/javascript/spidermonkey-win32/include/jsdbgapi.h @@ -13,7 +13,6 @@ #include "jsapi.h" #include "jsprvtd.h" -#if defined(__cplusplus) namespace JS { struct FrameDescription @@ -47,9 +46,6 @@ JS_FRIEND_API(void) js_DumpValue(const js::Value &val); JS_FRIEND_API(void) js_DumpId(jsid id); JS_FRIEND_API(void) js_DumpStackFrame(JSContext *cx, js::StackFrame *start = NULL); # endif -#endif - -JS_BEGIN_EXTERN_C JS_FRIEND_API(void) js_DumpBacktrace(JSContext *cx); @@ -432,6 +428,4 @@ JS_UnwrapObjectAndInnerize(JSObject *obj); extern JS_FRIEND_API(JSBool) js_CallContextDebugHandler(JSContext *cx); -JS_END_EXTERN_C - #endif /* jsdbgapi_h___ */ diff --git a/scripting/javascript/spidermonkey-win32/include/jsdhash.h b/scripting/javascript/spidermonkey-win32/include/jsdhash.h index 7af17046b0..9553658b57 100644 --- a/scripting/javascript/spidermonkey-win32/include/jsdhash.h +++ b/scripting/javascript/spidermonkey-win32/include/jsdhash.h @@ -14,8 +14,6 @@ #include "jstypes.h" #include "jsutil.h" -JS_BEGIN_EXTERN_C - #if defined(__GNUC__) && defined(__i386__) && (__GNUC__ >= 3) && !defined(XP_OS2) #define JS_DHASH_FASTCALL __attribute__ ((regparm (3),stdcall)) #elif defined(XP_WIN) @@ -598,6 +596,4 @@ extern JS_PUBLIC_API(void) JS_DHashTableDumpMeter(JSDHashTable *table, JSDHashEnumerator dump, FILE *fp); #endif -JS_END_EXTERN_C - #endif /* jsdhash_h___ */ diff --git a/scripting/javascript/spidermonkey-win32/include/jsfriendapi.h b/scripting/javascript/spidermonkey-win32/include/jsfriendapi.h index 63e79aeb14..51e7b90533 100644 --- a/scripting/javascript/spidermonkey-win32/include/jsfriendapi.h +++ b/scripting/javascript/spidermonkey-win32/include/jsfriendapi.h @@ -8,12 +8,28 @@ #define jsfriendapi_h___ #include "jsclass.h" +#include "jscpucfg.h" #include "jspubtd.h" #include "jsprvtd.h" +#include "js/HeapAPI.h" + #include "mozilla/GuardObjects.h" -JS_BEGIN_EXTERN_C +/* + * This macro checks if the stack pointer has exceeded a given limit. If + * |tolerance| is non-zero, it returns true only if the stack pointer has + * exceeded the limit by more than |tolerance| bytes. + */ +#if JS_STACK_GROWTH_DIRECTION > 0 +# define JS_CHECK_STACK_SIZE_WITH_TOLERANCE(limit, sp, tolerance) \ + ((uintptr_t)(sp) < (limit)+(tolerance)) +#else +# define JS_CHECK_STACK_SIZE_WITH_TOLERANCE(limit, sp, tolerance) \ + ((uintptr_t)(sp) > (limit)-(tolerance)) +#endif + +#define JS_CHECK_STACK_SIZE(limit, lval) JS_CHECK_STACK_SIZE_WITH_TOLERANCE(limit, lval, 0) extern JS_FRIEND_API(void) JS_SetGrayGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data); @@ -139,8 +155,6 @@ extern JS_FRIEND_API(void) js_DumpChars(const jschar *s, size_t n); #endif -#ifdef __cplusplus - extern JS_FRIEND_API(bool) JS_CopyPropertiesFrom(JSContext *cx, JSObject *target, JSObject *obj); @@ -171,12 +185,6 @@ struct JSFunctionSpecWithHelp { extern JS_FRIEND_API(bool) JS_DefineFunctionsWithHelp(JSContext *cx, JSObject *obj, const JSFunctionSpecWithHelp *fs); -#endif - -JS_END_EXTERN_C - -#ifdef __cplusplus - typedef bool (* JS_SourceHook)(JSContext *cx, JSScript *script, jschar **src, uint32_t *length); extern JS_FRIEND_API(void) @@ -184,6 +192,8 @@ JS_SetSourceHook(JSRuntime *rt, JS_SourceHook hook); namespace js { +extern mozilla::ThreadLocal TlsPerThreadData; + inline JSRuntime * GetRuntime(const JSContext *cx) { @@ -259,15 +269,34 @@ TraceWeakMaps(WeakMapTracer *trc); extern JS_FRIEND_API(bool) GCThingIsMarkedGray(void *thing); +extern JS_FRIEND_API(bool) +AreGCGrayBitsValid(JSRuntime *rt); + +/* + * Unsets the gray bit for anything reachable from |thing|. |kind| should not be + * JSTRACE_SHAPE. |thing| should be non-null. + */ +extern JS_FRIEND_API(void) +UnmarkGrayGCThingRecursively(void *thing, JSGCTraceKind kind); + typedef void -(GCThingCallback)(void *closure, void *gcthing); +(*GCThingCallback)(void *closure, void *gcthing); extern JS_FRIEND_API(void) -VisitGrayWrapperTargets(JSCompartment *comp, GCThingCallback *callback, void *closure); +VisitGrayWrapperTargets(JSCompartment *comp, GCThingCallback callback, void *closure); extern JS_FRIEND_API(JSObject *) GetWeakmapKeyDelegate(JSObject *key); +JS_FRIEND_API(JSGCTraceKind) +GCThingTraceKind(void *thing); + +/* + * Invoke cellCallback on every gray JS_OBJECT in the given compartment. + */ +extern JS_FRIEND_API(void) +IterateGrayObjects(JSCompartment *compartment, GCThingCallback cellCallback, void *data); + /* * Shadow declarations of JS internal structures, for access by inline access * functions below. Do not use these structures in any other way. When adding @@ -527,7 +556,13 @@ GetNativeStackLimit(const JSRuntime *rt) return RuntimeFriendFields::get(rt)->nativeStackLimit; } -#define JS_CHECK_RECURSION(cx, onerror) \ +/* + * These macros report a stack overflow and run |onerror| if we are close to + * using up the C stack. The JS_CHECK_CHROME_RECURSION variant gives us a little + * extra space so that we can ensure that crucial code is able to run. + */ + +#define JS_CHECK_RECURSION(cx, onerror) \ JS_BEGIN_MACRO \ int stackDummy_; \ if (!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(js::GetRuntime(cx)), &stackDummy_)) { \ @@ -536,6 +571,18 @@ GetNativeStackLimit(const JSRuntime *rt) } \ JS_END_MACRO +#define JS_CHECK_CHROME_RECURSION(cx, onerror) \ + JS_BEGIN_MACRO \ + int stackDummy_; \ + if (!JS_CHECK_STACK_SIZE_WITH_TOLERANCE(js::GetNativeStackLimit(js::GetRuntime(cx)), \ + &stackDummy_, \ + 1024 * sizeof(size_t))) \ + { \ + js_ReportOverRecursed(cx); \ + onerror; \ + } \ + JS_END_MACRO + JS_FRIEND_API(void) StartPCCountProfiling(JSContext *cx); @@ -979,8 +1026,6 @@ uint32_t GetListBaseExpandoSlot(); } /* namespace js */ -#endif - /* Implemented in jsdate.cpp. */ /* @@ -1017,8 +1062,6 @@ js_GetSCOffset(JSStructuredCloneWriter* writer); /* Typed Array functions, implemented in jstypedarray.cpp */ -#ifdef __cplusplus - namespace js { namespace ArrayBufferView { @@ -1038,6 +1081,12 @@ enum ViewType { */ TYPE_UINT8_CLAMPED, + /* + * Type returned for a DataView. Note that there is no single element type + * in this case. + */ + TYPE_DATAVIEW, + TYPE_MAX }; @@ -1045,9 +1094,6 @@ enum ViewType { } /* namespace js */ typedef js::ArrayBufferView::ViewType JSArrayBufferViewType; -#else -typedef uint32_t JSArrayBufferViewType; -#endif /* __cplusplus */ /* * Create a new typed array with nelements elements. @@ -1150,41 +1196,40 @@ JS_NewArrayBuffer(JSContext *cx, uint32_t nbytes); * the various accessor JSAPI calls defined below. */ extern JS_FRIEND_API(JSBool) -JS_IsTypedArrayObject(JSObject *obj, JSContext *cx); +JS_IsTypedArrayObject(JSObject *obj); /* * Check whether obj supports JS_GetArrayBufferView* APIs. Note that this may * return false if a security wrapper is encountered that denies the * unwrapping. If this test or one of the more specific tests succeeds, then it * is safe to call the various ArrayBufferView accessor JSAPI calls defined - * below. cx MUST be non-NULL and valid. + * below. */ extern JS_FRIEND_API(JSBool) -JS_IsArrayBufferViewObject(JSObject *obj, JSContext *cx); +JS_IsArrayBufferViewObject(JSObject *obj); /* * Test for specific typed array types (ArrayBufferView subtypes) */ extern JS_FRIEND_API(JSBool) -JS_IsInt8Array(JSObject *obj, JSContext *cx); +JS_IsInt8Array(JSObject *obj); extern JS_FRIEND_API(JSBool) -JS_IsUint8Array(JSObject *obj, JSContext *cx); +JS_IsUint8Array(JSObject *obj); extern JS_FRIEND_API(JSBool) -JS_IsUint8ClampedArray(JSObject *obj, JSContext *cx); +JS_IsUint8ClampedArray(JSObject *obj); extern JS_FRIEND_API(JSBool) -JS_IsInt16Array(JSObject *obj, JSContext *cx); +JS_IsInt16Array(JSObject *obj); extern JS_FRIEND_API(JSBool) -JS_IsUint16Array(JSObject *obj, JSContext *cx); +JS_IsUint16Array(JSObject *obj); extern JS_FRIEND_API(JSBool) -JS_IsInt32Array(JSObject *obj, JSContext *cx); +JS_IsInt32Array(JSObject *obj); extern JS_FRIEND_API(JSBool) -JS_IsUint32Array(JSObject *obj, JSContext *cx); +JS_IsUint32Array(JSObject *obj); extern JS_FRIEND_API(JSBool) -JS_IsFloat32Array(JSObject *obj, JSContext *cx); +JS_IsFloat32Array(JSObject *obj); extern JS_FRIEND_API(JSBool) -JS_IsFloat64Array(JSObject *obj, JSContext *cx); - +JS_IsFloat64Array(JSObject *obj); /* * Unwrap Typed arrays all at once. Return NULL without throwing if the object @@ -1192,38 +1237,37 @@ JS_IsFloat64Array(JSObject *obj, JSContext *cx); * success, filling both outparameters. */ extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsInt8Array(JSContext *cx, JSObject *obj, uint32_t *length, int8_t **data); +JS_GetObjectAsInt8Array(JSObject *obj, uint32_t *length, int8_t **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsUint8Array(JSContext *cx, JSObject *obj, uint32_t *length, uint8_t **data); +JS_GetObjectAsUint8Array(JSObject *obj, uint32_t *length, uint8_t **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsUint8ClampedArray(JSContext *cx, JSObject *obj, uint32_t *length, uint8_t **data); +JS_GetObjectAsUint8ClampedArray(JSObject *obj, uint32_t *length, uint8_t **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsInt16Array(JSContext *cx, JSObject *obj, uint32_t *length, int16_t **data); +JS_GetObjectAsInt16Array(JSObject *obj, uint32_t *length, int16_t **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsUint16Array(JSContext *cx, JSObject *obj, uint32_t *length, uint16_t **data); +JS_GetObjectAsUint16Array(JSObject *obj, uint32_t *length, uint16_t **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsInt32Array(JSContext *cx, JSObject *obj, uint32_t *length, int32_t **data); +JS_GetObjectAsInt32Array(JSObject *obj, uint32_t *length, int32_t **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsUint32Array(JSContext *cx, JSObject *obj, uint32_t *length, uint32_t **data); +JS_GetObjectAsUint32Array(JSObject *obj, uint32_t *length, uint32_t **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsFloat32Array(JSContext *cx, JSObject *obj, uint32_t *length, float **data); +JS_GetObjectAsFloat32Array(JSObject *obj, uint32_t *length, float **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsFloat64Array(JSContext *cx, JSObject *obj, uint32_t *length, double **data); +JS_GetObjectAsFloat64Array(JSObject *obj, uint32_t *length, double **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsArrayBufferView(JSContext *cx, JSObject *obj, uint32_t *length, uint8_t **data); +JS_GetObjectAsArrayBufferView(JSObject *obj, uint32_t *length, uint8_t **data); extern JS_FRIEND_API(JSObject *) -JS_GetObjectAsArrayBuffer(JSContext *cx, JSObject *obj, uint32_t *length, uint8_t **data); +JS_GetObjectAsArrayBuffer(JSObject *obj, uint32_t *length, uint8_t **data); /* - * Get the type of elements in a typed array. + * Get the type of elements in a typed array, or TYPE_DATAVIEW if a DataView. * - * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow - * be known that it would pass such a test: it is a typed array or a wrapper of - * a typed array, and the unwrapping will succeed. If cx is NULL, then DEBUG - * builds may be unable to assert when unwrapping should be disallowed. + * |obj| must have passed a JS_IsArrayBufferView/JS_Is*Array test, or somehow + * be known that it would pass such a test: it is an ArrayBufferView or a + * wrapper of an ArrayBufferView, and the unwrapping will succeed. */ extern JS_FRIEND_API(JSArrayBufferViewType) -JS_GetTypedArrayType(JSObject *obj, JSContext *maybecx); +JS_GetArrayBufferViewType(JSObject *obj); /* * Check whether obj supports the JS_GetArrayBuffer* APIs. Note that this may @@ -1232,18 +1276,17 @@ JS_GetTypedArrayType(JSObject *obj, JSContext *maybecx); * accessor JSAPI calls defined below. */ extern JS_FRIEND_API(JSBool) -JS_IsArrayBufferObject(JSObject *obj, JSContext *maybecx); +JS_IsArrayBufferObject(JSObject *obj); /* * Return the available byte length of an array buffer. * * |obj| must have passed a JS_IsArrayBufferObject test, or somehow be known * that it would pass such a test: it is an ArrayBuffer or a wrapper of an - * ArrayBuffer, and the unwrapping will succeed. If cx is NULL, then DEBUG - * builds may be unable to assert when unwrapping should be disallowed. + * ArrayBuffer, and the unwrapping will succeed. */ extern JS_FRIEND_API(uint32_t) -JS_GetArrayBufferByteLength(JSObject *obj, JSContext *maybecx); +JS_GetArrayBufferByteLength(JSObject *obj); /* * Return a pointer to an array buffer's data. The buffer is still owned by the @@ -1252,22 +1295,20 @@ JS_GetArrayBufferByteLength(JSObject *obj, JSContext *maybecx); * * |obj| must have passed a JS_IsArrayBufferObject test, or somehow be known * that it would pass such a test: it is an ArrayBuffer or a wrapper of an - * ArrayBuffer, and the unwrapping will succeed. If cx is NULL, then DEBUG - * builds may be unable to assert when unwrapping should be disallowed. + * ArrayBuffer, and the unwrapping will succeed. */ extern JS_FRIEND_API(uint8_t *) -JS_GetArrayBufferData(JSObject *obj, JSContext *maybecx); +JS_GetArrayBufferData(JSObject *obj); /* * Return the number of elements in a typed array. * * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow * be known that it would pass such a test: it is a typed array or a wrapper of - * a typed array, and the unwrapping will succeed. If cx is NULL, then DEBUG - * builds may be unable to assert when unwrapping should be disallowed. + * a typed array, and the unwrapping will succeed. */ extern JS_FRIEND_API(uint32_t) -JS_GetTypedArrayLength(JSObject *obj, JSContext *cx); +JS_GetTypedArrayLength(JSObject *obj); /* * Return the byte offset from the start of an array buffer to the start of a @@ -1275,22 +1316,20 @@ JS_GetTypedArrayLength(JSObject *obj, JSContext *cx); * * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow * be known that it would pass such a test: it is a typed array or a wrapper of - * a typed array, and the unwrapping will succeed. If cx is NULL, then DEBUG - * builds may be unable to assert when unwrapping should be disallowed. + * a typed array, and the unwrapping will succeed. */ extern JS_FRIEND_API(uint32_t) -JS_GetTypedArrayByteOffset(JSObject *obj, JSContext *cx); +JS_GetTypedArrayByteOffset(JSObject *obj); /* * Return the byte length of a typed array. * * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow * be known that it would pass such a test: it is a typed array or a wrapper of - * a typed array, and the unwrapping will succeed. If cx is NULL, then DEBUG - * builds may be unable to assert when unwrapping should be disallowed. + * a typed array, and the unwrapping will succeed. */ extern JS_FRIEND_API(uint32_t) -JS_GetTypedArrayByteLength(JSObject *obj, JSContext *cx); +JS_GetTypedArrayByteLength(JSObject *obj); /* * Check whether obj supports JS_ArrayBufferView* APIs. Note that this may @@ -1298,13 +1337,13 @@ JS_GetTypedArrayByteLength(JSObject *obj, JSContext *cx); * unwrapping. */ extern JS_FRIEND_API(JSBool) -JS_IsArrayBufferViewObject(JSObject *obj, JSContext *cx); +JS_IsArrayBufferViewObject(JSObject *obj); /* * More generic name for JS_GetTypedArrayByteLength to cover DataViews as well */ extern JS_FRIEND_API(uint32_t) -JS_GetArrayBufferViewByteLength(JSObject *obj, JSContext *cx); +JS_GetArrayBufferViewByteLength(JSObject *obj); /* * Return a pointer to the start of the data referenced by a typed array. The @@ -1313,43 +1352,48 @@ JS_GetArrayBufferViewByteLength(JSObject *obj, JSContext *cx); * * |obj| must have passed a JS_Is*Array test, or somehow be known that it would * pass such a test: it is a typed array or a wrapper of a typed array, and the - * unwrapping will succeed. If cx is NULL, then DEBUG builds may be unable to - * assert when unwrapping should be disallowed. + * unwrapping will succeed. */ extern JS_FRIEND_API(int8_t *) -JS_GetInt8ArrayData(JSObject *obj, JSContext *maybecx); +JS_GetInt8ArrayData(JSObject *obj); extern JS_FRIEND_API(uint8_t *) -JS_GetUint8ArrayData(JSObject *obj, JSContext *maybecx); +JS_GetUint8ArrayData(JSObject *obj); extern JS_FRIEND_API(uint8_t *) -JS_GetUint8ClampedArrayData(JSObject *obj, JSContext *maybecx); +JS_GetUint8ClampedArrayData(JSObject *obj); extern JS_FRIEND_API(int16_t *) -JS_GetInt16ArrayData(JSObject *obj, JSContext *maybecx); +JS_GetInt16ArrayData(JSObject *obj); extern JS_FRIEND_API(uint16_t *) -JS_GetUint16ArrayData(JSObject *obj, JSContext *maybecx); +JS_GetUint16ArrayData(JSObject *obj); extern JS_FRIEND_API(int32_t *) -JS_GetInt32ArrayData(JSObject *obj, JSContext *maybecx); +JS_GetInt32ArrayData(JSObject *obj); extern JS_FRIEND_API(uint32_t *) -JS_GetUint32ArrayData(JSObject *obj, JSContext *maybecx); +JS_GetUint32ArrayData(JSObject *obj); extern JS_FRIEND_API(float *) -JS_GetFloat32ArrayData(JSObject *obj, JSContext *maybecx); +JS_GetFloat32ArrayData(JSObject *obj); extern JS_FRIEND_API(double *) -JS_GetFloat64ArrayData(JSObject *obj, JSContext *maybecx); +JS_GetFloat64ArrayData(JSObject *obj); /* * Same as above, but for any kind of ArrayBufferView. Prefer the type-specific * versions when possible. */ extern JS_FRIEND_API(void *) -JS_GetArrayBufferViewData(JSObject *obj, JSContext *maybecx); +JS_GetArrayBufferViewData(JSObject *obj); /* - * Check whether obj supports JS_GetDataView* APIs. Note that this may fail and - * throw an exception if a security wrapper is encountered that denies the - * operation. + * Return the ArrayBuffer underlying an ArrayBufferView. If the buffer has been + * neutered, this will still return the neutered buffer. |obj| must be an + * object that would return true for JS_IsArrayBufferViewObject(). + */ +extern JS_FRIEND_API(JSObject *) +JS_GetArrayBufferViewBuffer(JSObject *obj); + +/* + * Check whether obj supports JS_GetDataView* APIs. */ JS_FRIEND_API(JSBool) -JS_IsDataViewObject(JSContext *cx, JSObject *obj, JSBool *isDataView); +JS_IsDataViewObject(JSObject *obj); /* * Return the byte offset of a data view into its array buffer. |obj| must be a @@ -1357,11 +1401,10 @@ JS_IsDataViewObject(JSContext *cx, JSObject *obj, JSBool *isDataView); * * |obj| must have passed a JS_IsDataViewObject test, or somehow be known that * it would pass such a test: it is a data view or a wrapper of a data view, - * and the unwrapping will succeed. If cx is NULL, then DEBUG builds may be - * unable to assert when unwrapping should be disallowed. + * and the unwrapping will succeed. */ JS_FRIEND_API(uint32_t) -JS_GetDataViewByteOffset(JSObject *obj, JSContext *maybecx); +JS_GetDataViewByteOffset(JSObject *obj); /* * Return the byte length of a data view. @@ -1372,7 +1415,7 @@ JS_GetDataViewByteOffset(JSObject *obj, JSContext *maybecx); * unable to assert when unwrapping should be disallowed. */ JS_FRIEND_API(uint32_t) -JS_GetDataViewByteLength(JSObject *obj, JSContext *maybecx); +JS_GetDataViewByteLength(JSObject *obj); /* * Return a pointer to the beginning of the data referenced by a DataView. @@ -1383,9 +1426,8 @@ JS_GetDataViewByteLength(JSObject *obj, JSContext *maybecx); * unable to assert when unwrapping should be disallowed. */ JS_FRIEND_API(void *) -JS_GetDataViewData(JSObject *obj, JSContext *maybecx); +JS_GetDataViewData(JSObject *obj); -#ifdef __cplusplus /* * This struct contains metadata passed from the DOM to the JS Engine for JIT * optimizations on DOM property accessors. Eventually, this should be made @@ -1420,15 +1462,16 @@ FUNCTION_VALUE_TO_JITINFO(const JS::Value& v) return reinterpret_cast(&v.toObject())->jitinfo; } +/* Statically asserted in jsfun.h. */ +static const unsigned JS_FUNCTION_INTERPRETED_BIT = 0x1; + static JS_ALWAYS_INLINE void SET_JITINFO(JSFunction * func, const JSJitInfo *info) { js::shadow::Function *fun = reinterpret_cast(func); - /* JS_ASSERT(func->isNative()). 0x4000 is JSFUN_INTERPRETED */ - JS_ASSERT(!(fun->flags & 0x4000)); + JS_ASSERT(!(fun->flags & JS_FUNCTION_INTERPRETED_BIT)); fun->jitinfo = info; } -#endif /* __cplusplus */ /* * Engine-internal extensions of jsid. This code is here only until we @@ -1494,8 +1537,6 @@ JSID_TO_ATOM(jsid id) JS_STATIC_ASSERT(sizeof(jsid) == JS_BYTES_PER_WORD); -#ifdef __cplusplus - namespace js { static JS_ALWAYS_INLINE Value @@ -1517,8 +1558,12 @@ IdToJsval(jsid id) return IdToValue(id); } +extern JS_FRIEND_API(bool) +IsReadOnlyDateMethod(JS::IsAcceptableThis test, JS::NativeImpl method); + +extern JS_FRIEND_API(bool) +IsTypedArrayThisCheck(JS::IsAcceptableThis test); + } /* namespace js */ -#endif /* __cplusplus */ - #endif /* jsfriendapi_h___ */ diff --git a/scripting/javascript/spidermonkey-win32/include/jsgc.h b/scripting/javascript/spidermonkey-win32/include/jsgc.h deleted file mode 100644 index 97e4610a44..0000000000 --- a/scripting/javascript/spidermonkey-win32/include/jsgc.h +++ /dev/null @@ -1,1230 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef jsgc_h___ -#define jsgc_h___ - -/* - * JS Garbage Collector. - */ -#include - -#include "mozilla/Util.h" - -#include "jsalloc.h" -#include "jstypes.h" -#include "jsprvtd.h" -#include "jspubtd.h" -#include "jslock.h" -#include "jsutil.h" -#include "jsversion.h" - -#include "ds/BitArray.h" -#include "gc/Heap.h" -#include "gc/Statistics.h" -#include "js/HashTable.h" -#include "js/Vector.h" -#include "js/TemplateLib.h" - -struct JSCompartment; - -#if JS_STACK_GROWTH_DIRECTION > 0 -# define JS_CHECK_STACK_SIZE(limit, lval) ((uintptr_t)(lval) < limit) -#else -# define JS_CHECK_STACK_SIZE(limit, lval) ((uintptr_t)(lval) > limit) -#endif - -namespace js { - -class GCHelperThread; -struct Shape; -struct SliceBudget; - -namespace ion { - class IonCode; -} - -namespace gc { - -enum State { - NO_INCREMENTAL, - MARK_ROOTS, - MARK, - SWEEP, - SWEEP_END, - INVALID -}; - -class ChunkPool { - Chunk *emptyChunkListHead; - size_t emptyCount; - - public: - ChunkPool() - : emptyChunkListHead(NULL), - emptyCount(0) { } - - size_t getEmptyCount() const { - return emptyCount; - } - - inline bool wantBackgroundAllocation(JSRuntime *rt) const; - - /* Must be called with the GC lock taken. */ - inline Chunk *get(JSRuntime *rt); - - /* Must be called either during the GC or with the GC lock taken. */ - inline void put(Chunk *chunk); - - /* - * Return the list of chunks that can be released outside the GC lock. - * Must be called either during the GC or with the GC lock taken. - */ - Chunk *expire(JSRuntime *rt, bool releaseAll); - - /* Must be called with the GC lock taken. */ - void expireAndFree(JSRuntime *rt, bool releaseAll); -}; - -static inline JSGCTraceKind -MapAllocToTraceKind(AllocKind thingKind) -{ - static const JSGCTraceKind map[FINALIZE_LIMIT] = { - JSTRACE_OBJECT, /* FINALIZE_OBJECT0 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT0_BACKGROUND */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT2 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT2_BACKGROUND */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT4 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT4_BACKGROUND */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT8 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT8_BACKGROUND */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT12 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT12_BACKGROUND */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT16 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT16_BACKGROUND */ - JSTRACE_SCRIPT, /* FINALIZE_SCRIPT */ - JSTRACE_SHAPE, /* FINALIZE_SHAPE */ - JSTRACE_BASE_SHAPE, /* FINALIZE_BASE_SHAPE */ - JSTRACE_TYPE_OBJECT,/* FINALIZE_TYPE_OBJECT */ -#if JS_HAS_XML_SUPPORT /* FINALIZE_XML */ - JSTRACE_XML, -#endif - JSTRACE_STRING, /* FINALIZE_SHORT_STRING */ - JSTRACE_STRING, /* FINALIZE_STRING */ - JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING */ - JSTRACE_IONCODE, /* FINALIZE_IONCODE */ - }; - return map[thingKind]; -} - -static inline bool -IsNurseryAllocable(AllocKind kind) -{ - JS_ASSERT(kind >= 0 && unsigned(kind) < FINALIZE_LIMIT); - static const bool map[FINALIZE_LIMIT] = { - false, /* FINALIZE_OBJECT0 */ - true, /* FINALIZE_OBJECT0_BACKGROUND */ - false, /* FINALIZE_OBJECT2 */ - true, /* FINALIZE_OBJECT2_BACKGROUND */ - false, /* FINALIZE_OBJECT4 */ - true, /* FINALIZE_OBJECT4_BACKGROUND */ - false, /* FINALIZE_OBJECT8 */ - true, /* FINALIZE_OBJECT8_BACKGROUND */ - false, /* FINALIZE_OBJECT12 */ - true, /* FINALIZE_OBJECT12_BACKGROUND */ - false, /* FINALIZE_OBJECT16 */ - true, /* FINALIZE_OBJECT16_BACKGROUND */ - false, /* FINALIZE_SCRIPT */ - false, /* FINALIZE_SHAPE */ - false, /* FINALIZE_BASE_SHAPE */ - false, /* FINALIZE_TYPE_OBJECT */ -#if JS_HAS_XML_SUPPORT - false, /* FINALIZE_XML */ -#endif - true, /* FINALIZE_SHORT_STRING */ - true, /* FINALIZE_STRING */ - false /* FINALIZE_EXTERNAL_STRING */ - }; - return map[kind]; -} - -static inline bool -IsBackgroundFinalized(AllocKind kind) -{ - JS_ASSERT(kind >= 0 && unsigned(kind) < FINALIZE_LIMIT); - static const bool map[FINALIZE_LIMIT] = { - false, /* FINALIZE_OBJECT0 */ - true, /* FINALIZE_OBJECT0_BACKGROUND */ - false, /* FINALIZE_OBJECT2 */ - true, /* FINALIZE_OBJECT2_BACKGROUND */ - false, /* FINALIZE_OBJECT4 */ - true, /* FINALIZE_OBJECT4_BACKGROUND */ - false, /* FINALIZE_OBJECT8 */ - true, /* FINALIZE_OBJECT8_BACKGROUND */ - false, /* FINALIZE_OBJECT12 */ - true, /* FINALIZE_OBJECT12_BACKGROUND */ - false, /* FINALIZE_OBJECT16 */ - true, /* FINALIZE_OBJECT16_BACKGROUND */ - false, /* FINALIZE_SCRIPT */ - false, /* FINALIZE_SHAPE */ - false, /* FINALIZE_BASE_SHAPE */ - false, /* FINALIZE_TYPE_OBJECT */ -#if JS_HAS_XML_SUPPORT - false, /* FINALIZE_XML */ -#endif - true, /* FINALIZE_SHORT_STRING */ - true, /* FINALIZE_STRING */ - false /* FINALIZE_EXTERNAL_STRING */ - }; - return map[kind]; -} - -inline JSGCTraceKind -GetGCThingTraceKind(const void *thing); - -/* - * ArenaList::head points to the start of the list. Normally cursor points - * to the first arena in the list with some free things and all arenas - * before cursor are fully allocated. However, as the arena currently being - * allocated from is considered full while its list of free spans is moved - * into the freeList, during the GC or cell enumeration, when an - * unallocated freeList is moved back to the arena, we can see an arena - * with some free cells before the cursor. The cursor is an indirect - * pointer to allow for efficient list insertion at the cursor point and - * other list manipulations. - */ -struct ArenaList { - ArenaHeader *head; - ArenaHeader **cursor; - - ArenaList() { - clear(); - } - - void clear() { - head = NULL; - cursor = &head; - } - - void insert(ArenaHeader *arena); -}; - -struct ArenaLists { - - private: - /* - * For each arena kind its free list is represented as the first span with - * free things. Initially all the spans are initialized as empty. After we - * find a new arena with available things we move its first free span into - * the list and set the arena as fully allocated. way we do not need to - * update the arena header after the initial allocation. When starting the - * GC we only move the head of the of the list of spans back to the arena - * only for the arena that was not fully allocated. - */ - FreeSpan freeLists[FINALIZE_LIMIT]; - - ArenaList arenaLists[FINALIZE_LIMIT]; - - /* - * The background finalization adds the finalized arenas to the list at - * the *cursor position. backgroundFinalizeState controls the interaction - * between the GC lock and the access to the list from the allocation - * thread. - * - * BFS_DONE indicates that the finalizations is not running or cannot - * affect this arena list. The allocation thread can access the list - * outside the GC lock. - * - * In BFS_RUN and BFS_JUST_FINISHED the allocation thread must take the - * lock. The former indicates that the finalization still runs. The latter - * signals that finalization just added to the list finalized arenas. In - * that case the lock effectively serves as a read barrier to ensure that - * the allocation thread see all the writes done during finalization. - */ - enum BackgroundFinalizeState { - BFS_DONE, - BFS_RUN, - BFS_JUST_FINISHED - }; - - volatile uintptr_t backgroundFinalizeState[FINALIZE_LIMIT]; - - public: - /* For each arena kind, a list of arenas remaining to be swept. */ - ArenaHeader *arenaListsToSweep[FINALIZE_LIMIT]; - - public: - ArenaLists() { - for (size_t i = 0; i != FINALIZE_LIMIT; ++i) - freeLists[i].initAsEmpty(); - for (size_t i = 0; i != FINALIZE_LIMIT; ++i) - backgroundFinalizeState[i] = BFS_DONE; - for (size_t i = 0; i != FINALIZE_LIMIT; ++i) - arenaListsToSweep[i] = NULL; - } - - ~ArenaLists() { - for (size_t i = 0; i != FINALIZE_LIMIT; ++i) { - /* - * We can only call this during the shutdown after the last GC when - * the background finalization is disabled. - */ - JS_ASSERT(backgroundFinalizeState[i] == BFS_DONE); - ArenaHeader **headp = &arenaLists[i].head; - while (ArenaHeader *aheader = *headp) { - *headp = aheader->next; - aheader->chunk()->releaseArena(aheader); - } - } - } - - const FreeSpan *getFreeList(AllocKind thingKind) const { - return &freeLists[thingKind]; - } - - ArenaHeader *getFirstArena(AllocKind thingKind) const { - return arenaLists[thingKind].head; - } - - ArenaHeader *getFirstArenaToSweep(AllocKind thingKind) const { - return arenaListsToSweep[thingKind]; - } - - bool arenaListsAreEmpty() const { - for (size_t i = 0; i != FINALIZE_LIMIT; ++i) { - /* - * The arena cannot be empty if the background finalization is not yet - * done. - */ - if (backgroundFinalizeState[i] != BFS_DONE) - return false; - if (arenaLists[i].head) - return false; - } - return true; - } - - bool arenasAreFull(AllocKind thingKind) const { - return !*arenaLists[thingKind].cursor; - } - - void unmarkAll() { - for (size_t i = 0; i != FINALIZE_LIMIT; ++i) { - /* The background finalization must have stopped at this point. */ - JS_ASSERT(backgroundFinalizeState[i] == BFS_DONE || - backgroundFinalizeState[i] == BFS_JUST_FINISHED); - for (ArenaHeader *aheader = arenaLists[i].head; aheader; aheader = aheader->next) { - uintptr_t *word = aheader->chunk()->bitmap.arenaBits(aheader); - memset(word, 0, ArenaBitmapWords * sizeof(uintptr_t)); - } - } - } - - bool doneBackgroundFinalize(AllocKind kind) const { - return backgroundFinalizeState[kind] == BFS_DONE || - backgroundFinalizeState[kind] == BFS_JUST_FINISHED; - } - - /* - * Return the free list back to the arena so the GC finalization will not - * run the finalizers over unitialized bytes from free things. - */ - void purge() { - for (size_t i = 0; i != FINALIZE_LIMIT; ++i) { - FreeSpan *headSpan = &freeLists[i]; - if (!headSpan->isEmpty()) { - ArenaHeader *aheader = headSpan->arenaHeader(); - aheader->setFirstFreeSpan(headSpan); - headSpan->initAsEmpty(); - } - } - } - - inline void prepareForIncrementalGC(JSRuntime *rt); - - /* - * Temporarily copy the free list heads to the arenas so the code can see - * the proper value in ArenaHeader::freeList when accessing the latter - * outside the GC. - */ - void copyFreeListsToArenas() { - for (size_t i = 0; i != FINALIZE_LIMIT; ++i) - copyFreeListToArena(AllocKind(i)); - } - - void copyFreeListToArena(AllocKind thingKind) { - FreeSpan *headSpan = &freeLists[thingKind]; - if (!headSpan->isEmpty()) { - ArenaHeader *aheader = headSpan->arenaHeader(); - JS_ASSERT(!aheader->hasFreeThings()); - aheader->setFirstFreeSpan(headSpan); - } - } - - /* - * Clear the free lists in arenas that were temporarily set there using - * copyToArenas. - */ - void clearFreeListsInArenas() { - for (size_t i = 0; i != FINALIZE_LIMIT; ++i) - clearFreeListInArena(AllocKind(i)); - } - - - void clearFreeListInArena(AllocKind kind) { - FreeSpan *headSpan = &freeLists[kind]; - if (!headSpan->isEmpty()) { - ArenaHeader *aheader = headSpan->arenaHeader(); - JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(headSpan)); - aheader->setAsFullyUsed(); - } - } - - /* - * Check that the free list is either empty or were synchronized with the - * arena using copyToArena(). - */ - bool isSynchronizedFreeList(AllocKind kind) { - FreeSpan *headSpan = &freeLists[kind]; - if (headSpan->isEmpty()) - return true; - ArenaHeader *aheader = headSpan->arenaHeader(); - if (aheader->hasFreeThings()) { - /* - * If the arena has a free list, it must be the same as one in - * lists. - */ - JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(headSpan)); - return true; - } - return false; - } - - JS_ALWAYS_INLINE void *allocateFromFreeList(AllocKind thingKind, size_t thingSize) { - return freeLists[thingKind].allocate(thingSize); - } - - static void *refillFreeList(JSContext *cx, AllocKind thingKind); - - void checkEmptyFreeLists() { -#ifdef DEBUG - for (size_t i = 0; i < mozilla::ArrayLength(freeLists); ++i) - JS_ASSERT(freeLists[i].isEmpty()); -#endif - } - - void checkEmptyFreeList(AllocKind kind) { - JS_ASSERT(freeLists[kind].isEmpty()); - } - - void queueObjectsForSweep(FreeOp *fop); - void queueStringsForSweep(FreeOp *fop); - void queueShapesForSweep(FreeOp *fop); - void queueScriptsForSweep(FreeOp *fop); - void queueIonCodeForSweep(FreeOp *fop); - - bool foregroundFinalize(FreeOp *fop, AllocKind thingKind, SliceBudget &sliceBudget); - static void backgroundFinalize(FreeOp *fop, ArenaHeader *listHead, bool onBackgroundThread); - - private: - inline void finalizeNow(FreeOp *fop, AllocKind thingKind); - inline void queueForForegroundSweep(FreeOp *fop, AllocKind thingKind); - inline void queueForBackgroundSweep(FreeOp *fop, AllocKind thingKind); - - inline void *allocateFromArena(JSCompartment *comp, AllocKind thingKind); -}; - -/* - * Initial allocation size for data structures holding chunks is set to hold - * chunks with total capacity of 16MB to avoid buffer resizes during browser - * startup. - */ -const size_t INITIAL_CHUNK_CAPACITY = 16 * 1024 * 1024 / ChunkSize; - -/* The number of GC cycles an empty chunk can survive before been released. */ -const size_t MAX_EMPTY_CHUNK_AGE = 4; - -inline Cell * -AsCell(JSObject *obj) -{ - return reinterpret_cast(obj); -} - -} /* namespace gc */ - -struct GCPtrHasher -{ - typedef void *Lookup; - - static HashNumber hash(void *key) { - return HashNumber(uintptr_t(key) >> JS_GCTHING_ZEROBITS); - } - - static bool match(void *l, void *k) { return l == k; } -}; - -typedef HashMap GCLocks; - -struct RootInfo { - RootInfo() {} - RootInfo(const char *name, JSGCRootType type) : name(name), type(type) {} - const char *name; - JSGCRootType type; -}; - -typedef js::HashMap, - js::SystemAllocPolicy> RootedValueMap; - -} /* namespace js */ - -extern JS_FRIEND_API(JSGCTraceKind) -js_GetGCThingTraceKind(void *thing); - -extern JSBool -js_InitGC(JSRuntime *rt, uint32_t maxbytes); - -extern void -js_FinishGC(JSRuntime *rt); - -extern JSBool -js_AddRoot(JSContext *cx, js::Value *vp, const char *name); - -extern JSBool -js_AddGCThingRoot(JSContext *cx, void **rp, const char *name); - -#ifdef DEBUG -extern void -js_DumpNamedRoots(JSRuntime *rt, - void (*dump)(const char *name, void *rp, JSGCRootType type, void *data), - void *data); -#endif - -extern uint32_t -js_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data); - -/* Table of pointers with count valid members. */ -typedef struct JSPtrTable { - size_t count; - void **array; -} JSPtrTable; - -extern JSBool -js_LockGCThingRT(JSRuntime *rt, void *thing); - -extern void -js_UnlockGCThingRT(JSRuntime *rt, void *thing); - -extern bool -js_IsAddressableGCThing(JSRuntime *rt, uintptr_t w, js::gc::AllocKind *thingKind, void **thing); - -namespace js { - -extern void -MarkCompartmentActive(js::StackFrame *fp); - -extern void -TraceRuntime(JSTracer *trc); - -extern JS_FRIEND_API(void) -MarkContext(JSTracer *trc, JSContext *acx); - -/* Must be called with GC lock taken. */ -extern void -TriggerGC(JSRuntime *rt, js::gcreason::Reason reason); - -/* Must be called with GC lock taken. */ -extern void -TriggerCompartmentGC(JSCompartment *comp, js::gcreason::Reason reason); - -extern void -MaybeGC(JSContext *cx); - -extern void -ShrinkGCBuffers(JSRuntime *rt); - -extern void -ReleaseAllJITCode(FreeOp *op); - -extern JS_FRIEND_API(void) -PrepareForFullGC(JSRuntime *rt); - -/* - * Kinds of js_GC invocation. - */ -typedef enum JSGCInvocationKind { - /* Normal invocation. */ - GC_NORMAL = 0, - - /* Minimize GC triggers and release empty GC chunks right away. */ - GC_SHRINK = 1 -} JSGCInvocationKind; - -extern void -GC(JSRuntime *rt, JSGCInvocationKind gckind, js::gcreason::Reason reason); - -extern void -GCSlice(JSRuntime *rt, JSGCInvocationKind gckind, js::gcreason::Reason reason, int64_t millis = 0); - -extern void -GCFinalSlice(JSRuntime *rt, JSGCInvocationKind gckind, js::gcreason::Reason reason); - -extern void -GCDebugSlice(JSRuntime *rt, bool limit, int64_t objCount); - -extern void -PrepareForDebugGC(JSRuntime *rt); - -} /* namespace js */ - -namespace js { - -void -InitTracer(JSTracer *trc, JSRuntime *rt, JSTraceCallback callback); - -/* - * Helper that implements sweeping and allocation for kinds that can be swept - * and allocated off the main thread. - * - * In non-threadsafe builds, all actual sweeping and allocation is performed - * on the main thread, but GCHelperThread encapsulates this from clients as - * much as possible. - */ -class GCHelperThread { - enum State { - IDLE, - SWEEPING, - ALLOCATING, - CANCEL_ALLOCATION, - SHUTDOWN - }; - - /* - * During the finalization we do not free immediately. Rather we add the - * corresponding pointers to a buffer which we later release on a - * separated thread. - * - * The buffer is implemented as a vector of 64K arrays of pointers, not as - * a simple vector, to avoid realloc calls during the vector growth and to - * not bloat the binary size of the inlined freeLater method. Any OOM - * during buffer growth results in the pointer being freed immediately. - */ - static const size_t FREE_ARRAY_SIZE = size_t(1) << 16; - static const size_t FREE_ARRAY_LENGTH = FREE_ARRAY_SIZE / sizeof(void *); - - JSRuntime *const rt; - PRThread *thread; - PRCondVar *wakeup; - PRCondVar *done; - volatile State state; - - bool sweepFlag; - bool shrinkFlag; - - Vector freeVector; - void **freeCursor; - void **freeCursorEnd; - - bool backgroundAllocation; - - friend struct js::gc::ArenaLists; - - JS_FRIEND_API(void) - replenishAndFreeLater(void *ptr); - - static void freeElementsAndArray(void **array, void **end) { - JS_ASSERT(array <= end); - for (void **p = array; p != end; ++p) - js_free(*p); - js_free(array); - } - - static void threadMain(void* arg); - void threadLoop(); - - /* Must be called with the GC lock taken. */ - void doSweep(); - - public: - GCHelperThread(JSRuntime *rt) - : rt(rt), - thread(NULL), - wakeup(NULL), - done(NULL), - state(IDLE), - sweepFlag(false), - shrinkFlag(false), - freeCursor(NULL), - freeCursorEnd(NULL), - backgroundAllocation(true) - { } - - bool init(); - void finish(); - - /* Must be called with the GC lock taken. */ - void startBackgroundSweep(bool shouldShrink); - - /* Must be called with the GC lock taken. */ - void startBackgroundShrink(); - - /* Must be called without the GC lock taken. */ - void waitBackgroundSweepEnd(); - - /* Must be called without the GC lock taken. */ - void waitBackgroundSweepOrAllocEnd(); - - /* Must be called with the GC lock taken. */ - inline void startBackgroundAllocationIfIdle(); - - bool canBackgroundAllocate() const { - return backgroundAllocation; - } - - void disableBackgroundAllocation() { - backgroundAllocation = false; - } - - PRThread *getThread() const { - return thread; - } - - /* - * Outside the GC lock may give true answer when in fact the sweeping has - * been done. - */ - bool sweeping() const { - return state == SWEEPING; - } - - bool shouldShrink() const { - JS_ASSERT(sweeping()); - return shrinkFlag; - } - - void freeLater(void *ptr) { - JS_ASSERT(!sweeping()); - if (freeCursor != freeCursorEnd) - *freeCursor++ = ptr; - else - replenishAndFreeLater(ptr); - } -}; - - -struct GCChunkHasher { - typedef gc::Chunk *Lookup; - - /* - * Strip zeros for better distribution after multiplying by the golden - * ratio. - */ - static HashNumber hash(gc::Chunk *chunk) { - JS_ASSERT(!(uintptr_t(chunk) & gc::ChunkMask)); - return HashNumber(uintptr_t(chunk) >> gc::ChunkShift); - } - - static bool match(gc::Chunk *k, gc::Chunk *l) { - JS_ASSERT(!(uintptr_t(k) & gc::ChunkMask)); - JS_ASSERT(!(uintptr_t(l) & gc::ChunkMask)); - return k == l; - } -}; - -typedef HashSet GCChunkSet; - -template -struct MarkStack { - T *stack; - T *tos; - T *limit; - - T *ballast; - T *ballastLimit; - - size_t sizeLimit; - - MarkStack(size_t sizeLimit) - : stack(NULL), - tos(NULL), - limit(NULL), - ballast(NULL), - ballastLimit(NULL), - sizeLimit(sizeLimit) { } - - ~MarkStack() { - if (stack != ballast) - js_free(stack); - js_free(ballast); - } - - bool init(size_t ballastcap) { - JS_ASSERT(!stack); - - if (ballastcap == 0) - return true; - - ballast = js_pod_malloc(ballastcap); - if (!ballast) - return false; - ballastLimit = ballast + ballastcap; - initFromBallast(); - return true; - } - - void initFromBallast() { - stack = ballast; - limit = ballastLimit; - if (size_t(limit - stack) > sizeLimit) - limit = stack + sizeLimit; - tos = stack; - } - - void setSizeLimit(size_t size) { - JS_ASSERT(isEmpty()); - - sizeLimit = size; - reset(); - } - - bool push(T item) { - if (tos == limit) { - if (!enlarge()) - return false; - } - JS_ASSERT(tos < limit); - *tos++ = item; - return true; - } - - bool push(T item1, T item2, T item3) { - T *nextTos = tos + 3; - if (nextTos > limit) { - if (!enlarge()) - return false; - nextTos = tos + 3; - } - JS_ASSERT(nextTos <= limit); - tos[0] = item1; - tos[1] = item2; - tos[2] = item3; - tos = nextTos; - return true; - } - - bool isEmpty() const { - return tos == stack; - } - - T pop() { - JS_ASSERT(!isEmpty()); - return *--tos; - } - - ptrdiff_t position() const { - return tos - stack; - } - - void reset() { - if (stack != ballast) - js_free(stack); - initFromBallast(); - JS_ASSERT(stack == ballast); - } - - bool enlarge() { - size_t tosIndex = tos - stack; - size_t cap = limit - stack; - if (cap == sizeLimit) - return false; - size_t newcap = cap * 2; - if (newcap == 0) - newcap = 32; - if (newcap > sizeLimit) - newcap = sizeLimit; - - T *newStack; - if (stack == ballast) { - newStack = js_pod_malloc(newcap); - if (!newStack) - return false; - for (T *src = stack, *dst = newStack; src < tos; ) - *dst++ = *src++; - } else { - newStack = (T *)js_realloc(stack, sizeof(T) * newcap); - if (!newStack) - return false; - } - stack = newStack; - tos = stack + tosIndex; - limit = newStack + newcap; - return true; - } - - size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const { - size_t n = 0; - if (stack != ballast) - n += mallocSizeOf(stack); - n += mallocSizeOf(ballast); - return n; - } -}; - -/* - * This class records how much work has been done in a given GC slice, so that - * we can return before pausing for too long. Some slices are allowed to run for - * unlimited time, and others are bounded. To reduce the number of gettimeofday - * calls, we only check the time every 1000 operations. - */ -struct SliceBudget { - int64_t deadline; /* in microseconds */ - intptr_t counter; - - static const intptr_t CounterReset = 1000; - - static const int64_t Unlimited = 0; - static int64_t TimeBudget(int64_t millis); - static int64_t WorkBudget(int64_t work); - - /* Equivalent to SliceBudget(UnlimitedBudget). */ - SliceBudget(); - - /* Instantiate as SliceBudget(Time/WorkBudget(n)). */ - SliceBudget(int64_t budget); - - void reset() { - deadline = INT64_MAX; - counter = INTPTR_MAX; - } - - void step(intptr_t amt = 1) { - counter -= amt; - } - - bool checkOverBudget(); - - bool isOverBudget() { - if (counter >= 0) - return false; - return checkOverBudget(); - } -}; - -static const size_t MARK_STACK_LENGTH = 32768; - -struct GCMarker : public JSTracer { - private: - /* - * We use a common mark stack to mark GC things of different types and use - * the explicit tags to distinguish them when it cannot be deduced from - * the context of push or pop operation. - */ - enum StackTag { - ValueArrayTag, - ObjectTag, - TypeTag, - XmlTag, - ArenaTag, - SavedValueArrayTag, - IonCodeTag, - LastTag = IonCodeTag - }; - - static const uintptr_t StackTagMask = 7; - - static void staticAsserts() { - JS_STATIC_ASSERT(StackTagMask >= uintptr_t(LastTag)); - JS_STATIC_ASSERT(StackTagMask <= gc::Cell::CellMask); - } - - public: - explicit GCMarker(); - bool init(); - - void setSizeLimit(size_t size) { stack.setSizeLimit(size); } - size_t sizeLimit() const { return stack.sizeLimit; } - - void start(JSRuntime *rt); - void stop(); - void reset(); - - void pushObject(JSObject *obj) { - pushTaggedPtr(ObjectTag, obj); - } - - void pushArenaList(gc::ArenaHeader *firstArena) { - pushTaggedPtr(ArenaTag, firstArena); - } - - void pushType(types::TypeObject *type) { - pushTaggedPtr(TypeTag, type); - } - -#if JS_HAS_XML_SUPPORT - void pushXML(JSXML *xml) { - pushTaggedPtr(XmlTag, xml); - } - -#endif - - void pushIonCode(ion::IonCode *code) { - pushTaggedPtr(IonCodeTag, code); - } - - uint32_t getMarkColor() const { - return color; - } - - /* - * The only valid color transition during a GC is from black to gray. It is - * wrong to switch the mark color from gray to black. The reason is that the - * cycle collector depends on the invariant that there are no black to gray - * edges in the GC heap. This invariant lets the CC not trace through black - * objects. If this invariant is violated, the cycle collector may free - * objects that are still reachable. - */ - void setMarkColorGray() { - JS_ASSERT(isDrained()); - JS_ASSERT(color == gc::BLACK); - color = gc::GRAY; - } - - inline void delayMarkingArena(gc::ArenaHeader *aheader); - void delayMarkingChildren(const void *thing); - void markDelayedChildren(gc::ArenaHeader *aheader); - bool markDelayedChildren(SliceBudget &budget); - bool hasDelayedChildren() const { - return !!unmarkedArenaStackTop; - } - - bool isDrained() { - return isMarkStackEmpty() && !unmarkedArenaStackTop; - } - - bool drainMarkStack(SliceBudget &budget); - - /* - * Gray marking must be done after all black marking is complete. However, - * we do not have write barriers on XPConnect roots. Therefore, XPConnect - * roots must be accumulated in the first slice of incremental GC. We - * accumulate these roots in the GrayRootMarker and then mark them later, - * after black marking is complete. This accumulation can fail, but in that - * case we switch to non-incremental GC. - */ - bool hasBufferedGrayRoots() const; - void startBufferingGrayRoots(); - void endBufferingGrayRoots(); - void markBufferedGrayRoots(); - - static void GrayCallback(JSTracer *trc, void **thing, JSGCTraceKind kind); - - size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const; - - MarkStack stack; - - private: -#ifdef DEBUG - void checkCompartment(void *p); -#else - void checkCompartment(void *p) {} -#endif - - void pushTaggedPtr(StackTag tag, void *ptr) { - checkCompartment(ptr); - uintptr_t addr = reinterpret_cast(ptr); - JS_ASSERT(!(addr & StackTagMask)); - if (!stack.push(addr | uintptr_t(tag))) - delayMarkingChildren(ptr); - } - - void pushValueArray(JSObject *obj, void *start, void *end) { - checkCompartment(obj); - - JS_ASSERT(start <= end); - uintptr_t tagged = reinterpret_cast(obj) | GCMarker::ValueArrayTag; - uintptr_t startAddr = reinterpret_cast(start); - uintptr_t endAddr = reinterpret_cast(end); - - /* - * Push in the reverse order so obj will be on top. If we cannot push - * the array, we trigger delay marking for the whole object. - */ - if (!stack.push(endAddr, startAddr, tagged)) - delayMarkingChildren(obj); - } - - bool isMarkStackEmpty() { - return stack.isEmpty(); - } - - bool restoreValueArray(JSObject *obj, void **vpp, void **endp); - void saveValueRanges(); - inline void processMarkStackTop(SliceBudget &budget); - void processMarkStackOther(SliceBudget &budget, uintptr_t tag, uintptr_t addr); - - void appendGrayRoot(void *thing, JSGCTraceKind kind); - - /* The color is only applied to objects, functions and xml. */ - uint32_t color; - - DebugOnly started; - - /* Pointer to the top of the stack of arenas we are delaying marking on. */ - js::gc::ArenaHeader *unmarkedArenaStackTop; - /* Count of arenas that are currently in the stack. */ - DebugOnly markLaterArenas; - - struct GrayRoot { - void *thing; - JSGCTraceKind kind; -#ifdef DEBUG - JSTraceNamePrinter debugPrinter; - const void *debugPrintArg; - size_t debugPrintIndex; -#endif - - GrayRoot(void *thing, JSGCTraceKind kind) - : thing(thing), kind(kind) {} - }; - - bool grayFailed; - Vector grayRoots; -}; - -void -SetMarkStackLimit(JSRuntime *rt, size_t limit); - -void -MarkStackRangeConservatively(JSTracer *trc, Value *begin, Value *end); - -typedef void (*IterateChunkCallback)(JSRuntime *rt, void *data, gc::Chunk *chunk); -typedef void (*IterateArenaCallback)(JSRuntime *rt, void *data, gc::Arena *arena, - JSGCTraceKind traceKind, size_t thingSize); -typedef void (*IterateCellCallback)(JSRuntime *rt, void *data, void *thing, - JSGCTraceKind traceKind, size_t thingSize); - -/* - * This function calls |compartmentCallback| on every compartment, - * |arenaCallback| on every in-use arena, and |cellCallback| on every in-use - * cell in the GC heap. - */ -extern JS_FRIEND_API(void) -IterateCompartmentsArenasCells(JSRuntime *rt, void *data, - JSIterateCompartmentCallback compartmentCallback, - IterateArenaCallback arenaCallback, - IterateCellCallback cellCallback); - -/* - * Invoke chunkCallback on every in-use chunk. - */ -extern JS_FRIEND_API(void) -IterateChunks(JSRuntime *rt, void *data, IterateChunkCallback chunkCallback); - -/* - * Invoke cellCallback on every in-use object of the specified thing kind for - * the given compartment or for all compartments if it is null. - */ -extern JS_FRIEND_API(void) -IterateCells(JSRuntime *rt, JSCompartment *compartment, gc::AllocKind thingKind, - void *data, IterateCellCallback cellCallback); - -/* - * Invoke cellCallback on every gray JS_OBJECT in the given compartment. - */ -extern JS_FRIEND_API(void) -IterateGrayObjects(JSCompartment *compartment, GCThingCallback *cellCallback, void *data); - -} /* namespace js */ - -extern void -js_FinalizeStringRT(JSRuntime *rt, JSString *str); - -/* - * Macro to test if a traversal is the marking phase of the GC. - */ -#define IS_GC_MARKING_TRACER(trc) \ - ((trc)->callback == NULL || (trc)->callback == GCMarker::GrayCallback) - -namespace js { -namespace gc { - -JSCompartment * -NewCompartment(JSContext *cx, JSPrincipals *principals); - -/* Tries to run a GC no matter what (used for GC zeal). */ -void -RunDebugGC(JSContext *cx); - -void -SetDeterministicGC(JSContext *cx, bool enabled); - -void -SetValidateGC(JSContext *cx, bool enabled); - -const int ZealPokeValue = 1; -const int ZealAllocValue = 2; -const int ZealFrameGCValue = 3; -const int ZealVerifierPreValue = 4; -const int ZealFrameVerifierPreValue = 5; -const int ZealStackRootingSafeValue = 6; -const int ZealStackRootingValue = 7; -const int ZealIncrementalRootsThenFinish = 8; -const int ZealIncrementalMarkAllThenFinish = 9; -const int ZealIncrementalMultipleSlices = 10; -const int ZealVerifierPostValue = 11; -const int ZealFrameVerifierPostValue = 12; -const int ZealPurgeAnalysisValue = 13; - -enum VerifierType { - PreBarrierVerifier, - PostBarrierVerifier -}; - -#ifdef JS_GC_ZEAL - -/* Check that write barriers have been used correctly. See jsgc.cpp. */ -void -VerifyBarriers(JSRuntime *rt, VerifierType type); - -void -MaybeVerifyBarriers(JSContext *cx, bool always = false); - -#else - -static inline void -VerifyBarriers(JSRuntime *rt, VerifierType type) -{ -} - -static inline void -MaybeVerifyBarriers(JSContext *cx, bool always = false) -{ -} - -#endif - -} /* namespace gc */ - -static inline JSCompartment * -GetGCThingCompartment(void *thing) -{ - JS_ASSERT(thing); - return reinterpret_cast(thing)->compartment(); -} - -static inline JSCompartment * -GetObjectCompartment(JSObject *obj) -{ - return GetGCThingCompartment(obj); -} - -void -PurgeJITCaches(JSCompartment *c); - -} /* namespace js */ - -#endif /* jsgc_h___ */ diff --git a/scripting/javascript/spidermonkey-win32/include/jslock.h b/scripting/javascript/spidermonkey-win32/include/jslock.h index 11c2893c14..aa5e1c83e7 100644 --- a/scripting/javascript/spidermonkey-win32/include/jslock.h +++ b/scripting/javascript/spidermonkey-win32/include/jslock.h @@ -38,25 +38,4 @@ typedef struct PRLock PRLock; #endif /* JS_THREADSAFE */ -namespace js { - -class AutoAtomicIncrement -{ - int32_t *p; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER - - public: - AutoAtomicIncrement(int32_t *p JS_GUARD_OBJECT_NOTIFIER_PARAM) - : p(p) { - JS_GUARD_OBJECT_NOTIFIER_INIT; - JS_ATOMIC_INCREMENT(p); - } - - ~AutoAtomicIncrement() { - JS_ATOMIC_DECREMENT(p); - } -}; - -} /* namespace js */ - #endif /* jslock_h___ */ diff --git a/scripting/javascript/spidermonkey-win32/include/json.h b/scripting/javascript/spidermonkey-win32/include/json.h index 4794d64e65..bb21f91451 100644 --- a/scripting/javascript/spidermonkey-win32/include/json.h +++ b/scripting/javascript/spidermonkey-win32/include/json.h @@ -38,7 +38,7 @@ enum DecodingMode { STRICT, LEGACY }; namespace js { extern JS_FRIEND_API(JSBool) -ParseJSONWithReviver(JSContext *cx, const jschar *chars, size_t length, HandleValue filter, +ParseJSONWithReviver(JSContext *cx, JS::StableCharPtr chars, size_t length, HandleValue filter, MutableHandleValue vp, DecodingMode decodingMode = STRICT); } /* namespace js */ diff --git a/scripting/javascript/spidermonkey-win32/include/jsprf.h b/scripting/javascript/spidermonkey-win32/include/jsprf.h index 4c72b691f8..4c2bef91d2 100644 --- a/scripting/javascript/spidermonkey-win32/include/jsprf.h +++ b/scripting/javascript/spidermonkey-win32/include/jsprf.h @@ -16,10 +16,9 @@ ** %hd, %hu, %hx, %hX, %ho - 16-bit versions of above ** %ld, %lu, %lx, %lX, %lo - 32-bit versions of above ** %lld, %llu, %llx, %llX, %llo - 64 bit versions of above -** %s - string -** %hs - 16-bit version of above (only available if js_CStringsAreUTF8) +** %s - ascii string +** %hs - ucs2 string ** %c - character -** %hc - 16-bit version of above (only available if js_CStringsAreUTF8) ** %p - pointer (deals with machine dependent pointer size) ** %f - float ** %g - float @@ -28,8 +27,6 @@ #include #include -JS_BEGIN_EXTERN_C - /* ** sprintf into a fixed size buffer. Guarantees that a NUL is at the end ** of the buffer. Returns the length of the written output, NOT including @@ -77,6 +74,4 @@ extern JS_PUBLIC_API(char*) JS_vsmprintf(const char *fmt, va_list ap); extern JS_PUBLIC_API(char*) JS_vsprintf_append(char *last, const char *fmt, va_list ap); extern JS_PUBLIC_API(uint32_t) JS_vsxprintf(JSStuffFunc f, void *arg, const char *fmt, va_list ap); -JS_END_EXTERN_C - #endif /* jsprf_h___ */ diff --git a/scripting/javascript/spidermonkey-win32/include/jsprototypes.h b/scripting/javascript/spidermonkey-win32/include/jsprototypes.h index 5c65eb25cd..1abbc831a9 100644 --- a/scripting/javascript/spidermonkey-win32/include/jsprototypes.h +++ b/scripting/javascript/spidermonkey-win32/include/jsprototypes.h @@ -60,5 +60,6 @@ macro(Set, 38, js_InitSetClass) \ macro(DataView, 39, js_InitTypedArrayClasses) \ macro(ParallelArray, 40, js_InitParallelArrayClass) \ + macro(Intl, 41, js_InitIntlClass) \ #endif /* jsprototypes_h___ */ diff --git a/scripting/javascript/spidermonkey-win32/include/jsproxy.h b/scripting/javascript/spidermonkey-win32/include/jsproxy.h index 075abc7123..73ea5c8c70 100644 --- a/scripting/javascript/spidermonkey-win32/include/jsproxy.h +++ b/scripting/javascript/spidermonkey-win32/include/jsproxy.h @@ -26,7 +26,9 @@ class JS_FRIEND_API(Wrapper); * * Proxy traps are grouped into fundamental and derived traps. Every proxy has * to at least provide implementations for the fundamental traps, but the - * derived traps can be implemented in terms of the fundamental ones. + * derived traps can be implemented in terms of the fundamental ones + * BaseProxyHandler provides implementations of the derived traps in terms of + * the (pure virtual) fundamental traps. * * To minimize code duplication, a set of abstract proxy handler classes is * provided, from which other handlers may inherit. These abstract classes @@ -34,9 +36,9 @@ class JS_FRIEND_API(Wrapper); * * BaseProxyHandler * | - * IndirectProxyHandler - * | * DirectProxyHandler + * | + * Wrapper */ /* @@ -69,20 +71,6 @@ class JS_FRIEND_API(BaseProxyHandler) { return false; } - /* - * The function Wrapper::wrapperHandler takes a pointer to a - * BaseProxyHandler and returns a pointer to a Wrapper if and only if the - * BaseProxyHandler is a wrapper handler (otherwise, it returns NULL). - * - * Unfortunately, we can't inherit Wrapper from BaseProxyHandler, since that - * would create a dreaded diamond, and we can't use dynamic_cast to cast - * BaseProxyHandler to Wrapper, since that would require us to compile with - * run-time type information. Hence the need for this virtual function. - */ - virtual Wrapper *toWrapper() { - return NULL; - } - /* ES5 Harmony fundamental proxy traps. */ virtual bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, PropertyDescriptor *desc) = 0; @@ -130,17 +118,14 @@ class JS_FRIEND_API(BaseProxyHandler) { }; /* - * IndirectProxyHandler assumes that a target exists. Moreover, it assumes the - * target is a JSObject. Consequently, it provides default implementations for - * the fundamental traps that forward their behavior to the target. The derived - * traps, however, are inherited from BaseProxyHandler, and therefore still - * implemented in terms of the fundamental ones. This allows consumers of this - * class to define custom behavior without implementing the entire gamut of - * proxy traps. + * DirectProxyHandler includes a notion of a target object. All traps are + * reimplemented such that they forward their behavior to the target. This + * allows consumers of this class to forward to another object as transparently + * and efficiently as possible. */ -class JS_PUBLIC_API(IndirectProxyHandler) : public BaseProxyHandler { - public: - explicit IndirectProxyHandler(void *family); +class JS_PUBLIC_API(DirectProxyHandler) : public BaseProxyHandler { +public: + explicit DirectProxyHandler(void *family); /* ES5 Harmony fundamental proxy traps. */ virtual bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, @@ -158,11 +143,21 @@ class JS_PUBLIC_API(IndirectProxyHandler) : public BaseProxyHandler { virtual bool enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props) MOZ_OVERRIDE; + /* ES5 Harmony derived proxy traps. */ + virtual bool has(JSContext *cx, JSObject *proxy, jsid id, + bool *bp) MOZ_OVERRIDE; + virtual bool hasOwn(JSContext *cx, JSObject *proxy, jsid id, + bool *bp) MOZ_OVERRIDE; + virtual bool get(JSContext *cx, JSObject *proxy, JSObject *receiver, + jsid id, Value *vp) MOZ_OVERRIDE; + virtual bool set(JSContext *cx, JSObject *proxy, JSObject *receiver, + jsid id, bool strict, Value *vp) MOZ_OVERRIDE; + virtual bool keys(JSContext *cx, JSObject *proxy, + AutoIdVector &props) MOZ_OVERRIDE; + virtual bool iterate(JSContext *cx, JSObject *proxy, unsigned flags, + Value *vp) MOZ_OVERRIDE; + /* Spidermonkey extensions. */ - virtual bool call(JSContext *cx, JSObject *proxy, unsigned argc, - Value *vp) MOZ_OVERRIDE; - virtual bool construct(JSContext *cx, JSObject *proxy, unsigned argc, - Value *argv, Value *rval) MOZ_OVERRIDE; virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args) MOZ_OVERRIDE; virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, @@ -182,33 +177,6 @@ class JS_PUBLIC_API(IndirectProxyHandler) : public BaseProxyHandler { virtual JSObject *weakmapKeyDelegate(JSObject *proxy); }; -/* - * DirectProxyHandler has the same assumptions about the target as its base, - * IndirectProxyHandler. Its fundamental traps are inherited from this class, - * and therefore forward their behavior to the target. The derived traps, - * however, are overrided so that, they too, forward their behavior to the - * target. This allows consumers of this class to forward to another object as - * transparently as possible. - */ -class JS_PUBLIC_API(DirectProxyHandler) : public IndirectProxyHandler { -public: - explicit DirectProxyHandler(void *family); - - /* ES5 Harmony derived proxy traps. */ - virtual bool has(JSContext *cx, JSObject *proxy, jsid id, - bool *bp) MOZ_OVERRIDE; - virtual bool hasOwn(JSContext *cx, JSObject *proxy, jsid id, - bool *bp) MOZ_OVERRIDE; - virtual bool get(JSContext *cx, JSObject *proxy, JSObject *receiver, - jsid id, Value *vp) MOZ_OVERRIDE; - virtual bool set(JSContext *cx, JSObject *proxy, JSObject *receiver, - jsid id, bool strict, Value *vp) MOZ_OVERRIDE; - virtual bool keys(JSContext *cx, JSObject *proxy, - AutoIdVector &props) MOZ_OVERRIDE; - virtual bool iterate(JSContext *cx, JSObject *proxy, unsigned flags, - Value *vp) MOZ_OVERRIDE; -}; - /* Dispatch point for handlers that executes the appropriate C++ or scripted traps. */ class Proxy { public: @@ -350,13 +318,12 @@ NewProxyObject(JSContext *cx, BaseProxyHandler *handler, const Value &priv, JSObject *proto, JSObject *parent, JSObject *call = NULL, JSObject *construct = NULL); -} /* namespace js */ +JSObject * +RenewProxyObject(JSContext *cx, JSObject *obj, BaseProxyHandler *handler, Value priv); -JS_BEGIN_EXTERN_C +} /* namespace js */ extern JS_FRIEND_API(JSObject *) js_InitProxyClass(JSContext *cx, JSHandleObject obj); -JS_END_EXTERN_C - #endif diff --git a/scripting/javascript/spidermonkey-win32/include/jsprvtd.h b/scripting/javascript/spidermonkey-win32/include/jsprvtd.h index 2fbfc54aa1..d399b3fd93 100644 --- a/scripting/javascript/spidermonkey-win32/include/jsprvtd.h +++ b/scripting/javascript/spidermonkey-win32/include/jsprvtd.h @@ -29,8 +29,6 @@ #include "js/Vector.h" #endif -JS_BEGIN_EXTERN_C - /* * Convenience constants. */ @@ -129,7 +127,7 @@ class ScriptFrameIter; class Proxy; class JS_FRIEND_API(BaseProxyHandler); -class JS_FRIEND_API(DirectWrapper); +class JS_FRIEND_API(Wrapper); class JS_FRIEND_API(CrossCompartmentWrapper); class TempAllocPolicy; @@ -379,16 +377,5 @@ typedef JSObject * typedef JSObject * (* JSIteratorOp)(JSContext *cx, JSHandleObject obj, JSBool keysonly); -/* - * The following determines whether JS_EncodeCharacters and JS_DecodeBytes - * treat char[] as utf-8 or simply as bytes that need to be inflated/deflated. - */ -#ifdef JS_C_STRINGS_ARE_UTF8 -# define js_CStringsAreUTF8 JS_TRUE -#else -extern JSBool js_CStringsAreUTF8; -#endif - -JS_END_EXTERN_C #endif /* jsprvtd_h___ */ diff --git a/scripting/javascript/spidermonkey-win32/include/jspubtd.h b/scripting/javascript/spidermonkey-win32/include/jspubtd.h index c14ef29c24..caf75de0f6 100644 --- a/scripting/javascript/spidermonkey-win32/include/jspubtd.h +++ b/scripting/javascript/spidermonkey-win32/include/jspubtd.h @@ -39,6 +39,8 @@ namespace JS { class Value; } */ #ifdef __cplusplus +#define JS_NO_JSVAL_JSID_STRUCT_TYPES + # if defined(DEBUG) && !defined(JS_NO_JSVAL_JSID_STRUCT_TYPES) # define JS_USE_JSID_STRUCT_TYPES # endif @@ -60,8 +62,6 @@ typedef ptrdiff_t jsid; # define JSID_BITS(id) (id) #endif -JS_BEGIN_EXTERN_C - #ifdef WIN32 typedef wchar_t jschar; #else @@ -217,8 +217,6 @@ typedef JSBool JSCallOnceType; #endif typedef JSBool (*JSInitCallback)(void); -JS_END_EXTERN_C - #ifdef __cplusplus namespace js { @@ -311,14 +309,6 @@ struct RuntimeFriendFields { /* Limit pointer for checking native stack consumption. */ uintptr_t nativeStackLimit; -#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) - /* - * Stack allocated GC roots for stack GC heap pointers, which may be - * overwritten if moved during a GC. - */ - Rooted *thingGCRooters[THING_ROOT_LIMIT]; -#endif - RuntimeFriendFields() : interrupt(0), nativeStackLimit(0) { } @@ -328,6 +318,32 @@ struct RuntimeFriendFields { } }; +class PerThreadData; + +struct PerThreadDataFriendFields +{ + PerThreadDataFriendFields(); + +#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) + /* + * Stack allocated GC roots for stack GC heap pointers, which may be + * overwritten if moved during a GC. + */ + Rooted *thingGCRooters[THING_ROOT_LIMIT]; +#endif + + static PerThreadDataFriendFields *get(js::PerThreadData *pt) { + return reinterpret_cast(pt); + } + + static PerThreadDataFriendFields *getMainThread(JSRuntime *rt) { + // mainThread must always appear directly after |RuntimeFriendFields|. + // Tested by a JS_STATIC_ASSERT in |jsfriendapi.cpp| + return reinterpret_cast( + reinterpret_cast(rt) + sizeof(RuntimeFriendFields)); + } +}; + } /* namespace js */ #endif /* __cplusplus */ diff --git a/scripting/javascript/spidermonkey-win32/include/jstypes.h b/scripting/javascript/spidermonkey-win32/include/jstypes.h index d0cf183e0f..90f5d44a64 100644 --- a/scripting/javascript/spidermonkey-win32/include/jstypes.h +++ b/scripting/javascript/spidermonkey-win32/include/jstypes.h @@ -46,11 +46,11 @@ ** ***********************************************************************/ -#define JS_EXTERN_API(type) extern MOZ_EXPORT_API(type) -#define JS_EXPORT_API(type) MOZ_EXPORT_API(type) -#define JS_EXPORT_DATA(type) MOZ_EXPORT_DATA(type) -#define JS_IMPORT_API(type) MOZ_IMPORT_API(type) -#define JS_IMPORT_DATA(type) MOZ_IMPORT_DATA(type) +#define JS_EXTERN_API(type) extern MOZ_EXPORT type +#define JS_EXPORT_API(type) MOZ_EXPORT type +#define JS_EXPORT_DATA(type) MOZ_EXPORT type +#define JS_IMPORT_API(type) MOZ_IMPORT_API type +#define JS_IMPORT_DATA(type) MOZ_IMPORT_DATA type /* * The linkage of JS API functions differs depending on whether the file is @@ -62,11 +62,11 @@ # define JS_PUBLIC_API(t) t # define JS_PUBLIC_DATA(t) t #elif defined(EXPORT_JS_API) || defined(STATIC_EXPORTABLE_JS_API) -# define JS_PUBLIC_API(t) MOZ_EXPORT_API(t) -# define JS_PUBLIC_DATA(t) MOZ_EXPORT_DATA(t) +# define JS_PUBLIC_API(t) MOZ_EXPORT t +# define JS_PUBLIC_DATA(t) MOZ_EXPORT t #else -# define JS_PUBLIC_API(t) MOZ_IMPORT_API(t) -# define JS_PUBLIC_DATA(t) MOZ_IMPORT_DATA(t) +# define JS_PUBLIC_API(t) MOZ_IMPORT_API t +# define JS_PUBLIC_DATA(t) MOZ_IMPORT_DATA t #endif #define JS_FRIEND_API(t) JS_PUBLIC_API(t) @@ -181,8 +181,6 @@ #endif -JS_BEGIN_EXTERN_C - /************************************************************************ ** TYPES: JSBool ** DESCRIPTION: @@ -282,6 +280,4 @@ typedef int JSBool; # define JS_EXTENSION_(s) s #endif -JS_END_EXTERN_C - #endif /* jstypes_h___ */ diff --git a/scripting/javascript/spidermonkey-win32/include/jsutil.h b/scripting/javascript/spidermonkey-win32/include/jsutil.h index 9f0c922a23..ef7f94a824 100644 --- a/scripting/javascript/spidermonkey-win32/include/jsutil.h +++ b/scripting/javascript/spidermonkey-win32/include/jsutil.h @@ -369,23 +369,23 @@ class Compressor z_stream zs; const unsigned char *inp; size_t inplen; + size_t outbytes; + public: - Compressor(const unsigned char *inp, size_t inplen, unsigned char *out) - : inp(inp), - inplen(inplen) - { - JS_ASSERT(inplen > 0); - zs.opaque = NULL; - zs.next_in = (Bytef *)inp; - zs.avail_in = 0; - zs.next_out = out; - zs.avail_out = inplen; - } + enum Status { + MOREOUTPUT, + DONE, + CONTINUE, + OOM + }; + + Compressor(const unsigned char *inp, size_t inplen); + ~Compressor(); bool init(); + void setOutput(unsigned char *out, size_t outlen); + size_t outWritten() const { return outbytes; } /* Compress some of the input. Return true if it should be called again. */ - bool compressMore(); - /* Finalize compression. Return the length of the compressed input. */ - size_t finish(); + Status compressMore(); }; /* diff --git a/scripting/javascript/spidermonkey-win32/include/jsval.h b/scripting/javascript/spidermonkey-win32/include/jsval.h index 67c8be4252..949127aa0d 100644 --- a/scripting/javascript/spidermonkey-win32/include/jsval.h +++ b/scripting/javascript/spidermonkey-win32/include/jsval.h @@ -14,8 +14,6 @@ #include "js/Utility.h" -JS_BEGIN_EXTERN_C - /* * Try to get jsvals 64-bit aligned. We could almost assert that all values are * aligned, but MSVC and GCC occasionally break alignment. @@ -835,8 +833,6 @@ JS_CANONICALIZE_NAN(double d) return d; } -JS_END_EXTERN_C - #ifdef __cplusplus static jsval_layout JSVAL_TO_IMPL(JS::Value); static JS::Value IMPL_TO_JSVAL(jsval_layout); diff --git a/scripting/javascript/spidermonkey-win32/include/jsversion.h b/scripting/javascript/spidermonkey-win32/include/jsversion.h index 5a620c8fcc..475525177a 100644 --- a/scripting/javascript/spidermonkey-win32/include/jsversion.h +++ b/scripting/javascript/spidermonkey-win32/include/jsversion.h @@ -170,4 +170,7 @@ MOZ_NOT_REACHED("don't call this! to be used in the new object representation") #endif +/* ECMAScript Internationalization API isn't fully implemented yet. */ +#define ENABLE_INTL_API 0 + #endif /* jsversion_h___ */ diff --git a/scripting/javascript/spidermonkey-win32/include/jswrapper.h b/scripting/javascript/spidermonkey-win32/include/jswrapper.h index 1ec99cb4f8..e5ef59c6c9 100644 --- a/scripting/javascript/spidermonkey-win32/include/jswrapper.h +++ b/scripting/javascript/spidermonkey-win32/include/jswrapper.h @@ -18,36 +18,24 @@ namespace js { class DummyFrameGuard; /* - * A wrapper is essentially a proxy that restricts access to certain traps. The - * way in which a wrapper restricts access to its traps depends on the - * particular policy for that wrapper. To allow a wrapper's policy to be - * customized, the Wrapper base class contains two functions, enter/leave, which - * are called as a policy enforcement check before/after each trap is forwarded. - * - * To minimize code duplication, a set of abstract wrapper classes is - * provided, from which other wrappers may inherit. These abstract classes are - * organized in the following hierarchy: - * - * BaseProxyHandler Wrapper - * | | | - * IndirectProxyHandler | | - * | | | | - * | IndirectWrapper | - * | | - * DirectProxyHandler | - * | | - * DirectWrapper + * A wrapper is a proxy with a target object to which it generally forwards + * operations, but may restrict access to certain operations or instrument + * the trap operations in various ways. A wrapper is distinct from a Direct Proxy + * Handler in the sense that it can be "unwrapped" in C++, exposing the underlying + * object (Direct Proxy Handlers have an underlying target object, but don't + * expect to expose this object via any kind of unwrapping operation). Callers + * should be careful to avoid unwrapping security wrappers in the wrong context. */ -class JS_FRIEND_API(Wrapper) +class JS_FRIEND_API(Wrapper) : public DirectProxyHandler { unsigned mFlags; + bool mSafeToUnwrap; public: enum Action { GET, SET, - CALL, - PUNCTURE + CALL }; enum Flags { @@ -55,128 +43,42 @@ class JS_FRIEND_API(Wrapper) LAST_USED_FLAG = CROSS_COMPARTMENT }; - typedef enum { - PermitObjectAccess, - PermitPropertyAccess, - DenyAccess - } Permission; + /* + * Wrappers can explicitly specify that they are unsafe to unwrap from a + * security perspective (as is the case for SecurityWrappers). If a wrapper + * is not safe to unwrap, operations requiring full access to the underlying + * object (via UnwrapObjectChecked) will throw. Otherwise, they will succeed. + */ + void setSafeToUnwrap(bool safe) { mSafeToUnwrap = safe; }; + bool isSafeToUnwrap() { return mSafeToUnwrap; }; static JSObject *New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, Wrapper *handler); + static JSObject *Renew(JSContext *cx, JSObject *existing, JSObject *obj, Wrapper *handler); + static Wrapper *wrapperHandler(RawObject wrapper); static JSObject *wrappedObject(RawObject wrapper); - explicit Wrapper(unsigned flags); - unsigned flags() const { return mFlags; } - /* - * The function Wrapper::New takes a pointer to a Wrapper as the handler - * object. It then passes it on to the function NewProxyObject, which - * expects a pointer to a BaseProxyHandler as the handler object. We don't - * want to change Wrapper::New to take a pointer to a BaseProxyHandler, - * because that would allow the creation of wrappers with non-wrapper - * handlers. Unfortunately, we can't inherit Wrapper from BaseProxyHandler, - * since that would create a dreaded diamond, and we can't use dynamic_cast - * to cast Wrapper to BaseProxyHandler, since that would require us to - * compile with run time type information. Hence the need for this virtual - * function. - */ - virtual BaseProxyHandler *toBaseProxyHandler() = 0; - /* Policy enforcement traps. * * enter() allows the policy to specify whether the caller may perform |act| * on the underlying object's |id| property. In the case when |act| is CALL, * |id| is generally JSID_VOID. * - * The |act| parameter to enter() specifies the action being performed. GET, - * SET, and CALL are self-explanatory, but PUNCTURE requires more - * explanation: - * - * GET and SET allow for a very fine-grained security membrane, through - * which access can be granted or denied on a per-property, per-object, and - * per-action basis. Sometimes though, we just want to asks if we can access - * _everything_ behind the wrapper barrier. For example, when the structured - * clone algorithm runs up against a cross-compartment wrapper, it needs to - * know whether it can enter the compartment and keep cloning, or whether it - * should throw. This is the role of PUNCTURE. - * - * PUNCTURE allows the policy to specify whether the wrapper barrier may - * be lifted - that is to say, whether the caller is allowed to access - * anything that the wrapped object could access. This is a very powerful - * permission, and thus should generally be denied for security wrappers - * except under very special circumstances. When |act| is PUNCTURE, |id| - * should be JSID_VOID. + * The |act| parameter to enter() specifies the action being performed. */ virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, Action act, bool *bp); -}; -/* - * IndirectWrapper forwards its traps by forwarding them to - * IndirectProxyHandler. In effect, IndirectWrapper behaves the same as - * IndirectProxyHandler, except that it adds policy enforcement checks to each - * fundamental trap. - */ -class JS_FRIEND_API(IndirectWrapper) : public Wrapper, - public IndirectProxyHandler -{ - public: - explicit IndirectWrapper(unsigned flags); + explicit Wrapper(unsigned flags, bool hasPrototype = false); - virtual BaseProxyHandler* toBaseProxyHandler() { - return this; - } - - virtual Wrapper *toWrapper() { - return this; - } - - /* ES5 Harmony fundamental wrapper traps. */ - virtual bool getPropertyDescriptor(JSContext *cx, JSObject *wrapper, - jsid id, bool set, - PropertyDescriptor *desc) MOZ_OVERRIDE; - virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, - jsid id, bool set, - PropertyDescriptor *desc) MOZ_OVERRIDE; - virtual bool defineProperty(JSContext *cx, JSObject *wrapper, jsid id, - PropertyDescriptor *desc) MOZ_OVERRIDE; - virtual bool getOwnPropertyNames(JSContext *cx, JSObject *wrapper, - AutoIdVector &props) MOZ_OVERRIDE; - virtual bool delete_(JSContext *cx, JSObject *wrapper, jsid id, - bool *bp) MOZ_OVERRIDE; - virtual bool enumerate(JSContext *cx, JSObject *wrapper, - AutoIdVector &props) MOZ_OVERRIDE; - - /* Spidermonkey extensions. */ - virtual bool defaultValue(JSContext *cx, JSObject *wrapper_, JSType hint, - Value *vp) MOZ_OVERRIDE; -}; - -/* - * DirectWrapper forwards its traps by forwarding them to DirectProxyHandler. - * In effect, DirectWrapper behaves the same as DirectProxyHandler, except that - * it adds policy enforcement checks to each trap. - */ -class JS_FRIEND_API(DirectWrapper) : public Wrapper, public DirectProxyHandler -{ - public: - explicit DirectWrapper(unsigned flags, bool hasPrototype = false); - - virtual ~DirectWrapper(); - - virtual BaseProxyHandler* toBaseProxyHandler() { - return this; - } - - virtual Wrapper *toWrapper() { - return this; - } + virtual ~Wrapper(); /* ES5 Harmony fundamental wrapper traps. */ virtual bool getPropertyDescriptor(JSContext *cx, JSObject *wrapper, @@ -214,14 +116,14 @@ class JS_FRIEND_API(DirectWrapper) : public Wrapper, public DirectProxyHandler virtual bool defaultValue(JSContext *cx, JSObject *wrapper_, JSType hint, Value *vp) MOZ_OVERRIDE; - static DirectWrapper singleton; - static DirectWrapper singletonWithPrototype; + static Wrapper singleton; + static Wrapper singletonWithPrototype; static void *getWrapperFamily(); }; /* Base class for all cross compartment wrapper handlers. */ -class JS_FRIEND_API(CrossCompartmentWrapper) : public DirectWrapper +class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper { public: CrossCompartmentWrapper(unsigned flags, bool hasPrototype = false); @@ -280,13 +182,22 @@ class JS_FRIEND_API(SecurityWrapper) : public Base public: SecurityWrapper(unsigned flags); + virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, Wrapper::Action act, + bool *bp) MOZ_OVERRIDE; virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args) MOZ_OVERRIDE; virtual bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx) MOZ_OVERRIDE; virtual bool regexp_toShared(JSContext *cx, JSObject *proxy, RegExpGuard *g) MOZ_OVERRIDE; + + /* + * Allow our subclasses to select the superclass behavior they want without + * needing to specify an exact superclass. + */ + typedef Base Permissive; + typedef SecurityWrapper Restrictive; }; -typedef SecurityWrapper SameCompartmentSecurityWrapper; +typedef SecurityWrapper SameCompartmentSecurityWrapper; typedef SecurityWrapper CrossCompartmentSecurityWrapper; class JS_FRIEND_API(DeadObjectProxy) : public BaseProxyHandler @@ -327,7 +238,8 @@ class JS_FRIEND_API(DeadObjectProxy) : public BaseProxyHandler }; extern JSObject * -TransparentObjectWrapper(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSObject *parent, +TransparentObjectWrapper(JSContext *cx, JSObject *existing, JSObject *obj, + JSObject *wrappedProto, JSObject *parent, unsigned flags); // Proxy family for wrappers. Public so that IsWrapper() can be fully inlined by @@ -352,16 +264,19 @@ UnwrapObject(JSObject *obj, bool stopAtOuter = true, unsigned *flagsp = NULL); // code should never be unwrapping outer window wrappers, we always stop at // outer windows. JS_FRIEND_API(JSObject *) -UnwrapObjectChecked(JSContext *cx, RawObject obj); +UnwrapObjectChecked(RawObject obj); // Unwrap only the outermost security wrapper, with the same semantics as // above. This is the checked version of Wrapper::wrappedObject. JS_FRIEND_API(JSObject *) -UnwrapOneChecked(JSContext *cx, HandleObject obj); +UnwrapOneChecked(RawObject obj); JS_FRIEND_API(bool) IsCrossCompartmentWrapper(RawObject obj); +bool +IsDeadProxyObject(RawObject obj); + JSObject * NewDeadProxyObject(JSContext *cx, JSObject *parent); @@ -381,6 +296,34 @@ JS_FRIEND_API(bool) RecomputeWrappers(JSContext *cx, const CompartmentFilter &sourceFilter, const CompartmentFilter &targetFilter); +/* + * This auto class should be used around any code, such as brain transplants, + * that may touch dead compartments. Brain transplants can cause problems + * because they operate on all compartments, whether live or dead. A brain + * transplant can cause a formerly dead object to be "reanimated" by causing a + * read or write barrier to be invoked on it during the transplant. In this way, + * a compartment becomes a zombie, kept alive by repeatedly consuming + * (transplanted) brains. + * + * To work around this issue, we observe when mark bits are set on objects in + * dead compartments. If this happens during a brain transplant, we do a full, + * non-incremental GC at the end of the brain transplant. This will clean up any + * objects that were improperly marked. + */ +struct JS_FRIEND_API(AutoMaybeTouchDeadCompartments) +{ + // The version that takes an object just uses it for its runtime. + AutoMaybeTouchDeadCompartments(JSContext *cx); + AutoMaybeTouchDeadCompartments(JSObject *obj); + ~AutoMaybeTouchDeadCompartments(); + + private: + JSRuntime *runtime; + unsigned markCount; + bool inIncremental; + bool manipulatingDeadCompartments; +}; + } /* namespace js */ #endif diff --git a/scripting/javascript/spidermonkey-win32/include/mozilla/Attributes.h b/scripting/javascript/spidermonkey-win32/include/mozilla/Attributes.h index 6b4e81612c..ca990be15a 100644 --- a/scripting/javascript/spidermonkey-win32/include/mozilla/Attributes.h +++ b/scripting/javascript/spidermonkey-win32/include/mozilla/Attributes.h @@ -93,6 +93,8 @@ # endif # if __GNUC_MINOR__ >= 4 # define MOZ_HAVE_CXX11_DELETE +# endif +# if __GNUC_MINOR__ >= 5 # define MOZ_HAVE_CXX11_ENUM_TYPE # define MOZ_HAVE_CXX11_STRONG_ENUMS # endif @@ -356,6 +358,9 @@ * supported, as with MOZ_ENUM_TYPE(). For simplicity, it is currently * mandatory. As with MOZ_ENUM_TYPE(), it will do nothing on compilers that do * not support it. + * + * Note that the workaround implemented here is not compatible with enums + * nested inside a class. */ #if defined(MOZ_HAVE_CXX11_STRONG_ENUMS) /* All compilers that support strong enums also support an explicit diff --git a/scripting/javascript/spidermonkey-win32/include/mozilla/BloomFilter.h b/scripting/javascript/spidermonkey-win32/include/mozilla/BloomFilter.h index 9effa1756c..8680ef2907 100644 --- a/scripting/javascript/spidermonkey-win32/include/mozilla/BloomFilter.h +++ b/scripting/javascript/spidermonkey-win32/include/mozilla/BloomFilter.h @@ -13,6 +13,7 @@ #ifndef mozilla_BloomFilter_h_ #define mozilla_BloomFilter_h_ +#include "mozilla/Assertions.h" #include "mozilla/Likely.h" #include "mozilla/StandardInteger.h" #include "mozilla/Util.h" diff --git a/scripting/javascript/spidermonkey-win32/include/mozilla/CheckedInt.h b/scripting/javascript/spidermonkey-win32/include/mozilla/CheckedInt.h index 790fc6eabe..b56ac42b19 100644 --- a/scripting/javascript/spidermonkey-win32/include/mozilla/CheckedInt.h +++ b/scripting/javascript/spidermonkey-win32/include/mozilla/CheckedInt.h @@ -385,13 +385,13 @@ IsSubValid(T x, T y) } template::value, + bool IsTSigned = IsSigned::value, bool TwiceBiggerTypeIsSupported = IsSupported::Type>::value> struct IsMulValidImpl {}; -template -struct IsMulValidImpl +template +struct IsMulValidImpl { static bool run(T x, T y) { @@ -451,7 +451,7 @@ IsDivValid(T x, T y) } // This is just to shut up msvc warnings about negating unsigned ints. -template::value> +template::value> struct OppositeIfSignedImpl { static T run(T x) { return -x; } diff --git a/scripting/javascript/spidermonkey-win32/include/mozilla/EnumSet.h b/scripting/javascript/spidermonkey-win32/include/mozilla/EnumSet.h new file mode 100644 index 0000000000..b18b005669 --- /dev/null +++ b/scripting/javascript/spidermonkey-win32/include/mozilla/EnumSet.h @@ -0,0 +1,175 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* A set abstraction for enumeration values. */ + +#ifndef mozilla_EnumSet_h +#define mozilla_EnumSet_h + +#include "mozilla/Assertions.h" +#include "mozilla/StandardInteger.h" + +namespace mozilla { + +/** + * EnumSet is a set of values defined by an enumeration. It is implemented + * using a 32 bit mask for each value so it will only work for enums with an int + * representation less than 32. It works both for enum and enum class types. + */ +template +class EnumSet +{ + public: + EnumSet() + : mBitField(0) + { } + + EnumSet(T aEnum) + : mBitField(aEnum) + { } + + EnumSet(T aEnum1, T aEnum2) + : mBitField(bitFor(aEnum1) | + bitFor(aEnum2)) + { } + + EnumSet(T aEnum1, T aEnum2, T aEnum3) + : mBitField(bitFor(aEnum1) | + bitFor(aEnum2) | + bitFor(aEnum3)) + { } + + EnumSet(T aEnum1, T aEnum2, T aEnum3, T aEnum4) + : mBitField(bitFor(aEnum1) | + bitFor(aEnum2) | + bitFor(aEnum3) | + bitFor(aEnum4)) + { } + + EnumSet(const EnumSet& aEnumSet) + : mBitField(aEnumSet.mBitField) + { } + + /** + * Add an element + */ + void operator+=(T aEnum) { + mBitField |= bitFor(aEnum); + } + + /** + * Add an element + */ + EnumSet operator+(T aEnum) const { + EnumSet result(*this); + result += aEnum; + return result; + } + + /** + * Union + */ + void operator+=(const EnumSet aEnumSet) { + mBitField |= aEnumSet.mBitField; + } + + /** + * Union + */ + EnumSet operator+(const EnumSet aEnumSet) const { + EnumSet result(*this); + result += aEnumSet; + return result; + } + + /** + * Remove an element + */ + void operator-=(T aEnum) { + mBitField &= ~(bitFor(aEnum)); + } + + /** + * Remove an element + */ + EnumSet operator-(T aEnum) const { + EnumSet result(*this); + result -= aEnum; + return result; + } + + /** + * Remove a set of elements + */ + void operator-=(const EnumSet aEnumSet) { + mBitField &= ~(aEnumSet.mBitField); + } + + /** + * Remove a set of elements + */ + EnumSet operator-(const EnumSet aEnumSet) const { + EnumSet result(*this); + result -= aEnumSet; + return result; + } + + /** + * Intersection + */ + void operator&=(const EnumSet aEnumSet) { + mBitField &= aEnumSet.mBitField; + } + + /** + * Intersection + */ + EnumSet operator&(const EnumSet aEnumSet) const { + EnumSet result(*this); + result &= aEnumSet; + return result; + } + + /** + * Equality + */ + + bool operator==(const EnumSet aEnumSet) const { + return mBitField == aEnumSet.mBitField; + } + + /** + * Test is an element is contained in the set + */ + bool contains(T aEnum) const { + return mBitField & bitFor(aEnum); + } + + /** + * Return the number of elements in the set + */ + + uint8_t size() { + uint8_t count = 0; + for (uint32_t bitField = mBitField; bitField; bitField >>= 1) { + if (bitField & 1) + count++; + } + return count; + } + + private: + static uint32_t bitFor(T aEnum) { + uint32_t bitNumber(aEnum); + MOZ_ASSERT(bitNumber < 32); + return 1U << bitNumber; + } + + uint32_t mBitField; +}; + +} // namespace mozilla + +#endif // mozilla_EnumSet_h_ diff --git a/scripting/javascript/spidermonkey-win32/include/mozilla/GuardObjects.h b/scripting/javascript/spidermonkey-win32/include/mozilla/GuardObjects.h index 95aa37a19d..e3297c8fcf 100644 --- a/scripting/javascript/spidermonkey-win32/include/mozilla/GuardObjects.h +++ b/scripting/javascript/spidermonkey-win32/include/mozilla/GuardObjects.h @@ -66,7 +66,7 @@ namespace detail { * For more details, and examples of using these macros, see * https://developer.mozilla.org/en/Using_RAII_classes_in_Mozilla */ -class MOZ_EXPORT_API(GuardObjectNotifier) +class MOZ_EXPORT GuardObjectNotifier { private: bool* statementDone; @@ -83,7 +83,7 @@ class MOZ_EXPORT_API(GuardObjectNotifier) } }; -class MOZ_EXPORT_API(GuardObjectNotificationReceiver) +class MOZ_EXPORT GuardObjectNotificationReceiver { private: bool statementDone; diff --git a/scripting/javascript/spidermonkey-win32/include/mozilla/HashFunctions.h b/scripting/javascript/spidermonkey-win32/include/mozilla/HashFunctions.h index badfc3c808..96242b629a 100644 --- a/scripting/javascript/spidermonkey-win32/include/mozilla/HashFunctions.h +++ b/scripting/javascript/spidermonkey-win32/include/mozilla/HashFunctions.h @@ -351,7 +351,7 @@ HashString(const wchar_t* str, size_t length) * same result out of HashBytes as you would out of HashString. */ MOZ_WARN_UNUSED_RESULT -extern MFBT_API(uint32_t) +extern MFBT_API uint32_t HashBytes(const void* bytes, size_t length); } /* namespace mozilla */ diff --git a/scripting/javascript/spidermonkey-win32/include/mozilla/LinkedList.h b/scripting/javascript/spidermonkey-win32/include/mozilla/LinkedList.h index d7d3b23607..5cfd60e4ac 100644 --- a/scripting/javascript/spidermonkey-win32/include/mozilla/LinkedList.h +++ b/scripting/javascript/spidermonkey-win32/include/mozilla/LinkedList.h @@ -13,6 +13,10 @@ * LinkedListElement. A given object may be in only one linked list at a * time. * + * A LinkedListElement automatically removes itself from the list upon + * destruction, and a LinkedList will fatally assert in debug builds if it's + * non-empty when it's destructed. + * * For example, you might use LinkedList in a simple observer list class as * follows. * @@ -36,6 +40,8 @@ * void removeObserver(Observer* observer) { * // Will assert if |observer| is not part of some list. * observer.remove(); + * // Or, will assert if |observer| is not part of |list| specifically. + * // observer.removeFrom(list); * } * * void notifyObservers(char* topic) { @@ -101,8 +107,19 @@ class LinkedListElement LinkedListElement* prev; const bool isSentinel; + LinkedListElement* thisDuringConstruction() { return this; } + public: - LinkedListElement() : next(this), prev(this), isSentinel(false) { } + LinkedListElement() + : next(thisDuringConstruction()), + prev(thisDuringConstruction()), + isSentinel(false) + { } + + ~LinkedListElement() { + if (!isSentinel && isInList()) + remove(); + } /* * Get the next element in the list, or NULL if this is the last element in @@ -158,6 +175,15 @@ class LinkedListElement prev = this; } + /* + * Identical to remove(), but also asserts in debug builds that this element + * is in list. + */ + void removeFrom(const LinkedList& list) { + list.assertContains(asT()); + remove(); + } + /* * Return true if |this| part is of a linked list, and false otherwise. */ @@ -175,11 +201,10 @@ class LinkedListElement }; LinkedListElement(NodeKind nodeKind) - : next(this), - prev(this), + : next(thisDuringConstruction()), + prev(thisDuringConstruction()), isSentinel(nodeKind == NODE_KIND_SENTINEL) - { - } + { } /* * Return |this| cast to T* if we're a normal node, or return NULL if we're @@ -240,6 +265,10 @@ class LinkedList public: LinkedList() : sentinel(LinkedListElement::NODE_KIND_SENTINEL) { } + ~LinkedList() { + MOZ_ASSERT(isEmpty()); + } + /* * Add elem to the front of the list. */ @@ -375,6 +404,21 @@ class LinkedList } private: + friend class LinkedListElement; + + void assertContains(const T* t) const { +#ifdef DEBUG + for (const T* elem = getFirst(); + elem; + elem = elem->getNext()) + { + if (elem == t) + return; + } + MOZ_NOT_REACHED("element wasn't found in this list!"); +#endif + } + LinkedList& operator=(const LinkedList& other) MOZ_DELETE; LinkedList(const LinkedList& other) MOZ_DELETE; }; diff --git a/scripting/javascript/spidermonkey-win32/include/mozilla/NullPtr.h b/scripting/javascript/spidermonkey-win32/include/mozilla/NullPtr.h index e6fc892759..c2ea7dd91f 100644 --- a/scripting/javascript/spidermonkey-win32/include/mozilla/NullPtr.h +++ b/scripting/javascript/spidermonkey-win32/include/mozilla/NullPtr.h @@ -20,7 +20,7 @@ # endif #elif defined(__GNUC__) # if defined(_GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L -# if (__GNUC__ * 1000 + __GNU_MINOR__) >= 4006 +# if (__GNUC__ * 1000 + __GNUC_MINOR__) >= 4006 # define MOZ_HAVE_CXX11_NULLPTR # endif # endif diff --git a/scripting/javascript/spidermonkey-win32/include/mozilla/RangedPtr.h b/scripting/javascript/spidermonkey-win32/include/mozilla/RangedPtr.h index 7c8d58147d..adecf7c1a0 100644 --- a/scripting/javascript/spidermonkey-win32/include/mozilla/RangedPtr.h +++ b/scripting/javascript/spidermonkey-win32/include/mozilla/RangedPtr.h @@ -128,13 +128,13 @@ class RangedPtr RangedPtr operator+(size_t inc) { MOZ_ASSERT(inc <= size_t(-1) / sizeof(T)); - MOZ_ASSERT(ptr + inc > ptr); + MOZ_ASSERT(ptr + inc >= ptr); return create(ptr + inc); } RangedPtr operator-(size_t dec) { MOZ_ASSERT(dec <= size_t(-1) / sizeof(T)); - MOZ_ASSERT(ptr - dec < ptr); + MOZ_ASSERT(ptr - dec <= ptr); return create(ptr - dec); } diff --git a/scripting/javascript/spidermonkey-win32/include/mozilla/SHA1.h b/scripting/javascript/spidermonkey-win32/include/mozilla/SHA1.h index 510ef75f0f..a6604e699f 100644 --- a/scripting/javascript/spidermonkey-win32/include/mozilla/SHA1.h +++ b/scripting/javascript/spidermonkey-win32/include/mozilla/SHA1.h @@ -5,44 +5,57 @@ /* Simple class for computing SHA1. */ -/* - * To compute the SHA1 of a buffer using this class you should write something - * like: - * void SHA1(const uint8_t* buf, unsigned size, uint8_t hash[20]) - * { - * SHA1Sum S; - * S.update(buf, size); - * S.finish(hash); - * } - * If there are multiple buffers or chunks, the update method can be called - * multiple times and the SHA1 is computed on the concatenation of all the - * buffers passed to it. - * The finish method may only be called once and cannot be followed by calls - * to update. - */ - #ifndef mozilla_SHA1_h_ #define mozilla_SHA1_h_ #include "mozilla/StandardInteger.h" #include "mozilla/Types.h" -namespace mozilla { -class SHA1Sum { - union { - uint32_t w[16]; /* input buffer */ - uint8_t b[64]; - } u; - uint64_t size; /* count of hashed bytes. */ - unsigned H[22]; /* 5 state variables, 16 tmp values, 1 extra */ - bool mDone; +#include -public: - static const unsigned int HashSize = 20; - MFBT_API() SHA1Sum(); - MFBT_API(void) update(const void* dataIn, uint32_t len); - MFBT_API(void) finish(uint8_t hashout[20]); +namespace mozilla { + +/** + * This class computes the SHA1 hash of a byte sequence, or of the concatenation + * of multiple sequences. For example, computing the SHA1 of two sequences of + * bytes could be done as follows: + * + * void SHA1(const uint8_t* buf1, uint32_t size1, + * const uint8_t* buf2, uint32_t size2, + * SHA1Sum::Hash& hash) + * { + * SHA1Sum s; + * s.update(buf1, size1); + * s.update(buf2, size2); + * s.finish(hash); + * } + * + * The finish method may only be called once and cannot be followed by calls + * to update. + */ +class SHA1Sum +{ + union { + uint32_t w[16]; /* input buffer */ + uint8_t b[64]; + } u; + uint64_t size; /* count of hashed bytes. */ + unsigned H[22]; /* 5 state variables, 16 tmp values, 1 extra */ + bool mDone; + + public: + MFBT_API SHA1Sum(); + + static const size_t HashSize = 20; + typedef uint8_t Hash[HashSize]; + + /* Add len bytes of dataIn to the data sequence being hashed. */ + MFBT_API void update(const void* dataIn, uint32_t len); + + /* Compute the final hash of all data into hashOut. */ + MFBT_API void finish(SHA1Sum::Hash& hashOut); }; -} + +} /* namespace mozilla */ #endif /* mozilla_SHA1_h_ */ diff --git a/scripting/javascript/spidermonkey-win32/include/mozilla/TypeTraits.h b/scripting/javascript/spidermonkey-win32/include/mozilla/TypeTraits.h index 8f04e7a4ac..fda156496e 100644 --- a/scripting/javascript/spidermonkey-win32/include/mozilla/TypeTraits.h +++ b/scripting/javascript/spidermonkey-win32/include/mozilla/TypeTraits.h @@ -9,6 +9,25 @@ namespace mozilla { +namespace detail { + +/** + * The trickery used to implement IsBaseOf here makes it possible to use it for + * the cases of private and multiple inheritance. This code was inspired by the + * sample code here: + * + * http://stackoverflow.com/questions/2910979/how-is-base-of-works + */ +template +class IsBaseOfHelper +{ + public: + operator Base*() const; + operator Derived*(); +}; + +} /* namespace detail */ + /* * IsBaseOf allows to know whether a given class is derived from another. * @@ -25,12 +44,47 @@ template class IsBaseOf { private: - static char test(Base* b); - static int test(...); + template + static char test(Derived*, T); + static int test(Base*, int); public: static const bool value = - sizeof(test(static_cast(0))) == sizeof(char); + sizeof(test(detail::IsBaseOfHelper(), int())) == sizeof(char); +}; + +template +class IsBaseOf +{ + private: + template + static char test(Derived*, T); + static int test(Base*, int); + + public: + static const bool value = + sizeof(test(detail::IsBaseOfHelper(), int())) == sizeof(char); +}; + +template +class IsBaseOf +{ + public: + static const bool value = false; +}; + +template +class IsBaseOf +{ + public: + static const bool value = true; +}; + +template +class IsBaseOf +{ + public: + static const bool value = true; }; /* diff --git a/scripting/javascript/spidermonkey-win32/include/mozilla/Types.h b/scripting/javascript/spidermonkey-win32/include/mozilla/Types.h index f803586ec1..56e5cb82fb 100644 --- a/scripting/javascript/spidermonkey-win32/include/mozilla/Types.h +++ b/scripting/javascript/spidermonkey-win32/include/mozilla/Types.h @@ -27,63 +27,60 @@ /* Implement compiler and linker macros needed for APIs. */ /* - * MOZ_EXPORT_API is used to declare and define a method which is externally + * MOZ_EXPORT is used to declare and define a symbol or type which is externally * visible to users of the current library. It encapsulates various decorations - * needed to properly export the method's symbol. MOZ_EXPORT_DATA serves the - * same purpose for data. + * needed to properly export the method's symbol. * * api.h: - * extern MOZ_EXPORT_API(int) MeaningOfLife(void); - * extern MOZ_EXPORT_DATA(int) LuggageCombination; + * extern MOZ_EXPORT int MeaningOfLife(void); + * extern MOZ_EXPORT int LuggageCombination; * * api.c: - * MOZ_EXPORT_API(int) MeaningOfLife(void) { return 42; } - * MOZ_EXPORT_DATA(int) LuggageCombination = 12345; + * int MeaningOfLife(void) { return 42; } + * int LuggageCombination = 12345; * * If you are merely sharing a method across files, just use plain |extern|. * These macros are designed for use by library interfaces -- not for normal * methods or data used cross-file. */ #if defined(WIN32) || defined(XP_OS2) -# define MOZ_EXPORT_API(type) __declspec(dllexport) type -# define MOZ_EXPORT_DATA(type) __declspec(dllexport) type +# define MOZ_EXPORT __declspec(dllexport) #else /* Unix */ # ifdef HAVE_VISIBILITY_ATTRIBUTE -# define MOZ_EXTERNAL_VIS __attribute__((visibility("default"))) +# define MOZ_EXPORT __attribute__((visibility("default"))) # elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) -# define MOZ_EXTERNAL_VIS __global +# define MOZ_EXPORT __global # else -# define MOZ_EXTERNAL_VIS +# define MOZ_EXPORT /* nothing */ # endif -# define MOZ_EXPORT_API(type) MOZ_EXTERNAL_VIS type -# define MOZ_EXPORT_DATA(type) MOZ_EXTERNAL_VIS type #endif + /* - * Whereas implementers use MOZ_EXPORT_API and MOZ_EXPORT_DATA to declare and - * define library symbols, users use MOZ_IMPORT_API and MOZ_IMPORT_DATA to - * access them. Most often the implementer of the library will expose an API - * macro which expands to either the export or import version of the macro, - * depending upon the compilation mode. + * Whereas implementers use MOZ_EXPORT to declare and define library symbols, + * users use MOZ_IMPORT_API and MOZ_IMPORT_DATA to access them. Most often the + * implementer of the library will expose an API macro which expands to either + * the export or import version of the macro, depending upon the compilation + * mode. */ #ifdef _WIN32 # if defined(__MWERKS__) -# define MOZ_IMPORT_API(x) x +# define MOZ_IMPORT_API /* nothing */ # else -# define MOZ_IMPORT_API(x) __declspec(dllimport) x +# define MOZ_IMPORT_API __declspec(dllimport) # endif #elif defined(XP_OS2) -# define MOZ_IMPORT_API(x) __declspec(dllimport) x +# define MOZ_IMPORT_API __declspec(dllimport) #else -# define MOZ_IMPORT_API(x) MOZ_EXPORT_API(x) +# define MOZ_IMPORT_API MOZ_EXPORT #endif #if defined(_WIN32) && !defined(__MWERKS__) -# define MOZ_IMPORT_DATA(x) __declspec(dllimport) x +# define MOZ_IMPORT_DATA __declspec(dllimport) #elif defined(XP_OS2) -# define MOZ_IMPORT_DATA(x) __declspec(dllimport) x +# define MOZ_IMPORT_DATA __declspec(dllimport) #else -# define MOZ_IMPORT_DATA(x) MOZ_EXPORT_DATA(x) +# define MOZ_IMPORT_DATA MOZ_EXPORT #endif /* @@ -92,19 +89,22 @@ * declarations when using mfbt. */ #if defined(IMPL_MFBT) -# define MFBT_API(type) MOZ_EXPORT_API(type) -# define MFBT_DATA(type) MOZ_EXPORT_DATA(type) +# define MFBT_API MOZ_EXPORT +# define MFBT_DATA MOZ_EXPORT #else /* - * When mozglue is linked in the program, we need the MFBT API symbols - * to be weak. + * On linux mozglue is linked in the program and we link libxul.so with + * -z,defs. Normally that causes the linker to reject undefined references in + * libxul.so, but as a loophole it allows undefined references to weak + * symbols. We add the weak attribute to the import version of the MFBT API + * macros to exploit this. */ # if defined(MOZ_GLUE_IN_PROGRAM) -# define MFBT_API(type) __attribute__((weak)) MOZ_IMPORT_API(type) -# define MFBT_DATA(type) __attribute__((weak)) MOZ_IMPORT_DATA(type) +# define MFBT_API __attribute__((weak)) MOZ_IMPORT_API +# define MFBT_DATA __attribute__((weak)) MOZ_IMPORT_DATA # else -# define MFBT_API(type) MOZ_IMPORT_API(type) -# define MFBT_DATA(type) MOZ_IMPORT_DATA(type) +# define MFBT_API MOZ_IMPORT_API +# define MFBT_DATA MOZ_IMPORT_DATA # endif #endif @@ -117,7 +117,7 @@ * * MOZ_BEGIN_EXTERN_C * - * extern MOZ_EXPORT_API(int) MostRandomNumber(void); + * extern MOZ_EXPORT int MostRandomNumber(void); * ...other declarations... * * MOZ_END_EXTERN_C diff --git a/scripting/javascript/spidermonkey-win32/include/mozilla/WeakPtr.h b/scripting/javascript/spidermonkey-win32/include/mozilla/WeakPtr.h index e20767141e..721c28e48d 100644 --- a/scripting/javascript/spidermonkey-win32/include/mozilla/WeakPtr.h +++ b/scripting/javascript/spidermonkey-win32/include/mozilla/WeakPtr.h @@ -126,6 +126,10 @@ class WeakPtr return ref->get(); } + T* get() const { + return ref->get(); + } + private: friend class SupportsWeakPtr; diff --git a/scripting/javascript/spidermonkey-win32/lib/mozjs.dll.REMOVED.git-id b/scripting/javascript/spidermonkey-win32/lib/mozjs.dll.REMOVED.git-id index 9b9d0c1a44..41fedc58e2 100644 --- a/scripting/javascript/spidermonkey-win32/lib/mozjs.dll.REMOVED.git-id +++ b/scripting/javascript/spidermonkey-win32/lib/mozjs.dll.REMOVED.git-id @@ -1 +1 @@ -2019e722c1a486e661742aa6b4422c2f772bd4ee \ No newline at end of file +43ce75f61d5b91c7a8bb8ad7e5a9c2efcbffa1af \ No newline at end of file diff --git a/scripting/javascript/spidermonkey-win32/lib/mozjs.lib.REMOVED.git-id b/scripting/javascript/spidermonkey-win32/lib/mozjs.lib.REMOVED.git-id index a9665aa434..1f1fd5aba2 100644 --- a/scripting/javascript/spidermonkey-win32/lib/mozjs.lib.REMOVED.git-id +++ b/scripting/javascript/spidermonkey-win32/lib/mozjs.lib.REMOVED.git-id @@ -1 +1 @@ -26d223e312ac7447dd1c98fd809046c475433568 \ No newline at end of file +c49652620f963a06b32e644232593e52bb2f1a9b \ No newline at end of file diff --git a/scripting/lua/cocos2dx_support/LuaCocos2d.cpp.REMOVED.git-id b/scripting/lua/cocos2dx_support/LuaCocos2d.cpp.REMOVED.git-id index dc13dbcbfb..0e8e5f712e 100644 --- a/scripting/lua/cocos2dx_support/LuaCocos2d.cpp.REMOVED.git-id +++ b/scripting/lua/cocos2dx_support/LuaCocos2d.cpp.REMOVED.git-id @@ -1 +1 @@ -f0dfb853e96eda746fe727b5984febf65ffb2d65 \ No newline at end of file +c789948fe4f395f9c7d4591e2b864ee72a50a917 \ No newline at end of file diff --git a/tools/cxx-generator b/tools/cxx-generator index b92f99a3e5..ff1bd686c8 160000 --- a/tools/cxx-generator +++ b/tools/cxx-generator @@ -1 +1 @@ -Subproject commit b92f99a3e53c5d4c19eb166e649664bd7fd80bab +Subproject commit ff1bd686c877b108c0738fee8f280c55fc9f20f3 diff --git a/tools/tolua++/CCAction.pkg b/tools/tolua++/CCAction.pkg index 7d8daa3a1e..74cb15d5b4 100644 --- a/tools/tolua++/CCAction.pkg +++ b/tools/tolua++/CCAction.pkg @@ -13,7 +13,6 @@ class CCAction : public CCObject // static CCAction* create(); int getTag(void); void setTag(int nTag); - CCObject* copyWithZone(CCZone* pZone); }; class CCFiniteTimeAction : public CCAction @@ -42,7 +41,6 @@ class CCSpeed : public CCAction void setSpeed(float fSpeed); CCActionInterval* reverse(void); bool isDone(void); - CCObject* copyWithZone(CCZone* pZone); static CCSpeed* create(CCActionInterval *pAction, float fRate); }; @@ -52,7 +50,6 @@ class CCFollow : public CCAction bool isBoundarySet(void); void setBoudarySet(bool bValue); bool isDone(void); - CCObject* copyWithZone(CCZone* pZone); static CCFollow* create(CCNode *pFollowedNode, CCRect rect); static CCFollow* create(CCNode *pFollowedNode); @@ -60,7 +57,6 @@ class CCFollow : public CCAction class CCSequence : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCSequence* createWithTwoActions(CCFiniteTimeAction *pActionOne, CCFiniteTimeAction *pActionTwo); @@ -69,7 +65,6 @@ class CCSequence : public CCActionInterval class CCRepeat : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); bool isDone(void); CCActionInterval* reverse(void); @@ -78,7 +73,6 @@ class CCRepeat : public CCActionInterval class CCRepeatForever : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); bool isDone(void); CCActionInterval* reverse(void); @@ -87,7 +81,6 @@ class CCRepeatForever : public CCActionInterval class CCSpawn : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCSpawn* createWithTwoActions(CCFiniteTimeAction *pAction1, CCFiniteTimeAction *pAction2); @@ -96,14 +89,12 @@ class CCSpawn : public CCActionInterval class CCRotateTo : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); static CCRotateTo* create(float duration, float fDeltaAngle); }; class CCRotateBy : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCRotateBy* create(float duration, float fDeltaAngle); @@ -111,14 +102,12 @@ class CCRotateBy : public CCActionInterval class CCMoveTo : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); static CCMoveTo* create(float duration, CCPoint position); }; class CCMoveBy : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCMoveBy* create(float duration, CCPoint deltaPosition); @@ -126,14 +115,12 @@ class CCMoveBy : public CCActionInterval class CCSkewTo : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); static CCSkewTo* create(float t, float sx, float sy); }; class CCSkewBy : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCSkewBy* create(float t, float deltaSkewX, float deltaSkewY); @@ -141,7 +128,6 @@ class CCSkewBy : public CCActionInterval class CCJumpBy : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCJumpBy* create(float duration, CCPoint position, float height, int jumps); @@ -149,7 +135,6 @@ class CCJumpBy : public CCActionInterval class CCJumpTo : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); static CCJumpTo* create(float duration, CCPoint position, float height, int jumps); }; @@ -168,7 +153,6 @@ class ccBezierConfig { class CCBezierBy : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCBezierBy* create(float t, ccBezierConfig c); @@ -176,14 +160,12 @@ class CCBezierBy : public CCActionInterval class CCBezierTo : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); static CCBezierTo* create(float t, ccBezierConfig c); }; class CCScaleTo : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); static CCScaleTo* create(float duration, float sx, float sy); static CCScaleTo* create(float duration, float s); @@ -191,7 +173,6 @@ class CCScaleTo : public CCActionInterval class CCScaleBy : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCScaleBy* create(float duration, float s); @@ -200,7 +181,6 @@ class CCScaleBy : public CCActionInterval class CCBlink : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCBlink* create(float duration, unsigned int uBlinks); @@ -208,7 +188,6 @@ class CCBlink : public CCActionInterval class CCFadeIn : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCFadeIn* create(float d); @@ -216,7 +195,6 @@ class CCFadeIn : public CCActionInterval class CCFadeOut : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCFadeOut* create(float d); @@ -224,21 +202,18 @@ class CCFadeOut : public CCActionInterval class CCFadeTo : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); static CCFadeTo* create(float duration, GLubyte opacity); }; class CCTintTo : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); static CCTintTo* create(float duration, GLubyte red, GLubyte green, GLubyte blue); }; class CCTintBy : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCTintBy* create(float duration, GLshort deltaRed, GLshort deltaGreen, GLshort deltaBlue); @@ -246,7 +221,6 @@ class CCTintBy : public CCActionInterval class CCDelayTime : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCDelayTime* create(float d); @@ -254,7 +228,6 @@ class CCDelayTime : public CCActionInterval class CCReverseTime : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCReverseTime* create(CCFiniteTimeAction *pAction); @@ -262,7 +235,6 @@ class CCReverseTime : public CCActionInterval class CCAnimate : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); CCAnimation* getAnimation(void); @@ -273,7 +245,6 @@ class CCAnimate : public CCActionInterval class CCTargetedAction : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); CCNode* getForcedTarget(void); void setForcedTarget(CCNode* target); @@ -284,14 +255,12 @@ class CCTargetedAction : public CCActionInterval // CCActionInstant class CCActionInstant : public CCFiniteTimeAction { - CCObject* copyWithZone(CCZone* pZone); CCFiniteTimeAction* reverse(void); bool isDone(void); }; class CCShow : public CCActionInstant { - CCObject* copyWithZone(CCZone* pZone); CCFiniteTimeAction* reverse(void); static CCShow* create(); @@ -299,7 +268,6 @@ class CCShow : public CCActionInstant class CCHide : public CCActionInstant { - CCObject* copyWithZone(CCZone* pZone); CCFiniteTimeAction* reverse(void); static CCHide* create(); @@ -307,14 +275,12 @@ class CCHide : public CCActionInstant class CCToggleVisibility : public CCActionInstant { - CCObject* copyWithZone(CCZone* pZone); static CCToggleVisibility* create(); }; class CCFlipX : public CCActionInstant { - CCObject* copyWithZone(CCZone* pZone); CCFiniteTimeAction* reverse(void); static CCFlipX* create(bool x); @@ -322,7 +288,6 @@ class CCFlipX : public CCActionInstant class CCFlipY : public CCActionInstant { - CCObject* copyWithZone(CCZone* pZone); CCFiniteTimeAction* reverse(void); static CCFlipY* create(bool y); @@ -330,8 +295,6 @@ class CCFlipY : public CCActionInstant class CCPlace : public CCActionInstant // { - CCObject* copyWithZone(CCZone* pZone); - static CCPlace* create(CCPoint pos); }; diff --git a/tools/tolua++/CCActionCamera.pkg b/tools/tolua++/CCActionCamera.pkg index b10c9f7e7d..0063c75a73 100644 --- a/tools/tolua++/CCActionCamera.pkg +++ b/tools/tolua++/CCActionCamera.pkg @@ -7,7 +7,6 @@ class CCActionCamera : public CCActionInterval class CCOrbitCamera : public CCActionCamera { - CCObject* copyWithZone(CCZone* pZone); void sphericalRadius(float *r, float *zenith, float *azimuth); static CCOrbitCamera * create(float t, float radius, float deltaRadius, float angleZ, float deltaAngleZ, float angleX, float deltaAngleX); diff --git a/tools/tolua++/CCActionCatmullRom.pkg b/tools/tolua++/CCActionCatmullRom.pkg index c761f5978c..d26651c2a1 100644 --- a/tools/tolua++/CCActionCatmullRom.pkg +++ b/tools/tolua++/CCActionCatmullRom.pkg @@ -10,7 +10,6 @@ class CCPointArray : public CCNode unsigned int count(); CCPointArray* reverse(); void reverseInline(); - CCObject* copyWithZone(CCZone *zone); const std::vector* getControlPoints(); void setControlPoints(std::vector *controlPoints); @@ -19,7 +18,6 @@ class CCPointArray : public CCNode class CCCardinalSplineTo : public CCActionInterval { - CCCardinalSplineTo* copyWithZone(CCZone* pZone); CCActionInterval* reverse(); CCPointArray* getPoints(); void setPoints(CCPointArray* points); diff --git a/tools/tolua++/CCActionEase.pkg b/tools/tolua++/CCActionEase.pkg index f8fe188a6e..11cd99e395 100644 --- a/tools/tolua++/CCActionEase.pkg +++ b/tools/tolua++/CCActionEase.pkg @@ -1,7 +1,6 @@ class CCActionEase : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCActionEase* create(CCActionInterval *pAction); @@ -9,7 +8,6 @@ class CCActionEase : public CCActionInterval class CCEaseRateAction : public CCActionEase { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCEaseRateAction* create(CCActionInterval* pAction, float fRate); @@ -17,7 +15,6 @@ class CCEaseRateAction : public CCActionEase class CCEaseIn : public CCEaseRateAction { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCEaseIn* create(CCActionInterval* pAction, float fRate); @@ -25,7 +22,6 @@ class CCEaseIn : public CCEaseRateAction class CCEaseOut : public CCEaseRateAction { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCEaseOut* create(CCActionInterval* pAction, float fRate); @@ -33,7 +29,6 @@ class CCEaseOut : public CCEaseRateAction class CCEaseInOut : public CCEaseRateAction { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCEaseInOut* create(CCActionInterval* pAction, float fRate); @@ -41,7 +36,6 @@ class CCEaseInOut : public CCEaseRateAction class CCEaseExponentialIn : public CCActionEase { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCEaseExponentialIn* create(CCActionInterval* pAction); @@ -49,7 +43,6 @@ class CCEaseExponentialIn : public CCActionEase class CCEaseExponentialOut : public CCActionEase { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCEaseExponentialOut* create(CCActionInterval* pAction); @@ -57,7 +50,6 @@ class CCEaseExponentialOut : public CCActionEase class CCEaseExponentialInOut : public CCActionEase { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCEaseExponentialInOut* create(CCActionInterval* pAction); @@ -65,7 +57,6 @@ class CCEaseExponentialInOut : public CCActionEase class CCEaseSineIn : public CCActionEase { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCEaseSineIn* create(CCActionInterval* pAction); @@ -73,7 +64,6 @@ class CCEaseSineIn : public CCActionEase class CCEaseSineOut : public CCActionEase { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCEaseSineOut* create(CCActionInterval* pAction); @@ -81,7 +71,6 @@ class CCEaseSineOut : public CCActionEase class CCEaseSineInOut : public CCActionEase { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCEaseSineInOut* create(CCActionInterval* pAction); @@ -89,7 +78,6 @@ class CCEaseSineInOut : public CCActionEase class CCEaseElastic : public CCActionEase { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); float getPeriod(void); @@ -100,7 +88,6 @@ class CCEaseElastic : public CCActionEase class CCEaseElasticIn : public CCEaseElastic { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCEaseElasticIn* create(CCActionInterval *pAction, float fPeriod = 0.3); @@ -108,7 +95,6 @@ class CCEaseElasticIn : public CCEaseElastic class CCEaseElasticOut : public CCEaseElastic { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCEaseElasticOut* create(CCActionInterval *pAction, float fPeriod = 0.3); @@ -116,7 +102,6 @@ class CCEaseElasticOut : public CCEaseElastic class CCEaseElasticInOut : public CCEaseElastic { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCEaseElasticInOut* create(CCActionInterval *pAction, float fPeriod = 0.3); @@ -124,7 +109,6 @@ class CCEaseElasticInOut : public CCEaseElastic class CCEaseBounce : public CCActionEase { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCEaseBounce* create(CCActionInterval* pAction); @@ -132,7 +116,6 @@ class CCEaseBounce : public CCActionEase class CCEaseBounceIn : public CCEaseBounce { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCEaseBounceIn* create(CCActionInterval* pAction); @@ -140,7 +123,6 @@ class CCEaseBounceIn : public CCEaseBounce class CCEaseBounceOut : public CCEaseBounce { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCEaseBounceOut* create(CCActionInterval* pAction); @@ -148,7 +130,6 @@ class CCEaseBounceOut : public CCEaseBounce class CCEaseBounceInOut : public CCEaseBounce { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCEaseBounceInOut* create(CCActionInterval* pAction); @@ -156,7 +137,6 @@ class CCEaseBounceInOut : public CCEaseBounce class CCEaseBackIn : public CCActionEase { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCEaseBackIn* create(CCActionInterval* pAction); @@ -164,7 +144,6 @@ class CCEaseBackIn : public CCActionEase class CCEaseBackOut : public CCActionEase { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCEaseBackOut* create(CCActionInterval* pAction); @@ -172,7 +151,6 @@ class CCEaseBackOut : public CCActionEase class CCEaseBackInOut : public CCActionEase { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); static CCEaseBackInOut* create(CCActionInterval* pAction); diff --git a/tools/tolua++/CCActionGrid.pkg b/tools/tolua++/CCActionGrid.pkg index 606efb5b5d..c2c7587541 100644 --- a/tools/tolua++/CCActionGrid.pkg +++ b/tools/tolua++/CCActionGrid.pkg @@ -1,7 +1,6 @@ class CCGridAction : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); CCActionInterval* reverse(void); CCGridBase* getGrid(void); diff --git a/tools/tolua++/CCActionGrid3D.pkg b/tools/tolua++/CCActionGrid3D.pkg index 5aa0869e23..99cf5e6c6d 100644 --- a/tools/tolua++/CCActionGrid3D.pkg +++ b/tools/tolua++/CCActionGrid3D.pkg @@ -1,8 +1,6 @@ class CCWaves3D : public CCGrid3DAction { - CCObject* copyWithZone(CCZone* pZone); - float getAmplitude(void); void setAmplitude(float fAmplitude); float getAmplitudeRate(void); @@ -13,22 +11,16 @@ class CCWaves3D : public CCGrid3DAction class CCFlipX3D : public CCGrid3DAction { - CCObject* copyWithZone(CCZone* pZone); - static CCFlipX3D* create(float duration); }; class CCFlipY3D : public CCFlipX3D { - CCObject* copyWithZone(CCZone* pZone); - static CCFlipY3D* create(float duration); }; class CCLens3D : public CCGrid3DAction { - CCObject* copyWithZone(CCZone* pZone); - float getLensEffect(void); void setLensEffect(float fLensEffect); CCPoint getPosition(void); @@ -39,8 +31,6 @@ class CCLens3D : public CCGrid3DAction class CCRipple3D : public CCGrid3DAction { - CCObject* copyWithZone(CCZone* pZone); - CCPoint getPosition(void); void setPosition(CCPoint position); float getAmplitude(void); @@ -53,15 +43,11 @@ class CCRipple3D : public CCGrid3DAction class CCShaky3D : public CCGrid3DAction { - CCObject* copyWithZone(CCZone* pZone); - static CCShaky3D* create(float duration, CCSize gridSize, int range, bool shakeZ); }; class CCLiquid : public CCGrid3DAction { - CCObject* copyWithZone(CCZone* pZone); - float getAmplitude(void); void setAmplitude(float fAmplitude); float getAmplitudeRate(void); @@ -72,8 +58,6 @@ class CCLiquid : public CCGrid3DAction class CCWaves : public CCGrid3DAction { - CCObject* copyWithZone(CCZone* pZone); - float getAmplitude(void); void setAmplitude(float fAmplitude); float getAmplitudeRate(void); @@ -84,8 +68,6 @@ class CCWaves : public CCGrid3DAction class CCTwirl : public CCGrid3DAction { - CCObject* copyWithZone(CCZone* pZone); - CCPoint getPosition(void); void setPosition(CCPoint position); float getAmplitude(void); diff --git a/tools/tolua++/CCActionProgressTimer.pkg b/tools/tolua++/CCActionProgressTimer.pkg index f4feb97fe5..6e3c911fd8 100644 --- a/tools/tolua++/CCActionProgressTimer.pkg +++ b/tools/tolua++/CCActionProgressTimer.pkg @@ -1,14 +1,10 @@ class CCProgressTo : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); - static CCProgressTo* create(float duration, float fPercent); }; class CCProgressFromTo : public CCActionInterval { - CCObject* copyWithZone(CCZone* pZone); - static CCProgressFromTo* create(float duration, float fFromPercentage, float fToPercentage); }; diff --git a/tools/tolua++/CCActionTiledGrid.pkg b/tools/tolua++/CCActionTiledGrid.pkg index 48fb9b02c1..6baa15906d 100644 --- a/tools/tolua++/CCActionTiledGrid.pkg +++ b/tools/tolua++/CCActionTiledGrid.pkg @@ -1,22 +1,16 @@ class CCShakyTiles3D : public CCTiledGrid3DAction { - CCObject* copyWithZone(CCZone* pZone); - - CCShakyTiles3D* create(float duration, CCSize gridSize, int nRange, bool bShakeZ); + static CCShakyTiles3D* create(float duration, CCSize gridSize, int nRange, bool bShakeZ); }; class CCShatteredTiles3D : public CCTiledGrid3DAction { - CCObject* copyWithZone(CCZone* pZone); - static CCShatteredTiles3D* create(float duration, CCSize gridSize, int nRange, bool bShatterZ); }; class CCShuffleTiles : public CCTiledGrid3DAction { - CCObject* copyWithZone(CCZone* pZone); - void shuffle(unsigned int *pArray, int nLen); CCSize getDelta(CCSize pos); void placeTile(CCPoint pos, Tile *t); @@ -52,8 +46,6 @@ class CCFadeOutDownTiles : public CCFadeOutUpTiles class CCTurnOffTiles : public CCTiledGrid3DAction { - CCObject* copyWithZone(CCZone* pZone); - void shuffle(unsigned int *pArray, int nLen); void turnOnTile(CCPoint pos); void turnOffTile(CCPoint pos); @@ -64,8 +56,6 @@ class CCTurnOffTiles : public CCTiledGrid3DAction class CCWavesTiles3D : public CCTiledGrid3DAction { - CCObject* copyWithZone(CCZone* pZone); - float getAmplitude(void); void setAmplitude(float fAmplitude); float getAmplitudeRate(void); @@ -76,8 +66,6 @@ class CCWavesTiles3D : public CCTiledGrid3DAction class CCJumpTiles3D : public CCTiledGrid3DAction { - CCObject* copyWithZone(CCZone* pZone); - float getAmplitude(void); void setAmplitude(float fAmplitude); float getAmplitudeRate(void); @@ -88,14 +76,10 @@ class CCJumpTiles3D : public CCTiledGrid3DAction class CCSplitRows : public CCTiledGrid3DAction { - CCObject* copyWithZone(CCZone* pZone); - static CCSplitRows* create(float duration, unsigned int nRows); }; class CCSplitCols : public CCTiledGrid3DAction { - CCObject* copyWithZone(CCZone* pZone); - static CCSplitCols* create(float duration, unsigned int nCols); }; diff --git a/tools/tolua++/CCAnimation.pkg b/tools/tolua++/CCAnimation.pkg index b5a3790872..36c78612a2 100644 --- a/tools/tolua++/CCAnimation.pkg +++ b/tools/tolua++/CCAnimation.pkg @@ -3,7 +3,6 @@ class CCAnimationFrame : public CCObject { CCAnimationFrame(); ~CCAnimationFrame(); - CCObject* copyWithZone(CCZone* pZone); bool initWithSpriteFrame(CCSpriteFrame* spriteFrame, float delayUnits, CCDictionary* userInfo); diff --git a/tools/tolua++/CCArray.pkg b/tools/tolua++/CCArray.pkg index 80b159a030..194f5e932f 100644 --- a/tools/tolua++/CCArray.pkg +++ b/tools/tolua++/CCArray.pkg @@ -1,7 +1,5 @@ class CCArray : public CCObject { - CCObject* copyWithZone(CCZone* pZone); - static CCArray* create(); static CCArray* createWithObject(CCObject* pObject);