зеркало из https://github.com/mozilla/pjs.git
Merge mozilla-central and mozilla-inbound
This commit is contained in:
Коммит
335ec03892
|
@ -188,3 +188,7 @@ https://www.bank2.com:443 privileged,cert=escapeattack2
|
|||
#
|
||||
https://redirproxy.example.com:443 privileged,redir=test1.example.com
|
||||
|
||||
# Host used for IndexedDB Quota testing
|
||||
http://bug704464-1.example.com:80 privileged
|
||||
http://bug704464-2.example.com:80 privileged
|
||||
http://bug704464-3.example.com:80 privileged
|
||||
|
|
|
@ -1738,7 +1738,7 @@ nsScriptSecurityManager::CheckFunctionAccess(JSContext *aCx, void *aFunObj,
|
|||
#ifdef DEBUG
|
||||
{
|
||||
JS_ASSERT(JS_ObjectIsFunction(aCx, (JSObject *)aFunObj));
|
||||
JSFunction *fun = (JSFunction *)JS_GetPrivate(aCx, (JSObject *)aFunObj);
|
||||
JSFunction *fun = JS_GetObjectFunction((JSObject *)aFunObj);
|
||||
JSScript *script = JS_GetFunctionScript(aCx, fun);
|
||||
|
||||
NS_ASSERTION(!script, "Null principal for non-native function!");
|
||||
|
@ -2219,7 +2219,7 @@ nsScriptSecurityManager::GetFunctionObjectPrincipal(JSContext *cx,
|
|||
return result;
|
||||
}
|
||||
|
||||
JSFunction *fun = (JSFunction *)JS_GetPrivate(cx, obj);
|
||||
JSFunction *fun = JS_GetObjectFunction(obj);
|
||||
JSScript *script = JS_GetFunctionScript(cx, fun);
|
||||
|
||||
if (!script)
|
||||
|
@ -2243,7 +2243,7 @@ nsScriptSecurityManager::GetFunctionObjectPrincipal(JSContext *cx,
|
|||
|
||||
script = frameScript;
|
||||
}
|
||||
else if (JS_GetFunctionObject(fun) != obj)
|
||||
else if (!js::IsOriginalScriptFunction(fun))
|
||||
{
|
||||
// Here, obj is a cloned function object. In this case, the
|
||||
// clone's prototype may have been precompiled from brutally
|
||||
|
@ -2285,7 +2285,7 @@ nsScriptSecurityManager::GetFramePrincipal(JSContext *cx,
|
|||
#ifdef DEBUG
|
||||
if (NS_SUCCEEDED(*rv) && !result)
|
||||
{
|
||||
JSFunction *fun = (JSFunction *)JS_GetPrivate(cx, obj);
|
||||
JSFunction *fun = JS_GetObjectFunction(obj);
|
||||
JSScript *script = JS_GetFunctionScript(cx, fun);
|
||||
|
||||
NS_ASSERTION(!script, "Null principal for non-native function!");
|
||||
|
@ -2432,7 +2432,7 @@ nsScriptSecurityManager::doGetObjectPrincipal(JSObject *aObj
|
|||
jsClass = js::GetObjectClass(aObj);
|
||||
|
||||
if (jsClass == &js::CallClass) {
|
||||
aObj = js::GetObjectParent(aObj);
|
||||
aObj = js::GetObjectParentMaybeScope(aObj);
|
||||
|
||||
if (!aObj)
|
||||
return nsnull;
|
||||
|
@ -2484,7 +2484,7 @@ nsScriptSecurityManager::doGetObjectPrincipal(JSObject *aObj
|
|||
}
|
||||
}
|
||||
|
||||
aObj = js::GetObjectParent(aObj);
|
||||
aObj = js::GetObjectParentMaybeScope(aObj);
|
||||
|
||||
if (!aObj)
|
||||
break;
|
||||
|
|
|
@ -279,7 +279,7 @@ AsyncConnectionHelper::Run()
|
|||
if (NS_SUCCEEDED(rv)) {
|
||||
bool hasSavepoint = false;
|
||||
if (mDatabase) {
|
||||
IndexedDatabaseManager::SetCurrentDatabase(mDatabase);
|
||||
IndexedDatabaseManager::SetCurrentWindow(mDatabase->Owner());
|
||||
|
||||
// Make the first savepoint.
|
||||
if (mTransaction) {
|
||||
|
@ -292,7 +292,7 @@ AsyncConnectionHelper::Run()
|
|||
mResultCode = DoDatabaseWork(connection);
|
||||
|
||||
if (mDatabase) {
|
||||
IndexedDatabaseManager::SetCurrentDatabase(nsnull);
|
||||
IndexedDatabaseManager::SetCurrentWindow(nsnull);
|
||||
|
||||
// Release or roll back the savepoint depending on the error code.
|
||||
if (hasSavepoint) {
|
||||
|
|
|
@ -98,11 +98,9 @@ GetQuotaPermissions(const nsACString& aASCIIOrigin,
|
|||
|
||||
} // anonymous namespace
|
||||
|
||||
CheckQuotaHelper::CheckQuotaHelper(IDBDatabase* aDatabase,
|
||||
CheckQuotaHelper::CheckQuotaHelper(nsPIDOMWindow* aWindow,
|
||||
mozilla::Mutex& aMutex)
|
||||
: mWindow(aDatabase->Owner()),
|
||||
mWindowSerial(mWindow->GetSerial()),
|
||||
mOrigin(aDatabase->Origin()),
|
||||
: mWindow(aWindow),
|
||||
mMutex(aMutex),
|
||||
mCondVar(mMutex, "CheckQuotaHelper::mCondVar"),
|
||||
mPromptResult(0),
|
||||
|
@ -175,51 +173,59 @@ CheckQuotaHelper::Run()
|
|||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
if (!mHasPrompted) {
|
||||
mPromptResult = GetQuotaPermissions(mOrigin, mWindow);
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
if (mASCIIOrigin.IsEmpty()) {
|
||||
rv = IndexedDatabaseManager::GetASCIIOriginFromWindow(mWindow,
|
||||
mASCIIOrigin);
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
if (mHasPrompted) {
|
||||
// Add permissions to the database, but only if we are in the parent
|
||||
// process (if we are in the child process, we have already
|
||||
// set the permission when the prompt was shown in the parent, as
|
||||
// we cannot set the permission from the child).
|
||||
if (mPromptResult != nsIPermissionManager::UNKNOWN_ACTION &&
|
||||
XRE_GetProcessType() == GeckoProcessType_Default) {
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
rv = NS_NewURI(getter_AddRefs(uri), mOrigin);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIPermissionManager> permissionManager =
|
||||
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
|
||||
NS_ENSURE_STATE(permissionManager);
|
||||
|
||||
rv = permissionManager->Add(uri, PERMISSION_INDEXEDDB_UNLIMITED,
|
||||
mPromptResult,
|
||||
nsIPermissionManager::EXPIRE_NEVER, 0);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
if (!mHasPrompted) {
|
||||
mPromptResult = GetQuotaPermissions(mASCIIOrigin, mWindow);
|
||||
}
|
||||
}
|
||||
else if (mPromptResult == nsIPermissionManager::UNKNOWN_ACTION) {
|
||||
PRUint32 quota = IndexedDatabaseManager::GetIndexedDBQuotaMB();
|
||||
|
||||
nsString quotaString;
|
||||
quotaString.AppendInt(quota);
|
||||
if (mHasPrompted) {
|
||||
// Add permissions to the database, but only if we are in the parent
|
||||
// process (if we are in the child process, we have already
|
||||
// set the permission when the prompt was shown in the parent, as
|
||||
// we cannot set the permission from the child).
|
||||
if (mPromptResult != nsIPermissionManager::UNKNOWN_ACTION &&
|
||||
XRE_GetProcessType() == GeckoProcessType_Default) {
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
rv = NS_NewURI(getter_AddRefs(uri), mASCIIOrigin);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIPermissionManager> permissionManager =
|
||||
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
|
||||
NS_ENSURE_STATE(permissionManager);
|
||||
|
||||
rv = permissionManager->Add(uri, PERMISSION_INDEXEDDB_UNLIMITED,
|
||||
mPromptResult,
|
||||
nsIPermissionManager::EXPIRE_NEVER, 0);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
else if (mPromptResult == nsIPermissionManager::UNKNOWN_ACTION) {
|
||||
PRUint32 quota = IndexedDatabaseManager::GetIndexedDBQuotaMB();
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = GetObserverService();
|
||||
NS_ENSURE_STATE(obs);
|
||||
nsString quotaString;
|
||||
quotaString.AppendInt(quota);
|
||||
|
||||
// We have to watch to make sure that the window doesn't go away without
|
||||
// responding to us. Otherwise our database threads will hang.
|
||||
rv = obs->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<nsIObserverService> obs = GetObserverService();
|
||||
NS_ENSURE_STATE(obs);
|
||||
|
||||
rv = obs->NotifyObservers(static_cast<nsIRunnable*>(this),
|
||||
TOPIC_QUOTA_PROMPT, quotaString.get());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// We have to watch to make sure that the window doesn't go away without
|
||||
// responding to us. Otherwise our database threads will hang.
|
||||
rv = obs->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
rv = obs->NotifyObservers(static_cast<nsIRunnable*>(this),
|
||||
TOPIC_QUOTA_PROMPT, quotaString.get());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
|
|
@ -65,23 +65,17 @@ public:
|
|||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
CheckQuotaHelper(IDBDatabase* aDatabase,
|
||||
CheckQuotaHelper(nsPIDOMWindow* aWindow,
|
||||
mozilla::Mutex& aMutex);
|
||||
|
||||
bool PromptAndReturnQuotaIsDisabled();
|
||||
|
||||
void Cancel();
|
||||
|
||||
PRUint32 WindowSerial()
|
||||
{
|
||||
return mWindowSerial;
|
||||
}
|
||||
|
||||
private:
|
||||
nsPIDOMWindow* mWindow;
|
||||
PRUint32 mWindowSerial;
|
||||
nsCString mOrigin;
|
||||
|
||||
nsCString mASCIIOrigin;
|
||||
mozilla::Mutex& mMutex;
|
||||
mozilla::CondVar mCondVar;
|
||||
PRUint32 mPromptResult;
|
||||
|
|
|
@ -64,12 +64,6 @@ USING_INDEXEDDB_NAMESPACE
|
|||
|
||||
namespace {
|
||||
|
||||
PRUint32 gDatabaseInstanceCount = 0;
|
||||
mozilla::Mutex* gPromptHelpersMutex = nsnull;
|
||||
|
||||
// Protected by gPromptHelpersMutex.
|
||||
nsTArray<nsRefPtr<CheckQuotaHelper> >* gPromptHelpers = nsnull;
|
||||
|
||||
class CreateObjectStoreHelper : public AsyncConnectionHelper
|
||||
{
|
||||
public:
|
||||
|
@ -195,11 +189,6 @@ IDBDatabase::IDBDatabase()
|
|||
mRunningVersionChange(false)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
if (!gDatabaseInstanceCount++) {
|
||||
NS_ASSERTION(!gPromptHelpersMutex, "Should be null!");
|
||||
gPromptHelpersMutex = new mozilla::Mutex("IDBDatabase gPromptHelpersMutex");
|
||||
}
|
||||
}
|
||||
|
||||
IDBDatabase::~IDBDatabase()
|
||||
|
@ -218,86 +207,20 @@ IDBDatabase::~IDBDatabase()
|
|||
if (mListenerManager) {
|
||||
mListenerManager->Disconnect();
|
||||
}
|
||||
|
||||
if (!--gDatabaseInstanceCount) {
|
||||
NS_ASSERTION(gPromptHelpersMutex, "Should not be null!");
|
||||
|
||||
delete gPromptHelpers;
|
||||
gPromptHelpers = nsnull;
|
||||
|
||||
delete gPromptHelpersMutex;
|
||||
gPromptHelpersMutex = nsnull;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
IDBDatabase::IsQuotaDisabled()
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(gPromptHelpersMutex, "This should never be null!");
|
||||
|
||||
MutexAutoLock lock(*gPromptHelpersMutex);
|
||||
|
||||
if (!gPromptHelpers) {
|
||||
gPromptHelpers = new nsAutoTArray<nsRefPtr<CheckQuotaHelper>, 10>();
|
||||
}
|
||||
|
||||
CheckQuotaHelper* foundHelper = nsnull;
|
||||
|
||||
PRUint32 count = gPromptHelpers->Length();
|
||||
for (PRUint32 index = 0; index < count; index++) {
|
||||
nsRefPtr<CheckQuotaHelper>& helper = gPromptHelpers->ElementAt(index);
|
||||
if (helper->WindowSerial() == Owner()->GetSerial()) {
|
||||
foundHelper = helper;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundHelper) {
|
||||
nsRefPtr<CheckQuotaHelper>* newHelper = gPromptHelpers->AppendElement();
|
||||
if (!newHelper) {
|
||||
NS_WARNING("Out of memory!");
|
||||
return false;
|
||||
}
|
||||
*newHelper = new CheckQuotaHelper(this, *gPromptHelpersMutex);
|
||||
foundHelper = *newHelper;
|
||||
|
||||
{
|
||||
// Unlock before calling out to XPCOM.
|
||||
MutexAutoUnlock unlock(*gPromptHelpersMutex);
|
||||
|
||||
nsresult rv = NS_DispatchToMainThread(foundHelper, NS_DISPATCH_NORMAL);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
}
|
||||
}
|
||||
|
||||
return foundHelper->PromptAndReturnQuotaIsDisabled();
|
||||
}
|
||||
|
||||
void
|
||||
IDBDatabase::Invalidate()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(gPromptHelpersMutex, "This should never be null!");
|
||||
|
||||
// Make sure we're closed too.
|
||||
Close();
|
||||
|
||||
// Cancel any quota prompts that are currently being displayed.
|
||||
{
|
||||
MutexAutoLock lock(*gPromptHelpersMutex);
|
||||
|
||||
if (gPromptHelpers) {
|
||||
PRUint32 count = gPromptHelpers->Length();
|
||||
for (PRUint32 index = 0; index < count; index++) {
|
||||
nsRefPtr<CheckQuotaHelper>& helper = gPromptHelpers->ElementAt(index);
|
||||
if (helper->WindowSerial() == Owner()->GetSerial()) {
|
||||
helper->Cancel();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// When the IndexedDatabaseManager needs to invalidate databases, all it has
|
||||
// is an origin, so we call back into the manager to cancel any prompts for
|
||||
// our owner.
|
||||
IndexedDatabaseManager::CancelPromptsForWindow(Owner());
|
||||
|
||||
mInvalidated = true;
|
||||
}
|
||||
|
|
|
@ -122,8 +122,6 @@ public:
|
|||
return doc.forget();
|
||||
}
|
||||
|
||||
bool IsQuotaDisabled();
|
||||
|
||||
nsCString& Origin()
|
||||
{
|
||||
return mASCIIOrigin;
|
||||
|
|
|
@ -403,24 +403,10 @@ IDBFactory::OpenCommon(const nsAString& aName,
|
|||
nsIScriptContext* context = sgo->GetContext();
|
||||
NS_ENSURE_TRUE(context, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
nsresult rv = nsContentUtils::GetSecurityManager()->
|
||||
GetSubjectPrincipal(getter_AddRefs(principal));
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
nsCString origin;
|
||||
if (nsContentUtils::IsSystemPrincipal(principal)) {
|
||||
origin.AssignLiteral("chrome");
|
||||
}
|
||||
else {
|
||||
rv = nsContentUtils::GetASCIIOrigin(principal, origin);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
if (origin.EqualsLiteral("null")) {
|
||||
NS_WARNING("IndexedDB databases not allowed for this principal!");
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
}
|
||||
nsresult rv =
|
||||
IndexedDatabaseManager::GetASCIIOriginFromWindow(window, origin);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsRefPtr<IDBOpenDBRequest> request =
|
||||
IDBOpenDBRequest::Create(context, window);
|
||||
|
|
|
@ -887,7 +887,7 @@ CommitHelper::Run()
|
|||
}
|
||||
|
||||
if (mConnection) {
|
||||
IndexedDatabaseManager::SetCurrentDatabase(database);
|
||||
IndexedDatabaseManager::SetCurrentWindow(database->Owner());
|
||||
|
||||
if (!mAborted) {
|
||||
NS_NAMED_LITERAL_CSTRING(release, "COMMIT TRANSACTION");
|
||||
|
@ -923,7 +923,7 @@ CommitHelper::Run()
|
|||
mConnection->Close();
|
||||
mConnection = nsnull;
|
||||
|
||||
IndexedDatabaseManager::SetCurrentDatabase(nsnull);
|
||||
IndexedDatabaseManager::SetCurrentWindow(nsnull);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
|
|
@ -41,6 +41,8 @@
|
|||
|
||||
#include "nsIFile.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIScriptObjectPrincipal.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsISHEntry.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
#include "nsITimer.h"
|
||||
|
@ -54,6 +56,7 @@
|
|||
#include "nsXPCOMPrivate.h"
|
||||
|
||||
#include "AsyncConnectionHelper.h"
|
||||
#include "CheckQuotaHelper.h"
|
||||
#include "IDBDatabase.h"
|
||||
#include "IDBEvents.h"
|
||||
#include "IDBFactory.h"
|
||||
|
@ -75,7 +78,7 @@
|
|||
#define PREF_INDEXEDDB_QUOTA "dom.indexedDB.warningQuota"
|
||||
|
||||
// A bad TLS index number.
|
||||
#define BAD_TLS_INDEX (PRUintn)-1
|
||||
#define BAD_TLS_INDEX (PRUintn)-1
|
||||
|
||||
USING_INDEXEDDB_NAMESPACE
|
||||
using namespace mozilla::services;
|
||||
|
@ -88,8 +91,6 @@ PRInt32 gShutdown = 0;
|
|||
// Does not hold a reference.
|
||||
IndexedDatabaseManager* gInstance = nsnull;
|
||||
|
||||
PRUintn gCurrentDatabaseIndex = BAD_TLS_INDEX;
|
||||
|
||||
PRInt32 gIndexedDBQuotaMB = DEFAULT_QUOTA_MB;
|
||||
|
||||
class QuotaCallback : public mozIStorageQuotaCallback
|
||||
|
@ -104,13 +105,7 @@ public:
|
|||
nsISupports* aUserData,
|
||||
PRInt64* _retval)
|
||||
{
|
||||
NS_ASSERTION(gCurrentDatabaseIndex != BAD_TLS_INDEX,
|
||||
"This should be impossible!");
|
||||
|
||||
IDBDatabase* database =
|
||||
static_cast<IDBDatabase*>(PR_GetThreadPrivate(gCurrentDatabaseIndex));
|
||||
|
||||
if (database && database->IsQuotaDisabled()) {
|
||||
if (IndexedDatabaseManager::QuotaIsLifted()) {
|
||||
*_retval = 0;
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -146,6 +141,8 @@ EnumerateToTArray(const nsACString& aKey,
|
|||
} // anonymous namespace
|
||||
|
||||
IndexedDatabaseManager::IndexedDatabaseManager()
|
||||
: mCurrentWindowIndex(BAD_TLS_INDEX),
|
||||
mQuotaHelperMutex("IndexedDatabaseManager.mQuotaHelperMutex")
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(!gInstance, "More than one instance!");
|
||||
|
@ -172,30 +169,31 @@ IndexedDatabaseManager::GetOrCreate()
|
|||
nsRefPtr<IndexedDatabaseManager> instance(gInstance);
|
||||
|
||||
if (!instance) {
|
||||
// We need a thread-local to hold our current database.
|
||||
if (gCurrentDatabaseIndex == BAD_TLS_INDEX) {
|
||||
if (PR_NewThreadPrivateIndex(&gCurrentDatabaseIndex, nsnull) !=
|
||||
PR_SUCCESS) {
|
||||
NS_ERROR("PR_NewThreadPrivateIndex failed!");
|
||||
gCurrentDatabaseIndex = BAD_TLS_INDEX;
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
if (NS_FAILED(Preferences::AddIntVarCache(&gIndexedDBQuotaMB,
|
||||
PREF_INDEXEDDB_QUOTA,
|
||||
DEFAULT_QUOTA_MB))) {
|
||||
NS_WARNING("Unable to respond to quota pref changes!");
|
||||
gIndexedDBQuotaMB = DEFAULT_QUOTA_MB;
|
||||
}
|
||||
if (NS_FAILED(Preferences::AddIntVarCache(&gIndexedDBQuotaMB,
|
||||
PREF_INDEXEDDB_QUOTA,
|
||||
DEFAULT_QUOTA_MB))) {
|
||||
NS_WARNING("Unable to respond to quota pref changes!");
|
||||
gIndexedDBQuotaMB = DEFAULT_QUOTA_MB;
|
||||
}
|
||||
|
||||
instance = new IndexedDatabaseManager();
|
||||
|
||||
if (!instance->mLiveDatabases.Init()) {
|
||||
if (!instance->mLiveDatabases.Init() ||
|
||||
!instance->mQuotaHelperHash.Init()) {
|
||||
NS_WARNING("Out of memory!");
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
// We need a thread-local to hold the current window.
|
||||
NS_ASSERTION(instance->mCurrentWindowIndex == BAD_TLS_INDEX, "Huh?");
|
||||
|
||||
if (PR_NewThreadPrivateIndex(&instance->mCurrentWindowIndex, nsnull) !=
|
||||
PR_SUCCESS) {
|
||||
NS_ERROR("PR_NewThreadPrivateIndex failed, IndexedDB disabled");
|
||||
instance->mCurrentWindowIndex = BAD_TLS_INDEX;
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
// Make a timer here to avoid potential failures later. We don't actually
|
||||
// initialize the timer until shutdown.
|
||||
instance->mShutdownTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
|
||||
|
@ -548,30 +546,23 @@ IndexedDatabaseManager::OnDatabaseClosed(IDBDatabase* aDatabase)
|
|||
}
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
IndexedDatabaseManager::SetCurrentDatabase(IDBDatabase* aDatabase)
|
||||
void
|
||||
IndexedDatabaseManager::SetCurrentWindowInternal(nsPIDOMWindow* aWindow)
|
||||
{
|
||||
NS_ASSERTION(gCurrentDatabaseIndex != BAD_TLS_INDEX,
|
||||
"This should have been set already!");
|
||||
|
||||
if (aWindow) {
|
||||
#ifdef DEBUG
|
||||
if (aDatabase) {
|
||||
NS_ASSERTION(!PR_GetThreadPrivate(gCurrentDatabaseIndex),
|
||||
"Someone forgot to unset gCurrentDatabaseIndex!");
|
||||
NS_ASSERTION(!PR_GetThreadPrivate(mCurrentWindowIndex),
|
||||
"Somebody forgot to clear the current window!");
|
||||
#endif
|
||||
PR_SetThreadPrivate(mCurrentWindowIndex, aWindow);
|
||||
}
|
||||
else {
|
||||
NS_ASSERTION(PR_GetThreadPrivate(gCurrentDatabaseIndex),
|
||||
"Someone forgot to set gCurrentDatabaseIndex!");
|
||||
}
|
||||
#ifdef DEBUG
|
||||
NS_ASSERTION(PR_GetThreadPrivate(mCurrentWindowIndex),
|
||||
"Somebody forgot to clear the current window!");
|
||||
#endif
|
||||
|
||||
if (PR_SetThreadPrivate(gCurrentDatabaseIndex, aDatabase) != PR_SUCCESS) {
|
||||
NS_WARNING("Failed to set gCurrentDatabaseIndex!");
|
||||
return false;
|
||||
PR_SetThreadPrivate(mCurrentWindowIndex, nsnull);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -662,6 +653,102 @@ IndexedDatabaseManager::EnsureQuotaManagementForDirectory(nsIFile* aDirectory)
|
|||
return rv;
|
||||
}
|
||||
|
||||
bool
|
||||
IndexedDatabaseManager::QuotaIsLiftedInternal()
|
||||
{
|
||||
nsPIDOMWindow* window = nsnull;
|
||||
nsRefPtr<CheckQuotaHelper> helper = nsnull;
|
||||
bool createdHelper = false;
|
||||
|
||||
window =
|
||||
static_cast<nsPIDOMWindow*>(PR_GetThreadPrivate(mCurrentWindowIndex));
|
||||
|
||||
// Once IDB is supported outside of Windows this should become an early
|
||||
// return true.
|
||||
NS_ASSERTION(window, "Why don't we have a Window here?");
|
||||
|
||||
// Hold the lock from here on.
|
||||
MutexAutoLock autoLock(mQuotaHelperMutex);
|
||||
|
||||
mQuotaHelperHash.Get(window, getter_AddRefs(helper));
|
||||
|
||||
if (!helper) {
|
||||
helper = new CheckQuotaHelper(window, mQuotaHelperMutex);
|
||||
createdHelper = true;
|
||||
|
||||
bool result = mQuotaHelperHash.Put(window, helper);
|
||||
NS_ENSURE_TRUE(result, result);
|
||||
|
||||
// Unlock while calling out to XPCOM
|
||||
{
|
||||
MutexAutoUnlock autoUnlock(mQuotaHelperMutex);
|
||||
|
||||
nsresult rv = NS_DispatchToMainThread(helper);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
}
|
||||
|
||||
// Relocked. If any other threads hit the quota limit on the same Window,
|
||||
// they are using the helper we created here and are now blocking in
|
||||
// PromptAndReturnQuotaDisabled.
|
||||
}
|
||||
|
||||
bool result = helper->PromptAndReturnQuotaIsDisabled();
|
||||
|
||||
// If this thread created the helper and added it to the hash, this thread
|
||||
// must remove it.
|
||||
if (createdHelper) {
|
||||
mQuotaHelperHash.Remove(window);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
IndexedDatabaseManager::CancelPromptsForWindowInternal(nsPIDOMWindow* aWindow)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
nsRefPtr<CheckQuotaHelper> helper;
|
||||
|
||||
MutexAutoLock autoLock(mQuotaHelperMutex);
|
||||
|
||||
mQuotaHelperHash.Get(aWindow, getter_AddRefs(helper));
|
||||
|
||||
if (helper) {
|
||||
helper->Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
IndexedDatabaseManager::GetASCIIOriginFromWindow(nsPIDOMWindow* aWindow,
|
||||
nsCString& aASCIIOrigin)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(),
|
||||
"We're about to touch a window off the main thread!");
|
||||
|
||||
nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow);
|
||||
NS_ENSURE_TRUE(sop, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
|
||||
NS_ENSURE_TRUE(principal, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
if (nsContentUtils::IsSystemPrincipal(principal)) {
|
||||
aASCIIOrigin.AssignLiteral("chrome");
|
||||
}
|
||||
else {
|
||||
nsresult rv = nsContentUtils::GetASCIIOrigin(principal, aASCIIOrigin);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
if (aASCIIOrigin.EqualsLiteral("null")) {
|
||||
NS_WARNING("IndexedDB databases not allowed for this principal!");
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
IndexedDatabaseManager::DispatchHelper(AsyncConnectionHelper* aHelper)
|
||||
|
|
|
@ -44,6 +44,8 @@
|
|||
#include "mozilla/dom/indexedDB/IDBDatabase.h"
|
||||
#include "mozilla/dom/indexedDB/IDBRequest.h"
|
||||
|
||||
#include "mozilla/Mutex.h"
|
||||
|
||||
#include "nsIIndexedDatabaseManager.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIRunnable.h"
|
||||
|
@ -51,6 +53,7 @@
|
|||
#include "nsIURI.h"
|
||||
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
|
||||
#define INDEXEDDB_MANAGER_CONTRACTID "@mozilla.org/dom/indexeddb/manager;1"
|
||||
|
@ -62,6 +65,8 @@ BEGIN_INDEXEDDB_NAMESPACE
|
|||
|
||||
class AsyncConnectionHelper;
|
||||
|
||||
class CheckQuotaHelper;
|
||||
|
||||
class IndexedDatabaseManager : public nsIIndexedDatabaseManager,
|
||||
public nsIObserver
|
||||
{
|
||||
|
@ -129,14 +134,45 @@ public:
|
|||
// Used to check if there are running transactions in a given window.
|
||||
bool HasOpenTransactions(nsPIDOMWindow* aWindow);
|
||||
|
||||
static bool
|
||||
SetCurrentDatabase(IDBDatabase* aDatabase);
|
||||
// Set the Window that the current thread is doing operations for.
|
||||
// The caller is responsible for ensuring that aWindow is held alive.
|
||||
static inline void
|
||||
SetCurrentWindow(nsPIDOMWindow* aWindow)
|
||||
{
|
||||
IndexedDatabaseManager* mgr = Get();
|
||||
NS_ASSERTION(mgr, "Must have a manager here!");
|
||||
|
||||
return mgr->SetCurrentWindowInternal(aWindow);
|
||||
}
|
||||
|
||||
static PRUint32
|
||||
GetIndexedDBQuotaMB();
|
||||
|
||||
nsresult EnsureQuotaManagementForDirectory(nsIFile* aDirectory);
|
||||
|
||||
// Determine if the quota is lifted for the Window the current thread is
|
||||
// using.
|
||||
static inline bool
|
||||
QuotaIsLifted()
|
||||
{
|
||||
IndexedDatabaseManager* mgr = Get();
|
||||
NS_ASSERTION(mgr, "Must have a manager here!");
|
||||
|
||||
return mgr->QuotaIsLiftedInternal();
|
||||
}
|
||||
|
||||
static inline void
|
||||
CancelPromptsForWindow(nsPIDOMWindow* aWindow)
|
||||
{
|
||||
IndexedDatabaseManager* mgr = Get();
|
||||
NS_ASSERTION(mgr, "Must have a manager here!");
|
||||
|
||||
mgr->CancelPromptsForWindowInternal(aWindow);
|
||||
}
|
||||
|
||||
static nsresult
|
||||
GetASCIIOriginFromWindow(nsPIDOMWindow* aWindow, nsCString& aASCIIOrigin);
|
||||
|
||||
private:
|
||||
IndexedDatabaseManager();
|
||||
~IndexedDatabaseManager();
|
||||
|
@ -147,6 +183,10 @@ private:
|
|||
WaitingOnDatabasesCallback aCallback,
|
||||
void* aClosure);
|
||||
|
||||
void SetCurrentWindowInternal(nsPIDOMWindow* aWindow);
|
||||
bool QuotaIsLiftedInternal();
|
||||
void CancelPromptsForWindowInternal(nsPIDOMWindow* aWindow);
|
||||
|
||||
// Called when a database is created.
|
||||
bool RegisterDatabase(IDBDatabase* aDatabase);
|
||||
|
||||
|
@ -267,6 +307,15 @@ private:
|
|||
// Maintains a list of live databases per origin.
|
||||
nsClassHashtable<nsCStringHashKey, nsTArray<IDBDatabase*> > mLiveDatabases;
|
||||
|
||||
// TLS storage index for the current thread's window
|
||||
PRUintn mCurrentWindowIndex;
|
||||
|
||||
// Lock protecting mQuotaHelperHash
|
||||
mozilla::Mutex mQuotaHelperMutex;
|
||||
|
||||
// A map of Windows to the corresponding quota helper.
|
||||
nsRefPtrHashtable<nsPtrHashKey<nsPIDOMWindow>, CheckQuotaHelper> mQuotaHelperHash;
|
||||
|
||||
// Maintains a list of origins that we're currently enumerating to gather
|
||||
// usage statistics.
|
||||
nsAutoTArray<nsRefPtr<AsyncUsageRunnable>, 1> mUsageRunnables;
|
||||
|
@ -290,6 +339,21 @@ private:
|
|||
nsTArray<nsCString> mTrackedQuotaPaths;
|
||||
};
|
||||
|
||||
class AutoEnterWindow
|
||||
{
|
||||
public:
|
||||
AutoEnterWindow(nsPIDOMWindow* aWindow)
|
||||
{
|
||||
NS_ASSERTION(aWindow, "This should never be null!");
|
||||
IndexedDatabaseManager::SetCurrentWindow(aWindow);
|
||||
}
|
||||
|
||||
~AutoEnterWindow()
|
||||
{
|
||||
IndexedDatabaseManager::SetCurrentWindow(nsnull);
|
||||
}
|
||||
};
|
||||
|
||||
END_INDEXEDDB_NAMESPACE
|
||||
|
||||
#endif /* mozilla_dom_indexeddb_indexeddatabasemanager_h__ */
|
||||
|
|
|
@ -1090,6 +1090,14 @@ OpenDatabaseHelper::DoDatabaseWork()
|
|||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
NS_ASSERTION(mOpenDBRequest, "This should never be null!");
|
||||
|
||||
// Once we support IDB outside of Windows this assertion will no longer hold.
|
||||
nsPIDOMWindow* window = mOpenDBRequest->Owner();
|
||||
NS_ASSERTION(window, "This should never be null");
|
||||
|
||||
AutoEnterWindow autoWindow(window);
|
||||
|
||||
nsCOMPtr<nsIFile> dbFile;
|
||||
nsresult rv = GetDatabaseFile(mASCIIOrigin, mName, getter_AddRefs(dbFile));
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
|
|
@ -129,6 +129,8 @@ BROWSER_TEST_FILES = \
|
|||
browser_quotaPrompt.html \
|
||||
browser_quotaPromptAllow.js \
|
||||
browser_quotaPromptDeny.js \
|
||||
browser_quotaPromptDatabases.html \
|
||||
browser_quotaPromptDatabases.js \
|
||||
head.js \
|
||||
$(NULL)
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
|
||||
// Make sure this is a unique origin or the tests will randomly fail!
|
||||
const testPageURL = "http://test1.example.org/browser/" +
|
||||
const testPageURL = "http://bug704464-1.example.com/browser/" +
|
||||
"dom/indexedDB/test/browser_quotaPrompt.html";
|
||||
const notificationID = "indexedDB-quota-prompt";
|
||||
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Indexed Database Test</title>
|
||||
|
||||
<script type="text/javascript;version=1.7">
|
||||
const READ_WRITE = Components.interfaces.nsIIDBTransaction.READ_WRITE;
|
||||
|
||||
let db;
|
||||
let i = 0;
|
||||
|
||||
function onAddMore() {
|
||||
const name = window.location.pathname + i++;
|
||||
|
||||
let request = mozIndexedDB.open(name, 1);
|
||||
request.onerror = errorHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
|
||||
request.onsuccess = function(event) {
|
||||
setTimeout(testFinishedCallback, 0, "complete");
|
||||
}
|
||||
request.onerror = function(event) {
|
||||
setTimeout(testFinishedCallback, 0, "abort");
|
||||
}
|
||||
}
|
||||
|
||||
function onDone() {
|
||||
window.removeEventListener("indexedDB-addMore", onAddMore, true);
|
||||
window.removeEventListener("indexedDB-done", onDone, true);
|
||||
|
||||
testResult = "finished";
|
||||
testException = undefined;
|
||||
finishTest();
|
||||
}
|
||||
|
||||
function testSteps()
|
||||
{
|
||||
window.addEventListener("indexedDB-addMore", onAddMore, true);
|
||||
window.addEventListener("indexedDB-done", onDone, true);
|
||||
|
||||
setTimeout(testFinishedCallback, 0, "ready");
|
||||
yield;
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="text/javascript;version=1.7" src="browserHelpers.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="runTest();" onunload="finishTestNow();"></body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,76 @@
|
|||
/**
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
// Make sure this is a unique origin or the tests will randomly fail!
|
||||
const testPageURL = "http://bug704464-3.example.com/browser/" +
|
||||
"dom/indexedDB/test/browser_quotaPromptDatabases.html";
|
||||
const notificationID = "indexedDB-quota-prompt";
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
requestLongerTimeout(10);
|
||||
setPermission(testPageURL, "indexedDB");
|
||||
removePermission(testPageURL, "indexedDB-unlimited");
|
||||
Services.prefs.setIntPref("dom.indexedDB.warningQuota", 2);
|
||||
executeSoon(test1);
|
||||
}
|
||||
|
||||
let addMoreTest1Count = 0;
|
||||
|
||||
function test1()
|
||||
{
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
|
||||
gBrowser.selectedBrowser.addEventListener("load", function () {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
|
||||
|
||||
let seenPopupCount;
|
||||
|
||||
setFinishedCallback(function(result) {
|
||||
is(result, "ready", "Got 'ready' result");
|
||||
|
||||
setFinishedCallback(function(result) {
|
||||
is(result, "complete", "Got 'complete' result");
|
||||
|
||||
if (addMoreTest1Count >= seenPopupCount + 5) {
|
||||
setFinishedCallback(function(result) {
|
||||
is(result, "finished", "Got 'finished' result");
|
||||
is(getPermission(testPageURL, "indexedDB-unlimited"),
|
||||
Components.interfaces.nsIPermissionManager.ALLOW_ACTION,
|
||||
"Correct permission set");
|
||||
gBrowser.removeCurrentTab();
|
||||
unregisterAllPopupEventHandlers();
|
||||
addMoreTest1Count = seenPopupCount;
|
||||
executeSoon(finish);
|
||||
});
|
||||
executeSoon(function() { dispatchEvent("indexedDB-done"); });
|
||||
}
|
||||
else {
|
||||
++addMoreTest1Count;
|
||||
executeSoon(function() { dispatchEvent("indexedDB-addMore"); });
|
||||
}
|
||||
});
|
||||
++addMoreTest1Count;
|
||||
executeSoon(function() { dispatchEvent("indexedDB-addMore"); });
|
||||
});
|
||||
|
||||
registerPopupEventHandler("popupshowing", function () {
|
||||
ok(true, "prompt showing");
|
||||
seenPopupCount = addMoreTest1Count - 1;
|
||||
});
|
||||
registerPopupEventHandler("popupshown", function () {
|
||||
ok(true, "prompt shown");
|
||||
triggerMainCommand(this);
|
||||
});
|
||||
registerPopupEventHandler("popuphidden", function () {
|
||||
ok(true, "prompt hidden");
|
||||
});
|
||||
|
||||
}, true);
|
||||
|
||||
info("loading test page: " + testPageURL);
|
||||
content.location = testPageURL;
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
|
||||
// Make sure this is a unique origin or the tests will randomly fail!
|
||||
const testPageURL = "http://test2.example.org/browser/" +
|
||||
const testPageURL = "http://bug704464-2.example.com/browser/" +
|
||||
"dom/indexedDB/test/browser_quotaPrompt.html";
|
||||
const notificationID = "indexedDB-quota-prompt";
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
|
||||
// FIXME(bug 332648): Give me a real API please!
|
||||
#include "jscntxt.h"
|
||||
#include "jsfriendapi.h"
|
||||
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "nsJSNPRuntime.h"
|
||||
|
@ -1483,7 +1484,7 @@ CallNPMethodInternal(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
|||
// the function object.
|
||||
|
||||
if (npobj->_class->invoke) {
|
||||
JSFunction *fun = (JSFunction *)::JS_GetPrivate(cx, funobj);
|
||||
JSFunction *fun = ::JS_GetObjectFunction(funobj);
|
||||
JSString *name = ::JS_InternJSString(cx, ::JS_GetFunctionId(fun));
|
||||
NPIdentifier id = StringToNPIdentifier(cx, name);
|
||||
|
||||
|
|
|
@ -91,8 +91,8 @@ public:
|
|||
InitClass(JSContext* aCx, JSObject* aObj, JSObject* aParentProto,
|
||||
bool aMainRuntime)
|
||||
{
|
||||
JSObject* proto = JS_InitClass(aCx, aObj, aParentProto, &sClass, Construct,
|
||||
0, sProperties, sFunctions, NULL, NULL);
|
||||
JSObject* proto = js::InitClassWithReserved(aCx, aObj, aParentProto, &sClass, Construct,
|
||||
0, sProperties, sFunctions, NULL, NULL);
|
||||
if (!proto) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -102,11 +102,10 @@ public:
|
|||
parent->AssertIsOnWorkerThread();
|
||||
|
||||
JSObject* constructor = JS_GetConstructor(aCx, proto);
|
||||
if (!constructor ||
|
||||
!JS_SetReservedSlot(aCx, constructor, CONSTRUCTOR_SLOT_PARENT,
|
||||
PRIVATE_TO_JSVAL(parent))) {
|
||||
if (!constructor)
|
||||
return NULL;
|
||||
}
|
||||
js::SetFunctionNativeReserved(constructor, CONSTRUCTOR_SLOT_PARENT,
|
||||
PRIVATE_TO_JSVAL(parent));
|
||||
}
|
||||
|
||||
return proto;
|
||||
|
@ -153,11 +152,8 @@ protected:
|
|||
return false;
|
||||
}
|
||||
|
||||
jsval priv;
|
||||
if (!JS_GetReservedSlot(aCx, JSVAL_TO_OBJECT(JS_CALLEE(aCx, aVp)),
|
||||
CONSTRUCTOR_SLOT_PARENT, &priv)) {
|
||||
return false;
|
||||
}
|
||||
jsval priv = js::GetFunctionNativeReserved(JSVAL_TO_OBJECT(JS_CALLEE(aCx, aVp)),
|
||||
CONSTRUCTOR_SLOT_PARENT);
|
||||
|
||||
RuntimeService* runtimeService;
|
||||
WorkerPrivate* parent;
|
||||
|
@ -345,8 +341,8 @@ public:
|
|||
InitClass(JSContext* aCx, JSObject* aObj, JSObject* aParentProto,
|
||||
bool aMainRuntime)
|
||||
{
|
||||
JSObject* proto = JS_InitClass(aCx, aObj, aParentProto, &sClass, Construct,
|
||||
0, NULL, NULL, NULL, NULL);
|
||||
JSObject* proto = js::InitClassWithReserved(aCx, aObj, aParentProto, &sClass, Construct,
|
||||
0, NULL, NULL, NULL, NULL);
|
||||
if (!proto) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -356,11 +352,10 @@ public:
|
|||
parent->AssertIsOnWorkerThread();
|
||||
|
||||
JSObject* constructor = JS_GetConstructor(aCx, proto);
|
||||
if (!constructor ||
|
||||
!JS_SetReservedSlot(aCx, constructor, CONSTRUCTOR_SLOT_PARENT,
|
||||
PRIVATE_TO_JSVAL(parent))) {
|
||||
if (!constructor)
|
||||
return NULL;
|
||||
}
|
||||
js::SetFunctionNativeReserved(constructor, CONSTRUCTOR_SLOT_PARENT,
|
||||
PRIVATE_TO_JSVAL(parent));
|
||||
}
|
||||
|
||||
return proto;
|
||||
|
|
|
@ -271,11 +271,8 @@ private:
|
|||
JSObject* wrapper = JSVAL_TO_OBJECT(JS_CALLEE(aCx, aVp));
|
||||
JS_ASSERT(JS_ObjectIsFunction(aCx, wrapper));
|
||||
|
||||
jsval scope, listener;
|
||||
if (!JS_GetReservedSlot(aCx, wrapper, SLOT_wrappedScope, &scope) ||
|
||||
!JS_GetReservedSlot(aCx, wrapper, SLOT_wrappedFunction, &listener)) {
|
||||
return false;
|
||||
}
|
||||
jsval scope = js::GetFunctionNativeReserved(wrapper, SLOT_wrappedScope);
|
||||
jsval listener = js::GetFunctionNativeReserved(wrapper, SLOT_wrappedFunction);
|
||||
|
||||
JS_ASSERT(JSVAL_IS_OBJECT(scope));
|
||||
|
||||
|
@ -319,11 +316,8 @@ private:
|
|||
|
||||
JS_ASSERT(JSVAL_IS_OBJECT(adaptor));
|
||||
|
||||
jsval listener;
|
||||
if (!JS_GetReservedSlot(aCx, JSVAL_TO_OBJECT(adaptor), SLOT_wrappedFunction,
|
||||
&listener)) {
|
||||
return false;
|
||||
}
|
||||
jsval listener = js::GetFunctionNativeReserved(JSVAL_TO_OBJECT(adaptor),
|
||||
SLOT_wrappedFunction);
|
||||
|
||||
*aVp = listener;
|
||||
return true;
|
||||
|
@ -339,8 +333,8 @@ private:
|
|||
return false;
|
||||
}
|
||||
|
||||
JSFunction* adaptor = JS_NewFunction(aCx, UnwrapErrorEvent, 1, 0,
|
||||
JS_GetGlobalObject(aCx), "unwrap");
|
||||
JSFunction* adaptor = js::NewFunctionWithReserved(aCx, UnwrapErrorEvent, 1, 0,
|
||||
JS_GetGlobalObject(aCx), "unwrap");
|
||||
if (!adaptor) {
|
||||
return false;
|
||||
}
|
||||
|
@ -350,11 +344,9 @@ private:
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!JS_SetReservedSlot(aCx, listener, SLOT_wrappedScope,
|
||||
OBJECT_TO_JSVAL(aObj)) ||
|
||||
!JS_SetReservedSlot(aCx, listener, SLOT_wrappedFunction, *aVp)) {
|
||||
return false;
|
||||
}
|
||||
js::SetFunctionNativeReserved(listener, SLOT_wrappedScope,
|
||||
OBJECT_TO_JSVAL(aObj));
|
||||
js::SetFunctionNativeReserved(listener, SLOT_wrappedFunction, *aVp);
|
||||
|
||||
jsval val = OBJECT_TO_JSVAL(listener);
|
||||
return scope->SetEventListenerOnEventTarget(aCx, name + 2, &val);
|
||||
|
|
|
@ -747,7 +747,7 @@ jsd_GetValueParent(JSDContext* jsdc, JSDValue* jsdval)
|
|||
|
||||
return NULL;
|
||||
}
|
||||
parent = JS_GetParent(jsdc->dumbContext,obj);
|
||||
parent = JS_GetParentOrScopeChain(jsdc->dumbContext,obj);
|
||||
JS_LeaveCrossCompartmentCall(call);
|
||||
JS_EndRequest(jsdc->dumbContext);
|
||||
if(!parent)
|
||||
|
|
|
@ -357,6 +357,7 @@ class HashTable : private AllocPolicy
|
|||
public:
|
||||
HashTable(AllocPolicy ap)
|
||||
: AllocPolicy(ap),
|
||||
hashShift(sHashBits),
|
||||
entryCount(0),
|
||||
gen(0),
|
||||
removedCount(0),
|
||||
|
|
|
@ -564,6 +564,11 @@ public:
|
|||
return AssemblerType::getDifferenceBetweenLabels(from.m_label, to.m_label);
|
||||
}
|
||||
|
||||
ptrdiff_t differenceBetween(DataLabelPtr from, Label to)
|
||||
{
|
||||
return AssemblerType::getDifferenceBetweenLabels(from.m_label, to.m_label);
|
||||
}
|
||||
|
||||
ptrdiff_t differenceBetween(DataLabelPtr from, Jump to)
|
||||
{
|
||||
return AssemblerType::getDifferenceBetweenLabels(from.m_label, to.m_jmp);
|
||||
|
|
|
@ -334,7 +334,7 @@ public:
|
|||
return branch32(cond, left, Imm32(right));
|
||||
}
|
||||
|
||||
Jump branchPtr(Condition cond, AbsoluteAddress left, ImmPtr right)
|
||||
Jump branchPtr(Condition cond, AbsoluteAddress left, ImmPtr right, RegisterID scratch)
|
||||
{
|
||||
return branch32(cond, left, Imm32(right));
|
||||
}
|
||||
|
|
|
@ -431,6 +431,12 @@ public:
|
|||
return branchPtr(cond, Address(scratchRegister), right);
|
||||
}
|
||||
|
||||
Jump branchPtr(Condition cond, AbsoluteAddress left, ImmPtr right, RegisterID scratch)
|
||||
{
|
||||
move(ImmPtr(left.m_ptr), scratch);
|
||||
return branchPtr(cond, Address(scratch), right);
|
||||
}
|
||||
|
||||
Jump branchPtr(Condition cond, Address left, RegisterID right)
|
||||
{
|
||||
m_assembler.cmpq_rm(right, left.offset, left.base);
|
||||
|
|
|
@ -110,9 +110,9 @@ public:
|
|||
MacroAssembler::repatchInt32(dataLabel32, value);
|
||||
}
|
||||
|
||||
void repatch(CodeLocationDataLabelPtr dataLabelPtr, void* value)
|
||||
void repatch(CodeLocationDataLabelPtr dataLabelPtr, const void* value)
|
||||
{
|
||||
MacroAssembler::repatchPointer(dataLabelPtr, value);
|
||||
MacroAssembler::repatchPointer(dataLabelPtr, (void*) value);
|
||||
}
|
||||
|
||||
void repatchLoadPtrToLEA(CodeLocationInstruction instruction)
|
||||
|
|
|
@ -641,7 +641,7 @@ InitTypeConstructor(JSContext* cx,
|
|||
JSObject*& typeProto,
|
||||
JSObject*& dataProto)
|
||||
{
|
||||
JSFunction* fun = JS_DefineFunction(cx, parent, spec.name, spec.call,
|
||||
JSFunction* fun = js::DefineFunctionWithReserved(cx, parent, spec.name, spec.call,
|
||||
spec.nargs, spec.flags);
|
||||
if (!fun)
|
||||
return false;
|
||||
|
@ -672,8 +672,7 @@ InitTypeConstructor(JSContext* cx,
|
|||
|
||||
// Stash ctypes.{Pointer,Array,Struct}Type.prototype on a reserved slot of
|
||||
// the type constructor, for faster lookup.
|
||||
if (!JS_SetReservedSlot(cx, obj, SLOT_FN_CTORPROTO, OBJECT_TO_JSVAL(typeProto)))
|
||||
return false;
|
||||
js::SetFunctionNativeReserved(obj, SLOT_FN_CTORPROTO, OBJECT_TO_JSVAL(typeProto));
|
||||
|
||||
// Create an object to serve as the common ancestor for all CData objects
|
||||
// created from the given type constructor. This has ctypes.CData.prototype
|
||||
|
@ -725,14 +724,18 @@ InitInt64Class(JSContext* cx,
|
|||
if (!JS_FreezeObject(cx, ctor))
|
||||
return NULL;
|
||||
|
||||
// Stash ctypes.{Int64,UInt64}.prototype on a reserved slot of the 'join'
|
||||
// function.
|
||||
jsval join;
|
||||
ASSERT_OK(JS_GetProperty(cx, ctor, "join", &join));
|
||||
if (!JS_SetReservedSlot(cx, JSVAL_TO_OBJECT(join), SLOT_FN_INT64PROTO,
|
||||
OBJECT_TO_JSVAL(prototype)))
|
||||
// Redefine the 'join' function as an extended native and stash
|
||||
// ctypes.{Int64,UInt64}.prototype in a reserved slot of the new function.
|
||||
JS_ASSERT(clasp == &sInt64ProtoClass || clasp == &sUInt64ProtoClass);
|
||||
JSNative native = (clasp == &sInt64ProtoClass) ? Int64::Join : UInt64::Join;
|
||||
JSFunction* fun = js::DefineFunctionWithReserved(cx, ctor, "join", native,
|
||||
2, CTYPESFN_FLAGS);
|
||||
if (!fun)
|
||||
return NULL;
|
||||
|
||||
js::SetFunctionNativeReserved(fun, SLOT_FN_INT64PROTO,
|
||||
OBJECT_TO_JSVAL(prototype));
|
||||
|
||||
if (!JS_FreezeObject(cx, prototype))
|
||||
return NULL;
|
||||
|
||||
|
@ -3045,8 +3048,7 @@ CType::GetProtoFromCtor(JSContext* cx, JSObject* obj, CTypeProtoSlot slot)
|
|||
{
|
||||
// Get ctypes.{Pointer,Array,Struct}Type.prototype from a reserved slot
|
||||
// on the type constructor.
|
||||
jsval protoslot;
|
||||
ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FN_CTORPROTO, &protoslot));
|
||||
jsval protoslot = js::GetFunctionNativeReserved(obj, SLOT_FN_CTORPROTO);
|
||||
JSObject* proto = JSVAL_TO_OBJECT(protoslot);
|
||||
JS_ASSERT(proto);
|
||||
JS_ASSERT(CType::IsCTypeProto(cx, proto));
|
||||
|
@ -6291,8 +6293,7 @@ Int64::Join(JSContext* cx, uintN argc, jsval* vp)
|
|||
// Get Int64.prototype from the function's reserved slot.
|
||||
JSObject* callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
|
||||
|
||||
jsval slot;
|
||||
ASSERT_OK(JS_GetReservedSlot(cx, callee, SLOT_FN_INT64PROTO, &slot));
|
||||
jsval slot = js::GetFunctionNativeReserved(callee, SLOT_FN_INT64PROTO);
|
||||
JSObject* proto = JSVAL_TO_OBJECT(slot);
|
||||
JS_ASSERT(JS_GET_CLASS(cx, proto) == &sInt64ProtoClass);
|
||||
|
||||
|
@ -6459,8 +6460,7 @@ UInt64::Join(JSContext* cx, uintN argc, jsval* vp)
|
|||
// Get UInt64.prototype from the function's reserved slot.
|
||||
JSObject* callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
|
||||
|
||||
jsval slot;
|
||||
ASSERT_OK(JS_GetReservedSlot(cx, callee, SLOT_FN_INT64PROTO, &slot));
|
||||
jsval slot = js::GetFunctionNativeReserved(callee, SLOT_FN_INT64PROTO);
|
||||
JSObject* proto = JSVAL_TO_OBJECT(slot);
|
||||
JS_ASSERT(JS_GET_CLASS(cx, proto) == &sUInt64ProtoClass);
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ DefineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *script)
|
|||
JSPROP_ENUMERATE | JSPROP_PERMANENT, 0, 0, DNP_SKIP_TYPE);
|
||||
if (!shape)
|
||||
return false;
|
||||
def.knownSlot = shape->slot;
|
||||
def.knownSlot = shape->slot();
|
||||
}
|
||||
|
||||
Vector<JSScript *, 16> worklist(cx);
|
||||
|
@ -123,10 +123,10 @@ DefineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *script)
|
|||
JSObject *obj = arr->vector[i];
|
||||
if (!obj->isFunction())
|
||||
continue;
|
||||
JSFunction *fun = obj->getFunctionPrivate();
|
||||
JSFunction *fun = obj->toFunction();
|
||||
JS_ASSERT(fun->isInterpreted());
|
||||
JSScript *inner = fun->script();
|
||||
if (outer->isHeavyweightFunction) {
|
||||
if (outer->function() && outer->function()->isHeavyweight()) {
|
||||
outer->isOuterFunction = true;
|
||||
inner->isInnerFunction = true;
|
||||
}
|
||||
|
|
|
@ -1383,7 +1383,7 @@ frontend::PushBlockScope(TreeContext *tc, StmtInfo *stmt, ObjectBox *blockBox, p
|
|||
PushStatement(tc, stmt, STMT_BLOCK, top);
|
||||
stmt->flags |= SIF_SCOPE;
|
||||
blockBox->parent = tc->blockChainBox;
|
||||
blockBox->object->setParent(tc->blockChain());
|
||||
blockBox->object->setStaticBlockScopeChain(tc->blockChain());
|
||||
stmt->downScope = tc->topScopeStmt;
|
||||
tc->topScopeStmt = stmt;
|
||||
tc->blockChainBox = blockBox;
|
||||
|
@ -1721,7 +1721,7 @@ frontend::LexicalLookup(TreeContext *tc, JSAtom *atom, jsint *slotp, StmtInfo *s
|
|||
|
||||
if (slotp) {
|
||||
JS_ASSERT(obj->getSlot(JSSLOT_BLOCK_DEPTH).isInt32());
|
||||
*slotp = obj->getSlot(JSSLOT_BLOCK_DEPTH).toInt32() + shape->shortid;
|
||||
*slotp = obj->getSlot(JSSLOT_BLOCK_DEPTH).toInt32() + shape->shortid();
|
||||
}
|
||||
return stmt;
|
||||
}
|
||||
|
@ -1781,8 +1781,8 @@ LookupCompileTimeConstant(JSContext *cx, BytecodeEmitter *bce, JSAtom *atom, Val
|
|||
* from our variable object here.
|
||||
*/
|
||||
if (!shape->writable() && !shape->configurable() &&
|
||||
shape->hasDefaultGetter() && obj->containsSlot(shape->slot)) {
|
||||
*constp = obj->getSlot(shape->slot);
|
||||
shape->hasDefaultGetter() && obj->containsSlot(shape->slot())) {
|
||||
*constp = obj->getSlot(shape->slot());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2020,8 +2020,13 @@ EmitEnterBlock(JSContext *cx, ParseNode *pn, BytecodeEmitter *bce)
|
|||
* js::Bindings::extensibleParents.
|
||||
*/
|
||||
if ((bce->flags & TCF_FUN_EXTENSIBLE_SCOPE) ||
|
||||
bce->bindings.extensibleParents())
|
||||
blockObj->setBlockOwnShape(cx);
|
||||
bce->bindings.extensibleParents()) {
|
||||
HeapPtrShape shape;
|
||||
shape.init(blockObj->lastProperty());
|
||||
if (!Shape::setExtensibleParents(cx, &shape))
|
||||
return false;
|
||||
blockObj->setLastPropertyInfallible(shape);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -2332,7 +2337,6 @@ BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
JS_ASSERT(bce->inFunction());
|
||||
JS_ASSERT_IF(cookie.slot() != UpvarCookie::CALLEE_SLOT, bce->roLexdeps->lookup(atom));
|
||||
JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
|
||||
JS_ASSERT(bce->fun()->u.i.skipmin <= skip);
|
||||
|
||||
/*
|
||||
* If op is a mutating opcode, this upvar's lookup skips too many levels,
|
||||
|
@ -3844,13 +3848,6 @@ frontend::EmitFunctionScript(JSContext *cx, BytecodeEmitter *bce, ParseNode *bod
|
|||
bce->switchToMain();
|
||||
}
|
||||
|
||||
if (bce->flags & TCF_FUN_UNBRAND_THIS) {
|
||||
bce->switchToProlog();
|
||||
if (Emit1(cx, bce, JSOP_UNBRANDTHIS) < 0)
|
||||
return false;
|
||||
bce->switchToMain();
|
||||
}
|
||||
|
||||
return EmitTree(cx, bce, body) &&
|
||||
Emit1(cx, bce, JSOP_STOP) >= 0 &&
|
||||
JSScript::NewScriptFromEmitter(cx, bce);
|
||||
|
@ -4756,7 +4753,7 @@ ParseNode::getConstantValue(JSContext *cx, bool strictChecks, Value *vp)
|
|||
case PNK_RC: {
|
||||
JS_ASSERT(isOp(JSOP_NEWINIT) && !(pn_xflags & PNX_NONCONST));
|
||||
|
||||
gc::AllocKind kind = GuessObjectGCKind(pn_count, false);
|
||||
gc::AllocKind kind = GuessObjectGCKind(pn_count);
|
||||
JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass, kind);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
@ -5447,6 +5444,40 @@ EmitWith(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
return PopStatementBCE(cx, bce);
|
||||
}
|
||||
|
||||
static bool
|
||||
SetMethodFunction(JSContext *cx, FunctionBox *funbox, JSAtom *atom)
|
||||
{
|
||||
/*
|
||||
* Replace a boxed function with a new one with a method atom. Methods
|
||||
* require a function with the extended size finalize kind, which normal
|
||||
* functions don't have. We don't eagerly allocate functions with the
|
||||
* expanded size for boxed functions, as most functions are not methods.
|
||||
*/
|
||||
JSFunction *fun = js_NewFunction(cx, NULL, NULL,
|
||||
funbox->function()->nargs,
|
||||
funbox->function()->flags,
|
||||
funbox->function()->getParent(),
|
||||
funbox->function()->atom,
|
||||
JSFunction::ExtendedFinalizeKind);
|
||||
if (!fun)
|
||||
return false;
|
||||
|
||||
JSScript *script = funbox->function()->script();
|
||||
if (script) {
|
||||
fun->setScript(script);
|
||||
if (!script->typeSetFunction(cx, fun))
|
||||
return false;
|
||||
}
|
||||
|
||||
JS_ASSERT(funbox->function()->joinable());
|
||||
fun->setJoinable();
|
||||
|
||||
fun->setMethodAtom(atom);
|
||||
|
||||
funbox->object = fun;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
EmitForIn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
|
||||
{
|
||||
|
@ -6329,6 +6360,8 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
pn2->pn_left->isOp(JSOP_SETPROP) &&
|
||||
pn2->pn_right->isOp(JSOP_LAMBDA) &&
|
||||
pn2->pn_right->pn_funbox->joinable()) {
|
||||
if (!SetMethodFunction(cx, pn2->pn_right->pn_funbox, pn2->pn_left->pn_atom))
|
||||
return JS_FALSE;
|
||||
pn2->pn_left->setOp(JSOP_SETMETHOD);
|
||||
}
|
||||
if (!EmitTree(cx, bce, pn2))
|
||||
|
@ -7096,7 +7129,7 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
*/
|
||||
JSObject *obj = NULL;
|
||||
if (!bce->hasSharps() && bce->compileAndGo()) {
|
||||
gc::AllocKind kind = GuessObjectGCKind(pn->pn_count, false);
|
||||
gc::AllocKind kind = GuessObjectGCKind(pn->pn_count);
|
||||
obj = NewBuiltinClassInstance(cx, &ObjectClass, kind);
|
||||
if (!obj)
|
||||
return JS_FALSE;
|
||||
|
@ -7144,6 +7177,8 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
obj = NULL;
|
||||
op = JSOP_INITMETHOD;
|
||||
pn2->setOp(op);
|
||||
if (!SetMethodFunction(cx, init->pn_funbox, pn3->pn_atom))
|
||||
return JS_FALSE;
|
||||
} else {
|
||||
/*
|
||||
* Disable NEWOBJECT on initializers that set __proto__, which has
|
||||
|
@ -7171,11 +7206,6 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
}
|
||||
}
|
||||
|
||||
if (bce->funbox && bce->funbox->shouldUnbrand(methodInits, slowMethodInits)) {
|
||||
obj = NULL;
|
||||
if (Emit1(cx, bce, JSOP_UNBRAND) < 0)
|
||||
return JS_FALSE;
|
||||
}
|
||||
if (!EmitEndInit(cx, bce, pn->pn_count))
|
||||
return JS_FALSE;
|
||||
|
||||
|
|
|
@ -203,15 +203,6 @@ struct StmtInfo {
|
|||
|
||||
/* bits 0x40000 and 0x80000 are unused */
|
||||
|
||||
/*
|
||||
* Flag signifying that the current function seems to be a constructor that
|
||||
* sets this.foo to define "methods", at least one of which can't be a null
|
||||
* closure, so we should avoid over-specializing property cache entries and
|
||||
* trace inlining guards to method function object identity, which will vary
|
||||
* per instance.
|
||||
*/
|
||||
#define TCF_FUN_UNBRAND_THIS 0x100000
|
||||
|
||||
/*
|
||||
* "Module pattern", i.e., a lambda that is immediately applied and the whole
|
||||
* of an expression statement.
|
||||
|
|
|
@ -133,20 +133,6 @@ FunctionBox::scopeIsExtensible() const
|
|||
return tcflags & TCF_FUN_EXTENSIBLE_SCOPE;
|
||||
}
|
||||
|
||||
bool
|
||||
FunctionBox::shouldUnbrand(uintN methods, uintN slowMethods) const
|
||||
{
|
||||
if (slowMethods != 0) {
|
||||
for (const FunctionBox *funbox = this; funbox; funbox = funbox->parent) {
|
||||
if (!(funbox->tcflags & TCF_FUN_MODULE_PATTERN))
|
||||
return true;
|
||||
if (funbox->inLoop)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Add |node| to |parser|'s free node list. */
|
||||
void
|
||||
ParseNodeAllocator::freeNode(ParseNode *pn)
|
||||
|
|
|
@ -1271,18 +1271,6 @@ struct FunctionBox : public ObjectBox
|
|||
* ancestor?
|
||||
*/
|
||||
bool scopeIsExtensible() const;
|
||||
|
||||
/*
|
||||
* Unbrand an object being initialized or constructed if any method cannot
|
||||
* be joined to one compiler-created null closure shared among N different
|
||||
* closure environments.
|
||||
*
|
||||
* We despecialize from caching function objects, caching slots or shapes
|
||||
* instead, because an unbranded object may still have joined methods (for
|
||||
* which shape->isMethod), since PropertyCache::fill gives precedence to
|
||||
* joined methods over branded methods.
|
||||
*/
|
||||
bool shouldUnbrand(uintN methods, uintN slowMethods) const;
|
||||
};
|
||||
|
||||
struct FunctionBoxQueue {
|
||||
|
|
|
@ -533,7 +533,7 @@ js::CheckStrictParameters(JSContext *cx, TreeContext *tc)
|
|||
|
||||
/* Start with lastVariable(), not lastArgument(), for destructuring. */
|
||||
for (Shape::Range r = tc->bindings.lastVariable(); !r.empty(); r.popFront()) {
|
||||
jsid id = r.front().propid;
|
||||
jsid id = r.front().propid();
|
||||
if (!JSID_IS_ATOM(id))
|
||||
continue;
|
||||
|
||||
|
@ -932,8 +932,11 @@ Parser::newFunction(TreeContext *tc, JSAtom *atom, FunctionSyntaxKind kind)
|
|||
JSFUN_INTERPRETED | (kind == Expression ? JSFUN_LAMBDA : 0),
|
||||
parent, atom);
|
||||
if (fun && !tc->compileAndGo()) {
|
||||
fun->clearParent();
|
||||
fun->clearType();
|
||||
if (!fun->clearParent(context))
|
||||
return NULL;
|
||||
if (!fun->clearType(context))
|
||||
return NULL;
|
||||
fun->setEnvironment(NULL);
|
||||
}
|
||||
return fun;
|
||||
}
|
||||
|
@ -1169,7 +1172,7 @@ LeaveFunction(ParseNode *fn, TreeContext *funtc, PropertyName *funName = NULL,
|
|||
* we create it eagerly whenever parameters are (or might, in the case of
|
||||
* calls to eval) be assigned.
|
||||
*/
|
||||
if (funtc->inStrictMode() && funbox->object->getFunctionPrivate()->nargs > 0) {
|
||||
if (funtc->inStrictMode() && funbox->object->toFunction()->nargs > 0) {
|
||||
AtomDeclsIter iter(&funtc->decls);
|
||||
Definition *dn;
|
||||
|
||||
|
@ -1951,7 +1954,7 @@ BindLet(JSContext *cx, BindData *data, JSAtom *atom, TreeContext *tc)
|
|||
* BytecodeEmitter.cpp:EmitEnterBlock so they don't tie up unused space
|
||||
* in the so-called "static" prototype Block.
|
||||
*/
|
||||
blockObj->setSlot(shape->slot, PrivateValue(pn));
|
||||
blockObj->setSlot(shape->slot(), PrivateValue(pn));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1965,7 +1968,7 @@ PopStatement(TreeContext *tc)
|
|||
JS_ASSERT(!obj->isClonedBlock());
|
||||
|
||||
for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) {
|
||||
JSAtom *atom = JSID_TO_ATOM(r.front().propid);
|
||||
JSAtom *atom = JSID_TO_ATOM(r.front().propid());
|
||||
|
||||
/* Beware the empty destructuring dummy. */
|
||||
if (atom == tc->parser->context->runtime->atomState.emptyAtom)
|
||||
|
@ -1973,14 +1976,7 @@ PopStatement(TreeContext *tc)
|
|||
tc->decls.remove(atom);
|
||||
}
|
||||
|
||||
/*
|
||||
* js_CloneBlockObject requires obj's shape to be frozen. Compare
|
||||
* Bindings::makeImmutable.
|
||||
*
|
||||
* (This is a second pass over the shapes, if obj has a dictionary, but
|
||||
* that is rare.)
|
||||
*/
|
||||
obj->lastProp->freezeIfDictionary();
|
||||
JS_ASSERT(!obj->inDictionaryMode());
|
||||
}
|
||||
PopStatementTC(tc);
|
||||
}
|
||||
|
@ -2051,7 +2047,7 @@ DefineGlobal(ParseNode *pn, BytecodeEmitter *bce, PropertyName *name)
|
|||
return true;
|
||||
}
|
||||
|
||||
def = GlobalScope::GlobalDef(shape->slot);
|
||||
def = GlobalScope::GlobalDef(shape->slot());
|
||||
} else {
|
||||
def = GlobalScope::GlobalDef(name, funbox);
|
||||
}
|
||||
|
@ -3687,7 +3683,7 @@ Parser::letStatement()
|
|||
stmt->downScope = tc->topScopeStmt;
|
||||
tc->topScopeStmt = stmt;
|
||||
|
||||
obj->setParent(tc->blockChain());
|
||||
obj->setStaticBlockScopeChain(tc->blockChain());
|
||||
blockbox->parent = tc->blockChainBox;
|
||||
tc->blockChainBox = blockbox;
|
||||
stmt->blockBox = blockbox;
|
||||
|
@ -7156,8 +7152,10 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot)
|
|||
return NULL;
|
||||
|
||||
if (!tc->compileAndGo()) {
|
||||
reobj->clearParent();
|
||||
reobj->clearType();
|
||||
if (!reobj->clearParent(context))
|
||||
return NULL;
|
||||
if (!reobj->clearType(context))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pn->pn_objbox = tc->parser->newObjectBox(reobj);
|
||||
|
|
|
@ -164,7 +164,6 @@ FindFunArgs(FunctionBox *funbox, int level, FunctionBoxQueue *queue)
|
|||
do {
|
||||
ParseNode *fn = funbox->node;
|
||||
JS_ASSERT(fn->isArity(PN_FUNC));
|
||||
JSFunction *fun = funbox->function();
|
||||
int fnlevel = level;
|
||||
|
||||
/*
|
||||
|
@ -245,12 +244,10 @@ FindFunArgs(FunctionBox *funbox, int level, FunctionBoxQueue *queue)
|
|||
|
||||
/*
|
||||
* Finally, after we've traversed all of the current function's kids,
|
||||
* minimize fun's skipmin against our accumulated skipmin. Do likewise
|
||||
* with allskipmin, but minimize across funbox and all of its siblings,
|
||||
* to compute our return value.
|
||||
* minimize allskipmin against our accumulated skipmin. Minimize across
|
||||
* funbox and all of its siblings, to compute our return value.
|
||||
*/
|
||||
if (skipmin != UpvarCookie::FREE_LEVEL) {
|
||||
fun->u.i.skipmin = skipmin;
|
||||
if (skipmin < allskipmin)
|
||||
allskipmin = skipmin;
|
||||
}
|
||||
|
@ -512,46 +509,6 @@ FlagHeavyweights(Definition *dn, FunctionBox *funbox, uint32 *tcflags)
|
|||
*tcflags |= TCF_FUN_HEAVYWEIGHT;
|
||||
}
|
||||
|
||||
static void
|
||||
ConsiderUnbranding(FunctionBox *funbox)
|
||||
{
|
||||
/*
|
||||
* We've already recursively set our kids' kinds, which also classifies
|
||||
* enclosing functions holding upvars referenced in those descendants'
|
||||
* bodies. So now we can check our "methods".
|
||||
*
|
||||
* Despecialize from branded method-identity-based shape to shape- or
|
||||
* slot-based shape if this function smells like a constructor and too many
|
||||
* of its methods are *not* joinable null closures (i.e., they have one or
|
||||
* more upvars fetched via the display).
|
||||
*/
|
||||
bool returnsExpr = !!(funbox->tcflags & TCF_RETURN_EXPR);
|
||||
#if JS_HAS_EXPR_CLOSURES
|
||||
{
|
||||
ParseNode *pn2 = funbox->node->pn_body;
|
||||
if (pn2->isKind(PNK_UPVARS))
|
||||
pn2 = pn2->pn_tree;
|
||||
if (pn2->isKind(PNK_ARGSBODY))
|
||||
pn2 = pn2->last();
|
||||
if (!pn2->isKind(PNK_STATEMENTLIST))
|
||||
returnsExpr = true;
|
||||
}
|
||||
#endif
|
||||
if (!returnsExpr) {
|
||||
uintN methodSets = 0, slowMethodSets = 0;
|
||||
|
||||
for (ParseNode *method = funbox->methods; method; method = method->pn_link) {
|
||||
JS_ASSERT(method->isOp(JSOP_LAMBDA) || method->isOp(JSOP_LAMBDA_FC));
|
||||
++methodSets;
|
||||
if (!method->pn_funbox->joinable())
|
||||
++slowMethodSets;
|
||||
}
|
||||
|
||||
if (funbox->shouldUnbrand(methodSets, slowMethodSets))
|
||||
funbox->tcflags |= TCF_FUN_UNBRAND_THIS;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
SetFunctionKinds(FunctionBox *funbox, uint32 *tcflags, bool isDirectEval)
|
||||
{
|
||||
|
@ -559,10 +516,8 @@ SetFunctionKinds(FunctionBox *funbox, uint32 *tcflags, bool isDirectEval)
|
|||
ParseNode *fn = funbox->node;
|
||||
ParseNode *pn = fn->pn_body;
|
||||
|
||||
if (funbox->kids) {
|
||||
if (funbox->kids)
|
||||
SetFunctionKinds(funbox->kids, tcflags, isDirectEval);
|
||||
ConsiderUnbranding(funbox);
|
||||
}
|
||||
|
||||
JSFunction *fun = funbox->function();
|
||||
|
||||
|
@ -674,8 +629,8 @@ SetFunctionKinds(FunctionBox *funbox, uint32 *tcflags, bool isDirectEval)
|
|||
* must have their OWN_SHAPE flags set; the comments for
|
||||
* js::Bindings::extensibleParents explain why.
|
||||
*/
|
||||
static void
|
||||
MarkExtensibleScopeDescendants(FunctionBox *funbox, bool hasExtensibleParent)
|
||||
static bool
|
||||
MarkExtensibleScopeDescendants(JSContext *context, FunctionBox *funbox, bool hasExtensibleParent)
|
||||
{
|
||||
for (; funbox; funbox = funbox->siblings) {
|
||||
/*
|
||||
|
@ -685,14 +640,20 @@ MarkExtensibleScopeDescendants(FunctionBox *funbox, bool hasExtensibleParent)
|
|||
*/
|
||||
|
||||
JS_ASSERT(!funbox->bindings.extensibleParents());
|
||||
if (hasExtensibleParent)
|
||||
funbox->bindings.setExtensibleParents();
|
||||
if (hasExtensibleParent) {
|
||||
if (!funbox->bindings.setExtensibleParents(context))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (funbox->kids) {
|
||||
MarkExtensibleScopeDescendants(funbox->kids,
|
||||
hasExtensibleParent || funbox->scopeIsExtensible());
|
||||
if (!MarkExtensibleScopeDescendants(context, funbox->kids,
|
||||
hasExtensibleParent || funbox->scopeIsExtensible())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -703,7 +664,8 @@ frontend::AnalyzeFunctions(TreeContext *tc)
|
|||
return true;
|
||||
if (!MarkFunArgs(tc->parser->context, tc->functionList, tc->parser->functionCount))
|
||||
return false;
|
||||
MarkExtensibleScopeDescendants(tc->functionList, false);
|
||||
if (!MarkExtensibleScopeDescendants(tc->parser->context, tc->functionList, false))
|
||||
return false;
|
||||
bool isDirectEval = !!tc->parser->callerFrame;
|
||||
SetFunctionKinds(tc->functionList, &tc->flags, isDirectEval);
|
||||
return true;
|
||||
|
|
|
@ -271,7 +271,8 @@ typedef HeapPtr<JSFunction> HeapPtrFunction;
|
|||
typedef HeapPtr<JSString> HeapPtrString;
|
||||
typedef HeapPtr<JSScript> HeapPtrScript;
|
||||
typedef HeapPtr<Shape> HeapPtrShape;
|
||||
typedef HeapPtr<const Shape> HeapPtrConstShape;
|
||||
typedef HeapPtr<BaseShape> HeapPtrBaseShape;
|
||||
typedef HeapPtr<types::TypeObject> HeapPtrTypeObject;
|
||||
typedef HeapPtr<JSXML> HeapPtrXML;
|
||||
|
||||
/* Useful for hashtables with a HeapPtr as key. */
|
||||
|
|
|
@ -192,7 +192,6 @@ Statistics::beginGC(JSCompartment *comp, Reason reason)
|
|||
Probes::GCStart(compartment);
|
||||
|
||||
GCCrashData crashData;
|
||||
crashData.isRegen = runtime->shapeGen & SHAPE_OVERFLOW_BIT;
|
||||
crashData.isCompartment = !!compartment;
|
||||
crash::SaveCrashData(crash::JS_CRASH_TAG_GC, &crashData, sizeof(crashData));
|
||||
}
|
||||
|
@ -277,8 +276,7 @@ Statistics::endGC()
|
|||
if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback) {
|
||||
(*cb)(JS_TELEMETRY_GC_REASON, triggerReason);
|
||||
(*cb)(JS_TELEMETRY_GC_IS_COMPARTMENTAL, compartment ? 1 : 0);
|
||||
(*cb)(JS_TELEMETRY_GC_IS_SHAPE_REGEN,
|
||||
runtime->shapeGen & SHAPE_OVERFLOW_BIT ? 1 : 0);
|
||||
(*cb)(JS_TELEMETRY_GC_IS_SHAPE_REGEN, 0);
|
||||
(*cb)(JS_TELEMETRY_GC_MS, t(PHASE_GC));
|
||||
(*cb)(JS_TELEMETRY_GC_MARK_MS, t(PHASE_MARK));
|
||||
(*cb)(JS_TELEMETRY_GC_SWEEP_MS, t(PHASE_SWEEP));
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
var o4 = Object.freeze({
|
||||
set: function(summary) {}
|
||||
});
|
|
@ -0,0 +1,7 @@
|
|||
a = "".__proto__
|
||||
b = uneval().__proto__
|
||||
for (var i = 0; i < 2; i++) {
|
||||
a.__defineSetter__("valueOf", function() {})
|
||||
a + ""
|
||||
delete b.valueOf
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
for (let j = 0; j < (20); ++(__lookupSetter__)) {
|
||||
function g() { j; }
|
||||
j++;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
Function.prototype.__proto__["p"] = 3
|
||||
c = [].__proto__
|
||||
c[5] = 3
|
||||
Namespace.prototype.__proto__[4] = function() {}
|
||||
gc()
|
||||
Function("\
|
||||
{\
|
||||
function f(d) {}\
|
||||
for each(let z in[0]) {\
|
||||
f(z)\
|
||||
}\
|
||||
}\
|
||||
")()
|
|
@ -0,0 +1 @@
|
|||
for (let x in [<y/>.(let(x) function() {})]) {}
|
|
@ -0,0 +1,3 @@
|
|||
Object.defineProperty(Namespace.prototype, "toString", {
|
||||
enumerable: true
|
||||
})
|
|
@ -0,0 +1,12 @@
|
|||
function f(s) {
|
||||
eval(s);
|
||||
return function() {
|
||||
with({}) {};
|
||||
return b;
|
||||
};
|
||||
}
|
||||
var b = 1;
|
||||
var g1 = f("");
|
||||
var g2 = f("var b = 2;");
|
||||
g1('');
|
||||
assertEq(g2(''), 2);
|
|
@ -0,0 +1,11 @@
|
|||
c = (0).__proto__
|
||||
function f(o) {
|
||||
o.__proto__ = null
|
||||
for (x in o) {}
|
||||
}
|
||||
for (i = 0; i < 9; i++) {
|
||||
f(c)
|
||||
Function.prototype.__proto__.__proto__ = c
|
||||
for (x in Function.prototype.__proto__) {}
|
||||
f(Math.__proto__)
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
// |jit-test| error: TypeError
|
||||
function f(o) {
|
||||
for (j = 0; j < 9; j++) {
|
||||
if (j) {
|
||||
o.__proto__ = null
|
||||
}
|
||||
for (v in o) {}
|
||||
}
|
||||
}
|
||||
for (i = 0; i < 9; i++) {
|
||||
(new Boolean).__proto__.__defineGetter__("toString", function() {})
|
||||
f(Boolean.prototype)
|
||||
}
|
|
@ -3,7 +3,7 @@ setDebug(true);
|
|||
x = "notset";
|
||||
function main() {
|
||||
/* The JSOP_STOP in main. */
|
||||
a = { valueOf: function () { trap(main, 58, "success()"); } };
|
||||
a = { valueOf: function () { trap(main, 57, "success()"); } };
|
||||
b = "";
|
||||
eval();
|
||||
a + b;
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
function TestCase(n, d, e, a)
|
||||
this.name=n;
|
||||
function reportCompare (expected, actual, description) {
|
||||
new TestCase
|
||||
}
|
||||
reportCompare(true, "isGenerator" in Function, "Function.prototype.isGenerator present");
|
||||
var p = Proxy.create({
|
||||
has : function(id) {}
|
||||
});
|
||||
function test() {
|
||||
Object.prototype.__proto__=null
|
||||
if (new TestCase)
|
||||
Object.prototype.__proto__=p
|
||||
}
|
||||
test();
|
||||
new TestCase;
|
||||
test()
|
|
@ -0,0 +1,7 @@
|
|||
a = []
|
||||
function f(o) {
|
||||
o[5] = {}
|
||||
}
|
||||
for (var i = 0; i < 20; i++) {
|
||||
with(a) f(a)
|
||||
}
|
|
@ -174,7 +174,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
|
|||
LifoAlloc &tla = cx->typeLifoAlloc();
|
||||
|
||||
unsigned length = script->length;
|
||||
unsigned nargs = script->hasFunction ? script->function()->nargs : 0;
|
||||
unsigned nargs = script->function() ? script->function()->nargs : 0;
|
||||
|
||||
numSlots = TotalSlots(script);
|
||||
|
||||
|
@ -226,15 +226,16 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
|
|||
if (cx->compartment->debugMode())
|
||||
usesReturnValue_ = true;
|
||||
|
||||
bool heavyweight = script->function() && script->function()->isHeavyweight();
|
||||
|
||||
isInlineable = true;
|
||||
if (script->nClosedArgs || script->nClosedVars ||
|
||||
(script->hasFunction && script->function()->isHeavyweight()) ||
|
||||
if (script->nClosedArgs || script->nClosedVars || heavyweight ||
|
||||
script->usesEval || script->usesArguments || cx->compartment->debugMode()) {
|
||||
isInlineable = false;
|
||||
}
|
||||
|
||||
modifiesArguments_ = false;
|
||||
if (script->nClosedArgs || (script->hasFunction && script->function()->isHeavyweight()))
|
||||
if (script->nClosedArgs || heavyweight)
|
||||
modifiesArguments_ = true;
|
||||
|
||||
canTrackVars = true;
|
||||
|
|
|
@ -394,7 +394,7 @@ static inline uint32 ArgSlot(uint32 arg) {
|
|||
return 2 + arg;
|
||||
}
|
||||
static inline uint32 LocalSlot(JSScript *script, uint32 local) {
|
||||
return 2 + (script->hasFunction ? script->function()->nargs : 0) + local;
|
||||
return 2 + (script->function() ? script->function()->nargs : 0) + local;
|
||||
}
|
||||
static inline uint32 TotalSlots(JSScript *script) {
|
||||
return LocalSlot(script, 0) + script->nfixed;
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
#include "vm/Stack-inl.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
static const char NORMAL_ZERO[] =
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
#include "jsobj.h"
|
||||
#include "jswrapper.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
struct OuterWrapper : js::Wrapper
|
||||
{
|
||||
OuterWrapper() : Wrapper(0) {}
|
||||
|
|
|
@ -50,13 +50,8 @@ BEGIN_TEST(testConservativeGC)
|
|||
bool checkObjectFields(JSObject *savedCopy, JSObject *obj)
|
||||
{
|
||||
/* Ignore fields which are unstable across GCs. */
|
||||
CHECK(savedCopy->lastProp == obj->lastProp);
|
||||
CHECK(savedCopy->getClass() == obj->getClass());
|
||||
CHECK(savedCopy->flags == obj->flags);
|
||||
CHECK(savedCopy->newType == obj->newType);
|
||||
CHECK(savedCopy->lastProperty() == obj->lastProperty());
|
||||
CHECK(savedCopy->getProto() == obj->getProto());
|
||||
CHECK(savedCopy->parent == obj->parent);
|
||||
CHECK(savedCopy->privateData == obj->privateData);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
#include "jsnum.h"
|
||||
#include "jsstr.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
#include "vm/String-inl.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#include "tests.h"
|
||||
#include "jsfun.h" // for js::IsInternalFunctionObject
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
BEGIN_TEST(testLookup_bug522590)
|
||||
{
|
||||
// Define a function that makes method-bearing objects.
|
||||
|
@ -26,7 +28,7 @@ BEGIN_TEST(testLookup_bug522590)
|
|||
JSObject *funobj = JSVAL_TO_OBJECT(r);
|
||||
CHECK(funobj->isFunction());
|
||||
CHECK(!js::IsInternalFunctionObject(funobj));
|
||||
CHECK(funobj->getFunctionPrivate() != (JSFunction *) funobj);
|
||||
CHECK(funobj->toFunction()->isClonedMethod());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "jscntxt.h"
|
||||
|
||||
#include "jscntxtinlines.h"
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
|
|
|
@ -336,11 +336,11 @@ JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv, const char *format
|
|||
*va_arg(ap, JSObject **) = obj;
|
||||
break;
|
||||
case 'f':
|
||||
obj = js_ValueToFunctionObject(cx, sp, 0);
|
||||
obj = js_ValueToFunction(cx, sp, 0);
|
||||
if (!obj)
|
||||
return JS_FALSE;
|
||||
*sp = OBJECT_TO_JSVAL(obj);
|
||||
*va_arg(ap, JSFunction **) = obj->getFunctionPrivate();
|
||||
*va_arg(ap, JSFunction **) = obj->toFunction();
|
||||
break;
|
||||
case 'v':
|
||||
*va_arg(ap, jsval *) = *sp;
|
||||
|
@ -429,7 +429,7 @@ JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp)
|
|||
break;
|
||||
case JSTYPE_FUNCTION:
|
||||
*vp = v;
|
||||
obj = js_ValueToFunctionObject(cx, vp, JSV2F_SEARCH_STACK);
|
||||
obj = js_ValueToFunction(cx, vp, JSV2F_SEARCH_STACK);
|
||||
ok = (obj != NULL);
|
||||
break;
|
||||
case JSTYPE_STRING:
|
||||
|
@ -645,7 +645,6 @@ JSRuntime::JSRuntime()
|
|||
compartmentCallback(NULL),
|
||||
activityCallback(NULL),
|
||||
activityCallbackArg(NULL),
|
||||
protoHazardShape(0),
|
||||
gcSystemAvailableChunkListHead(NULL),
|
||||
gcUserAvailableChunkListHead(NULL),
|
||||
gcKeepAtoms(0),
|
||||
|
@ -672,7 +671,6 @@ JSRuntime::JSRuntime()
|
|||
gcPoke(false),
|
||||
gcMarkAndSweep(false),
|
||||
gcRunning(false),
|
||||
gcRegenShapes(false),
|
||||
#ifdef JS_GC_ZEAL
|
||||
gcZeal_(0),
|
||||
gcZealFrequency(0),
|
||||
|
@ -726,7 +724,6 @@ JSRuntime::JSRuntime()
|
|||
threadData(thisFromCtor()),
|
||||
#endif
|
||||
trustedPrincipals_(NULL),
|
||||
shapeGen(0),
|
||||
wrapObjectCallback(NULL),
|
||||
preWrapObjectCallback(NULL),
|
||||
inOOMReport(0)
|
||||
|
@ -2383,6 +2380,10 @@ JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing,
|
|||
name = "shape";
|
||||
break;
|
||||
|
||||
case JSTRACE_BASE_SHAPE:
|
||||
name = "base_shape";
|
||||
break;
|
||||
|
||||
case JSTRACE_TYPE_OBJECT:
|
||||
name = "type_object";
|
||||
break;
|
||||
|
@ -2411,7 +2412,7 @@ JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing,
|
|||
JSObject *obj = (JSObject *)thing;
|
||||
Class *clasp = obj->getClass();
|
||||
if (clasp == &FunctionClass) {
|
||||
JSFunction *fun = obj->getFunctionPrivate();
|
||||
JSFunction *fun = obj->toFunction();
|
||||
if (!fun) {
|
||||
JS_snprintf(buf, bufsize, "<newborn>");
|
||||
} else if (fun != obj) {
|
||||
|
@ -2446,6 +2447,7 @@ JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing,
|
|||
}
|
||||
|
||||
case JSTRACE_SHAPE:
|
||||
case JSTRACE_BASE_SHAPE:
|
||||
case JSTRACE_TYPE_OBJECT:
|
||||
break;
|
||||
|
||||
|
@ -3080,14 +3082,9 @@ JS_GetInstancePrivate(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv)
|
|||
JS_PUBLIC_API(JSObject *)
|
||||
JS_GetPrototype(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JSObject *proto;
|
||||
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, obj);
|
||||
proto = obj->getProto();
|
||||
|
||||
/* Beware ref to dead object (we may be called from obj's finalizer). */
|
||||
return proto && !proto->isNewborn() ? proto : NULL;
|
||||
return obj->getProto();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
|
@ -3101,21 +3098,19 @@ JS_SetPrototype(JSContext *cx, JSObject *obj, JSObject *proto)
|
|||
JS_PUBLIC_API(JSObject *)
|
||||
JS_GetParent(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(!obj->isInternalScope());
|
||||
assertSameCompartment(cx, obj);
|
||||
JSObject *parent = obj->getParent();
|
||||
|
||||
/* Beware ref to dead object (we may be called from obj's finalizer). */
|
||||
return parent && !parent->isNewborn() ? parent : NULL;
|
||||
return obj->getParent();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_SetParent(JSContext *cx, JSObject *obj, JSObject *parent)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
JS_ASSERT(!obj->isInternalScope());
|
||||
JS_ASSERT(parent || !obj->getParent());
|
||||
assertSameCompartment(cx, obj, parent);
|
||||
obj->setParent(parent);
|
||||
return true;
|
||||
return obj->setParent(cx, parent);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject *)
|
||||
|
@ -3131,8 +3126,7 @@ JS_GetConstructor(JSContext *cx, JSObject *proto)
|
|||
if (!proto->getProperty(cx, cx->runtime->atomState.constructorAtom, &cval))
|
||||
return NULL;
|
||||
}
|
||||
JSObject *funobj;
|
||||
if (!IsFunctionObject(cval, &funobj)) {
|
||||
if (!IsFunctionObject(cval)) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_CONSTRUCTOR,
|
||||
proto->getClass()->name);
|
||||
return NULL;
|
||||
|
@ -3206,14 +3200,13 @@ JS_NewObject(JSContext *cx, JSClass *jsclasp, JSObject *proto, JSObject *parent)
|
|||
JS_ASSERT(clasp != &FunctionClass);
|
||||
JS_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL));
|
||||
|
||||
if (proto)
|
||||
proto->getNewType(cx, NULL, /* markUnknown = */ true);
|
||||
if (proto && !proto->setNewTypeUnknown(cx))
|
||||
return NULL;
|
||||
|
||||
JSObject *obj = NewNonFunction<WithProto::Class>(cx, clasp, proto, parent);
|
||||
JSObject *obj = NewObjectWithClassProto(cx, clasp, proto, parent);
|
||||
if (obj) {
|
||||
if (clasp->ext.equality)
|
||||
MarkTypeObjectFlags(cx, obj, OBJECT_FLAG_SPECIAL_EQUALITY);
|
||||
obj->syncSpecialEquality();
|
||||
MarkTypeObjectUnknownProperties(cx, obj->type());
|
||||
}
|
||||
|
||||
|
@ -3235,11 +3228,9 @@ JS_NewObjectWithGivenProto(JSContext *cx, JSClass *jsclasp, JSObject *proto, JSO
|
|||
JS_ASSERT(clasp != &FunctionClass);
|
||||
JS_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL));
|
||||
|
||||
JSObject *obj = NewNonFunction<WithProto::Given>(cx, clasp, proto, parent);
|
||||
if (obj) {
|
||||
obj->syncSpecialEquality();
|
||||
JSObject *obj = NewObjectWithGivenProto(cx, clasp, proto, parent);
|
||||
if (obj)
|
||||
MarkTypeObjectUnknownProperties(cx, obj->type());
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
@ -3349,13 +3340,13 @@ LookupResult(JSContext *cx, JSObject *obj, JSObject *obj2, jsid id,
|
|||
Shape *shape = (Shape *) prop;
|
||||
|
||||
if (shape->isMethod()) {
|
||||
vp->setObject(shape->methodObject());
|
||||
vp->setObject(*obj2->nativeGetMethod(shape));
|
||||
return !!obj2->methodReadBarrier(cx, *shape, vp);
|
||||
}
|
||||
|
||||
/* Peek at the native property's slot value, without doing a Get. */
|
||||
if (obj2->containsSlot(shape->slot)) {
|
||||
*vp = obj2->nativeGetSlot(shape->slot);
|
||||
if (shape->hasSlot()) {
|
||||
*vp = obj2->nativeGetSlot(shape->slot());
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
|
@ -3654,12 +3645,10 @@ JS_DefineObject(JSContext *cx, JSObject *obj, const char *name, JSClass *jsclasp
|
|||
if (!clasp)
|
||||
clasp = &ObjectClass; /* default class is Object */
|
||||
|
||||
JSObject *nobj = NewObject<WithProto::Class>(cx, clasp, proto, obj);
|
||||
JSObject *nobj = NewObjectWithClassProto(cx, clasp, proto, obj);
|
||||
if (!nobj)
|
||||
return NULL;
|
||||
|
||||
nobj->syncSpecialEquality();
|
||||
|
||||
if (!DefineProperty(cx, obj, name, ObjectValue(*nobj), NULL, NULL, attrs, 0, 0))
|
||||
return NULL;
|
||||
|
||||
|
@ -3726,12 +3715,12 @@ GetPropertyDescriptorById(JSContext *cx, JSObject *obj, jsid id, uintN flags,
|
|||
if (shape->isMethod()) {
|
||||
desc->getter = JS_PropertyStub;
|
||||
desc->setter = JS_StrictPropertyStub;
|
||||
desc->value.setObject(shape->methodObject());
|
||||
desc->value.setObject(*obj2->nativeGetMethod(shape));
|
||||
} else {
|
||||
desc->getter = shape->getter();
|
||||
desc->setter = shape->setter();
|
||||
if (obj2->containsSlot(shape->slot))
|
||||
desc->value = obj2->nativeGetSlot(shape->slot);
|
||||
if (shape->hasSlot())
|
||||
desc->value = obj2->nativeGetSlot(shape->slot());
|
||||
else
|
||||
desc->value.setUndefined();
|
||||
}
|
||||
|
@ -4143,7 +4132,7 @@ JS_NewPropertyIterator(JSContext *cx, JSObject *obj)
|
|||
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, obj);
|
||||
iterobj = NewNonFunction<WithProto::Class>(cx, &prop_iter_class, NULL, obj);
|
||||
iterobj = NewObjectWithClassProto(cx, &prop_iter_class, NULL, obj);
|
||||
if (!iterobj)
|
||||
return NULL;
|
||||
|
||||
|
@ -4191,11 +4180,11 @@ JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp)
|
|||
shape = shape->previous();
|
||||
|
||||
if (!shape->previous()) {
|
||||
JS_ASSERT(JSID_IS_EMPTY(shape->propid));
|
||||
JS_ASSERT(shape->isEmptyShape());
|
||||
*idp = JSID_VOID;
|
||||
} else {
|
||||
iterobj->setPrivate(const_cast<Shape *>(shape->previous()));
|
||||
*idp = shape->propid;
|
||||
*idp = shape->propid();
|
||||
}
|
||||
} else {
|
||||
/* Non-native case: use the ida enumerated when iterobj was created. */
|
||||
|
@ -4382,9 +4371,9 @@ JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
JSFunction *fun = funobj->getFunctionPrivate();
|
||||
JSFunction *fun = funobj->toFunction();
|
||||
if (!fun->isInterpreted())
|
||||
return CloneFunctionObject(cx, fun, parent);
|
||||
return CloneFunctionObject(cx, fun, parent, fun->getAllocKind());
|
||||
|
||||
if (fun->script()->compileAndGo) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
|
@ -4393,7 +4382,7 @@ JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent)
|
|||
}
|
||||
|
||||
if (!fun->isFlatClosure())
|
||||
return CloneFunctionObject(cx, fun, parent);
|
||||
return CloneFunctionObject(cx, fun, parent, fun->getAllocKind());
|
||||
|
||||
/*
|
||||
* A flat closure carries its own environment, so why clone it? In case
|
||||
|
@ -4424,13 +4413,13 @@ JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent)
|
|||
JSMSG_BAD_CLONE_FUNOBJ_SCOPE);
|
||||
return NULL;
|
||||
}
|
||||
obj = obj->getParent();
|
||||
obj = obj->scopeChain();
|
||||
}
|
||||
|
||||
Value v;
|
||||
if (!obj->getGeneric(cx, r.front().propid, &v))
|
||||
if (!obj->getGeneric(cx, r.front().propid(), &v))
|
||||
return NULL;
|
||||
clone->setFlatClosureUpvar(i, v);
|
||||
clone->toFunction()->setFlatClosureUpvar(i, v);
|
||||
}
|
||||
|
||||
return clone;
|
||||
|
@ -4477,14 +4466,15 @@ JS_IsNativeFunction(JSObject *funobj, JSNative call)
|
|||
{
|
||||
if (!funobj->isFunction())
|
||||
return false;
|
||||
JSFunction *fun = funobj->getFunctionPrivate();
|
||||
JSFunction *fun = funobj->toFunction();
|
||||
return fun->isNative() && fun->native() == call;
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_generic_native_method_dispatcher(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
JSFunctionSpec *fs = (JSFunctionSpec *) vp->toObject().getReservedSlot(0).toPrivate();
|
||||
JSFunctionSpec *fs = (JSFunctionSpec *)
|
||||
vp->toObject().toFunction()->getExtendedSlot(0).toPrivate();
|
||||
JS_ASSERT((fs->flags & JSFUN_GENERIC_NATIVE) != 0);
|
||||
|
||||
if (argc < 1) {
|
||||
|
@ -4539,7 +4529,8 @@ JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs)
|
|||
fun = js_DefineFunction(cx, ctor, ATOM_TO_JSID(atom),
|
||||
js_generic_native_method_dispatcher,
|
||||
fs->nargs + 1,
|
||||
flags);
|
||||
flags,
|
||||
JSFunction::ExtendedFinalizeKind);
|
||||
if (!fun)
|
||||
return JS_FALSE;
|
||||
|
||||
|
@ -4547,9 +4538,7 @@ JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs)
|
|||
* As jsapi.h notes, fs must point to storage that lives as long
|
||||
* as fun->object lives.
|
||||
*/
|
||||
Value priv = PrivateValue(fs);
|
||||
if (!js_SetReservedSlot(cx, fun, 0, priv))
|
||||
return JS_FALSE;
|
||||
fun->setExtendedSlot(0, PrivateValue(fs));
|
||||
}
|
||||
|
||||
fun = js_DefineFunction(cx, obj, ATOM_TO_JSID(atom), fs->call, fs->nargs, flags);
|
||||
|
|
|
@ -3154,8 +3154,7 @@ struct JSClass {
|
|||
#define JSCLASS_NEW_ENUMERATE (1<<1) /* has JSNewEnumerateOp hook */
|
||||
#define JSCLASS_NEW_RESOLVE (1<<2) /* has JSNewResolveOp hook */
|
||||
#define JSCLASS_PRIVATE_IS_NSISUPPORTS (1<<3) /* private is (nsISupports *) */
|
||||
#define JSCLASS_CONCURRENT_FINALIZER (1<<4) /* finalize is called on background thread */
|
||||
#define JSCLASS_NEW_RESOLVE_GETS_START (1<<5) /* JSNewResolveOp gets starting
|
||||
#define JSCLASS_NEW_RESOLVE_GETS_START (1<<4) /* JSNewResolveOp gets starting
|
||||
object in prototype chain
|
||||
passed in via *objp in/out
|
||||
parameter */
|
||||
|
|
|
@ -281,10 +281,10 @@ JSObject::willBeSparseDenseArray(uintN requiredCapacity, uintN newElementsHint)
|
|||
JS_ASSERT(isDenseArray());
|
||||
JS_ASSERT(requiredCapacity > MIN_SPARSE_INDEX);
|
||||
|
||||
uintN cap = numSlots();
|
||||
uintN cap = getDenseArrayCapacity();
|
||||
JS_ASSERT(requiredCapacity >= cap);
|
||||
|
||||
if (requiredCapacity >= JSObject::NSLOTS_LIMIT)
|
||||
if (requiredCapacity >= JSObject::NELEMENTS_LIMIT)
|
||||
return true;
|
||||
|
||||
uintN minimalDenseCount = requiredCapacity / 4;
|
||||
|
@ -352,7 +352,7 @@ JSObject::arrayGetOwnDataElement(JSContext *cx, size_t i, Value *vp)
|
|||
if (!shape || !shape->isDataDescriptor())
|
||||
vp->setMagic(JS_ARRAY_HOLE);
|
||||
else
|
||||
*vp = getSlot(shape->slot);
|
||||
*vp = getSlot(shape->slot());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -634,9 +634,7 @@ array_length_setter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value
|
|||
if (oldinit > newlen)
|
||||
obj->setDenseArrayInitializedLength(newlen);
|
||||
if (oldcap > newlen)
|
||||
obj->shrinkDenseArrayElements(cx, newlen);
|
||||
if (oldinit > newlen && !cx->typeInferenceEnabled())
|
||||
obj->backfillDenseArrayHoles(cx);
|
||||
obj->shrinkElements(cx, newlen);
|
||||
} else if (oldlen - newlen < (1 << 24)) {
|
||||
do {
|
||||
--oldlen;
|
||||
|
@ -1219,7 +1217,7 @@ array_fix(JSContext *cx, JSObject *obj, bool *success, AutoIdVector *props)
|
|||
|
||||
Class js::ArrayClass = {
|
||||
"Array",
|
||||
Class::NON_NATIVE | JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array),
|
||||
Class::NON_NATIVE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array),
|
||||
JS_PropertyStub, /* addProperty */
|
||||
JS_PropertyStub, /* delProperty */
|
||||
JS_PropertyStub, /* getProperty */
|
||||
|
@ -1277,7 +1275,6 @@ Class js::ArrayClass = {
|
|||
|
||||
Class js::SlowArrayClass = {
|
||||
"Array",
|
||||
JSCLASS_HAS_PRIVATE |
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Array),
|
||||
slowarray_addProperty,
|
||||
JS_PropertyStub, /* delProperty */
|
||||
|
@ -1288,12 +1285,36 @@ Class js::SlowArrayClass = {
|
|||
JS_ConvertStub
|
||||
};
|
||||
|
||||
bool
|
||||
JSObject::allocateSlowArrayElements(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(hasClass(&js::SlowArrayClass));
|
||||
JS_ASSERT(elements == emptyObjectElements);
|
||||
|
||||
ObjectElements *header = cx->new_<ObjectElements>(0, 0);
|
||||
if (!header)
|
||||
return false;
|
||||
|
||||
elements = header->elements();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
AddLengthProperty(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
/*
|
||||
* Add the 'length' property for a newly created or converted slow array,
|
||||
* and update the elements to be an empty array owned by the object.
|
||||
* The shared emptyObjectElements singleton cannot be used for slow arrays,
|
||||
* as accesses to 'length' will use the elements header.
|
||||
*/
|
||||
|
||||
const jsid lengthId = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
|
||||
JS_ASSERT(!obj->nativeLookup(cx, lengthId));
|
||||
|
||||
if (!obj->allocateSlowArrayElements(cx))
|
||||
return false;
|
||||
|
||||
return obj->addProperty(cx, lengthId, array_length_getter, array_length_setter,
|
||||
SHAPE_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, 0, 0);
|
||||
}
|
||||
|
@ -1309,62 +1330,50 @@ JSObject::makeDenseArraySlow(JSContext *cx)
|
|||
MarkTypeObjectFlags(cx, this,
|
||||
OBJECT_FLAG_NON_PACKED_ARRAY |
|
||||
OBJECT_FLAG_NON_DENSE_ARRAY);
|
||||
markDenseArrayNotPacked(cx);
|
||||
|
||||
uint32 arrayCapacity = getDenseArrayCapacity();
|
||||
uint32 arrayInitialized = getDenseArrayInitializedLength();
|
||||
|
||||
/*
|
||||
* Get an allocated array of the existing elements, evicting from the fixed
|
||||
* slots if necessary.
|
||||
*/
|
||||
if (!hasDynamicElements()) {
|
||||
if (!growElements(cx, arrayCapacity))
|
||||
return false;
|
||||
JS_ASSERT(hasDynamicElements());
|
||||
}
|
||||
|
||||
/*
|
||||
* Save old map now, before calling InitScopeForObject. We'll have to undo
|
||||
* on error. This is gross, but a better way is not obvious. Note: the
|
||||
* exact contents of the array are not preserved on error.
|
||||
*/
|
||||
js::Shape *oldMap = lastProp;
|
||||
js::Shape *oldShape = lastProperty();
|
||||
|
||||
/* Create a native scope. */
|
||||
gc::AllocKind kind = getAllocKind();
|
||||
js::EmptyShape *empty = InitScopeForObject(cx, this, &SlowArrayClass,
|
||||
getProto()->getNewType(cx), kind);
|
||||
if (!empty)
|
||||
Shape *shape = EmptyShape::getInitialShape(cx, &SlowArrayClass, getProto(),
|
||||
oldShape->getObjectParent(), kind);
|
||||
if (!shape)
|
||||
return false;
|
||||
setMap(empty);
|
||||
this->shape_ = shape;
|
||||
|
||||
backfillDenseArrayHoles(cx);
|
||||
/* Take ownership of the dense elements, reset to an empty dense array. */
|
||||
HeapValue *elems = elements;
|
||||
elements = emptyObjectElements;
|
||||
|
||||
uint32 arrayCapacity = getDenseArrayCapacity();
|
||||
uint32 arrayInitialized = getDenseArrayInitializedLength();
|
||||
|
||||
/*
|
||||
* Adjust the slots to account for the different layout between dense
|
||||
* arrays and other objects. The slots must be dynamic, and the fixed slots
|
||||
* are now available for newly added properties.
|
||||
*/
|
||||
if (denseArrayHasInlineSlots()) {
|
||||
if (!allocSlots(cx, numSlots())) {
|
||||
setMap(oldMap);
|
||||
return false;
|
||||
}
|
||||
JS_ASSERT(!denseArrayHasInlineSlots());
|
||||
}
|
||||
capacity = numFixedSlots() + arrayCapacity;
|
||||
clasp = &SlowArrayClass;
|
||||
|
||||
/*
|
||||
* Root all values in the array during conversion, as SlowArrayClass only
|
||||
* protects up to its slot span.
|
||||
*/
|
||||
AutoValueArray autoArray(cx, Valueify(slots), arrayInitialized);
|
||||
|
||||
/* The initialized length is used iff this is a dense array. */
|
||||
initializedLength() = 0;
|
||||
JS_ASSERT(newType == NULL);
|
||||
/* Root all values in the array during conversion. */
|
||||
AutoValueArray autoArray(cx, (Value *) elems, arrayInitialized);
|
||||
|
||||
/*
|
||||
* Begin with the length property to share more of the property tree.
|
||||
* The getter/setter here will directly access the object's private value.
|
||||
*/
|
||||
if (!AddLengthProperty(cx, this)) {
|
||||
setMap(oldMap);
|
||||
capacity = arrayCapacity;
|
||||
initializedLength() = arrayInitialized;
|
||||
clasp = &ArrayClass;
|
||||
this->shape_ = oldShape;
|
||||
cx->free_(getElementsHeader());
|
||||
elements = elems;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1373,37 +1382,30 @@ JSObject::makeDenseArraySlow(JSContext *cx)
|
|||
* remove holes, so that shapes use successive slots (as for other objects).
|
||||
*/
|
||||
uint32 next = 0;
|
||||
for (uint32 i = 0; i < arrayCapacity; i++) {
|
||||
for (uint32 i = 0; i < arrayInitialized; i++) {
|
||||
/* Dense array indexes can always fit in a jsid. */
|
||||
jsid id;
|
||||
JS_ALWAYS_TRUE(ValueToId(cx, Int32Value(i), &id));
|
||||
|
||||
if (slots[i].isMagic(JS_ARRAY_HOLE))
|
||||
if (elems[i].isMagic(JS_ARRAY_HOLE))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* No barrier is needed here because the set of reachable objects before
|
||||
* and after slowification is the same. During slowification, the
|
||||
* autoArray rooter guarantees that all slots will be marked.
|
||||
*
|
||||
* It's important that we avoid a barrier here because the fixed slots
|
||||
* of a dense array can be garbage; a write barrier after the switch to
|
||||
* a slow array could cause a crash.
|
||||
*/
|
||||
initSlotUnchecked(next, slots[i]);
|
||||
|
||||
if (!addDataProperty(cx, id, next, JSPROP_ENUMERATE)) {
|
||||
setMap(oldMap);
|
||||
capacity = arrayCapacity;
|
||||
initializedLength() = arrayInitialized;
|
||||
clasp = &ArrayClass;
|
||||
this->shape_ = oldShape;
|
||||
cx->free_(getElementsHeader());
|
||||
elements = elems;
|
||||
return false;
|
||||
}
|
||||
|
||||
initSlot(next, elems[i]);
|
||||
|
||||
next++;
|
||||
}
|
||||
|
||||
clearSlotRange(next, capacity - next);
|
||||
ObjectElements *oldheader = ObjectElements::fromElements(elems);
|
||||
|
||||
getElementsHeader()->length = oldheader->length;
|
||||
cx->free_(oldheader);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1838,13 +1840,10 @@ InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, const Value *vector
|
|||
return false;
|
||||
|
||||
/* Avoid ensureDenseArrayElements to skip sparse array checks there. */
|
||||
if (!obj->ensureSlots(cx, length))
|
||||
if (!obj->ensureElements(cx, length))
|
||||
return false;
|
||||
|
||||
if (cx->typeInferenceEnabled())
|
||||
obj->setDenseArrayInitializedLength(length);
|
||||
else
|
||||
obj->backfillDenseArrayHoles(cx);
|
||||
obj->setDenseArrayInitializedLength(length);
|
||||
|
||||
bool hole = false;
|
||||
for (jsuint i = 0; i < length; i++) {
|
||||
|
@ -2301,11 +2300,10 @@ NewbornArrayPushImpl(JSContext *cx, JSObject *obj, const Value &v)
|
|||
JS_ASSERT(obj->isDenseArray());
|
||||
JS_ASSERT(length <= obj->getDenseArrayCapacity());
|
||||
|
||||
if (length == obj->getDenseArrayCapacity() && !obj->ensureSlots(cx, length + 1))
|
||||
if (!obj->ensureElements(cx, length + 1))
|
||||
return false;
|
||||
|
||||
if (cx->typeInferenceEnabled())
|
||||
obj->setDenseArrayInitializedLength(length + 1);
|
||||
obj->setDenseArrayInitializedLength(length + 1);
|
||||
obj->setDenseArrayLength(length + 1);
|
||||
obj->initDenseArrayElementWithType(cx, length, v);
|
||||
return true;
|
||||
|
@ -2376,7 +2374,7 @@ array_pop_dense(JSContext *cx, JSObject* obj, CallArgs &args)
|
|||
|
||||
if (!hole && DeleteArrayElement(cx, obj, index, true) < 0)
|
||||
return JS_FALSE;
|
||||
if (cx->typeInferenceEnabled() && obj->getDenseArrayInitializedLength() > index)
|
||||
if (obj->getDenseArrayInitializedLength() > index)
|
||||
obj->setDenseArrayInitializedLength(index);
|
||||
|
||||
obj->setArrayLength(cx, index);
|
||||
|
@ -2438,10 +2436,7 @@ js::array_shift(JSContext *cx, uintN argc, Value *vp)
|
|||
if (args.rval().isMagic(JS_ARRAY_HOLE))
|
||||
args.rval().setUndefined();
|
||||
obj->moveDenseArrayElements(0, 1, length);
|
||||
if (cx->typeInferenceEnabled())
|
||||
obj->setDenseArrayInitializedLength(obj->getDenseArrayInitializedLength() - 1);
|
||||
else
|
||||
obj->setDenseArrayElement(length, MagicValue(JS_ARRAY_HOLE));
|
||||
obj->setDenseArrayInitializedLength(obj->getDenseArrayInitializedLength() - 1);
|
||||
obj->setArrayLength(cx, length);
|
||||
if (!js_SuppressDeletedProperty(cx, obj, INT_TO_JSID(length)))
|
||||
return JS_FALSE;
|
||||
|
@ -2543,7 +2538,7 @@ TryReuseArrayType(JSObject *obj, JSObject *nobj)
|
|||
* and has the same prototype.
|
||||
*/
|
||||
JS_ASSERT(nobj->isDenseArray());
|
||||
JS_ASSERT(nobj->type() == nobj->getProto()->newType);
|
||||
JS_ASSERT(nobj->getProto()->hasNewType(nobj->type()));
|
||||
|
||||
if (obj->isArray() && !obj->hasSingletonType() && obj->getProto() == nobj->getProto())
|
||||
nobj->setType(obj->type());
|
||||
|
@ -2693,7 +2688,7 @@ array_splice(JSContext *cx, uintN argc, Value *vp)
|
|||
obj->setDenseArrayInitializedLength(finalLength);
|
||||
|
||||
/* Steps 12(c)-(d). */
|
||||
obj->shrinkDenseArrayElements(cx, finalLength);
|
||||
obj->shrinkElements(cx, finalLength);
|
||||
|
||||
/* Fix running enumerators for the deleted items. */
|
||||
if (!js_SuppressDeletedElements(cx, obj, finalLength, len))
|
||||
|
@ -2808,7 +2803,7 @@ mjit::stubs::ArrayConcatTwoArrays(VMFrame &f)
|
|||
/* No overflow here due to nslots limit. */
|
||||
uint32 len = initlen1 + initlen2;
|
||||
|
||||
if (!result->ensureSlots(f.cx, len))
|
||||
if (!result->ensureElements(f.cx, len))
|
||||
THROW();
|
||||
|
||||
JS_ASSERT(!result->getDenseArrayInitializedLength());
|
||||
|
@ -2846,8 +2841,6 @@ js::array_concat(JSContext *cx, uintN argc, Value *vp)
|
|||
return JS_FALSE;
|
||||
TryReuseArrayType(aobj, nobj);
|
||||
nobj->setArrayLength(cx, length);
|
||||
if (!aobj->isPackedDenseArray())
|
||||
nobj->markDenseArrayNotPacked(cx);
|
||||
vp->setObject(*nobj);
|
||||
if (argc == 0)
|
||||
return JS_TRUE;
|
||||
|
@ -2952,8 +2945,6 @@ array_slice(JSContext *cx, uintN argc, Value *vp)
|
|||
if (!nobj)
|
||||
return JS_FALSE;
|
||||
TryReuseArrayType(obj, nobj);
|
||||
if (!obj->isPackedDenseArray())
|
||||
nobj->markDenseArrayNotPacked(cx);
|
||||
args.rval().setObject(*nobj);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
@ -3609,8 +3600,14 @@ js_InitArrayClass(JSContext *cx, JSObject *obj)
|
|||
if (!ctor)
|
||||
return NULL;
|
||||
|
||||
/* The default 'new' object for Array.prototype has unknown properties. */
|
||||
arrayProto->getNewType(cx, NULL, /* markUnknown = */ true);
|
||||
/*
|
||||
* The default 'new' type of Array.prototype is required by type inference
|
||||
* to have unknown properties, to simplify handling of e.g. heterogenous
|
||||
* arrays in JSON and script literals and allows setDenseArrayElement to
|
||||
* be used without updating the indexed type set for such default arrays.
|
||||
*/
|
||||
if (!arrayProto->setNewTypeUnknown(cx))
|
||||
return NULL;
|
||||
|
||||
if (!LinkConstructorAndPrototype(cx, ctor, arrayProto))
|
||||
return NULL;
|
||||
|
@ -3632,34 +3629,78 @@ js_InitArrayClass(JSContext *cx, JSObject *obj)
|
|||
*/
|
||||
namespace js {
|
||||
|
||||
static inline bool
|
||||
EnsureNewArrayElements(JSContext *cx, JSObject *obj, jsuint length)
|
||||
{
|
||||
/*
|
||||
* If ensureElements creates dynamically allocated slots, then having
|
||||
* fixedSlots is a waste.
|
||||
*/
|
||||
DebugOnly<uint32> cap = obj->getDenseArrayCapacity();
|
||||
|
||||
if (!obj->ensureElements(cx, length))
|
||||
return false;
|
||||
|
||||
JS_ASSERT_IF(cap, !obj->hasDynamicElements());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<bool allocateCapacity>
|
||||
static JS_ALWAYS_INLINE JSObject *
|
||||
NewArray(JSContext *cx, jsuint length, JSObject *proto)
|
||||
{
|
||||
JS_ASSERT_IF(proto, proto->isArray());
|
||||
gc::AllocKind kind = GuessArrayGCKind(length);
|
||||
|
||||
gc::AllocKind kind = GuessObjectGCKind(length, true);
|
||||
JSObject *obj = detail::NewObject<WithProto::Class, false>(cx, &ArrayClass, proto, NULL, kind);
|
||||
#ifdef JS_THREADSAFE
|
||||
JS_ASSERT(CanBeFinalizedInBackground(kind, &ArrayClass));
|
||||
kind = GetBackgroundAllocKind(kind);
|
||||
#endif
|
||||
|
||||
GlobalObject *parent = GetCurrentGlobal(cx);
|
||||
|
||||
NewObjectCache &cache = cx->compartment->newObjectCache;
|
||||
|
||||
NewObjectCache::EntryIndex entry = -1;
|
||||
if (cache.lookupGlobal(&ArrayClass, parent, kind, &entry)) {
|
||||
JSObject *obj = cache.newObjectFromHit(cx, entry);
|
||||
if (!obj)
|
||||
return NULL;
|
||||
/* Fixup the elements pointer and length, which may be incorrect. */
|
||||
obj->setFixedElements();
|
||||
obj->setArrayLength(cx, length);
|
||||
if (allocateCapacity && !EnsureNewArrayElements(cx, obj, length))
|
||||
return NULL;
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (!proto && !FindProto(cx, &ArrayClass, parent, &proto))
|
||||
return NULL;
|
||||
|
||||
types::TypeObject *type = proto->getNewType(cx);
|
||||
if (!type)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Get a shape with zero fixed slots, regardless of the size class.
|
||||
* See JSObject::createDenseArray.
|
||||
*/
|
||||
Shape *shape = EmptyShape::getInitialShape(cx, &ArrayClass, proto,
|
||||
proto->getParent(), gc::FINALIZE_OBJECT0);
|
||||
if (!shape)
|
||||
return NULL;
|
||||
|
||||
JSObject* obj = JSObject::createDenseArray(cx, kind, shape, type, length);
|
||||
if (!obj)
|
||||
return NULL;
|
||||
|
||||
obj->setArrayLength(cx, length);
|
||||
if (entry != -1)
|
||||
cache.fillGlobal(entry, &ArrayClass, parent, kind, obj);
|
||||
|
||||
if (!cx->typeInferenceEnabled()) {
|
||||
obj->markDenseArrayNotPacked(cx);
|
||||
obj->backfillDenseArrayHoles(cx);
|
||||
}
|
||||
|
||||
if (allocateCapacity) {
|
||||
/* If ensureSlots creates dynamically allocated slots, then having fixedSlots is a waste. */
|
||||
DebugOnly<uint32> oldSlots = obj->numSlots();
|
||||
|
||||
if (!obj->ensureSlots(cx, length))
|
||||
return NULL;
|
||||
|
||||
JS_ASSERT_IF(obj->numFixedSlots(), oldSlots == obj->numSlots());
|
||||
}
|
||||
if (allocateCapacity && !EnsureNewArrayElements(cx, obj, length))
|
||||
return NULL;
|
||||
|
||||
Probes::createObject(cx, obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
@ -3710,8 +3751,7 @@ NewDenseCopiedArray(JSContext *cx, uint32 length, const Value *vp, JSObject *pro
|
|||
|
||||
JS_ASSERT(obj->getDenseArrayCapacity() >= length);
|
||||
|
||||
if (cx->typeInferenceEnabled())
|
||||
obj->setDenseArrayInitializedLength(vp ? length : 0);
|
||||
obj->setDenseArrayInitializedLength(vp ? length : 0);
|
||||
|
||||
if (vp)
|
||||
obj->initDenseArrayElements(0, vp, length);
|
||||
|
@ -3722,7 +3762,7 @@ NewDenseCopiedArray(JSContext *cx, uint32 length, const Value *vp, JSObject *pro
|
|||
JSObject *
|
||||
NewSlowEmptyArray(JSContext *cx)
|
||||
{
|
||||
JSObject *obj = NewNonFunction<WithProto::Class>(cx, &SlowArrayClass, NULL, NULL);
|
||||
JSObject *obj = NewBuiltinClassInstance(cx, &SlowArrayClass);
|
||||
if (!obj || !AddLengthProperty(cx, obj))
|
||||
return NULL;
|
||||
|
||||
|
|
|
@ -51,20 +51,6 @@
|
|||
/* Small arrays are dense, no matter what. */
|
||||
const uintN MIN_SPARSE_INDEX = 256;
|
||||
|
||||
inline uint32
|
||||
JSObject::getDenseArrayInitializedLength()
|
||||
{
|
||||
JS_ASSERT(isDenseArray());
|
||||
return initializedLength();
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSObject::isPackedDenseArray()
|
||||
{
|
||||
JS_ASSERT(isDenseArray());
|
||||
return flags & PACKED_ARRAY;
|
||||
}
|
||||
|
||||
namespace js {
|
||||
/* 2^32-2, inclusive */
|
||||
const uint32 MAX_ARRAY_INDEX = 4294967294u;
|
||||
|
|
|
@ -43,31 +43,11 @@
|
|||
#include "jsinferinlines.h"
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
inline void
|
||||
JSObject::setDenseArrayInitializedLength(uint32 length)
|
||||
{
|
||||
JS_ASSERT(isDenseArray());
|
||||
JS_ASSERT(length <= getDenseArrayCapacity());
|
||||
uint32 cur = initializedLength();
|
||||
prepareSlotRangeForOverwrite(length, cur);
|
||||
initializedLength() = length;
|
||||
}
|
||||
|
||||
inline void
|
||||
JSObject::markDenseArrayNotPacked(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(isDenseArray());
|
||||
if (flags & PACKED_ARRAY) {
|
||||
flags ^= PACKED_ARRAY;
|
||||
MarkTypeObjectFlags(cx, this, js::types::OBJECT_FLAG_NON_PACKED_ARRAY);
|
||||
}
|
||||
}
|
||||
|
||||
inline void
|
||||
JSObject::backfillDenseArrayHoles(JSContext *cx)
|
||||
{
|
||||
/* Ensure an array's elements are fully initialized. */
|
||||
ensureDenseArrayInitializedLength(cx, getDenseArrayCapacity(), 0);
|
||||
MarkTypeObjectFlags(cx, this, js::types::OBJECT_FLAG_NON_PACKED_ARRAY);
|
||||
}
|
||||
|
||||
inline void
|
||||
|
@ -78,13 +58,14 @@ JSObject::ensureDenseArrayInitializedLength(JSContext *cx, uint32 index, uint32
|
|||
* mark the elements through 'index + extra' as initialized in preparation
|
||||
* for a write.
|
||||
*/
|
||||
JS_ASSERT(index + extra <= capacity);
|
||||
if (initializedLength() < index)
|
||||
JS_ASSERT(index + extra <= getDenseArrayCapacity());
|
||||
uint32 &initlen = getElementsHeader()->initializedLength;
|
||||
if (initlen < index)
|
||||
markDenseArrayNotPacked(cx);
|
||||
|
||||
if (initializedLength() < index + extra) {
|
||||
js::InitValueRange(slots + initializedLength(), index + extra - initializedLength(), true);
|
||||
initializedLength() = index + extra;
|
||||
if (initlen < index + extra) {
|
||||
js::InitValueRange(elements + initlen, index + extra - initlen, true);
|
||||
initlen = index + extra;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,13 +74,7 @@ JSObject::ensureDenseArrayElements(JSContext *cx, uintN index, uintN extra)
|
|||
{
|
||||
JS_ASSERT(isDenseArray());
|
||||
|
||||
uintN currentCapacity = numSlots();
|
||||
|
||||
/*
|
||||
* Don't take excessive slow paths when inference is disabled, due to
|
||||
* uninitialized slots between initializedLength and capacity.
|
||||
*/
|
||||
JS_ASSERT_IF(!cx->typeInferenceEnabled(), currentCapacity == getDenseArrayInitializedLength());
|
||||
uintN currentCapacity = getDenseArrayCapacity();
|
||||
|
||||
uintN requiredCapacity;
|
||||
if (extra == 1) {
|
||||
|
@ -133,7 +108,7 @@ JSObject::ensureDenseArrayElements(JSContext *cx, uintN index, uintN extra)
|
|||
willBeSparseDenseArray(requiredCapacity, extra)) {
|
||||
return ED_SPARSE;
|
||||
}
|
||||
if (!growSlots(cx, requiredCapacity))
|
||||
if (!growElements(cx, requiredCapacity))
|
||||
return ED_FAILED;
|
||||
|
||||
ensureDenseArrayInitializedLength(cx, index, extra);
|
||||
|
|
|
@ -65,10 +65,9 @@ enum AllocKind {
|
|||
FINALIZE_OBJECT16,
|
||||
FINALIZE_OBJECT16_BACKGROUND,
|
||||
FINALIZE_OBJECT_LAST = FINALIZE_OBJECT16_BACKGROUND,
|
||||
FINALIZE_FUNCTION,
|
||||
FINALIZE_FUNCTION_AND_OBJECT_LAST = FINALIZE_FUNCTION,
|
||||
FINALIZE_SCRIPT,
|
||||
FINALIZE_SHAPE,
|
||||
FINALIZE_BASE_SHAPE,
|
||||
FINALIZE_TYPE_OBJECT,
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
FINALIZE_XML,
|
||||
|
@ -81,7 +80,6 @@ enum AllocKind {
|
|||
|
||||
static const unsigned FINALIZE_LIMIT = FINALIZE_LAST + 1;
|
||||
static const unsigned FINALIZE_OBJECT_LIMIT = FINALIZE_OBJECT_LAST + 1;
|
||||
static const unsigned FINALIZE_FUNCTION_AND_OBJECT_LIMIT = FINALIZE_FUNCTION_AND_OBJECT_LAST + 1;
|
||||
|
||||
/*
|
||||
* Live objects are marked black. How many other additional colors are available
|
||||
|
|
|
@ -360,6 +360,10 @@ struct Class
|
|||
bool isNative() const {
|
||||
return !(flags & NON_NATIVE);
|
||||
}
|
||||
|
||||
bool hasPrivate() const {
|
||||
return !!(flags & JSCLASS_HAS_PRIVATE);
|
||||
}
|
||||
};
|
||||
|
||||
JS_STATIC_ASSERT(offsetof(JSClass, name) == offsetof(Class, name));
|
||||
|
|
|
@ -1189,7 +1189,7 @@ js_ReportMissingArg(JSContext *cx, const Value &v, uintN arg)
|
|||
JS_snprintf(argbuf, sizeof argbuf, "%u", arg);
|
||||
bytes = NULL;
|
||||
if (IsFunctionObject(v)) {
|
||||
atom = v.toObject().getFunctionPrivate()->atom;
|
||||
atom = v.toObject().toFunction()->atom;
|
||||
bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK,
|
||||
v, atom);
|
||||
if (!bytes)
|
||||
|
|
|
@ -394,20 +394,6 @@ struct JSRuntime
|
|||
JSActivityCallback activityCallback;
|
||||
void *activityCallbackArg;
|
||||
|
||||
/*
|
||||
* Shape regenerated whenever a prototype implicated by an "add property"
|
||||
* property cache fill and induced trace guard has a readonly property or a
|
||||
* setter defined on it. This number proxies for the shapes of all objects
|
||||
* along the prototype chain of all objects in the runtime on which such an
|
||||
* add-property result has been cached/traced.
|
||||
*
|
||||
* See bug 492355 for more details.
|
||||
*
|
||||
* This comes early in JSRuntime to minimize the immediate format used by
|
||||
* trace-JITted code that reads it.
|
||||
*/
|
||||
uint32 protoHazardShape;
|
||||
|
||||
/* Garbage collector state, used by jsgc.c. */
|
||||
|
||||
/*
|
||||
|
@ -485,7 +471,6 @@ struct JSRuntime
|
|||
bool gcPoke;
|
||||
bool gcMarkAndSweep;
|
||||
bool gcRunning;
|
||||
bool gcRegenShapes;
|
||||
|
||||
/*
|
||||
* These options control the zealousness of the GC. The fundamental values
|
||||
|
@ -664,21 +649,6 @@ struct JSRuntime
|
|||
void setTrustedPrincipals(JSPrincipals *p) { trustedPrincipals_ = p; }
|
||||
JSPrincipals *trustedPrincipals() const { return trustedPrincipals_; }
|
||||
|
||||
/*
|
||||
* Object shape (property cache structural type) identifier generator.
|
||||
*
|
||||
* Type 0 stands for the empty scope, and must not be regenerated due to
|
||||
* uint32 wrap-around. Since js_GenerateShape (in jsinterp.cpp) uses
|
||||
* atomic pre-increment, the initial value for the first typed non-empty
|
||||
* scope will be 1.
|
||||
*
|
||||
* If this counter overflows into SHAPE_OVERFLOW_BIT (in jsinterp.h), the
|
||||
* cache is disabled, to avoid aliasing two different types. It stays
|
||||
* disabled until a triggered GC at some later moment compresses live
|
||||
* types, minimizing rt->shapeGen in the process.
|
||||
*/
|
||||
volatile uint32 shapeGen;
|
||||
|
||||
/* Literal table maintained by jsatom.c functions. */
|
||||
JSAtomState atomState;
|
||||
|
||||
|
@ -2135,29 +2105,6 @@ enum FrameExpandKind {
|
|||
FRAME_EXPAND_ALL = 1
|
||||
};
|
||||
|
||||
static JS_INLINE JSBool
|
||||
js_IsPropertyCacheDisabled(JSContext *cx)
|
||||
{
|
||||
return cx->runtime->shapeGen >= js::SHAPE_OVERFLOW_BIT;
|
||||
}
|
||||
|
||||
static JS_INLINE uint32
|
||||
js_RegenerateShapeForGC(JSRuntime *rt)
|
||||
{
|
||||
JS_ASSERT(rt->gcRunning);
|
||||
JS_ASSERT(rt->gcRegenShapes);
|
||||
|
||||
/*
|
||||
* Under the GC, compared with js_GenerateShape, we don't need to use
|
||||
* atomic increments but we still must make sure that after an overflow
|
||||
* the shape stays such.
|
||||
*/
|
||||
uint32 shape = rt->shapeGen;
|
||||
shape = (shape + 1) | (shape & js::SHAPE_OVERFLOW_BIT);
|
||||
rt->shapeGen = shape;
|
||||
return shape;
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
/************************************************************************/
|
||||
|
|
|
@ -334,7 +334,7 @@ CallJSNativeConstructor(JSContext *cx, Native native, const CallArgs &args)
|
|||
JS_ASSERT_IF(native != FunctionProxyClass.construct &&
|
||||
native != CallableObjectClass.construct &&
|
||||
native != js::CallOrConstructBoundFunction &&
|
||||
(!callee.isFunction() || callee.getFunctionPrivate()->u.n.clasp != &ObjectClass),
|
||||
(!callee.isFunction() || callee.toFunction()->u.n.clasp != &ObjectClass),
|
||||
!args.rval().isPrimitive() && callee != args.rval().toObject());
|
||||
|
||||
return true;
|
||||
|
@ -472,12 +472,6 @@ JSContext::ensureGeneratorStackSpace()
|
|||
return ok;
|
||||
}
|
||||
|
||||
inline js::RegExpStatics *
|
||||
JSContext::regExpStatics()
|
||||
{
|
||||
return js::GetGlobalForScopeChain(this)->getRegExpStatics();
|
||||
}
|
||||
|
||||
inline void
|
||||
JSContext::setPendingException(js::Value v) {
|
||||
this->throwing = true;
|
||||
|
|
|
@ -84,14 +84,7 @@ JSCompartment::JSCompartment(JSRuntime *rt)
|
|||
jaegerCompartment_(NULL),
|
||||
#endif
|
||||
propertyTree(thisForCtor()),
|
||||
emptyArgumentsShape(NULL),
|
||||
emptyBlockShape(NULL),
|
||||
emptyCallShape(NULL),
|
||||
emptyDeclEnvShape(NULL),
|
||||
emptyEnumeratorShape(NULL),
|
||||
emptyWithShape(NULL),
|
||||
initialRegExpShape(NULL),
|
||||
initialStringShape(NULL),
|
||||
emptyTypeObject(NULL),
|
||||
debugModeBits(rt->debugMode ? DebugFromC : 0),
|
||||
mathCache(NULL),
|
||||
breakpointSites(rt),
|
||||
|
@ -121,6 +114,8 @@ JSCompartment::init(JSContext *cx)
|
|||
activeAnalysis = activeInference = false;
|
||||
types.init(cx);
|
||||
|
||||
newObjectCache.reset();
|
||||
|
||||
if (!crossCompartmentWrappers.init())
|
||||
return false;
|
||||
|
||||
|
@ -260,7 +255,8 @@ JSCompartment::wrap(JSContext *cx, Value *vp)
|
|||
JS_ASSERT(obj->isCrossCompartmentWrapper());
|
||||
if (global->getClass() != &dummy_class && obj->getParent() != global) {
|
||||
do {
|
||||
obj->setParent(global);
|
||||
if (!obj->setParent(cx, global))
|
||||
return false;
|
||||
obj = obj->getProto();
|
||||
} while (obj && obj->isCrossCompartmentWrapper());
|
||||
}
|
||||
|
@ -314,7 +310,8 @@ JSCompartment::wrap(JSContext *cx, Value *vp)
|
|||
if (!crossCompartmentWrappers.put(GetProxyPrivate(wrapper), *vp))
|
||||
return false;
|
||||
|
||||
wrapper->setParent(global);
|
||||
if (!wrapper->setParent(cx, global))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -432,11 +429,11 @@ JSCompartment::markTypes(JSTracer *trc)
|
|||
}
|
||||
|
||||
for (size_t thingKind = FINALIZE_OBJECT0;
|
||||
thingKind < FINALIZE_FUNCTION_AND_OBJECT_LIMIT;
|
||||
thingKind < FINALIZE_OBJECT_LIMIT;
|
||||
thingKind++) {
|
||||
for (CellIterUnderGC i(this, AllocKind(thingKind)); !i.done(); i.next()) {
|
||||
JSObject *object = i.get<JSObject>();
|
||||
if (!object->isNewborn() && object->hasSingletonType())
|
||||
if (object->hasSingletonType())
|
||||
MarkRoot(trc, object, "mark_types_singleton");
|
||||
}
|
||||
}
|
||||
|
@ -459,24 +456,17 @@ JSCompartment::sweep(JSContext *cx, bool releaseTypes)
|
|||
}
|
||||
}
|
||||
|
||||
/* Remove dead empty shapes. */
|
||||
if (emptyArgumentsShape && IsAboutToBeFinalized(cx, emptyArgumentsShape))
|
||||
emptyArgumentsShape = NULL;
|
||||
if (emptyBlockShape && IsAboutToBeFinalized(cx, emptyBlockShape))
|
||||
emptyBlockShape = NULL;
|
||||
if (emptyCallShape && IsAboutToBeFinalized(cx, emptyCallShape))
|
||||
emptyCallShape = NULL;
|
||||
if (emptyDeclEnvShape && IsAboutToBeFinalized(cx, emptyDeclEnvShape))
|
||||
emptyDeclEnvShape = NULL;
|
||||
if (emptyEnumeratorShape && IsAboutToBeFinalized(cx, emptyEnumeratorShape))
|
||||
emptyEnumeratorShape = NULL;
|
||||
if (emptyWithShape && IsAboutToBeFinalized(cx, emptyWithShape))
|
||||
emptyWithShape = NULL;
|
||||
/* Remove dead references held weakly by the compartment. */
|
||||
|
||||
if (initialRegExpShape && IsAboutToBeFinalized(cx, initialRegExpShape))
|
||||
initialRegExpShape = NULL;
|
||||
if (initialStringShape && IsAboutToBeFinalized(cx, initialStringShape))
|
||||
initialStringShape = NULL;
|
||||
sweepBaseShapeTable(cx);
|
||||
sweepInitialShapeTable(cx);
|
||||
sweepNewTypeObjectTable(cx, newTypeObjects);
|
||||
sweepNewTypeObjectTable(cx, lazyTypeObjects);
|
||||
|
||||
if (emptyTypeObject && IsAboutToBeFinalized(cx, emptyTypeObject))
|
||||
emptyTypeObject = NULL;
|
||||
|
||||
newObjectCache.reset();
|
||||
|
||||
sweepBreakpoints(cx);
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "jsgc.h"
|
||||
#include "jsgcstats.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsscope.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
@ -248,37 +249,28 @@ struct JS_FRIEND_API(JSCompartment) {
|
|||
jsrefcount liveDictModeNodes;
|
||||
#endif
|
||||
|
||||
typedef js::ReadBarriered<js::EmptyShape> BarrieredEmptyShape;
|
||||
typedef js::ReadBarriered<const js::Shape> BarrieredShape;
|
||||
/* Set of all unowned base shapes in the compartment. */
|
||||
js::BaseShapeSet baseShapes;
|
||||
void sweepBaseShapeTable(JSContext *cx);
|
||||
|
||||
/*
|
||||
* Runtime-shared empty scopes for well-known built-in objects that lack
|
||||
* class prototypes (the usual locus of an emptyShape). Mnemonic: ABCDEW
|
||||
*/
|
||||
BarrieredEmptyShape emptyArgumentsShape;
|
||||
BarrieredEmptyShape emptyBlockShape;
|
||||
BarrieredEmptyShape emptyCallShape;
|
||||
BarrieredEmptyShape emptyDeclEnvShape;
|
||||
BarrieredEmptyShape emptyEnumeratorShape;
|
||||
BarrieredEmptyShape emptyWithShape;
|
||||
/* Set of initial shapes in the compartment. */
|
||||
js::InitialShapeSet initialShapes;
|
||||
void sweepInitialShapeTable(JSContext *cx);
|
||||
|
||||
typedef js::HashSet<js::EmptyShape *,
|
||||
js::DefaultHasher<js::EmptyShape *>,
|
||||
js::SystemAllocPolicy> EmptyShapeSet;
|
||||
/* Set of default 'new' or lazy types in the compartment. */
|
||||
js::types::TypeObjectSet newTypeObjects;
|
||||
js::types::TypeObjectSet lazyTypeObjects;
|
||||
void sweepNewTypeObjectTable(JSContext *cx, js::types::TypeObjectSet &table);
|
||||
|
||||
EmptyShapeSet emptyShapes;
|
||||
js::types::TypeObject *emptyTypeObject;
|
||||
|
||||
/*
|
||||
* Initial shapes given to RegExp and String objects, encoding the initial
|
||||
* sets of built-in instance properties and the fixed slots where they must
|
||||
* be stored (see JSObject::JSSLOT_(REGEXP|STRING)_*). Later property
|
||||
* additions may cause these shapes to not be used by a RegExp or String
|
||||
* (even along the entire shape parent chain, should the object go into
|
||||
* dictionary mode). But because all the initial properties are
|
||||
* non-configurable, they will always map to fixed slots.
|
||||
*/
|
||||
BarrieredShape initialRegExpShape;
|
||||
BarrieredShape initialStringShape;
|
||||
/* Get the default 'new' type for objects with a NULL prototype. */
|
||||
inline js::types::TypeObject *getEmptyType(JSContext *cx);
|
||||
|
||||
js::types::TypeObject *getLazyType(JSContext *cx, JSObject *proto);
|
||||
|
||||
/* Cache to speed up object creation. */
|
||||
js::NewObjectCache newObjectCache;
|
||||
|
||||
private:
|
||||
enum { DebugFromC = 1, DebugFromJS = 2 };
|
||||
|
|
|
@ -1228,9 +1228,11 @@ SetUTCTime(JSContext *cx, JSObject *obj, jsdouble t, Value *vp = NULL)
|
|||
{
|
||||
JS_ASSERT(obj->isDate());
|
||||
|
||||
size_t slotCap = JS_MIN(obj->numSlots(), JSObject::DATE_CLASS_RESERVED_SLOTS);
|
||||
for (size_t ind = JSObject::JSSLOT_DATE_COMPONENTS_START; ind < slotCap; ind++)
|
||||
for (size_t ind = JSObject::JSSLOT_DATE_COMPONENTS_START;
|
||||
ind < JSObject::DATE_CLASS_RESERVED_SLOTS;
|
||||
ind++) {
|
||||
obj->setSlot(ind, UndefinedValue());
|
||||
}
|
||||
|
||||
obj->setDateUTCTime(DoubleValue(t));
|
||||
if (vp)
|
||||
|
@ -1257,12 +1259,6 @@ FillLocalTimes(JSContext *cx, JSObject *obj)
|
|||
|
||||
jsdouble utcTime = obj->getDateUTCTime().toNumber();
|
||||
|
||||
/* Make sure there are slots to store the cached information. */
|
||||
if (obj->numSlots() < JSObject::DATE_CLASS_RESERVED_SLOTS) {
|
||||
if (!obj->growSlots(cx, JSObject::DATE_CLASS_RESERVED_SLOTS))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!JSDOUBLE_IS_FINITE(utcTime)) {
|
||||
for (size_t ind = JSObject::JSSLOT_DATE_COMPONENTS_START;
|
||||
ind < JSObject::DATE_CLASS_RESERVED_SLOTS;
|
||||
|
@ -2696,8 +2692,6 @@ js_InitDateClass(JSContext *cx, JSObject *obj)
|
|||
{
|
||||
return NULL;
|
||||
}
|
||||
if (!cx->typeInferenceEnabled())
|
||||
dateProto->brand(cx);
|
||||
|
||||
if (!DefineConstructorAndPrototype(cx, global, JSProto_Date, ctor, dateProto))
|
||||
return NULL;
|
||||
|
@ -2709,7 +2703,7 @@ JS_FRIEND_API(JSObject *)
|
|||
js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time)
|
||||
{
|
||||
JSObject *obj = NewBuiltinClassInstance(cx, &DateClass);
|
||||
if (!obj || !obj->ensureSlots(cx, JSObject::DATE_CLASS_RESERVED_SLOTS))
|
||||
if (!obj)
|
||||
return NULL;
|
||||
if (!SetUTCTime(cx, obj, msec_time))
|
||||
return NULL;
|
||||
|
|
|
@ -616,10 +616,15 @@ JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fpArg)
|
|||
return NULL;
|
||||
|
||||
JS_ASSERT(fp->callee().isFunction());
|
||||
JS_ASSERT(fp->callee().getPrivate() == fp->fun());
|
||||
return &fp->callee();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject *)
|
||||
JS_GetParentOrScopeChain(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
return obj->scopeChain();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp)
|
||||
{
|
||||
|
@ -786,7 +791,7 @@ JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp)
|
|||
shape = shape->previous();
|
||||
|
||||
if (!shape->previous()) {
|
||||
JS_ASSERT(JSID_IS_EMPTY(shape->propid));
|
||||
JS_ASSERT(shape->isEmptyShape());
|
||||
shape = NULL;
|
||||
}
|
||||
|
||||
|
@ -799,7 +804,7 @@ JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop,
|
|||
{
|
||||
assertSameCompartment(cx, obj);
|
||||
Shape *shape = (Shape *) sprop;
|
||||
pd->id = IdToJsval(shape->propid);
|
||||
pd->id = IdToJsval(shape->propid());
|
||||
|
||||
JSBool wasThrowing = cx->isExceptionPending();
|
||||
Value lastException = UndefinedValue();
|
||||
|
@ -807,7 +812,7 @@ JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop,
|
|||
lastException = cx->getPendingException();
|
||||
cx->clearPendingException();
|
||||
|
||||
if (!js_GetProperty(cx, obj, shape->propid, &pd->value)) {
|
||||
if (!js_GetProperty(cx, obj, shape->propid(), &pd->value)) {
|
||||
if (!cx->isExceptionPending()) {
|
||||
pd->flags = JSPD_ERROR;
|
||||
pd->value = JSVAL_VOID;
|
||||
|
@ -827,21 +832,21 @@ JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop,
|
|||
| (!shape->configurable() ? JSPD_PERMANENT : 0);
|
||||
pd->spare = 0;
|
||||
if (shape->getter() == GetCallArg) {
|
||||
pd->slot = shape->shortid;
|
||||
pd->slot = shape->shortid();
|
||||
pd->flags |= JSPD_ARGUMENT;
|
||||
} else if (shape->getter() == GetCallVar) {
|
||||
pd->slot = shape->shortid;
|
||||
pd->slot = shape->shortid();
|
||||
pd->flags |= JSPD_VARIABLE;
|
||||
} else {
|
||||
pd->slot = 0;
|
||||
}
|
||||
pd->alias = JSVAL_VOID;
|
||||
|
||||
if (obj->containsSlot(shape->slot)) {
|
||||
if (obj->containsSlot(shape->slot())) {
|
||||
for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) {
|
||||
const Shape &aprop = r.front();
|
||||
if (&aprop != shape && aprop.slot == shape->slot) {
|
||||
pd->alias = IdToJsval(aprop.propid);
|
||||
if (&aprop != shape && aprop.slot() == shape->slot()) {
|
||||
pd->alias = IdToJsval(aprop.propid());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1063,8 +1068,7 @@ JS_IsSystemObject(JSContext *cx, JSObject *obj)
|
|||
JS_PUBLIC_API(JSBool)
|
||||
JS_MakeSystemObject(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
obj->setSystem();
|
||||
return true;
|
||||
return obj->setSystem(cx);
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
|
|
|
@ -279,6 +279,9 @@ JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp);
|
|||
extern JS_PUBLIC_API(JSObject *)
|
||||
JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp);
|
||||
|
||||
extern JS_PUBLIC_API(JSObject *)
|
||||
JS_GetParentOrScopeChain(JSContext *cx, JSObject *obj);
|
||||
|
||||
/* XXXrginda Initially published with typo */
|
||||
#define JS_IsContructorFrame JS_IsConstructorFrame
|
||||
extern JS_PUBLIC_API(JSBool)
|
||||
|
@ -458,9 +461,7 @@ JS_GetScriptTotalSize(JSContext *cx, JSScript *script);
|
|||
* Return true if obj is a "system" object, that is, one created by
|
||||
* JS_NewSystemObject with the system flag set and not JS_NewObject.
|
||||
*
|
||||
* What "system" means is up to the API client, but it can be used to implement
|
||||
* access control policies based on script filenames and their prefixes, using
|
||||
* JS_FlagScriptFilenamePrefix and JS_GetTopScriptFilenameFlags.
|
||||
* What "system" means is up to the API client.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSBool)
|
||||
JS_IsSystemObject(JSContext *cx, JSObject *obj);
|
||||
|
|
|
@ -550,7 +550,7 @@ ValueToShortSource(JSContext *cx, const Value &v)
|
|||
/*
|
||||
* XXX Avoid function decompilation bloat for now.
|
||||
*/
|
||||
str = JS_GetFunctionId(obj->getFunctionPrivate());
|
||||
str = JS_GetFunctionId(obj->toFunction());
|
||||
if (!str && !(str = js_ValueToSource(cx, v))) {
|
||||
/*
|
||||
* Continue to soldier on if the function couldn't be
|
||||
|
@ -700,10 +700,6 @@ FilenameToString(JSContext *cx, const char *filename)
|
|||
return JS_NewStringCopyZ(cx, filename);
|
||||
}
|
||||
|
||||
enum {
|
||||
JSSLOT_ERROR_EXNTYPE = 0
|
||||
};
|
||||
|
||||
static JSBool
|
||||
Exception(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
|
@ -726,7 +722,7 @@ Exception(JSContext *cx, uintN argc, Value *vp)
|
|||
}
|
||||
|
||||
JSObject *errProto = &protov.toObject();
|
||||
JSObject *obj = NewNativeClassInstance(cx, &ErrorClass, errProto, errProto->getParent());
|
||||
JSObject *obj = NewObjectWithGivenProto(cx, &ErrorClass, errProto, NULL);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
|
@ -772,7 +768,7 @@ Exception(JSContext *cx, uintN argc, Value *vp)
|
|||
lineno = iter.done() ? 0 : js_FramePCToLineNumber(cx, iter.fp(), iter.pc());
|
||||
}
|
||||
|
||||
intN exnType = args.callee().getReservedSlot(JSSLOT_ERROR_EXNTYPE).toInt32();
|
||||
intN exnType = args.callee().toFunction()->getExtendedSlot(0).toInt32();
|
||||
if (!InitExnPrivate(cx, obj, message, filename, lineno, NULL, exnType))
|
||||
return false;
|
||||
|
||||
|
@ -1035,10 +1031,11 @@ InitErrorClass(JSContext *cx, GlobalObject *global, intN type, JSObject &proto)
|
|||
}
|
||||
|
||||
/* Create the corresponding constructor. */
|
||||
JSFunction *ctor = global->createConstructor(cx, Exception, &ErrorClass, name, 1);
|
||||
JSFunction *ctor = global->createConstructor(cx, Exception, &ErrorClass, name, 1,
|
||||
JSFunction::ExtendedFinalizeKind);
|
||||
if (!ctor)
|
||||
return NULL;
|
||||
ctor->setReservedSlot(JSSLOT_ERROR_EXNTYPE, Int32Value(int32(type)));
|
||||
ctor->setExtendedSlot(0, Int32Value(int32(type)));
|
||||
|
||||
if (!LinkConstructorAndPrototype(cx, ctor, errorProto))
|
||||
return NULL;
|
||||
|
@ -1174,7 +1171,7 @@ js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp,
|
|||
goto out;
|
||||
tv[0] = OBJECT_TO_JSVAL(errProto);
|
||||
|
||||
errObject = NewNativeClassInstance(cx, &ErrorClass, errProto, errProto->getParent());
|
||||
errObject = NewObjectWithGivenProto(cx, &ErrorClass, errProto, NULL);
|
||||
if (!errObject) {
|
||||
ok = JS_FALSE;
|
||||
goto out;
|
||||
|
@ -1362,7 +1359,7 @@ js_CopyErrorObject(JSContext *cx, JSObject *errobj, JSObject *scope)
|
|||
JSObject *proto;
|
||||
if (!js_GetClassPrototype(cx, scope->getGlobal(), GetExceptionProtoKey(copy->exnType), &proto))
|
||||
return NULL;
|
||||
JSObject *copyobj = NewNativeClassInstance(cx, &ErrorClass, proto, proto->getParent());
|
||||
JSObject *copyobj = NewObjectWithGivenProto(cx, &ErrorClass, proto, NULL);
|
||||
copyobj->setPrivate(copy);
|
||||
autoFree.p = NULL;
|
||||
return copyobj;
|
||||
|
|
|
@ -85,7 +85,7 @@ JS_FRIEND_API(JSFunction *)
|
|||
JS_GetObjectFunction(JSObject *obj)
|
||||
{
|
||||
if (obj->isFunction())
|
||||
return obj->getFunctionPrivate();
|
||||
return obj->toFunction();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -125,14 +125,6 @@ JS_NewObjectWithUniqueType(JSContext *cx, JSClass *clasp, JSObject *proto, JSObj
|
|||
return obj;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(uint32)
|
||||
JS_ObjectCountDynamicSlots(JSObject *obj)
|
||||
{
|
||||
if (obj->hasSlotsArray())
|
||||
return obj->numDynamicSlots(obj->numSlots());
|
||||
return 0;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_ShrinkingGC(JSContext *cx)
|
||||
{
|
||||
|
@ -186,20 +178,126 @@ AutoSwitchCompartment::~AutoSwitchCompartment()
|
|||
cx->compartment = oldCompartment;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
JS_FRIEND_API(void)
|
||||
js::CheckReservedSlot(const JSObject *obj, size_t slot)
|
||||
JS_FRIEND_API(size_t)
|
||||
js::GetObjectDynamicSlotSize(JSObject *obj, JSMallocSizeOfFun mallocSizeOf)
|
||||
{
|
||||
CheckSlot(obj, slot);
|
||||
JS_ASSERT(slot < JSSLOT_FREE(obj->getClass()));
|
||||
return obj->dynamicSlotSize(mallocSizeOf);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(size_t)
|
||||
js::GetCompartmentShapeTableSize(JSCompartment *c, JSMallocSizeOfFun mallocSizeOf)
|
||||
{
|
||||
return c->baseShapes.sizeOfExcludingThis(mallocSizeOf)
|
||||
+ c->initialShapes.sizeOfExcludingThis(mallocSizeOf)
|
||||
+ c->newTypeObjects.sizeOfExcludingThis(mallocSizeOf)
|
||||
+ c->lazyTypeObjects.sizeOfExcludingThis(mallocSizeOf);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
js::IsScopeObject(const JSObject *obj)
|
||||
{
|
||||
return obj->isInternalScope();
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSObject *)
|
||||
js::GetObjectParentMaybeScope(const JSObject *obj)
|
||||
{
|
||||
return obj->scopeChain();
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSObject *)
|
||||
js::GetGlobalForObjectCrossCompartment(JSObject *obj)
|
||||
{
|
||||
return obj->getGlobal();
|
||||
}
|
||||
|
||||
JS_FRIEND_API(uint32)
|
||||
js::GetObjectSlotSpan(const JSObject *obj)
|
||||
{
|
||||
return obj->slotSpan();
|
||||
}
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
js::IsOriginalScriptFunction(JSFunction *fun)
|
||||
{
|
||||
return fun->script()->function() == fun;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSFunction *)
|
||||
js::DefineFunctionWithReserved(JSContext *cx, JSObject *obj, const char *name, JSNative call,
|
||||
uintN nargs, uintN attrs)
|
||||
{
|
||||
JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, obj);
|
||||
JSAtom *atom = js_Atomize(cx, name, strlen(name));
|
||||
if (!atom)
|
||||
return NULL;
|
||||
return js_DefineFunction(cx, obj, ATOM_TO_JSID(atom), call, nargs, attrs,
|
||||
JSFunction::ExtendedFinalizeKind);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSFunction *)
|
||||
js::NewFunctionWithReserved(JSContext *cx, JSNative native, uintN nargs, uintN flags,
|
||||
JSObject *parent, const char *name)
|
||||
{
|
||||
JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
|
||||
JSAtom *atom;
|
||||
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, parent);
|
||||
|
||||
if (!name) {
|
||||
atom = NULL;
|
||||
} else {
|
||||
atom = js_Atomize(cx, name, strlen(name));
|
||||
if (!atom)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return js_NewFunction(cx, NULL, native, nargs, flags, parent, atom,
|
||||
JSFunction::ExtendedFinalizeKind);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSFunction *)
|
||||
js::NewFunctionByIdWithReserved(JSContext *cx, JSNative native, uintN nargs, uintN flags, JSObject *parent,
|
||||
jsid id)
|
||||
{
|
||||
JS_ASSERT(JSID_IS_STRING(id));
|
||||
JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, parent);
|
||||
|
||||
return js_NewFunction(cx, NULL, native, nargs, flags, parent, JSID_TO_ATOM(id),
|
||||
JSFunction::ExtendedFinalizeKind);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSObject *)
|
||||
js::InitClassWithReserved(JSContext *cx, JSObject *obj, JSObject *parent_proto,
|
||||
JSClass *clasp, JSNative constructor, uintN nargs,
|
||||
JSPropertySpec *ps, JSFunctionSpec *fs,
|
||||
JSPropertySpec *static_ps, JSFunctionSpec *static_fs)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, obj, parent_proto);
|
||||
return js_InitClass(cx, obj, parent_proto, Valueify(clasp), constructor,
|
||||
nargs, ps, fs, static_ps, static_fs, NULL,
|
||||
JSFunction::ExtendedFinalizeKind);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(const Value &)
|
||||
js::GetFunctionNativeReserved(JSObject *fun, size_t which)
|
||||
{
|
||||
JS_ASSERT(fun->toFunction()->isNative());
|
||||
return fun->toFunction()->getExtendedSlot(which);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
js::CheckSlot(const JSObject *obj, size_t slot)
|
||||
js::SetFunctionNativeReserved(JSObject *fun, size_t which, const Value &val)
|
||||
{
|
||||
JS_ASSERT(slot < obj->numSlots());
|
||||
JS_ASSERT(fun->toFunction()->isNative());
|
||||
fun->toFunction()->setExtendedSlot(which, val);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The below code is for temporary telemetry use. It can be removed when
|
||||
|
|
|
@ -113,7 +113,6 @@ typedef struct TypeInferenceMemoryStats
|
|||
int64 objects;
|
||||
int64 tables;
|
||||
int64 temporary;
|
||||
int64 emptyShapes;
|
||||
} TypeInferenceMemoryStats;
|
||||
|
||||
extern JS_FRIEND_API(void)
|
||||
|
@ -199,6 +198,12 @@ JS_FRIEND_API(JSBool) obj_defineGetter(JSContext *cx, uintN argc, js::Value *vp)
|
|||
JS_FRIEND_API(JSBool) obj_defineSetter(JSContext *cx, uintN argc, js::Value *vp);
|
||||
#endif
|
||||
|
||||
extern JS_FRIEND_API(size_t)
|
||||
GetObjectDynamicSlotSize(JSObject *obj, JSMallocSizeOfFun mallocSizeOf);
|
||||
|
||||
extern JS_FRIEND_API(size_t)
|
||||
GetCompartmentShapeTableSize(JSCompartment *c, JSMallocSizeOfFun mallocSizeOf);
|
||||
|
||||
/*
|
||||
* Check whether it is OK to assign an undeclared property with name
|
||||
* propname of the global object in the current script on cx. Reports
|
||||
|
@ -244,24 +249,34 @@ struct TypeObject {
|
|||
JSObject *proto;
|
||||
};
|
||||
|
||||
struct Object {
|
||||
void *_1;
|
||||
struct BaseShape {
|
||||
js::Class *clasp;
|
||||
uint32 flags;
|
||||
uint32 objShape;
|
||||
void *_2;
|
||||
JSObject *parent;
|
||||
void *privateData;
|
||||
jsuword capacity;
|
||||
js::Value *slots;
|
||||
TypeObject *type;
|
||||
};
|
||||
|
||||
struct Shape {
|
||||
BaseShape *base;
|
||||
jsid _1;
|
||||
uint32 slotInfo;
|
||||
|
||||
static const uint32 FIXED_SLOTS_SHIFT = 27;
|
||||
};
|
||||
|
||||
struct Object {
|
||||
Shape *shape;
|
||||
TypeObject *type;
|
||||
js::Value *slots;
|
||||
js::Value *_1;
|
||||
|
||||
size_t numFixedSlots() const { return shape->slotInfo >> Shape::FIXED_SLOTS_SHIFT; }
|
||||
Value *fixedSlots() const {
|
||||
return (Value *)((jsuword) this + sizeof(shadow::Object));
|
||||
}
|
||||
|
||||
js::Value &slotRef(size_t slot) const {
|
||||
size_t nfixed = flags >> FIXED_SLOTS_SHIFT;
|
||||
size_t nfixed = numFixedSlots();
|
||||
if (slot < nfixed)
|
||||
return ((Value *)((jsuword) this + sizeof(shadow::Object)))[slot];
|
||||
return fixedSlots()[slot];
|
||||
return slots[slot - nfixed];
|
||||
}
|
||||
};
|
||||
|
@ -284,7 +299,7 @@ extern JS_FRIEND_DATA(js::Class) XMLClass;
|
|||
inline js::Class *
|
||||
GetObjectClass(const JSObject *obj)
|
||||
{
|
||||
return reinterpret_cast<const shadow::Object*>(obj)->clasp;
|
||||
return reinterpret_cast<const shadow::Object*>(obj)->shape->base->clasp;
|
||||
}
|
||||
|
||||
inline JSClass *
|
||||
|
@ -293,12 +308,49 @@ GetObjectJSClass(const JSObject *obj)
|
|||
return js::Jsvalify(GetObjectClass(obj));
|
||||
}
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
IsScopeObject(const JSObject *obj);
|
||||
|
||||
inline JSObject *
|
||||
GetObjectParent(const JSObject *obj)
|
||||
{
|
||||
return reinterpret_cast<const shadow::Object*>(obj)->parent;
|
||||
JS_ASSERT(!IsScopeObject(obj));
|
||||
return reinterpret_cast<const shadow::Object*>(obj)->shape->base->parent;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSObject *)
|
||||
GetObjectParentMaybeScope(const JSObject *obj);
|
||||
|
||||
JS_FRIEND_API(JSObject *)
|
||||
GetGlobalForObjectCrossCompartment(JSObject *obj);
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
IsOriginalScriptFunction(JSFunction *fun);
|
||||
|
||||
JS_FRIEND_API(JSFunction *)
|
||||
DefineFunctionWithReserved(JSContext *cx, JSObject *obj, const char *name, JSNative call,
|
||||
uintN nargs, uintN attrs);
|
||||
|
||||
JS_FRIEND_API(JSFunction *)
|
||||
NewFunctionWithReserved(JSContext *cx, JSNative call, uintN nargs, uintN flags,
|
||||
JSObject *parent, const char *name);
|
||||
|
||||
JS_FRIEND_API(JSFunction *)
|
||||
NewFunctionByIdWithReserved(JSContext *cx, JSNative native, uintN nargs, uintN flags,
|
||||
JSObject *parent, jsid id);
|
||||
|
||||
JS_FRIEND_API(JSObject *)
|
||||
InitClassWithReserved(JSContext *cx, JSObject *obj, JSObject *parent_proto,
|
||||
JSClass *clasp, JSNative constructor, uintN nargs,
|
||||
JSPropertySpec *ps, JSFunctionSpec *fs,
|
||||
JSPropertySpec *static_ps, JSFunctionSpec *static_fs);
|
||||
|
||||
JS_FRIEND_API(const Value &)
|
||||
GetFunctionNativeReserved(JSObject *fun, size_t which);
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
SetFunctionNativeReserved(JSObject *fun, size_t which, const Value &val);
|
||||
|
||||
inline JSObject *
|
||||
GetObjectProto(const JSObject *obj)
|
||||
{
|
||||
|
@ -308,25 +360,11 @@ GetObjectProto(const JSObject *obj)
|
|||
inline void *
|
||||
GetObjectPrivate(const JSObject *obj)
|
||||
{
|
||||
return reinterpret_cast<const shadow::Object*>(obj)->privateData;
|
||||
const shadow::Object *nobj = reinterpret_cast<const shadow::Object*>(obj);
|
||||
void **addr = reinterpret_cast<void**>(&nobj->fixedSlots()[nobj->numFixedSlots()]);
|
||||
return *addr;
|
||||
}
|
||||
|
||||
inline JSObject *
|
||||
GetObjectGlobal(JSObject *obj)
|
||||
{
|
||||
while (JSObject *parent = GetObjectParent(obj))
|
||||
obj = parent;
|
||||
return obj;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
extern JS_FRIEND_API(void) CheckReservedSlot(const JSObject *obj, size_t slot);
|
||||
extern JS_FRIEND_API(void) CheckSlot(const JSObject *obj, size_t slot);
|
||||
#else
|
||||
inline void CheckReservedSlot(const JSObject *obj, size_t slot) {}
|
||||
inline void CheckSlot(const JSObject *obj, size_t slot) {}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Get a slot that is both reserved for object's clasp *and* is fixed (fits
|
||||
* within the maximum capacity for the object's fixed slots).
|
||||
|
@ -334,34 +372,32 @@ inline void CheckSlot(const JSObject *obj, size_t slot) {}
|
|||
inline const Value &
|
||||
GetReservedSlot(const JSObject *obj, size_t slot)
|
||||
{
|
||||
CheckReservedSlot(obj, slot);
|
||||
JS_ASSERT(slot < JSCLASS_RESERVED_SLOTS(GetObjectClass(obj)));
|
||||
return reinterpret_cast<const shadow::Object *>(obj)->slotRef(slot);
|
||||
}
|
||||
|
||||
inline void
|
||||
SetReservedSlot(JSObject *obj, size_t slot, const Value &value)
|
||||
{
|
||||
CheckReservedSlot(obj, slot);
|
||||
JS_ASSERT(slot < JSCLASS_RESERVED_SLOTS(GetObjectClass(obj)));
|
||||
reinterpret_cast<shadow::Object *>(obj)->slotRef(slot) = value;
|
||||
}
|
||||
|
||||
inline uint32
|
||||
GetNumSlots(const JSObject *obj)
|
||||
{
|
||||
return uint32(reinterpret_cast<const shadow::Object *>(obj)->capacity);
|
||||
}
|
||||
JS_FRIEND_API(uint32)
|
||||
GetObjectSlotSpan(const JSObject *obj);
|
||||
|
||||
inline const Value &
|
||||
GetSlot(const JSObject *obj, size_t slot)
|
||||
GetObjectSlot(const JSObject *obj, size_t slot)
|
||||
{
|
||||
CheckSlot(obj, slot);
|
||||
JS_ASSERT(slot < GetObjectSlotSpan(obj));
|
||||
return reinterpret_cast<const shadow::Object *>(obj)->slotRef(slot);
|
||||
}
|
||||
|
||||
inline uint32
|
||||
inline Shape *
|
||||
GetObjectShape(const JSObject *obj)
|
||||
{
|
||||
return reinterpret_cast<const shadow::Object*>(obj)->objShape;
|
||||
shadow::Shape *shape = reinterpret_cast<const shadow::Object*>(obj)->shape;
|
||||
return reinterpret_cast<Shape *>(shape);
|
||||
}
|
||||
|
||||
static inline js::PropertyOp
|
||||
|
|
606
js/src/jsfun.cpp
606
js/src/jsfun.cpp
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
371
js/src/jsfun.h
371
js/src/jsfun.h
|
@ -94,16 +94,17 @@
|
|||
global object */
|
||||
|
||||
#define JSFUN_EXPR_CLOSURE 0x1000 /* expression closure: function(x) x*x */
|
||||
#define JSFUN_EXTENDED 0x2000 /* structure is FunctionExtended */
|
||||
#define JSFUN_INTERPRETED 0x4000 /* use u.i if kind >= this value else u.n */
|
||||
#define JSFUN_FLAT_CLOSURE 0x8000 /* flat (aka "display") closure */
|
||||
#define JSFUN_NULL_CLOSURE 0xc000 /* null closure entrains no scope chain */
|
||||
#define JSFUN_KINDMASK 0xc000 /* encode interp vs. native and closure
|
||||
optimization level -- see above */
|
||||
|
||||
struct JSFunction : public JSObject_Slots2
|
||||
{
|
||||
/* Functions always have two fixed slots (FUN_CLASS_RESERVED_SLOTS). */
|
||||
namespace js { class FunctionExtended; }
|
||||
|
||||
struct JSFunction : public JSObject
|
||||
{
|
||||
uint16 nargs; /* maximum number of specified arguments,
|
||||
reflected as f.length/f.arity */
|
||||
uint16 flags; /* flags, see JSFUN_* below and in jsapi.h */
|
||||
|
@ -116,10 +117,7 @@ struct JSFunction : public JSObject_Slots2
|
|||
struct Scripted {
|
||||
JSScript *script_; /* interpreted bytecode descriptor or null;
|
||||
use the setter! */
|
||||
uint16 skipmin; /* net skip amount up (toward zero) from
|
||||
script_->staticLevel to nearest upvar,
|
||||
including upvars in nested functions */
|
||||
js::Shape *names; /* argument and variable names */
|
||||
JSObject *env; /* environment for new activations */
|
||||
} i;
|
||||
void *nativeOrScript;
|
||||
} u;
|
||||
|
@ -163,37 +161,17 @@ struct JSFunction : public JSObject_Slots2
|
|||
return flags & JSFUN_JOINABLE;
|
||||
}
|
||||
|
||||
JSObject &compiledFunObj() {
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
/*
|
||||
* FunctionClass reserves two slots, which are free in JSObject::fslots
|
||||
* without requiring dslots allocation. Null closures that can be joined to
|
||||
* a compiler-created function object use the first one to hold a mutable
|
||||
* methodAtom() state variable, needed for correct foo.caller handling.
|
||||
* For an interpreted function, accessors for the initial scope object of
|
||||
* activations (stack frames) of the function.
|
||||
*/
|
||||
enum {
|
||||
METHOD_ATOM_SLOT = JSSLOT_FUN_METHOD_ATOM
|
||||
};
|
||||
inline JSObject *environment() const;
|
||||
inline void setEnvironment(JSObject *obj);
|
||||
|
||||
static inline size_t offsetOfEnvironment() { return offsetof(JSFunction, u.i.env); }
|
||||
|
||||
public:
|
||||
inline void setJoinable();
|
||||
|
||||
/*
|
||||
* Method name imputed from property uniquely assigned to or initialized,
|
||||
* where the function does not need to be cloned to carry a scope chain or
|
||||
* flattened upvars.
|
||||
*/
|
||||
JSAtom *methodAtom() const {
|
||||
return (joinable() && getSlot(METHOD_ATOM_SLOT).isString())
|
||||
? &getSlot(METHOD_ATOM_SLOT).toString()->asAtom()
|
||||
: NULL;
|
||||
}
|
||||
|
||||
inline void setMethodAtom(JSAtom *atom);
|
||||
|
||||
js::HeapPtrScript &script() const {
|
||||
JS_ASSERT(isInterpreted());
|
||||
return *(js::HeapPtrScript *)&u.i.script_;
|
||||
|
@ -221,10 +199,6 @@ struct JSFunction : public JSObject_Slots2
|
|||
return offsetof(JSFunction, u.nativeOrScript);
|
||||
}
|
||||
|
||||
/* Number of extra fixed function object slots. */
|
||||
static const uint32 CLASS_RESERVED_SLOTS = JSObject::FUN_CLASS_RESERVED_SLOTS;
|
||||
|
||||
|
||||
js::Class *getConstructorClass() const {
|
||||
JS_ASSERT(isNative());
|
||||
return u.n.clasp;
|
||||
|
@ -234,228 +208,127 @@ struct JSFunction : public JSObject_Slots2
|
|||
JS_ASSERT(isNative());
|
||||
u.n.clasp = clasp;
|
||||
}
|
||||
|
||||
#if JS_BITS_PER_WORD == 32
|
||||
static const js::gc::AllocKind FinalizeKind = js::gc::FINALIZE_OBJECT2;
|
||||
static const js::gc::AllocKind ExtendedFinalizeKind = js::gc::FINALIZE_OBJECT4;
|
||||
#else
|
||||
static const js::gc::AllocKind FinalizeKind = js::gc::FINALIZE_OBJECT4;
|
||||
static const js::gc::AllocKind ExtendedFinalizeKind = js::gc::FINALIZE_OBJECT8;
|
||||
#endif
|
||||
|
||||
inline void trace(JSTracer *trc);
|
||||
|
||||
/* Bound function accessors. */
|
||||
|
||||
inline bool initBoundFunction(JSContext *cx, const js::Value &thisArg,
|
||||
const js::Value *args, uintN argslen);
|
||||
|
||||
inline JSObject *getBoundFunctionTarget() const;
|
||||
inline const js::Value &getBoundFunctionThis() const;
|
||||
inline const js::Value &getBoundFunctionArgument(uintN which) const;
|
||||
inline size_t getBoundFunctionArgumentCount() const;
|
||||
|
||||
private:
|
||||
inline js::FunctionExtended *toExtended();
|
||||
inline const js::FunctionExtended *toExtended() const;
|
||||
|
||||
inline bool isExtended() const {
|
||||
JS_STATIC_ASSERT(FinalizeKind != ExtendedFinalizeKind);
|
||||
JS_ASSERT(!!(flags & JSFUN_EXTENDED) == (getAllocKind() == ExtendedFinalizeKind));
|
||||
return !!(flags & JSFUN_EXTENDED);
|
||||
}
|
||||
|
||||
public:
|
||||
/* Accessors for data stored in extended functions. */
|
||||
|
||||
inline void initializeExtended();
|
||||
|
||||
inline void setExtendedSlot(size_t which, const js::Value &val);
|
||||
inline const js::Value &getExtendedSlot(size_t which) const;
|
||||
|
||||
/*
|
||||
* Flat closures with one or more upvars snapshot the upvars' values
|
||||
* into a vector of js::Values referenced from here. This is a private
|
||||
* pointer but is set only at creation and does not need to be barriered.
|
||||
*/
|
||||
static const uint32 FLAT_CLOSURE_UPVARS_SLOT = 0;
|
||||
|
||||
static inline size_t getFlatClosureUpvarsOffset();
|
||||
|
||||
inline js::Value getFlatClosureUpvar(uint32 i) const;
|
||||
inline void setFlatClosureUpvar(uint32 i, const js::Value &v);
|
||||
inline void initFlatClosureUpvar(uint32 i, const js::Value &v);
|
||||
|
||||
private:
|
||||
inline bool hasFlatClosureUpvars() const;
|
||||
inline js::HeapValue *getFlatClosureUpvars() const;
|
||||
public:
|
||||
|
||||
/* See comments in fun_finalize. */
|
||||
inline void finalizeUpvars();
|
||||
|
||||
/* Slot holding associated method property, needed for foo.caller handling. */
|
||||
static const uint32 METHOD_PROPERTY_SLOT = 0;
|
||||
|
||||
/* For cloned methods, slot holding the object this was cloned as a property from. */
|
||||
static const uint32 METHOD_OBJECT_SLOT = 1;
|
||||
|
||||
/* Whether this is a function cloned from a method. */
|
||||
inline bool isClonedMethod() const;
|
||||
|
||||
/* For a cloned method, pointer to the object the method was cloned for. */
|
||||
inline JSObject *methodObj() const;
|
||||
inline void setMethodObj(JSObject& obj);
|
||||
|
||||
/*
|
||||
* Method name imputed from property uniquely assigned to or initialized,
|
||||
* where the function does not need to be cloned to carry a scope chain or
|
||||
* flattened upvars. This is set on both the original and cloned function.
|
||||
*/
|
||||
inline JSAtom *methodAtom() const;
|
||||
inline void setMethodAtom(JSAtom *atom);
|
||||
};
|
||||
|
||||
inline JSFunction *
|
||||
JSObject::getFunctionPrivate() const
|
||||
JSObject::toFunction()
|
||||
{
|
||||
JS_ASSERT(isFunction());
|
||||
return reinterpret_cast<JSFunction *>(getPrivate());
|
||||
JS_ASSERT(JS_ObjectIsFunction(NULL, this));
|
||||
return static_cast<JSFunction *>(this);
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
struct FlatClosureData {
|
||||
HeapValue upvars[1];
|
||||
};
|
||||
|
||||
static JS_ALWAYS_INLINE bool
|
||||
IsFunctionObject(const js::Value &v)
|
||||
inline const JSFunction *
|
||||
JSObject::toFunction() const
|
||||
{
|
||||
return v.isObject() && v.toObject().isFunction();
|
||||
JS_ASSERT(JS_ObjectIsFunction(NULL, const_cast<JSObject *>(this)));
|
||||
return static_cast<const JSFunction *>(this);
|
||||
}
|
||||
|
||||
static JS_ALWAYS_INLINE bool
|
||||
IsFunctionObject(const js::Value &v, JSObject **funobj)
|
||||
{
|
||||
return v.isObject() && (*funobj = &v.toObject())->isFunction();
|
||||
}
|
||||
|
||||
static JS_ALWAYS_INLINE bool
|
||||
IsFunctionObject(const js::Value &v, JSObject **funobj, JSFunction **fun)
|
||||
{
|
||||
bool b = IsFunctionObject(v, funobj);
|
||||
if (b)
|
||||
*fun = (*funobj)->getFunctionPrivate();
|
||||
return b;
|
||||
}
|
||||
|
||||
static JS_ALWAYS_INLINE bool
|
||||
IsFunctionObject(const js::Value &v, JSFunction **fun)
|
||||
{
|
||||
JSObject *funobj;
|
||||
return IsFunctionObject(v, &funobj, fun);
|
||||
}
|
||||
|
||||
static JS_ALWAYS_INLINE bool
|
||||
IsNativeFunction(const js::Value &v)
|
||||
{
|
||||
JSFunction *fun;
|
||||
return IsFunctionObject(v, &fun) && fun->isNative();
|
||||
}
|
||||
|
||||
static JS_ALWAYS_INLINE bool
|
||||
IsNativeFunction(const js::Value &v, JSFunction **fun)
|
||||
{
|
||||
return IsFunctionObject(v, fun) && (*fun)->isNative();
|
||||
}
|
||||
|
||||
static JS_ALWAYS_INLINE bool
|
||||
IsNativeFunction(const js::Value &v, JSNative native)
|
||||
{
|
||||
JSFunction *fun;
|
||||
return IsFunctionObject(v, &fun) && fun->maybeNative() == native;
|
||||
}
|
||||
|
||||
/*
|
||||
* When we have an object of a builtin class, we don't quite know what its
|
||||
* valueOf/toString methods are, since these methods may have been overwritten
|
||||
* or shadowed. However, we can still do better than the general case by
|
||||
* hard-coding the necessary properties for us to find the native we expect.
|
||||
*
|
||||
* TODO: a per-thread shape-based cache would be faster and simpler.
|
||||
*/
|
||||
static JS_ALWAYS_INLINE bool
|
||||
ClassMethodIsNative(JSContext *cx, JSObject *obj, Class *clasp, jsid methodid, JSNative native)
|
||||
{
|
||||
JS_ASSERT(obj->getClass() == clasp);
|
||||
|
||||
Value v;
|
||||
if (!HasDataProperty(cx, obj, methodid, &v)) {
|
||||
JSObject *proto = obj->getProto();
|
||||
if (!proto || proto->getClass() != clasp || !HasDataProperty(cx, proto, methodid, &v))
|
||||
return false;
|
||||
}
|
||||
|
||||
return js::IsNativeFunction(v, native);
|
||||
}
|
||||
|
||||
extern JS_ALWAYS_INLINE bool
|
||||
SameTraceType(const Value &lhs, const Value &rhs)
|
||||
{
|
||||
return SameType(lhs, rhs) &&
|
||||
(lhs.isPrimitive() ||
|
||||
lhs.toObject().isFunction() == rhs.toObject().isFunction());
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if this is a compiler-created internal function accessed by
|
||||
* its own object. Such a function object must not be accessible to script
|
||||
* or embedding code.
|
||||
*/
|
||||
inline bool
|
||||
IsInternalFunctionObject(JSObject *funobj)
|
||||
{
|
||||
JS_ASSERT(funobj->isFunction());
|
||||
JSFunction *fun = (JSFunction *) funobj->getPrivate();
|
||||
return funobj == fun && (fun->flags & JSFUN_LAMBDA) && !funobj->getParent();
|
||||
}
|
||||
|
||||
/* Valueified JS_IsConstructing. */
|
||||
static JS_ALWAYS_INLINE bool
|
||||
IsConstructing(const Value *vp)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
JSObject *callee = &JS_CALLEE(cx, vp).toObject();
|
||||
if (callee->isFunction()) {
|
||||
JSFunction *fun = callee->getFunctionPrivate();
|
||||
JS_ASSERT((fun->flags & JSFUN_CONSTRUCTOR) != 0);
|
||||
} else {
|
||||
JS_ASSERT(callee->getClass()->construct != NULL);
|
||||
}
|
||||
#endif
|
||||
return vp[1].isMagic();
|
||||
}
|
||||
|
||||
inline bool
|
||||
IsConstructing(CallReceiver call);
|
||||
|
||||
static JS_ALWAYS_INLINE bool
|
||||
IsConstructing_PossiblyWithGivenThisObject(const Value *vp, JSObject **ctorThis)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
JSObject *callee = &JS_CALLEE(cx, vp).toObject();
|
||||
if (callee->isFunction()) {
|
||||
JSFunction *fun = callee->getFunctionPrivate();
|
||||
JS_ASSERT((fun->flags & JSFUN_CONSTRUCTOR) != 0);
|
||||
} else {
|
||||
JS_ASSERT(callee->getClass()->construct != NULL);
|
||||
}
|
||||
#endif
|
||||
bool isCtor = vp[1].isMagic();
|
||||
if (isCtor)
|
||||
*ctorThis = vp[1].getMagicObjectOrNullPayload();
|
||||
return isCtor;
|
||||
}
|
||||
|
||||
inline const char *
|
||||
GetFunctionNameBytes(JSContext *cx, JSFunction *fun, JSAutoByteString *bytes)
|
||||
{
|
||||
if (fun->atom)
|
||||
return bytes->encode(cx, fun->atom);
|
||||
return js_anonymous_str;
|
||||
}
|
||||
|
||||
extern JSFunctionSpec function_methods[];
|
||||
|
||||
extern JSBool
|
||||
Function(JSContext *cx, uintN argc, Value *vp);
|
||||
|
||||
extern bool
|
||||
IsBuiltinFunctionConstructor(JSFunction *fun);
|
||||
|
||||
/*
|
||||
* Preconditions: funobj->isInterpreted() && !funobj->isFunctionPrototype() &&
|
||||
* !funobj->isBoundFunction(). This is sufficient to establish that funobj has
|
||||
* a non-configurable non-method .prototype data property, thought it might not
|
||||
* have been resolved yet, and its value could be anything.
|
||||
*
|
||||
* Return the shape of the .prototype property of funobj, resolving it if
|
||||
* needed. On error, return NULL.
|
||||
*
|
||||
* This is not safe to call on trace because it defines properties, which can
|
||||
* trigger lookups that could reenter.
|
||||
*/
|
||||
const Shape *
|
||||
LookupInterpretedFunctionPrototype(JSContext *cx, JSObject *funobj);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
extern JSString *
|
||||
fun_toStringHelper(JSContext *cx, JSObject *obj, uintN indent);
|
||||
|
||||
extern JSFunction *
|
||||
js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs,
|
||||
uintN flags, JSObject *parent, JSAtom *atom);
|
||||
uintN flags, JSObject *parent, JSAtom *atom,
|
||||
js::gc::AllocKind kind = JSFunction::FinalizeKind);
|
||||
|
||||
extern void
|
||||
js_FinalizeFunction(JSContext *cx, JSFunction *fun);
|
||||
|
||||
extern JSObject * JS_FASTCALL
|
||||
js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent,
|
||||
JSObject *proto);
|
||||
extern JSFunction * JS_FASTCALL
|
||||
js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent, JSObject *proto,
|
||||
js::gc::AllocKind kind = JSFunction::FinalizeKind);
|
||||
|
||||
inline JSObject *
|
||||
CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent,
|
||||
bool ignoreSingletonClone = false);
|
||||
|
||||
inline JSObject *
|
||||
CloneFunctionObject(JSContext *cx, JSFunction *fun)
|
||||
{
|
||||
/*
|
||||
* Variant which makes an exact clone of fun, preserving parent and proto.
|
||||
* Calling the above version CloneFunctionObject(cx, fun, fun->getParent())
|
||||
* is not equivalent: API clients, including XPConnect, can reparent
|
||||
* objects so that fun->getGlobal() != fun->getProto()->getGlobal().
|
||||
* See ReparentWrapperIfFound.
|
||||
*/
|
||||
JS_ASSERT(fun->getParent() && fun->getProto());
|
||||
|
||||
if (fun->hasSingletonType())
|
||||
return fun;
|
||||
|
||||
return js_CloneFunctionObject(cx, fun, fun->getParent(), fun->getProto());
|
||||
}
|
||||
|
||||
extern JSObject * JS_FASTCALL
|
||||
extern JSFunction * JS_FASTCALL
|
||||
js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain);
|
||||
|
||||
extern JSObject *
|
||||
extern JSFunction *
|
||||
js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen);
|
||||
|
||||
extern JSFunction *
|
||||
js_DefineFunction(JSContext *cx, JSObject *obj, jsid id, JSNative native,
|
||||
uintN nargs, uintN flags);
|
||||
uintN nargs, uintN flags,
|
||||
js::gc::AllocKind kind = JSFunction::FinalizeKind);
|
||||
|
||||
/*
|
||||
* Flags for js_ValueToFunction and js_ReportIsNotFunction.
|
||||
|
@ -466,9 +339,6 @@ js_DefineFunction(JSContext *cx, JSObject *obj, jsid id, JSNative native,
|
|||
extern JSFunction *
|
||||
js_ValueToFunction(JSContext *cx, const js::Value *vp, uintN flags);
|
||||
|
||||
extern JSObject *
|
||||
js_ValueToFunctionObject(JSContext *cx, js::Value *vp, uintN flags);
|
||||
|
||||
extern JSObject *
|
||||
js_ValueToCallableObject(JSContext *cx, js::Value *vp, uintN flags);
|
||||
|
||||
|
@ -511,8 +381,35 @@ SetCallVar(JSContext *cx, JSObject *obj, jsid id, JSBool strict, js::Value *vp);
|
|||
extern JSBool
|
||||
SetCallUpvar(JSContext *cx, JSObject *obj, jsid id, JSBool strict, js::Value *vp);
|
||||
|
||||
/*
|
||||
* Function extended with reserved slots for use by various kinds of functions.
|
||||
* Most functions do not have these extensions, but enough are that efficient
|
||||
* storage is required (no malloc'ed reserved slots).
|
||||
*/
|
||||
class FunctionExtended : public JSFunction
|
||||
{
|
||||
friend struct JSFunction;
|
||||
|
||||
/* Reserved slots available for storage by particular native functions. */
|
||||
HeapValue extendedSlots[2];
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
|
||||
inline js::FunctionExtended *
|
||||
JSFunction::toExtended()
|
||||
{
|
||||
JS_ASSERT(isExtended());
|
||||
return static_cast<js::FunctionExtended *>(this);
|
||||
}
|
||||
|
||||
inline const js::FunctionExtended *
|
||||
JSFunction::toExtended() const
|
||||
{
|
||||
JS_ASSERT(isExtended());
|
||||
return static_cast<const js::FunctionExtended *>(this);
|
||||
}
|
||||
|
||||
extern JSBool
|
||||
js_GetArgsValue(JSContext *cx, js::StackFrame *fp, js::Value *vp);
|
||||
|
||||
|
|
|
@ -45,58 +45,363 @@
|
|||
|
||||
#include "vm/GlobalObject.h"
|
||||
|
||||
inline bool
|
||||
js::IsConstructing(CallReceiver call)
|
||||
{
|
||||
return IsConstructing(call.base());
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSFunction::inStrictMode() const
|
||||
{
|
||||
return script()->strictModeCode;
|
||||
}
|
||||
|
||||
inline JSObject *
|
||||
JSFunction::environment() const
|
||||
{
|
||||
JS_ASSERT(isInterpreted());
|
||||
return u.i.env;
|
||||
}
|
||||
|
||||
inline void
|
||||
JSFunction::setEnvironment(JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(isInterpreted());
|
||||
u.i.env = obj;
|
||||
}
|
||||
|
||||
inline void
|
||||
JSFunction::initializeExtended()
|
||||
{
|
||||
JS_ASSERT(isExtended());
|
||||
|
||||
JS_ASSERT(js::ArrayLength(toExtended()->extendedSlots) == 2);
|
||||
toExtended()->extendedSlots[0].init(js::UndefinedValue());
|
||||
toExtended()->extendedSlots[1].init(js::UndefinedValue());
|
||||
}
|
||||
|
||||
inline void
|
||||
JSFunction::setJoinable()
|
||||
{
|
||||
JS_ASSERT(isInterpreted());
|
||||
setSlot(METHOD_ATOM_SLOT, js::NullValue());
|
||||
flags |= JSFUN_JOINABLE;
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSFunction::isClonedMethod() const
|
||||
{
|
||||
return joinable() && isExtended() && getExtendedSlot(METHOD_OBJECT_SLOT).isObject();
|
||||
}
|
||||
|
||||
inline JSAtom *
|
||||
JSFunction::methodAtom() const
|
||||
{
|
||||
return (joinable() && isExtended() && getExtendedSlot(METHOD_PROPERTY_SLOT).isString())
|
||||
? (JSAtom *) getExtendedSlot(METHOD_PROPERTY_SLOT).toString()
|
||||
: NULL;
|
||||
}
|
||||
|
||||
inline void
|
||||
JSFunction::setMethodAtom(JSAtom *atom)
|
||||
{
|
||||
JS_ASSERT(joinable());
|
||||
setSlot(METHOD_ATOM_SLOT, js::StringValue(atom));
|
||||
setExtendedSlot(METHOD_PROPERTY_SLOT, js::StringValue(atom));
|
||||
}
|
||||
|
||||
inline JSObject *
|
||||
JSFunction::methodObj() const
|
||||
{
|
||||
JS_ASSERT(joinable());
|
||||
return isClonedMethod() ? &getExtendedSlot(METHOD_OBJECT_SLOT).toObject() : NULL;
|
||||
}
|
||||
|
||||
inline void
|
||||
JSFunction::setMethodObj(JSObject& obj)
|
||||
{
|
||||
JS_ASSERT(joinable());
|
||||
setExtendedSlot(METHOD_OBJECT_SLOT, js::ObjectValue(obj));
|
||||
}
|
||||
|
||||
inline void
|
||||
JSFunction::setExtendedSlot(size_t which, const js::Value &val)
|
||||
{
|
||||
JS_ASSERT(which < js::ArrayLength(toExtended()->extendedSlots));
|
||||
toExtended()->extendedSlots[which] = val;
|
||||
}
|
||||
|
||||
inline const js::Value &
|
||||
JSFunction::getExtendedSlot(size_t which) const
|
||||
{
|
||||
JS_ASSERT(which < js::ArrayLength(toExtended()->extendedSlots));
|
||||
return toExtended()->extendedSlots[which];
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSFunction::hasFlatClosureUpvars() const
|
||||
{
|
||||
JS_ASSERT(isFlatClosure());
|
||||
return isExtended() && !getExtendedSlot(FLAT_CLOSURE_UPVARS_SLOT).isUndefined();
|
||||
}
|
||||
|
||||
inline js::HeapValue *
|
||||
JSFunction::getFlatClosureUpvars() const
|
||||
{
|
||||
JS_ASSERT(hasFlatClosureUpvars());
|
||||
return (js::HeapValue *) getExtendedSlot(FLAT_CLOSURE_UPVARS_SLOT).toPrivate();
|
||||
}
|
||||
|
||||
inline void
|
||||
JSFunction::finalizeUpvars()
|
||||
{
|
||||
/*
|
||||
* Cloned function objects may be flat closures with upvars to free.
|
||||
*
|
||||
* We must not access JSScript here that is stored in JSFunction. The
|
||||
* script can be finalized before the function or closure instances. So we
|
||||
* just check if JSSLOT_FLAT_CLOSURE_UPVARS holds a private value encoded
|
||||
* as a double. We must also ignore newborn closures that do not have the
|
||||
* private pointer set.
|
||||
*
|
||||
* FIXME bug 648320 - allocate upvars on the GC heap to avoid doing it
|
||||
* here explicitly.
|
||||
*/
|
||||
if (hasFlatClosureUpvars()) {
|
||||
js::HeapValue *upvars = getFlatClosureUpvars();
|
||||
js::Foreground::free_(upvars);
|
||||
}
|
||||
}
|
||||
|
||||
inline js::Value
|
||||
JSFunction::getFlatClosureUpvar(uint32 i) const
|
||||
{
|
||||
JS_ASSERT(hasFlatClosureUpvars());
|
||||
JS_ASSERT(script()->bindings.countUpvars() == script()->upvars()->length);
|
||||
JS_ASSERT(i < script()->bindings.countUpvars());
|
||||
return getFlatClosureUpvars()[i];
|
||||
}
|
||||
|
||||
inline void
|
||||
JSFunction::setFlatClosureUpvar(uint32 i, const js::Value &v)
|
||||
{
|
||||
JS_ASSERT(isFlatClosure());
|
||||
JS_ASSERT(script()->bindings.countUpvars() == script()->upvars()->length);
|
||||
JS_ASSERT(i < script()->bindings.countUpvars());
|
||||
getFlatClosureUpvars()[i] = v;
|
||||
}
|
||||
|
||||
inline void
|
||||
JSFunction::initFlatClosureUpvar(uint32 i, const js::Value &v)
|
||||
{
|
||||
JS_ASSERT(isFlatClosure());
|
||||
JS_ASSERT(script()->bindings.countUpvars() == script()->upvars()->length);
|
||||
JS_ASSERT(i < script()->bindings.countUpvars());
|
||||
getFlatClosureUpvars()[i].init(v);
|
||||
}
|
||||
|
||||
/* static */ inline size_t
|
||||
JSFunction::getFlatClosureUpvarsOffset()
|
||||
{
|
||||
return offsetof(js::FunctionExtended, extendedSlots[FLAT_CLOSURE_UPVARS_SLOT]);
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
static JS_ALWAYS_INLINE bool
|
||||
IsFunctionObject(const js::Value &v)
|
||||
{
|
||||
return v.isObject() && v.toObject().isFunction();
|
||||
}
|
||||
|
||||
static JS_ALWAYS_INLINE bool
|
||||
IsFunctionObject(const js::Value &v, JSFunction **fun)
|
||||
{
|
||||
if (v.isObject() && v.toObject().isFunction()) {
|
||||
*fun = v.toObject().toFunction();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static JS_ALWAYS_INLINE bool
|
||||
IsNativeFunction(const js::Value &v)
|
||||
{
|
||||
JSFunction *fun;
|
||||
return IsFunctionObject(v, &fun) && fun->isNative();
|
||||
}
|
||||
|
||||
static JS_ALWAYS_INLINE bool
|
||||
IsNativeFunction(const js::Value &v, JSFunction **fun)
|
||||
{
|
||||
return IsFunctionObject(v, fun) && (*fun)->isNative();
|
||||
}
|
||||
|
||||
static JS_ALWAYS_INLINE bool
|
||||
IsNativeFunction(const js::Value &v, JSNative native)
|
||||
{
|
||||
JSFunction *fun;
|
||||
return IsFunctionObject(v, &fun) && fun->maybeNative() == native;
|
||||
}
|
||||
|
||||
/*
|
||||
* When we have an object of a builtin class, we don't quite know what its
|
||||
* valueOf/toString methods are, since these methods may have been overwritten
|
||||
* or shadowed. However, we can still do better than the general case by
|
||||
* hard-coding the necessary properties for us to find the native we expect.
|
||||
*
|
||||
* TODO: a per-thread shape-based cache would be faster and simpler.
|
||||
*/
|
||||
static JS_ALWAYS_INLINE bool
|
||||
ClassMethodIsNative(JSContext *cx, JSObject *obj, Class *clasp, jsid methodid, JSNative native)
|
||||
{
|
||||
JS_ASSERT(obj->getClass() == clasp);
|
||||
|
||||
Value v;
|
||||
if (!HasDataProperty(cx, obj, methodid, &v)) {
|
||||
JSObject *proto = obj->getProto();
|
||||
if (!proto || proto->getClass() != clasp || !HasDataProperty(cx, proto, methodid, &v))
|
||||
return false;
|
||||
}
|
||||
|
||||
return js::IsNativeFunction(v, native);
|
||||
}
|
||||
|
||||
extern JS_ALWAYS_INLINE bool
|
||||
SameTraceType(const Value &lhs, const Value &rhs)
|
||||
{
|
||||
return SameType(lhs, rhs) &&
|
||||
(lhs.isPrimitive() ||
|
||||
lhs.toObject().isFunction() == rhs.toObject().isFunction());
|
||||
}
|
||||
|
||||
/* Valueified JS_IsConstructing. */
|
||||
static JS_ALWAYS_INLINE bool
|
||||
IsConstructing(const Value *vp)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
JSObject *callee = &JS_CALLEE(cx, vp).toObject();
|
||||
if (callee->isFunction()) {
|
||||
JSFunction *fun = callee->toFunction();
|
||||
JS_ASSERT((fun->flags & JSFUN_CONSTRUCTOR) != 0);
|
||||
} else {
|
||||
JS_ASSERT(callee->getClass()->construct != NULL);
|
||||
}
|
||||
#endif
|
||||
return vp[1].isMagic();
|
||||
}
|
||||
|
||||
inline bool
|
||||
IsConstructing(CallReceiver call)
|
||||
{
|
||||
return IsConstructing(call.base());
|
||||
}
|
||||
|
||||
static JS_ALWAYS_INLINE bool
|
||||
IsConstructing_PossiblyWithGivenThisObject(const Value *vp, JSObject **ctorThis)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
JSObject *callee = &JS_CALLEE(cx, vp).toObject();
|
||||
if (callee->isFunction()) {
|
||||
JSFunction *fun = callee->toFunction();
|
||||
JS_ASSERT((fun->flags & JSFUN_CONSTRUCTOR) != 0);
|
||||
} else {
|
||||
JS_ASSERT(callee->getClass()->construct != NULL);
|
||||
}
|
||||
#endif
|
||||
bool isCtor = vp[1].isMagic();
|
||||
if (isCtor)
|
||||
*ctorThis = vp[1].getMagicObjectOrNullPayload();
|
||||
return isCtor;
|
||||
}
|
||||
|
||||
inline const char *
|
||||
GetFunctionNameBytes(JSContext *cx, JSFunction *fun, JSAutoByteString *bytes)
|
||||
{
|
||||
if (fun->atom)
|
||||
return bytes->encode(cx, fun->atom);
|
||||
return js_anonymous_str;
|
||||
}
|
||||
|
||||
extern JSFunctionSpec function_methods[];
|
||||
|
||||
extern JSBool
|
||||
Function(JSContext *cx, uintN argc, Value *vp);
|
||||
|
||||
extern bool
|
||||
IsBuiltinFunctionConstructor(JSFunction *fun);
|
||||
|
||||
/*
|
||||
* Preconditions: funobj->isInterpreted() && !funobj->isFunctionPrototype() &&
|
||||
* !funobj->isBoundFunction(). This is sufficient to establish that funobj has
|
||||
* a non-configurable non-method .prototype data property, thought it might not
|
||||
* have been resolved yet, and its value could be anything.
|
||||
*
|
||||
* Return the shape of the .prototype property of funobj, resolving it if
|
||||
* needed. On error, return NULL.
|
||||
*
|
||||
* This is not safe to call on trace because it defines properties, which can
|
||||
* trigger lookups that could reenter.
|
||||
*/
|
||||
const Shape *
|
||||
LookupInterpretedFunctionPrototype(JSContext *cx, JSObject *funobj);
|
||||
|
||||
static inline JSObject *
|
||||
SkipScopeParent(JSObject *parent)
|
||||
{
|
||||
if (!parent)
|
||||
return NULL;
|
||||
while (parent->isInternalScope())
|
||||
parent = parent->scopeChain();
|
||||
return parent;
|
||||
}
|
||||
|
||||
inline JSFunction *
|
||||
CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent,
|
||||
bool ignoreSingletonClone /* = false */)
|
||||
gc::AllocKind kind = JSFunction::FinalizeKind)
|
||||
{
|
||||
JS_ASSERT(parent);
|
||||
JSObject *proto = parent->getGlobal()->getOrCreateFunctionPrototype(cx);
|
||||
if (!proto)
|
||||
return NULL;
|
||||
|
||||
return js_CloneFunctionObject(cx, fun, parent, proto, kind);
|
||||
}
|
||||
|
||||
inline JSFunction *
|
||||
CloneFunctionObjectIfNotSingleton(JSContext *cx, JSFunction *fun, JSObject *parent)
|
||||
{
|
||||
/*
|
||||
* For attempts to clone functions at a function definition opcode or from
|
||||
* a method barrier, don't perform the clone if the function has singleton
|
||||
* type. CloneFunctionObject was called pessimistically, and we need to
|
||||
* preserve the type's property that if it is singleton there is only a
|
||||
* single object with its type in existence.
|
||||
* type. This was called pessimistically, and we need to preserve the
|
||||
* type's property that if it is singleton there is only a single object
|
||||
* with its type in existence.
|
||||
*/
|
||||
if (ignoreSingletonClone && fun->hasSingletonType()) {
|
||||
JS_ASSERT(fun->getProto() == proto);
|
||||
fun->setParent(parent);
|
||||
if (fun->hasSingletonType()) {
|
||||
if (!fun->setParent(cx, SkipScopeParent(parent)))
|
||||
return NULL;
|
||||
fun->setEnvironment(parent);
|
||||
return fun;
|
||||
}
|
||||
|
||||
return js_CloneFunctionObject(cx, fun, parent, proto);
|
||||
return CloneFunctionObject(cx, fun, parent);
|
||||
}
|
||||
|
||||
inline JSFunction *
|
||||
CloneFunctionObject(JSContext *cx, JSFunction *fun)
|
||||
{
|
||||
/*
|
||||
* Variant which makes an exact clone of fun, preserving parent and proto.
|
||||
* Calling the above version CloneFunctionObject(cx, fun, fun->getParent())
|
||||
* is not equivalent: API clients, including XPConnect, can reparent
|
||||
* objects so that fun->getGlobal() != fun->getProto()->getGlobal().
|
||||
* See ReparentWrapperIfFound.
|
||||
*/
|
||||
JS_ASSERT(fun->getParent() && fun->getProto());
|
||||
|
||||
if (fun->hasSingletonType())
|
||||
return fun;
|
||||
|
||||
return js_CloneFunctionObject(cx, fun, fun->environment(), fun->getProto(),
|
||||
JSFunction::ExtendedFinalizeKind);
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
inline void
|
||||
JSFunction::setScript(JSScript *script_)
|
||||
{
|
||||
|
|
|
@ -143,9 +143,9 @@ const uint32 Arena::ThingSizes[] = {
|
|||
sizeof(JSObject_Slots12), /* FINALIZE_OBJECT12_BACKGROUND */
|
||||
sizeof(JSObject_Slots16), /* FINALIZE_OBJECT16 */
|
||||
sizeof(JSObject_Slots16), /* FINALIZE_OBJECT16_BACKGROUND */
|
||||
sizeof(JSFunction), /* FINALIZE_FUNCTION */
|
||||
sizeof(JSScript), /* FINALIZE_SCRIPT */
|
||||
sizeof(Shape), /* FINALIZE_SHAPE */
|
||||
sizeof(BaseShape), /* FINALIZE_BASE_SHAPE */
|
||||
sizeof(types::TypeObject), /* FINALIZE_TYPE_OBJECT */
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
sizeof(JSXML), /* FINALIZE_XML */
|
||||
|
@ -170,9 +170,9 @@ const uint32 Arena::FirstThingOffsets[] = {
|
|||
OFFSET(JSObject_Slots12), /* FINALIZE_OBJECT12_BACKGROUND */
|
||||
OFFSET(JSObject_Slots16), /* FINALIZE_OBJECT16 */
|
||||
OFFSET(JSObject_Slots16), /* FINALIZE_OBJECT16_BACKGROUND */
|
||||
OFFSET(JSFunction), /* FINALIZE_FUNCTION */
|
||||
OFFSET(JSScript), /* FINALIZE_SCRIPT */
|
||||
OFFSET(Shape), /* FINALIZE_SHAPE */
|
||||
OFFSET(BaseShape), /* FINALIZE_BASE_SHAPE */
|
||||
OFFSET(types::TypeObject), /* FINALIZE_TYPE_OBJECT */
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
OFFSET(JSXML), /* FINALIZE_XML */
|
||||
|
@ -258,7 +258,7 @@ Arena::staticAsserts()
|
|||
|
||||
template<typename T>
|
||||
inline bool
|
||||
Arena::finalize(JSContext *cx, AllocKind thingKind, size_t thingSize)
|
||||
Arena::finalize(JSContext *cx, AllocKind thingKind, size_t thingSize, bool background)
|
||||
{
|
||||
/* Enforce requirements on size of T. */
|
||||
JS_ASSERT(thingSize % Cell::CellSize == 0);
|
||||
|
@ -307,7 +307,7 @@ Arena::finalize(JSContext *cx, AllocKind thingKind, size_t thingSize)
|
|||
} else {
|
||||
if (!newFreeSpanStart)
|
||||
newFreeSpanStart = thing;
|
||||
t->finalize(cx);
|
||||
t->finalize(cx, background);
|
||||
JS_POISON(t, JS_FREE_PATTERN, thingSize);
|
||||
}
|
||||
}
|
||||
|
@ -342,7 +342,7 @@ Arena::finalize(JSContext *cx, AllocKind thingKind, size_t thingSize)
|
|||
|
||||
template<typename T>
|
||||
inline void
|
||||
FinalizeTypedArenas(JSContext *cx, ArenaLists::ArenaList *al, AllocKind thingKind)
|
||||
FinalizeTypedArenas(JSContext *cx, ArenaLists::ArenaList *al, AllocKind thingKind, bool background)
|
||||
{
|
||||
/*
|
||||
* Release empty arenas and move non-full arenas with some free things into
|
||||
|
@ -354,7 +354,7 @@ FinalizeTypedArenas(JSContext *cx, ArenaLists::ArenaList *al, AllocKind thingKin
|
|||
ArenaHeader **ap = &al->head;
|
||||
size_t thingSize = Arena::thingSize(thingKind);
|
||||
while (ArenaHeader *aheader = *ap) {
|
||||
bool allClear = aheader->getArena()->finalize<T>(cx, thingKind, thingSize);
|
||||
bool allClear = aheader->getArena()->finalize<T>(cx, thingKind, thingSize, background);
|
||||
if (allClear) {
|
||||
*ap = aheader->next;
|
||||
aheader->chunk()->releaseArena(aheader);
|
||||
|
@ -379,7 +379,7 @@ FinalizeTypedArenas(JSContext *cx, ArenaLists::ArenaList *al, AllocKind thingKin
|
|||
* after the al->head.
|
||||
*/
|
||||
static void
|
||||
FinalizeArenas(JSContext *cx, ArenaLists::ArenaList *al, AllocKind thingKind)
|
||||
FinalizeArenas(JSContext *cx, ArenaLists::ArenaList *al, AllocKind thingKind, bool background)
|
||||
{
|
||||
switch(thingKind) {
|
||||
case FINALIZE_OBJECT0:
|
||||
|
@ -394,31 +394,33 @@ FinalizeArenas(JSContext *cx, ArenaLists::ArenaList *al, AllocKind thingKind)
|
|||
case FINALIZE_OBJECT12_BACKGROUND:
|
||||
case FINALIZE_OBJECT16:
|
||||
case FINALIZE_OBJECT16_BACKGROUND:
|
||||
case FINALIZE_FUNCTION:
|
||||
FinalizeTypedArenas<JSObject>(cx, al, thingKind);
|
||||
FinalizeTypedArenas<JSObject>(cx, al, thingKind, background);
|
||||
break;
|
||||
case FINALIZE_SCRIPT:
|
||||
FinalizeTypedArenas<JSScript>(cx, al, thingKind);
|
||||
FinalizeTypedArenas<JSScript>(cx, al, thingKind, background);
|
||||
break;
|
||||
case FINALIZE_SHAPE:
|
||||
FinalizeTypedArenas<Shape>(cx, al, thingKind);
|
||||
FinalizeTypedArenas<Shape>(cx, al, thingKind, background);
|
||||
break;
|
||||
case FINALIZE_BASE_SHAPE:
|
||||
FinalizeTypedArenas<BaseShape>(cx, al, thingKind, background);
|
||||
break;
|
||||
case FINALIZE_TYPE_OBJECT:
|
||||
FinalizeTypedArenas<types::TypeObject>(cx, al, thingKind);
|
||||
FinalizeTypedArenas<types::TypeObject>(cx, al, thingKind, background);
|
||||
break;
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
case FINALIZE_XML:
|
||||
FinalizeTypedArenas<JSXML>(cx, al, thingKind);
|
||||
FinalizeTypedArenas<JSXML>(cx, al, thingKind, background);
|
||||
break;
|
||||
#endif
|
||||
case FINALIZE_STRING:
|
||||
FinalizeTypedArenas<JSString>(cx, al, thingKind);
|
||||
FinalizeTypedArenas<JSString>(cx, al, thingKind, background);
|
||||
break;
|
||||
case FINALIZE_SHORT_STRING:
|
||||
FinalizeTypedArenas<JSShortString>(cx, al, thingKind);
|
||||
FinalizeTypedArenas<JSShortString>(cx, al, thingKind, background);
|
||||
break;
|
||||
case FINALIZE_EXTERNAL_STRING:
|
||||
FinalizeTypedArenas<JSExternalString>(cx, al, thingKind);
|
||||
FinalizeTypedArenas<JSExternalString>(cx, al, thingKind, background);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1458,7 +1460,7 @@ ArenaLists::finalizeNow(JSContext *cx, AllocKind thingKind)
|
|||
#ifdef JS_THREADSAFE
|
||||
JS_ASSERT(backgroundFinalizeState[thingKind] == BFS_DONE);
|
||||
#endif
|
||||
FinalizeArenas(cx, &arenaLists[thingKind], thingKind);
|
||||
FinalizeArenas(cx, &arenaLists[thingKind], thingKind, false);
|
||||
}
|
||||
|
||||
inline void
|
||||
|
@ -1470,7 +1472,6 @@ ArenaLists::finalizeLater(JSContext *cx, AllocKind thingKind)
|
|||
thingKind == FINALIZE_OBJECT8_BACKGROUND ||
|
||||
thingKind == FINALIZE_OBJECT12_BACKGROUND ||
|
||||
thingKind == FINALIZE_OBJECT16_BACKGROUND ||
|
||||
thingKind == FINALIZE_FUNCTION ||
|
||||
thingKind == FINALIZE_SHORT_STRING ||
|
||||
thingKind == FINALIZE_STRING);
|
||||
|
||||
|
@ -1501,7 +1502,7 @@ ArenaLists::finalizeLater(JSContext *cx, AllocKind thingKind)
|
|||
al->clear();
|
||||
backgroundFinalizeState[thingKind] = BFS_RUN;
|
||||
} else {
|
||||
FinalizeArenas(cx, al, thingKind);
|
||||
FinalizeArenas(cx, al, thingKind, false);
|
||||
backgroundFinalizeState[thingKind] = BFS_DONE;
|
||||
}
|
||||
|
||||
|
@ -1521,7 +1522,7 @@ ArenaLists::backgroundFinalize(JSContext *cx, ArenaHeader *listHead)
|
|||
JSCompartment *comp = listHead->compartment;
|
||||
ArenaList finalized;
|
||||
finalized.head = listHead;
|
||||
FinalizeArenas(cx, &finalized, thingKind);
|
||||
FinalizeArenas(cx, &finalized, thingKind, true);
|
||||
|
||||
/*
|
||||
* After we finish the finalization al->cursor must point to the end of
|
||||
|
@ -1574,13 +1575,6 @@ ArenaLists::finalizeObjects(JSContext *cx)
|
|||
finalizeLater(cx, FINALIZE_OBJECT16_BACKGROUND);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We must finalize Function instances after finalizing any other objects
|
||||
* even if we use the background finalization for the latter. See comments
|
||||
* in JSObject::finalizeUpvarsIfFlatClosure.
|
||||
*/
|
||||
finalizeLater(cx, FINALIZE_FUNCTION);
|
||||
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
finalizeNow(cx, FINALIZE_XML);
|
||||
#endif
|
||||
|
@ -1599,6 +1593,7 @@ void
|
|||
ArenaLists::finalizeShapes(JSContext *cx)
|
||||
{
|
||||
finalizeNow(cx, FINALIZE_SHAPE);
|
||||
finalizeNow(cx, FINALIZE_BASE_SHAPE);
|
||||
finalizeNow(cx, FINALIZE_TYPE_OBJECT);
|
||||
}
|
||||
|
||||
|
@ -2481,18 +2476,6 @@ BeginMarkPhase(JSContext *cx, GCMarker *gcmarker, JSGCInvocationKind gckind)
|
|||
{
|
||||
JSRuntime *rt = cx->runtime;
|
||||
|
||||
/*
|
||||
* Reset the property cache's type id generator so we can compress ids.
|
||||
* Same for the protoHazardShape proxy-shape standing in for all object
|
||||
* prototypes having readonly or setter properties.
|
||||
*/
|
||||
if (rt->shapeGen & SHAPE_OVERFLOW_BIT || (rt->gcZeal() && !rt->gcCurrentCompartment)) {
|
||||
JS_ASSERT(!rt->gcCurrentCompartment);
|
||||
rt->gcRegenShapes = true;
|
||||
rt->shapeGen = 0;
|
||||
rt->protoHazardShape = 0;
|
||||
}
|
||||
|
||||
for (GCCompartmentsIter c(rt); !c.done(); c.next())
|
||||
c->purge(cx);
|
||||
|
||||
|
@ -2942,7 +2925,6 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
|
|||
#endif
|
||||
|
||||
rt->gcMarkAndSweep = false;
|
||||
rt->gcRegenShapes = false;
|
||||
rt->setGCLastBytes(rt->gcBytes, gckind);
|
||||
rt->gcCurrentCompartment = NULL;
|
||||
|
||||
|
|
|
@ -559,7 +559,7 @@ struct Arena {
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
bool finalize(JSContext *cx, AllocKind thingKind, size_t thingSize);
|
||||
bool finalize(JSContext *cx, AllocKind thingKind, size_t thingSize, bool background);
|
||||
};
|
||||
|
||||
/* The chunk header (located at the end of the chunk to preserve arena alignment). */
|
||||
|
@ -952,9 +952,9 @@ MapAllocToTraceKind(AllocKind thingKind)
|
|||
JSTRACE_OBJECT, /* FINALIZE_OBJECT12_BACKGROUND */
|
||||
JSTRACE_OBJECT, /* FINALIZE_OBJECT16 */
|
||||
JSTRACE_OBJECT, /* FINALIZE_OBJECT16_BACKGROUND */
|
||||
JSTRACE_OBJECT, /* FINALIZE_FUNCTION */
|
||||
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,
|
||||
|
|
|
@ -68,22 +68,44 @@ const size_t SLOTS_TO_THING_KIND_LIMIT = 17;
|
|||
|
||||
/* Get the best kind to use when making an object with the given slot count. */
|
||||
static inline AllocKind
|
||||
GetGCObjectKind(size_t numSlots, bool isArray = false)
|
||||
GetGCObjectKind(size_t numSlots)
|
||||
{
|
||||
extern AllocKind slotsToThingKind[];
|
||||
|
||||
if (numSlots >= SLOTS_TO_THING_KIND_LIMIT) {
|
||||
/*
|
||||
* If the object will definitely want more than the maximum number of
|
||||
* fixed slots, use zero fixed slots for arrays and the maximum for
|
||||
* other objects. Arrays do not use their fixed slots anymore when
|
||||
* they have a slots array, while other objects will continue to do so.
|
||||
*/
|
||||
return isArray ? FINALIZE_OBJECT0 : FINALIZE_OBJECT16;
|
||||
}
|
||||
if (numSlots >= SLOTS_TO_THING_KIND_LIMIT)
|
||||
return FINALIZE_OBJECT16;
|
||||
return slotsToThingKind[numSlots];
|
||||
}
|
||||
|
||||
static inline AllocKind
|
||||
GetGCObjectKind(Class *clasp)
|
||||
{
|
||||
if (clasp == &FunctionClass)
|
||||
return JSFunction::FinalizeKind;
|
||||
uint32 nslots = JSCLASS_RESERVED_SLOTS(clasp);
|
||||
if (clasp->flags & JSCLASS_HAS_PRIVATE)
|
||||
nslots++;
|
||||
return GetGCObjectKind(nslots);
|
||||
}
|
||||
|
||||
/* As for GetGCObjectKind, but for dense array allocation. */
|
||||
static inline AllocKind
|
||||
GetGCArrayKind(size_t numSlots)
|
||||
{
|
||||
extern AllocKind slotsToThingKind[];
|
||||
|
||||
/*
|
||||
* Dense arrays can use their fixed slots to hold their elements array
|
||||
* (less two Values worth of ObjectElements header), but if more than the
|
||||
* maximum number of fixed slots is needed then the fixed slots will be
|
||||
* unused.
|
||||
*/
|
||||
JS_STATIC_ASSERT(ObjectElements::VALUES_PER_HEADER == 2);
|
||||
if (numSlots > JSObject::NELEMENTS_LIMIT || numSlots + 2 >= SLOTS_TO_THING_KIND_LIMIT)
|
||||
return FINALIZE_OBJECT2;
|
||||
return slotsToThingKind[numSlots + 2];
|
||||
}
|
||||
|
||||
static inline AllocKind
|
||||
GetGCObjectFixedSlotsKind(size_t numFixedSlots)
|
||||
{
|
||||
|
@ -151,6 +173,27 @@ GetGCKindSlots(AllocKind thingKind)
|
|||
}
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
GetGCKindSlots(AllocKind thingKind, Class *clasp)
|
||||
{
|
||||
size_t nslots = GetGCKindSlots(thingKind);
|
||||
|
||||
/* An object's private data uses the space taken by its last fixed slot. */
|
||||
if (clasp->flags & JSCLASS_HAS_PRIVATE) {
|
||||
JS_ASSERT(nslots > 0);
|
||||
nslots--;
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions have a larger finalize kind than FINALIZE_OBJECT to reserve
|
||||
* space for the extra fields in JSFunction, but have no fixed slots.
|
||||
*/
|
||||
if (clasp == &FunctionClass)
|
||||
nslots = 0;
|
||||
|
||||
return nslots;
|
||||
}
|
||||
|
||||
static inline void
|
||||
GCPoke(JSContext *cx, Value oldval)
|
||||
{
|
||||
|
@ -324,9 +367,6 @@ class CellIter: public CellIterImpl
|
|||
inline void EmptyArenaOp(Arena *arena) {}
|
||||
inline void EmptyCellOp(Cell *t) {}
|
||||
|
||||
} /* namespace gc */
|
||||
} /* namespace js */
|
||||
|
||||
/*
|
||||
* Allocates a new GC thing. After a successful allocation the caller must
|
||||
* fully initialize the thing before calling any function that can potentially
|
||||
|
@ -358,54 +398,80 @@ NewGCThing(JSContext *cx, js::gc::AllocKind kind, size_t thingSize)
|
|||
return static_cast<T *>(t);
|
||||
}
|
||||
|
||||
/* Alternate form which allocates a GC thing if doing so cannot trigger a GC. */
|
||||
template <typename T>
|
||||
inline T *
|
||||
TryNewGCThing(JSContext *cx, js::gc::AllocKind kind, size_t thingSize)
|
||||
{
|
||||
JS_ASSERT(thingSize == js::gc::Arena::thingSize(kind));
|
||||
#ifdef JS_THREADSAFE
|
||||
JS_ASSERT_IF((cx->compartment == cx->runtime->atomsCompartment),
|
||||
kind == js::gc::FINALIZE_STRING || kind == js::gc::FINALIZE_SHORT_STRING);
|
||||
#endif
|
||||
JS_ASSERT(!cx->runtime->gcRunning);
|
||||
JS_ASSERT(!JS_THREAD_DATA(cx)->noGCOrAllocationCheck);
|
||||
|
||||
#ifdef JS_GC_ZEAL
|
||||
if (cx->runtime->needZealousGC())
|
||||
return NULL;
|
||||
#endif
|
||||
|
||||
void *t = cx->compartment->arenas.allocateFromFreeList(kind, thingSize);
|
||||
return static_cast<T *>(t);
|
||||
}
|
||||
|
||||
} /* namespace gc */
|
||||
} /* namespace js */
|
||||
|
||||
inline JSObject *
|
||||
js_NewGCObject(JSContext *cx, js::gc::AllocKind kind)
|
||||
{
|
||||
JS_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_OBJECT_LAST);
|
||||
JSObject *obj = NewGCThing<JSObject>(cx, kind, js::gc::Arena::thingSize(kind));
|
||||
if (obj)
|
||||
obj->earlyInit(js::gc::GetGCKindSlots(kind));
|
||||
return obj;
|
||||
return js::gc::NewGCThing<JSObject>(cx, kind, js::gc::Arena::thingSize(kind));
|
||||
}
|
||||
|
||||
inline JSObject *
|
||||
js_TryNewGCObject(JSContext *cx, js::gc::AllocKind kind)
|
||||
{
|
||||
JS_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_OBJECT_LAST);
|
||||
return js::gc::TryNewGCThing<JSObject>(cx, kind, js::gc::Arena::thingSize(kind));
|
||||
}
|
||||
|
||||
inline JSString *
|
||||
js_NewGCString(JSContext *cx)
|
||||
{
|
||||
return NewGCThing<JSString>(cx, js::gc::FINALIZE_STRING, sizeof(JSString));
|
||||
return js::gc::NewGCThing<JSString>(cx, js::gc::FINALIZE_STRING, sizeof(JSString));
|
||||
}
|
||||
|
||||
inline JSShortString *
|
||||
js_NewGCShortString(JSContext *cx)
|
||||
{
|
||||
return NewGCThing<JSShortString>(cx, js::gc::FINALIZE_SHORT_STRING, sizeof(JSShortString));
|
||||
return js::gc::NewGCThing<JSShortString>(cx, js::gc::FINALIZE_SHORT_STRING, sizeof(JSShortString));
|
||||
}
|
||||
|
||||
inline JSExternalString *
|
||||
js_NewGCExternalString(JSContext *cx)
|
||||
{
|
||||
return NewGCThing<JSExternalString>(cx, js::gc::FINALIZE_EXTERNAL_STRING,
|
||||
sizeof(JSExternalString));
|
||||
}
|
||||
|
||||
inline JSFunction*
|
||||
js_NewGCFunction(JSContext *cx)
|
||||
{
|
||||
JSFunction *fun = NewGCThing<JSFunction>(cx, js::gc::FINALIZE_FUNCTION, sizeof(JSFunction));
|
||||
if (fun)
|
||||
fun->earlyInit(JSObject::FUN_CLASS_RESERVED_SLOTS);
|
||||
return fun;
|
||||
return js::gc::NewGCThing<JSExternalString>(cx, js::gc::FINALIZE_EXTERNAL_STRING,
|
||||
sizeof(JSExternalString));
|
||||
}
|
||||
|
||||
inline JSScript *
|
||||
js_NewGCScript(JSContext *cx)
|
||||
{
|
||||
return NewGCThing<JSScript>(cx, js::gc::FINALIZE_SCRIPT, sizeof(JSScript));
|
||||
return js::gc::NewGCThing<JSScript>(cx, js::gc::FINALIZE_SCRIPT, sizeof(JSScript));
|
||||
}
|
||||
|
||||
inline js::Shape *
|
||||
js_NewGCShape(JSContext *cx)
|
||||
{
|
||||
return NewGCThing<js::Shape>(cx, js::gc::FINALIZE_SHAPE, sizeof(js::Shape));
|
||||
return js::gc::NewGCThing<js::Shape>(cx, js::gc::FINALIZE_SHAPE, sizeof(js::Shape));
|
||||
}
|
||||
|
||||
inline js::BaseShape *
|
||||
js_NewGCBaseShape(JSContext *cx)
|
||||
{
|
||||
return js::gc::NewGCThing<js::BaseShape>(cx, js::gc::FINALIZE_BASE_SHAPE, sizeof(js::BaseShape));
|
||||
}
|
||||
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
|
|
|
@ -230,14 +230,27 @@ MarkShape(JSTracer *trc, const MarkablePtr<const Shape> &shape, const char *name
|
|||
MarkShapeUnbarriered(trc, shape.value, name);
|
||||
}
|
||||
|
||||
void
|
||||
MarkBaseShapeUnbarriered(JSTracer *trc, BaseShape *base, const char *name)
|
||||
{
|
||||
JS_ASSERT(trc);
|
||||
JS_ASSERT(base);
|
||||
JS_SET_TRACING_NAME(trc, name);
|
||||
Mark(trc, base);
|
||||
}
|
||||
|
||||
void
|
||||
MarkBaseShape(JSTracer *trc, const MarkablePtr<BaseShape> &base, const char *name)
|
||||
{
|
||||
MarkBaseShapeUnbarriered(trc, base.value, name);
|
||||
}
|
||||
|
||||
void
|
||||
MarkTypeObjectUnbarriered(JSTracer *trc, types::TypeObject *type, const char *name)
|
||||
{
|
||||
JS_ASSERT(trc);
|
||||
JS_ASSERT(type);
|
||||
JS_SET_TRACING_NAME(trc, name);
|
||||
if (type == &types::emptyTypeObject)
|
||||
return;
|
||||
Mark(trc, type);
|
||||
|
||||
/*
|
||||
|
@ -247,7 +260,7 @@ MarkTypeObjectUnbarriered(JSTracer *trc, types::TypeObject *type, const char *na
|
|||
* members, and we don't need to handle them here.
|
||||
*/
|
||||
if (IS_GC_MARKING_TRACER(trc)) {
|
||||
if (type->singleton)
|
||||
if (type->singleton && !type->lazy())
|
||||
MarkObject(trc, type->singleton, "type_singleton");
|
||||
if (type->interpretedFunction)
|
||||
MarkObject(trc, type->interpretedFunction, "type_function");
|
||||
|
@ -349,6 +362,20 @@ PushMarkStack(GCMarker *gcmarker, const Shape *thing)
|
|||
ScanShape(gcmarker, thing);
|
||||
}
|
||||
|
||||
static void
|
||||
ScanBaseShape(GCMarker *gcmarker, BaseShape *base);
|
||||
|
||||
void
|
||||
PushMarkStack(GCMarker *gcmarker, BaseShape *thing)
|
||||
{
|
||||
JS_OPT_ASSERT_IF(gcmarker->context->runtime->gcCurrentCompartment,
|
||||
thing->compartment() == gcmarker->context->runtime->gcCurrentCompartment);
|
||||
|
||||
/* We mark base shapes directly rather than pushing on the stack. */
|
||||
if (thing->markIfUnmarked(gcmarker->getMarkColor()))
|
||||
ScanBaseShape(gcmarker, thing);
|
||||
}
|
||||
|
||||
static void
|
||||
MarkAtomRange(JSTracer *trc, size_t len, JSAtom **vec, const char *name)
|
||||
{
|
||||
|
@ -447,6 +474,9 @@ MarkKind(JSTracer *trc, void *thing, JSGCTraceKind kind)
|
|||
case JSTRACE_SHAPE:
|
||||
Mark(trc, reinterpret_cast<Shape *>(thing));
|
||||
break;
|
||||
case JSTRACE_BASE_SHAPE:
|
||||
Mark(trc, reinterpret_cast<BaseShape *>(thing));
|
||||
break;
|
||||
case JSTRACE_TYPE_OBJECT:
|
||||
MarkTypeObjectUnbarriered(trc, reinterpret_cast<types::TypeObject *>(thing), "type_stack");
|
||||
break;
|
||||
|
@ -665,17 +695,19 @@ PrintPropertyGetterOrSetter(JSTracer *trc, char *buf, size_t bufsize)
|
|||
{
|
||||
JS_ASSERT(trc->debugPrinter == PrintPropertyGetterOrSetter);
|
||||
Shape *shape = (Shape *)trc->debugPrintArg;
|
||||
PrintPropertyId(buf, bufsize, shape->propid,
|
||||
PrintPropertyId(buf, bufsize, shape->propid(),
|
||||
trc->debugPrintIndex ? js_setter_str : js_getter_str);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static void
|
||||
PrintPropertyMethod(JSTracer *trc, char *buf, size_t bufsize)
|
||||
{
|
||||
JS_ASSERT(trc->debugPrinter == PrintPropertyMethod);
|
||||
Shape *shape = (Shape *)trc->debugPrintArg;
|
||||
PrintPropertyId(buf, bufsize, shape->propid, " method");
|
||||
PrintPropertyId(buf, bufsize, shape->propid(), " method");
|
||||
}
|
||||
#endif /* DEBUG */
|
||||
|
||||
static inline void
|
||||
ScanValue(GCMarker *gcmarker, const Value &v)
|
||||
|
@ -695,28 +727,35 @@ static void
|
|||
ScanShape(GCMarker *gcmarker, const Shape *shape)
|
||||
{
|
||||
restart:
|
||||
JSRuntime *rt = gcmarker->runtime;
|
||||
if (rt->gcRegenShapes)
|
||||
shape->shapeid = js_RegenerateShapeForGC(rt);
|
||||
PushMarkStack(gcmarker, shape->base());
|
||||
|
||||
if (JSID_IS_STRING(shape->propid))
|
||||
PushMarkStack(gcmarker, JSID_TO_STRING(shape->propid));
|
||||
else if (JS_UNLIKELY(JSID_IS_OBJECT(shape->propid)))
|
||||
PushMarkStack(gcmarker, JSID_TO_OBJECT(shape->propid));
|
||||
|
||||
if (shape->hasGetterValue() && shape->getter())
|
||||
PushMarkStack(gcmarker, shape->getterObject());
|
||||
if (shape->hasSetterValue() && shape->setter())
|
||||
PushMarkStack(gcmarker, shape->setterObject());
|
||||
|
||||
if (shape->isMethod())
|
||||
PushMarkStack(gcmarker, &shape->methodObject());
|
||||
jsid id = shape->maybePropid();
|
||||
if (JSID_IS_STRING(id))
|
||||
PushMarkStack(gcmarker, JSID_TO_STRING(id));
|
||||
else if (JS_UNLIKELY(JSID_IS_OBJECT(id)))
|
||||
PushMarkStack(gcmarker, JSID_TO_OBJECT(id));
|
||||
|
||||
shape = shape->previous();
|
||||
if (shape && shape->markIfUnmarked(gcmarker->getMarkColor()))
|
||||
goto restart;
|
||||
}
|
||||
|
||||
static void
|
||||
ScanBaseShape(GCMarker *gcmarker, BaseShape *base)
|
||||
{
|
||||
if (base->hasGetterObject())
|
||||
PushMarkStack(gcmarker, base->getterObject());
|
||||
|
||||
if (base->hasSetterObject())
|
||||
PushMarkStack(gcmarker, base->setterObject());
|
||||
|
||||
if (base->isOwned())
|
||||
PushMarkStack(gcmarker, base->baseUnowned());
|
||||
|
||||
if (JSObject *parent = base->getObjectParent())
|
||||
PushMarkStack(gcmarker, parent);
|
||||
}
|
||||
|
||||
static inline void
|
||||
ScanRope(GCMarker *gcmarker, JSRope *rope)
|
||||
{
|
||||
|
@ -762,21 +801,14 @@ static const uintN LARGE_OBJECT_CHUNK_SIZE = 2048;
|
|||
static void
|
||||
ScanObject(GCMarker *gcmarker, JSObject *obj)
|
||||
{
|
||||
if (obj->isNewborn())
|
||||
return;
|
||||
|
||||
types::TypeObject *type = obj->typeFromGC();
|
||||
if (type != &types::emptyTypeObject)
|
||||
PushMarkStack(gcmarker, type);
|
||||
PushMarkStack(gcmarker, type);
|
||||
|
||||
if (JSObject *parent = obj->getParent())
|
||||
PushMarkStack(gcmarker, parent);
|
||||
js::Shape *shape = obj->lastProperty();
|
||||
PushMarkStack(gcmarker, shape);
|
||||
|
||||
/*
|
||||
* Call the trace hook if necessary, and check for a newType on objects
|
||||
* which are not dense arrays (dense arrays have trace hooks).
|
||||
*/
|
||||
Class *clasp = obj->getClass();
|
||||
/* Call the trace hook if necessary. */
|
||||
Class *clasp = shape->getObjectClass();
|
||||
if (clasp->trace) {
|
||||
if (clasp == &ArrayClass) {
|
||||
if (obj->getDenseArrayInitializedLength() > LARGE_OBJECT_CHUNK_SIZE) {
|
||||
|
@ -786,31 +818,12 @@ ScanObject(GCMarker *gcmarker, JSObject *obj)
|
|||
clasp->trace(gcmarker, obj);
|
||||
}
|
||||
} else {
|
||||
if (obj->newType)
|
||||
PushMarkStack(gcmarker, obj->newType);
|
||||
clasp->trace(gcmarker, obj);
|
||||
}
|
||||
} else {
|
||||
if (obj->newType)
|
||||
PushMarkStack(gcmarker, obj->newType);
|
||||
}
|
||||
|
||||
if (obj->isNative()) {
|
||||
js::Shape *shape = obj->lastProp;
|
||||
PushMarkStack(gcmarker, shape);
|
||||
|
||||
if (gcmarker->runtime->gcRegenShapes) {
|
||||
/* We need to regenerate our shape if hasOwnShape(). */
|
||||
uint32 newShape = shape->shapeid;
|
||||
if (obj->hasOwnShape()) {
|
||||
newShape = js_RegenerateShapeForGC(gcmarker->runtime);
|
||||
JS_ASSERT(newShape != shape->shapeid);
|
||||
}
|
||||
obj->objShape = newShape;
|
||||
}
|
||||
|
||||
if (shape->isNative()) {
|
||||
uint32 nslots = obj->slotSpan();
|
||||
JS_ASSERT(obj->slotSpan() <= obj->numSlots());
|
||||
if (nslots > LARGE_OBJECT_CHUNK_SIZE) {
|
||||
if (gcmarker->largeStack.push(LargeMarkItem(obj)))
|
||||
return;
|
||||
|
@ -851,26 +864,16 @@ ScanLargeObject(GCMarker *gcmarker, LargeMarkItem &item)
|
|||
void
|
||||
MarkChildren(JSTracer *trc, JSObject *obj)
|
||||
{
|
||||
/* If obj has no map, it must be a newborn. */
|
||||
if (obj->isNewborn())
|
||||
return;
|
||||
|
||||
MarkTypeObject(trc, obj->typeFromGC(), "type");
|
||||
|
||||
/* Trace universal (ops-independent) members. */
|
||||
if (!obj->isDenseArray() && obj->newType)
|
||||
MarkTypeObject(trc, obj->newType, "new_type");
|
||||
if (obj->parent)
|
||||
MarkObject(trc, obj->parent, "parent");
|
||||
Shape *shape = obj->lastProperty();
|
||||
MarkShapeUnbarriered(trc, shape, "shape");
|
||||
|
||||
Class *clasp = obj->getClass();
|
||||
Class *clasp = shape->getObjectClass();
|
||||
if (clasp->trace)
|
||||
clasp->trace(trc, obj);
|
||||
|
||||
if (obj->isNative()) {
|
||||
MarkShape(trc, obj->lastProp, "shape");
|
||||
|
||||
JS_ASSERT(obj->slotSpan() <= obj->numSlots());
|
||||
if (shape->isNative()) {
|
||||
uint32 nslots = obj->slotSpan();
|
||||
for (uint32 i = 0; i < nslots; i++) {
|
||||
JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i);
|
||||
|
@ -921,6 +924,9 @@ MarkChildren(JSTracer *trc, JSScript *script)
|
|||
MarkValueRange(trc, constarray->length, constarray->vector, "consts");
|
||||
}
|
||||
|
||||
if (script->function())
|
||||
MarkObjectUnbarriered(trc, script->function(), "function");
|
||||
|
||||
if (!script->isCachedEval && script->globalObject)
|
||||
MarkObject(trc, script->globalObject, "object");
|
||||
|
||||
|
@ -937,24 +943,34 @@ void
|
|||
MarkChildren(JSTracer *trc, const Shape *shape)
|
||||
{
|
||||
restart:
|
||||
MarkId(trc, shape->propid, "propid");
|
||||
|
||||
if (shape->hasGetterValue() && shape->getter())
|
||||
MarkObjectWithPrinterUnbarriered(trc, shape->getterObject(),
|
||||
PrintPropertyGetterOrSetter, shape, 0);
|
||||
if (shape->hasSetterValue() && shape->setter())
|
||||
MarkObjectWithPrinterUnbarriered(trc, shape->setterObject(),
|
||||
PrintPropertyGetterOrSetter, shape, 1);
|
||||
|
||||
if (shape->isMethod())
|
||||
MarkObjectWithPrinterUnbarriered(trc, &shape->methodObject(),
|
||||
PrintPropertyMethod, shape, 0);
|
||||
MarkBaseShapeUnbarriered(trc, shape->base(), "base");
|
||||
MarkIdUnbarriered(trc, shape->maybePropid(), "propid");
|
||||
|
||||
shape = shape->previous();
|
||||
if (shape)
|
||||
goto restart;
|
||||
}
|
||||
|
||||
void
|
||||
MarkChildren(JSTracer *trc, BaseShape *base)
|
||||
{
|
||||
if (base->hasGetterObject()) {
|
||||
MarkObjectWithPrinterUnbarriered(trc, base->getterObject(),
|
||||
PrintPropertyGetterOrSetter, base, 0);
|
||||
}
|
||||
|
||||
if (base->hasSetterObject()) {
|
||||
MarkObjectWithPrinterUnbarriered(trc, base->setterObject(),
|
||||
PrintPropertyGetterOrSetter, base, 0);
|
||||
}
|
||||
|
||||
if (base->isOwned())
|
||||
MarkBaseShapeUnbarriered(trc, base->baseUnowned(), "base");
|
||||
|
||||
if (JSObject *parent = base->getObjectParent())
|
||||
MarkObjectUnbarriered(trc, parent, "parent");
|
||||
}
|
||||
|
||||
static void
|
||||
ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type)
|
||||
{
|
||||
|
@ -967,13 +983,6 @@ ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type)
|
|||
}
|
||||
}
|
||||
|
||||
if (type->emptyShapes) {
|
||||
for (unsigned i = 0; i < FINALIZE_OBJECT_LIMIT; i++) {
|
||||
if (type->emptyShapes[i])
|
||||
PushMarkStack(gcmarker, type->emptyShapes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (type->proto)
|
||||
PushMarkStack(gcmarker, type->proto);
|
||||
|
||||
|
@ -982,12 +991,15 @@ ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type)
|
|||
PushMarkStack(gcmarker, type->newScript->shape);
|
||||
}
|
||||
|
||||
if (type->interpretedFunction)
|
||||
PushMarkStack(gcmarker, type->interpretedFunction);
|
||||
|
||||
/*
|
||||
* Don't need to trace singleton or functionScript, an object with this
|
||||
* type must have already been traced and it will also hold a reference
|
||||
* on the script (singleton and functionScript types cannot be the newType
|
||||
* of another object). Attempts to mark type objects directly must use
|
||||
* MarkTypeObject, which will itself mark these extra bits.
|
||||
* Don't need to trace singleton, an object with this type must have
|
||||
* already been traced and it will also hold a reference on the script
|
||||
* (singleton and functionScript types cannot be the newType of another
|
||||
* object). Attempts to mark type objects directly must use MarkTypeObject,
|
||||
* which will itself mark these extra bits.
|
||||
*/
|
||||
}
|
||||
|
||||
|
@ -1003,17 +1015,10 @@ MarkChildren(JSTracer *trc, types::TypeObject *type)
|
|||
}
|
||||
}
|
||||
|
||||
if (type->emptyShapes) {
|
||||
for (unsigned i = 0; i < FINALIZE_OBJECT_LIMIT; i++) {
|
||||
if (type->emptyShapes[i])
|
||||
MarkShape(trc, type->emptyShapes[i], "empty_shape");
|
||||
}
|
||||
}
|
||||
|
||||
if (type->proto)
|
||||
MarkObject(trc, type->proto, "type_proto");
|
||||
|
||||
if (type->singleton)
|
||||
if (type->singleton && !type->lazy())
|
||||
MarkObject(trc, type->singleton, "type_singleton");
|
||||
|
||||
if (type->newScript) {
|
||||
|
@ -1092,6 +1097,10 @@ TraceChildren(JSTracer *trc, void *thing, JSGCTraceKind kind)
|
|||
MarkChildren(trc, static_cast<Shape *>(thing));
|
||||
break;
|
||||
|
||||
case JSTRACE_BASE_SHAPE:
|
||||
MarkChildren(trc, static_cast<BaseShape *>(thing));
|
||||
break;
|
||||
|
||||
case JSTRACE_TYPE_OBJECT:
|
||||
MarkChildren(trc, (types::TypeObject *)thing);
|
||||
break;
|
||||
|
@ -1120,7 +1129,6 @@ JSObject::scanSlots(GCMarker *gcmarker)
|
|||
* Scan the fixed slots and the dynamic slots separately, to avoid
|
||||
* branching inside nativeGetSlot().
|
||||
*/
|
||||
JS_ASSERT(slotSpan() <= numSlots());
|
||||
unsigned i, nslots = slotSpan();
|
||||
if (slots) {
|
||||
unsigned nfixed = numFixedSlots();
|
||||
|
|
|
@ -81,6 +81,9 @@ MarkShapeUnbarriered(JSTracer *trc, const Shape *shape, const char *name);
|
|||
void
|
||||
MarkShape(JSTracer *trc, const MarkablePtr<const Shape> &shape, const char *name);
|
||||
|
||||
void
|
||||
MarkBaseShapeUnbarriered(JSTracer *trc, BaseShape *shape, const char *name);
|
||||
|
||||
void
|
||||
MarkTypeObjectUnbarriered(JSTracer *trc, types::TypeObject *type, const char *name);
|
||||
|
||||
|
|
|
@ -134,6 +134,10 @@ GCMarker::dumpConservativeRoots()
|
|||
fprintf(fp, "shape");
|
||||
break;
|
||||
}
|
||||
case JSTRACE_BASE_SHAPE: {
|
||||
fprintf(fp, "base_shape");
|
||||
break;
|
||||
}
|
||||
case JSTRACE_TYPE_OBJECT: {
|
||||
fprintf(fp, "type_object");
|
||||
break;
|
||||
|
|
|
@ -795,7 +795,7 @@ static inline const Shape *
|
|||
GetSingletonShape(JSContext *cx, JSObject *obj, jsid id)
|
||||
{
|
||||
const Shape *shape = obj->nativeLookup(cx, id);
|
||||
if (shape && shape->hasDefaultGetterOrIsMethod() && shape->slot != SHAPE_INVALID_SLOT)
|
||||
if (shape && shape->hasDefaultGetterOrIsMethod() && shape->hasSlot())
|
||||
return shape;
|
||||
return NULL;
|
||||
}
|
||||
|
@ -814,7 +814,7 @@ ScriptAnalysis::pruneTypeBarriers(JSContext *cx, uint32 offset)
|
|||
if (barrier->singleton) {
|
||||
JS_ASSERT(barrier->type.isPrimitive(JSVAL_TYPE_UNDEFINED));
|
||||
const Shape *shape = GetSingletonShape(cx, barrier->singleton, barrier->singletonId);
|
||||
if (shape && !barrier->singleton->nativeGetSlot(shape->slot).isUndefined()) {
|
||||
if (shape && !barrier->singleton->nativeGetSlot(shape->slot()).isUndefined()) {
|
||||
/*
|
||||
* When we analyzed the script the singleton had an 'own'
|
||||
* property which was undefined (probably a 'var' variable
|
||||
|
@ -1046,7 +1046,7 @@ PropertyAccess(JSContext *cx, JSScript *script, jsbytecode *pc, TypeObject *obje
|
|||
* even if no undefined value is ever observed at pc.
|
||||
*/
|
||||
const Shape *shape = GetSingletonShape(cx, object->singleton, id);
|
||||
if (shape && object->singleton->nativeGetSlot(shape->slot).isUndefined())
|
||||
if (shape && object->singleton->nativeGetSlot(shape->slot()).isUndefined())
|
||||
script->analysis()->addSingletonTypeBarrier(cx, pc, target, object->singleton, id);
|
||||
}
|
||||
} else {
|
||||
|
@ -1164,7 +1164,7 @@ TypeConstraintCall::newType(JSContext *cx, TypeSet *source, Type type)
|
|||
return;
|
||||
}
|
||||
|
||||
if (obj->getFunctionPrivate()->isNative()) {
|
||||
if (obj->toFunction()->isNative()) {
|
||||
/*
|
||||
* The return value and all side effects within native calls should
|
||||
* be dynamically monitored, except when the compiler is generating
|
||||
|
@ -1180,7 +1180,7 @@ TypeConstraintCall::newType(JSContext *cx, TypeSet *source, Type type)
|
|||
* which specializes particular natives.
|
||||
*/
|
||||
|
||||
Native native = obj->getFunctionPrivate()->native();
|
||||
Native native = obj->toFunction()->native();
|
||||
|
||||
if (native == js::array_push) {
|
||||
for (size_t i = 0; i < callsite->argumentCount; i++) {
|
||||
|
@ -1210,7 +1210,7 @@ TypeConstraintCall::newType(JSContext *cx, TypeSet *source, Type type)
|
|||
return;
|
||||
}
|
||||
|
||||
callee = obj->getFunctionPrivate();
|
||||
callee = obj->toFunction();
|
||||
} else if (type.isTypeObject()) {
|
||||
callee = type.typeObject()->interpretedFunction;
|
||||
if (!callee)
|
||||
|
@ -1220,7 +1220,7 @@ TypeConstraintCall::newType(JSContext *cx, TypeSet *source, Type type)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!callee->script()->ensureHasTypes(cx, callee))
|
||||
if (!callee->script()->ensureHasTypes(cx))
|
||||
return;
|
||||
|
||||
unsigned nargs = callee->nargs;
|
||||
|
@ -1283,9 +1283,9 @@ TypeConstraintPropagateThis::newType(JSContext *cx, TypeSet *source, Type type)
|
|||
|
||||
if (type.isSingleObject()) {
|
||||
JSObject *object = type.singleObject();
|
||||
if (!object->isFunction() || !object->getFunctionPrivate()->isInterpreted())
|
||||
if (!object->isFunction() || !object->toFunction()->isInterpreted())
|
||||
return;
|
||||
callee = object->getFunctionPrivate();
|
||||
callee = object->toFunction();
|
||||
} else if (type.isTypeObject()) {
|
||||
TypeObject *object = type.typeObject();
|
||||
if (!object->interpretedFunction)
|
||||
|
@ -1296,7 +1296,7 @@ TypeConstraintPropagateThis::newType(JSContext *cx, TypeSet *source, Type type)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!callee->script()->ensureHasTypes(cx, callee))
|
||||
if (!callee->script()->ensureHasTypes(cx))
|
||||
return;
|
||||
|
||||
TypeSet *thisTypes = TypeScript::ThisTypes(callee->script());
|
||||
|
@ -1676,7 +1676,7 @@ types::MarkArgumentsCreated(JSContext *cx, JSScript *script)
|
|||
mjit::ExpandInlineFrames(cx->compartment);
|
||||
#endif
|
||||
|
||||
if (!script->ensureRanAnalysis(cx))
|
||||
if (!script->ensureRanAnalysis(cx, NULL))
|
||||
return;
|
||||
|
||||
ScriptAnalysis *analysis = script->analysis();
|
||||
|
@ -1961,8 +1961,6 @@ TypeSet::needsBarrier(JSContext *cx)
|
|||
// TypeCompartment
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
TypeObject types::emptyTypeObject(NULL, false, true);
|
||||
|
||||
void
|
||||
TypeCompartment::init(JSContext *cx)
|
||||
{
|
||||
|
@ -1976,7 +1974,7 @@ TypeObject *
|
|||
TypeCompartment::newTypeObject(JSContext *cx, JSScript *script,
|
||||
JSProtoKey key, JSObject *proto, bool unknown)
|
||||
{
|
||||
TypeObject *object = NewGCThing<TypeObject>(cx, gc::FINALIZE_TYPE_OBJECT, sizeof(TypeObject));
|
||||
TypeObject *object = gc::NewGCThing<TypeObject>(cx, gc::FINALIZE_TYPE_OBJECT, sizeof(TypeObject));
|
||||
if (!object)
|
||||
return NULL;
|
||||
new(object) TypeObject(proto, key == JSProto_Function, unknown);
|
||||
|
@ -2270,7 +2268,7 @@ TypeCompartment::monitorBytecode(JSContext *cx, JSScript *script, uint32 offset,
|
|||
cx->compartment->types.addPendingRecompile(cx, script);
|
||||
|
||||
/* Trigger recompilation of any inline callers. */
|
||||
if (script->hasFunction && !script->function()->hasLazyType())
|
||||
if (script->function() && !script->function()->hasLazyType())
|
||||
ObjectStateChange(cx, script->function()->type(), false, true);
|
||||
}
|
||||
|
||||
|
@ -2365,7 +2363,7 @@ ScriptAnalysis::addTypeBarrier(JSContext *cx, const jsbytecode *pc, TypeSet *tar
|
|||
cx->compartment->types.addPendingRecompile(cx, script);
|
||||
|
||||
/* Trigger recompilation of any inline callers. */
|
||||
if (script->hasFunction && !script->function()->hasLazyType())
|
||||
if (script->function() && !script->function()->hasLazyType())
|
||||
ObjectStateChange(cx, script->function()->type(), false, true);
|
||||
}
|
||||
|
||||
|
@ -2398,7 +2396,7 @@ ScriptAnalysis::addSingletonTypeBarrier(JSContext *cx, const jsbytecode *pc, Typ
|
|||
if (!code.typeBarriers) {
|
||||
/* Trigger recompilation as for normal type barriers. */
|
||||
cx->compartment->types.addPendingRecompile(cx, script);
|
||||
if (script->hasFunction && !script->function()->hasLazyType())
|
||||
if (script->function() && !script->function()->hasLazyType())
|
||||
ObjectStateChange(cx, script->function()->type(), false, true);
|
||||
}
|
||||
|
||||
|
@ -2480,7 +2478,6 @@ GetValueTypeForTable(JSContext *cx, const Value &v)
|
|||
{
|
||||
Type type = GetValueType(cx, v);
|
||||
JS_ASSERT(!type.isSingleObject());
|
||||
JS_ASSERT_IF(type.isTypeObject(), type.typeObject() != &emptyTypeObject);
|
||||
return type;
|
||||
}
|
||||
|
||||
|
@ -2524,7 +2521,7 @@ TypeCompartment::fixArrayType(JSContext *cx, JSObject *obj)
|
|||
* If the array is heterogenous, keep the existing type object, which has
|
||||
* unknown properties.
|
||||
*/
|
||||
JS_ASSERT(obj->isPackedDenseArray());
|
||||
JS_ASSERT(obj->isDenseArray());
|
||||
|
||||
unsigned len = obj->getDenseArrayInitializedLength();
|
||||
if (len == 0)
|
||||
|
@ -2584,7 +2581,7 @@ struct types::ObjectTableKey
|
|||
typedef JSObject * Lookup;
|
||||
|
||||
static inline uint32 hash(JSObject *obj) {
|
||||
return (uint32) (JSID_BITS(obj->lastProperty()->propid.get()) ^
|
||||
return (uint32) (JSID_BITS(obj->lastProperty()->propid()) ^
|
||||
obj->slotSpan() ^ obj->numFixedSlots() ^
|
||||
((uint32)(size_t)obj->getProto() >> 2));
|
||||
}
|
||||
|
@ -2596,8 +2593,8 @@ struct types::ObjectTableKey
|
|||
return false;
|
||||
}
|
||||
const Shape *shape = obj->lastProperty();
|
||||
while (!JSID_IS_EMPTY(shape->propid)) {
|
||||
if (shape->propid != v.ids[shape->slot])
|
||||
while (!shape->isEmptyShape()) {
|
||||
if (shape->propid() != v.ids[shape->slot()])
|
||||
return false;
|
||||
shape = shape->previous();
|
||||
}
|
||||
|
@ -2649,11 +2646,11 @@ TypeCompartment::fixObjectType(JSContext *cx, JSObject *obj)
|
|||
if (types[i].isPrimitive(JSVAL_TYPE_INT32)) {
|
||||
types[i] = Type::DoubleType();
|
||||
const Shape *shape = baseShape;
|
||||
while (!JSID_IS_EMPTY(shape->propid)) {
|
||||
if (shape->slot == i) {
|
||||
while (!shape->isEmptyShape()) {
|
||||
if (shape->slot() == i) {
|
||||
Type type = Type::DoubleType();
|
||||
if (!p->value.object->unknownProperties()) {
|
||||
jsid id = MakeTypeId(cx, shape->propid);
|
||||
jsid id = MakeTypeId(cx, shape->propid());
|
||||
p->value.object->addPropertyType(cx, id, type);
|
||||
}
|
||||
break;
|
||||
|
@ -2689,12 +2686,12 @@ TypeCompartment::fixObjectType(JSContext *cx, JSObject *obj)
|
|||
}
|
||||
|
||||
const Shape *shape = baseShape;
|
||||
while (!JSID_IS_EMPTY(shape->propid)) {
|
||||
ids[shape->slot] = shape->propid;
|
||||
types[shape->slot] = GetValueTypeForTable(cx, obj->getSlot(shape->slot));
|
||||
while (!shape->isEmptyShape()) {
|
||||
ids[shape->slot()] = shape->propid();
|
||||
types[shape->slot()] = GetValueTypeForTable(cx, obj->getSlot(shape->slot()));
|
||||
if (!objType->unknownProperties()) {
|
||||
jsid id = MakeTypeId(cx, shape->propid);
|
||||
objType->addPropertyType(cx, id, types[shape->slot]);
|
||||
jsid id = MakeTypeId(cx, shape->propid());
|
||||
objType->addPropertyType(cx, id, types[shape->slot()]);
|
||||
}
|
||||
shape = shape->previous();
|
||||
}
|
||||
|
@ -2754,8 +2751,8 @@ UpdatePropertyType(JSContext *cx, TypeSet *types, JSObject *obj, const Shape *sh
|
|||
{
|
||||
if (shape->hasGetterValue() || shape->hasSetterValue()) {
|
||||
types->addType(cx, Type::UnknownType());
|
||||
} else if (shape->hasDefaultGetterOrIsMethod() && shape->slot != SHAPE_INVALID_SLOT) {
|
||||
const Value &value = obj->nativeGetSlot(shape->slot);
|
||||
} else if (shape->hasDefaultGetterOrIsMethod() && shape->hasSlot()) {
|
||||
const Value &value = obj->nativeGetSlot(shape->slot());
|
||||
|
||||
/*
|
||||
* Don't add initial undefined types for singleton properties that are
|
||||
|
@ -2791,12 +2788,12 @@ TypeObject::addProperty(JSContext *cx, jsid id, Property **pprop)
|
|||
if (JSID_IS_VOID(id)) {
|
||||
/* Go through all shapes on the object to get integer-valued properties. */
|
||||
const Shape *shape = singleton->lastProperty();
|
||||
while (!JSID_IS_EMPTY(shape->propid)) {
|
||||
if (JSID_IS_VOID(MakeTypeId(cx, shape->propid)))
|
||||
while (!shape->isEmptyShape()) {
|
||||
if (JSID_IS_VOID(MakeTypeId(cx, shape->propid())))
|
||||
UpdatePropertyType(cx, &base->types, singleton, shape, true);
|
||||
shape = shape->previous();
|
||||
}
|
||||
} else {
|
||||
} else if (!JSID_IS_EMPTY(id)) {
|
||||
const Shape *shape = singleton->nativeLookup(cx, id);
|
||||
if (shape)
|
||||
UpdatePropertyType(cx, &base->types, singleton, shape, false);
|
||||
|
@ -2830,14 +2827,14 @@ TypeObject::addDefiniteProperties(JSContext *cx, JSObject *obj)
|
|||
AutoEnterTypeInference enter(cx);
|
||||
|
||||
const Shape *shape = obj->lastProperty();
|
||||
while (!JSID_IS_EMPTY(shape->propid)) {
|
||||
jsid id = MakeTypeId(cx, shape->propid);
|
||||
if (!JSID_IS_VOID(id) && obj->isFixedSlot(shape->slot) &&
|
||||
shape->slot <= (TYPE_FLAG_DEFINITE_MASK >> TYPE_FLAG_DEFINITE_SHIFT)) {
|
||||
while (!shape->isEmptyShape()) {
|
||||
jsid id = MakeTypeId(cx, shape->propid());
|
||||
if (!JSID_IS_VOID(id) && obj->isFixedSlot(shape->slot()) &&
|
||||
shape->slot() <= (TYPE_FLAG_DEFINITE_MASK >> TYPE_FLAG_DEFINITE_SHIFT)) {
|
||||
TypeSet *types = getProperty(cx, id, true);
|
||||
if (!types)
|
||||
return false;
|
||||
types->setDefinite(shape->slot);
|
||||
types->setDefinite(shape->slot());
|
||||
}
|
||||
shape = shape->previous();
|
||||
}
|
||||
|
@ -2858,8 +2855,8 @@ TypeObject::matchDefiniteProperties(JSObject *obj)
|
|||
|
||||
bool found = false;
|
||||
const Shape *shape = obj->lastProperty();
|
||||
while (!JSID_IS_EMPTY(shape->propid)) {
|
||||
if (shape->slot == slot && shape->propid == prop->id) {
|
||||
while (!shape->isEmptyShape()) {
|
||||
if (shape->slot() == slot && shape->propid() == prop->id) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
@ -2970,7 +2967,7 @@ TypeObject::setFlags(JSContext *cx, TypeObjectFlags flags)
|
|||
JS_ASSERT_IF(flags & OBJECT_FLAG_REENTRANT_FUNCTION,
|
||||
interpretedFunction->script()->reentrantOuterFunction);
|
||||
JS_ASSERT_IF(flags & OBJECT_FLAG_ITERATED,
|
||||
singleton->flags & JSObject::ITERATED);
|
||||
singleton->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON));
|
||||
}
|
||||
|
||||
this->flags |= flags;
|
||||
|
@ -3258,7 +3255,7 @@ ScriptAnalysis::resolveNameAccess(JSContext *cx, jsid id, bool addDependency)
|
|||
JSAtom *atom = JSID_TO_ATOM(id);
|
||||
|
||||
JSScript *script = this->script;
|
||||
while (script->hasFunction && script->nesting()) {
|
||||
while (script->function() && script->nesting()) {
|
||||
if (!script->ensureRanInference(cx))
|
||||
return access;
|
||||
|
||||
|
@ -3407,7 +3404,6 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
|||
case JSOP_DEFAULT:
|
||||
case JSOP_DEFAULTX:
|
||||
case JSOP_POPN:
|
||||
case JSOP_UNBRANDTHIS:
|
||||
case JSOP_STARTXML:
|
||||
case JSOP_STARTXMLEXPR:
|
||||
case JSOP_DEFXMLNS:
|
||||
|
@ -3505,7 +3501,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
|||
|
||||
case JSOP_STOP:
|
||||
/* If a stop is reachable then the return type may be void. */
|
||||
if (script->hasFunction)
|
||||
if (script->function())
|
||||
TypeScript::ReturnTypes(script)->addType(cx, Type::UndefinedType());
|
||||
break;
|
||||
|
||||
|
@ -3788,7 +3784,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
|||
|
||||
case JSOP_RETURN:
|
||||
case JSOP_SETRVAL:
|
||||
if (script->hasFunction)
|
||||
if (script->function())
|
||||
poppedTypes(pc, 0)->addSubset(cx, TypeScript::ReturnTypes(script));
|
||||
break;
|
||||
|
||||
|
@ -4041,12 +4037,8 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
|||
poppedTypes(pc, 1)->addSubset(cx, &pushed[0]);
|
||||
break;
|
||||
|
||||
case JSOP_UNBRAND:
|
||||
poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
|
||||
break;
|
||||
|
||||
case JSOP_GENERATOR:
|
||||
if (script->hasFunction) {
|
||||
if (script->function()) {
|
||||
if (script->hasGlobal()) {
|
||||
JSObject *proto = script->global()->getOrCreateGeneratorPrototype(cx);
|
||||
if (!proto)
|
||||
|
@ -4163,7 +4155,7 @@ ScriptAnalysis::analyzeTypes(JSContext *cx)
|
|||
for (unsigned i = 0; i < script->nfixed; i++)
|
||||
TypeScript::LocalTypes(script, i)->addType(cx, Type::UndefinedType());
|
||||
|
||||
TypeScriptNesting *nesting = script->hasFunction ? script->nesting() : NULL;
|
||||
TypeScriptNesting *nesting = script->function() ? script->nesting() : NULL;
|
||||
if (nesting && nesting->parent) {
|
||||
/*
|
||||
* Check whether NAME accesses can be resolved in parent scopes, and
|
||||
|
@ -4658,11 +4650,11 @@ AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun, JSO
|
|||
JSObject *funcallObj = funcallTypes->getSingleton(cx, false);
|
||||
JSObject *scriptObj = scriptTypes->getSingleton(cx, false);
|
||||
if (!funcallObj || !scriptObj || !scriptObj->isFunction() ||
|
||||
!scriptObj->getFunctionPrivate()->isInterpreted()) {
|
||||
!scriptObj->toFunction()->isInterpreted()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JSFunction *function = scriptObj->getFunctionPrivate();
|
||||
JSFunction *function = scriptObj->toFunction();
|
||||
JS_ASSERT(!function->script()->isInnerFunction);
|
||||
|
||||
/*
|
||||
|
@ -4850,7 +4842,7 @@ ScriptAnalysis::printTypes(JSContext *cx)
|
|||
|
||||
#ifdef DEBUG
|
||||
|
||||
if (script->hasFunction)
|
||||
if (script->function())
|
||||
printf("Function");
|
||||
else if (script->isCachedEval)
|
||||
printf("Eval");
|
||||
|
@ -4864,7 +4856,7 @@ ScriptAnalysis::printTypes(JSContext *cx)
|
|||
printf("\n this:");
|
||||
TypeScript::ThisTypes(script)->print(cx);
|
||||
|
||||
for (unsigned i = 0; script->hasFunction && i < script->function()->nargs; i++) {
|
||||
for (unsigned i = 0; script->function() && i < script->function()->nargs; i++) {
|
||||
printf("\n arg%u:", i);
|
||||
TypeScript::ArgTypes(script, i)->print(cx);
|
||||
}
|
||||
|
@ -4989,7 +4981,7 @@ MarkIteratorUnknownSlow(JSContext *cx)
|
|||
}
|
||||
|
||||
/* Trigger recompilation of any inline callers. */
|
||||
if (script->hasFunction && !script->function()->hasLazyType())
|
||||
if (script->function() && !script->function()->hasLazyType())
|
||||
ObjectStateChange(cx, script->function()->type(), false, true);
|
||||
}
|
||||
|
||||
|
@ -4997,8 +4989,8 @@ void
|
|||
TypeMonitorCallSlow(JSContext *cx, JSObject *callee,
|
||||
const CallArgs &args, bool constructing)
|
||||
{
|
||||
unsigned nargs = callee->getFunctionPrivate()->nargs;
|
||||
JSScript *script = callee->getFunctionPrivate()->script();
|
||||
unsigned nargs = callee->toFunction()->nargs;
|
||||
JSScript *script = callee->toFunction()->script();
|
||||
|
||||
if (!constructing)
|
||||
TypeScript::SetThis(cx, script, args.thisv());
|
||||
|
@ -5034,7 +5026,7 @@ TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, Type type)
|
|||
|
||||
/* Directly update associated type sets for applicable bytecodes. */
|
||||
if (js_CodeSpec[*pc].format & JOF_TYPESET) {
|
||||
if (!script->ensureRanAnalysis(cx)) {
|
||||
if (!script->ensureRanAnalysis(cx, NULL)) {
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
return;
|
||||
}
|
||||
|
@ -5127,7 +5119,7 @@ TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, Type type)
|
|||
}
|
||||
|
||||
/* Trigger recompilation of any inline callers. */
|
||||
if (script->hasFunction && !script->function()->hasLazyType())
|
||||
if (script->function() && !script->function()->hasLazyType())
|
||||
ObjectStateChange(cx, script->function()->type(), false, true);
|
||||
}
|
||||
|
||||
|
@ -5142,7 +5134,7 @@ TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Val
|
|||
|
||||
AutoEnterTypeInference enter(cx);
|
||||
|
||||
if (!script->ensureRanAnalysis(cx)) {
|
||||
if (!script->ensureRanAnalysis(cx, NULL)) {
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
return;
|
||||
}
|
||||
|
@ -5162,9 +5154,8 @@ TypeScript::SetScope(JSContext *cx, JSScript *script, JSObject *scope)
|
|||
{
|
||||
JS_ASSERT(script->types && !script->types->hasScope());
|
||||
|
||||
JSFunction *fun = script->types->function;
|
||||
JSFunction *fun = script->function();
|
||||
|
||||
JS_ASSERT(script->hasFunction == (fun != NULL));
|
||||
JS_ASSERT_IF(!fun, !script->isOuterFunction && !script->isInnerFunction);
|
||||
JS_ASSERT_IF(!scope, fun && !script->isInnerFunction);
|
||||
|
||||
|
@ -5183,6 +5174,14 @@ TypeScript::SetScope(JSContext *cx, JSScript *script, JSObject *scope)
|
|||
JS_ASSERT_IF(fun && scope, fun->getGlobal() == scope->getGlobal());
|
||||
script->types->global = fun ? fun->getGlobal() : scope->getGlobal();
|
||||
|
||||
/*
|
||||
* Update the parent in the script's bindings. The bindings are created
|
||||
* with a NULL parent, and fixing the parent now avoids the need to reshape
|
||||
* every time a call object is created from the bindings.
|
||||
*/
|
||||
if (!script->bindings.setParent(cx, script->types->global))
|
||||
return false;
|
||||
|
||||
if (!cx->typeInferenceEnabled())
|
||||
return true;
|
||||
|
||||
|
@ -5204,7 +5203,7 @@ TypeScript::SetScope(JSContext *cx, JSScript *script, JSObject *scope)
|
|||
* the script is nested inside.
|
||||
*/
|
||||
while (!scope->isCall())
|
||||
scope = scope->getParent();
|
||||
scope = scope->internalScopeChain();
|
||||
|
||||
CallObject &call = scope->asCall();
|
||||
|
||||
|
@ -5234,10 +5233,10 @@ TypeScript::SetScope(JSContext *cx, JSScript *script, JSObject *scope)
|
|||
* the parent's call object as the most recent one, so that it is not
|
||||
* marked as reentrant.
|
||||
*/
|
||||
if (!parent->ensureHasTypes(cx, parentFun))
|
||||
if (!parent->ensureHasTypes(cx))
|
||||
return false;
|
||||
if (!parent->types->hasScope()) {
|
||||
if (!SetScope(cx, parent, scope->getParent()))
|
||||
if (!SetScope(cx, parent, scope->internalScopeChain()))
|
||||
return false;
|
||||
parent->nesting()->activeCall = scope;
|
||||
parent->nesting()->argArray = Valueify(call.argArray());
|
||||
|
@ -5332,7 +5331,7 @@ CheckNestingParent(JSContext *cx, JSObject *scope, JSScript *script)
|
|||
JS_ASSERT(parent);
|
||||
|
||||
while (!scope->isCall() || scope->asCall().getCalleeFunction()->script() != parent)
|
||||
scope = scope->getParent();
|
||||
scope = scope->internalScopeChain();
|
||||
|
||||
if (scope != parent->nesting()->activeCall) {
|
||||
parent->reentrantOuterFunction = true;
|
||||
|
@ -5347,7 +5346,7 @@ CheckNestingParent(JSContext *cx, JSObject *scope, JSScript *script)
|
|||
* parent.
|
||||
*/
|
||||
if (parent->nesting()->parent) {
|
||||
scope = scope->getParent();
|
||||
scope = scope->internalScopeChain();
|
||||
script = parent;
|
||||
goto restart;
|
||||
}
|
||||
|
@ -5475,23 +5474,21 @@ IgnorePushed(const jsbytecode *pc, unsigned index)
|
|||
}
|
||||
|
||||
bool
|
||||
JSScript::makeTypes(JSContext *cx, JSFunction *fun)
|
||||
JSScript::makeTypes(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(!types);
|
||||
JS_ASSERT(hasFunction == (fun != NULL));
|
||||
|
||||
if (!cx->typeInferenceEnabled()) {
|
||||
types = (TypeScript *) cx->calloc_(sizeof(TypeScript));
|
||||
if (!types)
|
||||
return false;
|
||||
new(types) TypeScript(fun);
|
||||
new(types) TypeScript();
|
||||
return true;
|
||||
}
|
||||
|
||||
AutoEnterTypeInference enter(cx);
|
||||
|
||||
/* Open code for NumTypeSets since the types are not filled in yet. */
|
||||
unsigned count = 2 + (fun ? fun->nargs : 0) + nfixed + nTypeSets;
|
||||
unsigned count = TypeScript::NumTypeSets(this);
|
||||
|
||||
types = (TypeScript *) cx->calloc_(sizeof(TypeScript) + (sizeof(TypeSet) * count));
|
||||
if (!types) {
|
||||
|
@ -5499,7 +5496,7 @@ JSScript::makeTypes(JSContext *cx, JSFunction *fun)
|
|||
return false;
|
||||
}
|
||||
|
||||
new(types) TypeScript(fun);
|
||||
new(types) TypeScript();
|
||||
|
||||
#ifdef DEBUG
|
||||
TypeSet *typeArray = types->typeArray();
|
||||
|
@ -5515,7 +5512,7 @@ JSScript::makeTypes(JSContext *cx, JSFunction *fun)
|
|||
InferSpew(ISpewOps, "typeSet: %sT%p%s this #%u",
|
||||
InferSpewColor(thisTypes), thisTypes, InferSpewColorReset(),
|
||||
id());
|
||||
unsigned nargs = hasFunction ? function()->nargs : 0;
|
||||
unsigned nargs = function() ? function()->nargs : 0;
|
||||
for (unsigned i = 0; i < nargs; i++) {
|
||||
TypeSet *types = TypeScript::ArgTypes(this, i);
|
||||
InferSpew(ISpewOps, "typeSet: %sT%p%s arg%u #%u",
|
||||
|
@ -5558,9 +5555,7 @@ JSScript::makeAnalysis(JSContext *cx)
|
|||
bool
|
||||
JSScript::typeSetFunction(JSContext *cx, JSFunction *fun, bool singleton)
|
||||
{
|
||||
hasFunction = true;
|
||||
if (fun->isHeavyweight())
|
||||
isHeavyweightFunction = true;
|
||||
function_ = fun;
|
||||
|
||||
if (!cx->typeInferenceEnabled())
|
||||
return true;
|
||||
|
@ -5661,7 +5656,7 @@ JSObject::splicePrototype(JSContext *cx, JSObject *proto)
|
|||
}
|
||||
|
||||
if (!cx->typeInferenceEnabled()) {
|
||||
TypeObject *type = proto ? proto->getNewType(cx) : &emptyTypeObject;
|
||||
TypeObject *type = proto ? proto->getNewType(cx) : cx->compartment->getEmptyType(cx);
|
||||
if (!type)
|
||||
return false;
|
||||
type_ = type;
|
||||
|
@ -5707,8 +5702,8 @@ JSObject::makeLazyType(JSContext *cx)
|
|||
|
||||
type->singleton = this;
|
||||
|
||||
if (isFunction() && getFunctionPrivate() && getFunctionPrivate()->isInterpreted()) {
|
||||
type->interpretedFunction = getFunctionPrivate();
|
||||
if (isFunction() && toFunction()->isInterpreted()) {
|
||||
type->interpretedFunction = toFunction();
|
||||
JSScript *script = type->interpretedFunction->script();
|
||||
if (script->createdArgs)
|
||||
type->flags |= OBJECT_FLAG_CREATED_ARGUMENTS;
|
||||
|
@ -5718,7 +5713,7 @@ JSObject::makeLazyType(JSContext *cx)
|
|||
type->flags |= OBJECT_FLAG_REENTRANT_FUNCTION;
|
||||
}
|
||||
|
||||
if (flags & ITERATED)
|
||||
if (lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON))
|
||||
type->flags |= OBJECT_FLAG_ITERATED;
|
||||
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
|
@ -5730,12 +5725,11 @@ JSObject::makeLazyType(JSContext *cx)
|
|||
type->markUnknown(cx);
|
||||
#endif
|
||||
|
||||
if (clasp->ext.equality)
|
||||
if (getClass()->ext.equality)
|
||||
type->flags |= OBJECT_FLAG_SPECIAL_EQUALITY;
|
||||
|
||||
if (type->unknownProperties()) {
|
||||
type_ = type;
|
||||
flags &= ~LAZY_TYPE;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -5745,24 +5739,101 @@ JSObject::makeLazyType(JSContext *cx)
|
|||
| OBJECT_FLAG_NON_TYPED_ARRAY;
|
||||
|
||||
type_ = type;
|
||||
flags &= ~LAZY_TYPE;
|
||||
}
|
||||
|
||||
void
|
||||
JSObject::makeNewType(JSContext *cx, JSFunction *fun, bool unknown)
|
||||
/* static */ inline HashNumber
|
||||
TypeObjectEntry::hash(JSObject *proto)
|
||||
{
|
||||
JS_ASSERT(!newType);
|
||||
return PointerHasher<JSObject *, 3>::hash(proto);
|
||||
}
|
||||
|
||||
/* static */ inline bool
|
||||
TypeObjectEntry::match(TypeObject *key, JSObject *lookup)
|
||||
{
|
||||
return key->proto == lookup;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool
|
||||
JSObject::hasNewType(TypeObject *type)
|
||||
{
|
||||
TypeObjectSet &table = compartment()->newTypeObjects;
|
||||
|
||||
if (!table.initialized())
|
||||
return false;
|
||||
|
||||
TypeObjectSet::Ptr p = table.lookup(this);
|
||||
return p && *p == type;
|
||||
}
|
||||
#endif /* DEBUG */
|
||||
|
||||
bool
|
||||
JSObject::setNewTypeUnknown(JSContext *cx)
|
||||
{
|
||||
if (!setFlag(cx, js::BaseShape::NEW_TYPE_UNKNOWN))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* If the object already has a new type, mark that type as unknown. It will
|
||||
* not have the SETS_MARKED_UNKNOWN bit set, so may require a type set
|
||||
* crawl if prototypes of the object change dynamically in the future.
|
||||
*/
|
||||
TypeObjectSet &table = cx->compartment->newTypeObjects;
|
||||
if (table.initialized()) {
|
||||
if (TypeObjectSet::Ptr p = table.lookup(this))
|
||||
MarkTypeObjectUnknownProperties(cx, *p);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TypeObject *
|
||||
JSObject::getNewType(JSContext *cx, JSFunction *fun)
|
||||
{
|
||||
if (!setDelegate(cx))
|
||||
return NULL;
|
||||
|
||||
TypeObjectSet &table = cx->compartment->newTypeObjects;
|
||||
|
||||
if (!table.initialized() && !table.init())
|
||||
return NULL;
|
||||
|
||||
TypeObjectSet::AddPtr p = table.lookupForAdd(this);
|
||||
if (p) {
|
||||
TypeObject *type = *p;
|
||||
|
||||
/*
|
||||
* If set, the type's newScript indicates the script used to create
|
||||
* all objects in existence which have this type. If there are objects
|
||||
* in existence which are not created by calling 'new' on newScript,
|
||||
* we must clear the new script information from the type and will not
|
||||
* be able to assume any definite properties for instances of the type.
|
||||
* This case is rare, but can happen if, for example, two scripted
|
||||
* functions have the same value for their 'prototype' property, or if
|
||||
* Object.create is called with a prototype object that is also the
|
||||
* 'prototype' property of some scripted function.
|
||||
*/
|
||||
if (type->newScript && type->newScript->fun != fun)
|
||||
type->clearNewScript(cx);
|
||||
|
||||
if (cx->compartment->needsBarrier())
|
||||
TypeObject::readBarrier(type);
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
bool markUnknown = lastProperty()->hasObjectFlag(BaseShape::NEW_TYPE_UNKNOWN);
|
||||
|
||||
TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL,
|
||||
JSProto_Object, this, unknown);
|
||||
JSProto_Object, this, markUnknown);
|
||||
if (!type)
|
||||
return;
|
||||
return NULL;
|
||||
|
||||
newType.init(type);
|
||||
setDelegate();
|
||||
if (!table.relookupOrAdd(p, this, type))
|
||||
return NULL;
|
||||
|
||||
if (!cx->typeInferenceEnabled())
|
||||
return;
|
||||
return type;
|
||||
|
||||
AutoEnterTypeInference enter(cx);
|
||||
|
||||
|
@ -5783,7 +5854,7 @@ JSObject::makeNewType(JSContext *cx, JSFunction *fun, bool unknown)
|
|||
type->flags |= OBJECT_FLAG_UNKNOWN_MASK;
|
||||
#endif
|
||||
|
||||
if (clasp->ext.equality)
|
||||
if (getClass()->ext.equality)
|
||||
type->flags |= OBJECT_FLAG_SPECIAL_EQUALITY;
|
||||
|
||||
/*
|
||||
|
@ -5796,6 +5867,40 @@ JSObject::makeNewType(JSContext *cx, JSFunction *fun, bool unknown)
|
|||
*/
|
||||
if (type->unknownProperties())
|
||||
type->flags |= OBJECT_FLAG_SETS_MARKED_UNKNOWN;
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
TypeObject *
|
||||
JSCompartment::getLazyType(JSContext *cx, JSObject *proto)
|
||||
{
|
||||
TypeObjectSet &table = cx->compartment->lazyTypeObjects;
|
||||
|
||||
if (!table.initialized() && !table.init())
|
||||
return NULL;
|
||||
|
||||
TypeObjectSet::AddPtr p = table.lookupForAdd(proto);
|
||||
if (p) {
|
||||
TypeObject *type = *p;
|
||||
JS_ASSERT(type->lazy());
|
||||
|
||||
if (cx->compartment->needsBarrier())
|
||||
TypeObject::readBarrier(type);
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL,
|
||||
JSProto_Object, proto, false);
|
||||
if (!type)
|
||||
return NULL;
|
||||
|
||||
if (!table.relookupOrAdd(p, proto, type))
|
||||
return NULL;
|
||||
|
||||
type->singleton = (JSObject *) TypeObject::LAZY_SINGLETON;
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
@ -5847,15 +5952,6 @@ TypeSet::sweep(JSContext *cx, JSCompartment *compartment)
|
|||
flags &= ~TYPE_FLAG_PROPAGATED_PROPERTY;
|
||||
}
|
||||
|
||||
inline void
|
||||
JSObject::revertLazyType()
|
||||
{
|
||||
JS_ASSERT(hasSingletonType() && !hasLazyType());
|
||||
JS_ASSERT_IF(type_->proto, type_->proto->newType);
|
||||
flags |= LAZY_TYPE;
|
||||
type_ = (type_->proto) ? type_->proto->newType : &emptyTypeObject;
|
||||
}
|
||||
|
||||
inline void
|
||||
TypeObject::clearProperties()
|
||||
{
|
||||
|
@ -5880,7 +5976,6 @@ TypeObject::sweep(JSContext *cx)
|
|||
contribution = 0;
|
||||
|
||||
if (singleton) {
|
||||
JS_ASSERT(!emptyShapes);
|
||||
JS_ASSERT(!newScript);
|
||||
|
||||
/*
|
||||
|
@ -5889,25 +5984,10 @@ TypeObject::sweep(JSContext *cx)
|
|||
*/
|
||||
clearProperties();
|
||||
|
||||
if (!isMarked()) {
|
||||
/*
|
||||
* Singleton objects do not hold strong references on their types.
|
||||
* When removing the type, however, we need to fixup the singleton
|
||||
* so that it has a lazy type again. The generic 'new' type for the
|
||||
* proto must be live, since the type's prototype and its 'new'
|
||||
* type are both strong references.
|
||||
*/
|
||||
JS_ASSERT_IF(singleton->isMarked() && proto,
|
||||
proto->isMarked() && proto->newType->isMarked());
|
||||
singleton->revertLazyType();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isMarked()) {
|
||||
if (emptyShapes)
|
||||
Foreground::free_(emptyShapes);
|
||||
if (newScript)
|
||||
Foreground::free_(newScript);
|
||||
return;
|
||||
|
@ -5991,9 +6071,6 @@ struct SweepTypeObjectOp
|
|||
void
|
||||
SweepTypeObjects(JSContext *cx, JSCompartment *compartment)
|
||||
{
|
||||
JS_ASSERT(!emptyTypeObject.emptyShapes);
|
||||
JS_ASSERT(!emptyTypeObject.newScript);
|
||||
|
||||
SweepTypeObjectOp op(cx);
|
||||
gc::ForEachArenaAndCell(compartment, gc::FINALIZE_TYPE_OBJECT, gc::EmptyArenaOp, op);
|
||||
}
|
||||
|
@ -6077,6 +6154,18 @@ TypeCompartment::sweep(JSContext *cx)
|
|||
pendingCapacity = 0;
|
||||
}
|
||||
|
||||
void
|
||||
JSCompartment::sweepNewTypeObjectTable(JSContext *cx, TypeObjectSet &table)
|
||||
{
|
||||
if (table.initialized()) {
|
||||
for (TypeObjectSet::Enum e(table); !e.empty(); e.popFront()) {
|
||||
TypeObject *type = e.front();
|
||||
if (!type->isMarked())
|
||||
e.removeFront();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TypeCompartment::~TypeCompartment()
|
||||
{
|
||||
if (pendingArray)
|
||||
|
@ -6275,7 +6364,7 @@ JS_GetTypeInferenceObjectStats(void *object_, TypeInferenceMemoryStats *stats, J
|
|||
* every GC. The type object is normally destroyed too, but we don't
|
||||
* charge this to 'temporary' as this is not for GC heap values.
|
||||
*/
|
||||
JS_ASSERT(!object->newScript && !object->emptyShapes);
|
||||
JS_ASSERT(!object->newScript);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -6290,12 +6379,6 @@ JS_GetTypeInferenceObjectStats(void *object_, TypeInferenceMemoryStats *stats, J
|
|||
stats->objects += mallocSizeOf(object->newScript, computedSize);
|
||||
}
|
||||
|
||||
if (object->emptyShapes) {
|
||||
stats->emptyShapes +=
|
||||
mallocSizeOf(object->emptyShapes,
|
||||
sizeof(EmptyShape*) * gc::FINALIZE_OBJECT_LIMIT);
|
||||
}
|
||||
|
||||
/*
|
||||
* This counts memory that is in the temp pool but gets attributed
|
||||
* elsewhere. See JS_GetTypeInferenceMemoryStats for more details.
|
||||
|
|
|
@ -731,19 +731,16 @@ struct TypeObject : gc::Cell
|
|||
*/
|
||||
HeapPtrObject singleton;
|
||||
|
||||
/* Lazily filled array of empty shapes for each size of objects with this type. */
|
||||
HeapPtr<EmptyShape> *emptyShapes;
|
||||
/*
|
||||
* Value held by singleton if this is a standin type for a singleton JS
|
||||
* object whose type has not been constructed yet.
|
||||
*/
|
||||
static const size_t LAZY_SINGLETON = 1;
|
||||
bool lazy() const { return singleton == (JSObject *) LAZY_SINGLETON; }
|
||||
|
||||
/* Flags for this object. */
|
||||
TypeObjectFlags flags;
|
||||
|
||||
/*
|
||||
* If non-NULL, objects of this type have always been constructed using
|
||||
* 'new' on the specified script, which adds some number of properties to
|
||||
* the object in a definite order before the object escapes.
|
||||
*/
|
||||
HeapPtr<TypeNewScript> newScript;
|
||||
|
||||
/*
|
||||
* Estimate of the contribution of this object to the type sets it appears in.
|
||||
* This is the sum of the sizes of those sets at the point when the object
|
||||
|
@ -758,6 +755,13 @@ struct TypeObject : gc::Cell
|
|||
uint32 contribution;
|
||||
static const uint32 CONTRIBUTION_LIMIT = 2000;
|
||||
|
||||
/*
|
||||
* If non-NULL, objects of this type have always been constructed using
|
||||
* 'new' on the specified script, which adds some number of properties to
|
||||
* the object in a definite order before the object escapes.
|
||||
*/
|
||||
HeapPtr<TypeNewScript> newScript;
|
||||
|
||||
/*
|
||||
* Properties of this object. This may contain JSID_VOID, representing the
|
||||
* types of all integer indexes of the object, and/or JSID_EMPTY, holding
|
||||
|
@ -793,6 +797,10 @@ struct TypeObject : gc::Cell
|
|||
/* If this is an interpreted function, the function object. */
|
||||
HeapPtrFunction interpretedFunction;
|
||||
|
||||
#if JS_BITS_PER_WORD == 32
|
||||
void *padding;
|
||||
#endif
|
||||
|
||||
inline TypeObject(JSObject *proto, bool isFunction, bool unknown);
|
||||
|
||||
bool isFunction() { return !!(flags & OBJECT_FLAG_FUNCTION); }
|
||||
|
@ -812,16 +820,6 @@ struct TypeObject : gc::Cell
|
|||
return !!(flags & OBJECT_FLAG_UNKNOWN_PROPERTIES);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return an immutable, shareable, empty shape with the same clasp as this
|
||||
* and the same slotSpan as this had when empty.
|
||||
*
|
||||
* If |this| is the scope of an object |proto|, the resulting scope can be
|
||||
* used as the scope of a new object whose prototype is |proto|.
|
||||
*/
|
||||
inline bool canProvideEmptyShape(js::Class *clasp);
|
||||
inline js::EmptyShape *getEmptyShape(JSContext *cx, js::Class *aclasp, gc::AllocKind kind);
|
||||
|
||||
/*
|
||||
* Get or create a property of this object. Only call this for properties which
|
||||
* a script accesses explicitly. 'assign' indicates whether this is for an
|
||||
|
@ -874,10 +872,11 @@ struct TypeObject : gc::Cell
|
|||
* object pending deletion is released when weak references are sweeped
|
||||
* from all the compartment's type objects.
|
||||
*/
|
||||
void finalize(JSContext *cx) {}
|
||||
void finalize(JSContext *cx, bool background) {}
|
||||
|
||||
static inline void writeBarrierPre(TypeObject *type);
|
||||
static inline void writeBarrierPost(TypeObject *type, void *addr);
|
||||
static inline void readBarrier(TypeObject *type);
|
||||
|
||||
private:
|
||||
inline uint32 basePropertyCount() const;
|
||||
|
@ -888,8 +887,18 @@ struct TypeObject : gc::Cell
|
|||
}
|
||||
};
|
||||
|
||||
/* Global singleton for the generic type of objects with no prototype. */
|
||||
extern TypeObject emptyTypeObject;
|
||||
/*
|
||||
* Entries for the per-compartment set of type objects which are the default
|
||||
* 'new' or the lazy types of some prototype.
|
||||
*/
|
||||
struct TypeObjectEntry
|
||||
{
|
||||
typedef JSObject *Lookup;
|
||||
|
||||
static inline HashNumber hash(JSObject *base);
|
||||
static inline bool match(TypeObject *key, JSObject *lookup);
|
||||
};
|
||||
typedef HashSet<TypeObject *, TypeObjectEntry, SystemAllocPolicy> TypeObjectSet;
|
||||
|
||||
/*
|
||||
* Call to mark a script's arguments as having been created, recompile any
|
||||
|
@ -1020,9 +1029,6 @@ class TypeScript
|
|||
/* Analysis information for the script, cleared on each GC. */
|
||||
analyze::ScriptAnalysis *analysis;
|
||||
|
||||
/* Function for the script, if it has one. */
|
||||
HeapPtrFunction function;
|
||||
|
||||
/*
|
||||
* Information about the scope in which a script executes. This information
|
||||
* is not set until the script has executed at least once and SetScope
|
||||
|
@ -1041,8 +1047,7 @@ class TypeScript
|
|||
/* Dynamic types generated at points within this script. */
|
||||
TypeResult *dynamicList;
|
||||
|
||||
inline TypeScript(JSFunction *fun);
|
||||
inline ~TypeScript();
|
||||
inline TypeScript();
|
||||
|
||||
bool hasScope() { return size_t(global.get()) != GLOBAL_MISSING_SCOPE; }
|
||||
|
||||
|
@ -1270,4 +1275,8 @@ void TypeFailure(JSContext *cx, const char *fmt, ...);
|
|||
} /* namespace types */
|
||||
} /* namespace js */
|
||||
|
||||
namespace JS {
|
||||
template<> class AnchorPermitted<js::types::TypeObject *> { };
|
||||
}
|
||||
|
||||
#endif // jsinfer_h___
|
||||
|
|
|
@ -321,10 +321,10 @@ TypeMonitorCall(JSContext *cx, const js::CallArgs &args, bool constructing)
|
|||
|
||||
JSObject *callee = &args.callee();
|
||||
if (callee->isFunction()) {
|
||||
JSFunction *fun = callee->getFunctionPrivate();
|
||||
JSFunction *fun = callee->toFunction();
|
||||
if (fun->isInterpreted()) {
|
||||
JSScript *script = fun->script();
|
||||
if (!script->ensureRanAnalysis(cx, fun, callee->getParent()))
|
||||
if (!script->ensureRanAnalysis(cx, fun->environment()))
|
||||
return;
|
||||
if (cx->typeInferenceEnabled())
|
||||
TypeMonitorCallSlow(cx, callee, args, constructing);
|
||||
|
@ -460,15 +460,9 @@ UseNewTypeAtEntry(JSContext *cx, StackFrame *fp)
|
|||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline
|
||||
TypeScript::TypeScript(JSFunction *fun)
|
||||
: function(fun),
|
||||
global((js::GlobalObject *) GLOBAL_MISSING_SCOPE)
|
||||
{
|
||||
}
|
||||
|
||||
inline
|
||||
TypeScript::~TypeScript()
|
||||
TypeScript::TypeScript()
|
||||
{
|
||||
this->global = (js::GlobalObject *) GLOBAL_MISSING_SCOPE;
|
||||
}
|
||||
|
||||
/* static */ inline unsigned
|
||||
|
@ -700,8 +694,6 @@ TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js:
|
|||
void
|
||||
TypeScript::trace(JSTracer *trc)
|
||||
{
|
||||
if (function)
|
||||
gc::MarkObject(trc, function, "script_fun");
|
||||
if (hasScope() && global)
|
||||
gc::MarkObject(trc, global, "script_global");
|
||||
|
||||
|
@ -1268,7 +1260,7 @@ inline void
|
|||
TypeObject::writeBarrierPre(TypeObject *type)
|
||||
{
|
||||
#ifdef JSGC_INCREMENTAL
|
||||
if (!type || type == &js::types::emptyTypeObject)
|
||||
if (!type)
|
||||
return;
|
||||
|
||||
JSCompartment *comp = type->compartment();
|
||||
|
@ -1282,6 +1274,17 @@ TypeObject::writeBarrierPost(TypeObject *type, void *addr)
|
|||
{
|
||||
}
|
||||
|
||||
inline void
|
||||
TypeObject::readBarrier(TypeObject *type)
|
||||
{
|
||||
#ifdef JSGC_INCREMENTAL
|
||||
JSCompartment *comp = type->compartment();
|
||||
JS_ASSERT(comp->needsBarrier());
|
||||
|
||||
MarkTypeObjectUnbarriered(comp->barrierTracer(), type, "read barrier");
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void
|
||||
TypeNewScript::writeBarrierPre(TypeNewScript *newScript)
|
||||
{
|
||||
|
@ -1317,15 +1320,15 @@ Property::Property(const Property &o)
|
|||
} } /* namespace js::types */
|
||||
|
||||
inline bool
|
||||
JSScript::ensureHasTypes(JSContext *cx, JSFunction *fun)
|
||||
JSScript::ensureHasTypes(JSContext *cx)
|
||||
{
|
||||
return types || makeTypes(cx, fun);
|
||||
return types || makeTypes(cx);
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSScript::ensureRanAnalysis(JSContext *cx, JSFunction *fun, JSObject *scope)
|
||||
JSScript::ensureRanAnalysis(JSContext *cx, JSObject *scope)
|
||||
{
|
||||
if (!ensureHasTypes(cx, fun))
|
||||
if (!ensureHasTypes(cx))
|
||||
return false;
|
||||
if (!types->hasScope() && !js::types::TypeScript::SetScope(cx, this, scope))
|
||||
return false;
|
||||
|
@ -1338,7 +1341,7 @@ JSScript::ensureRanAnalysis(JSContext *cx, JSFunction *fun, JSObject *scope)
|
|||
inline bool
|
||||
JSScript::ensureRanInference(JSContext *cx)
|
||||
{
|
||||
if (!ensureRanAnalysis(cx))
|
||||
if (!ensureRanAnalysis(cx, NULL))
|
||||
return false;
|
||||
if (!analysis()->ranInference()) {
|
||||
js::types::AutoEnterTypeInference enter(cx);
|
||||
|
@ -1376,4 +1379,12 @@ js::analyze::ScriptAnalysis::addPushedType(JSContext *cx, uint32 offset, uint32
|
|||
pushed->addType(cx, type);
|
||||
}
|
||||
|
||||
inline js::types::TypeObject *
|
||||
JSCompartment::getEmptyType(JSContext *cx)
|
||||
{
|
||||
if (!emptyTypeObject)
|
||||
emptyTypeObject = types.newTypeObject(cx, NULL, JSProto_Object, NULL, true);
|
||||
return emptyTypeObject;
|
||||
}
|
||||
|
||||
#endif // jsinferinlines_h___
|
||||
|
|
|
@ -190,7 +190,7 @@ js::GetBlockChain(JSContext *cx, StackFrame *fp)
|
|||
else if (op == JSOP_ENTERBLOCK)
|
||||
blockChain = script->getObject(indexBase + GET_INDEX(pc));
|
||||
else if (op == JSOP_LEAVEBLOCK || op == JSOP_LEAVEBLOCKEXPR)
|
||||
blockChain = blockChain->getParent();
|
||||
blockChain = blockChain->getStaticBlockScopeChain();
|
||||
else if (op == JSOP_BLOCKCHAIN)
|
||||
blockChain = script->getObject(indexBase + GET_INDEX(pc));
|
||||
else if (op == JSOP_NULLBLOCKCHAIN)
|
||||
|
@ -295,7 +295,7 @@ GetScopeChainFull(JSContext *cx, StackFrame *fp, JSObject *blockChain)
|
|||
*/
|
||||
limitClone = &fp->scopeChain();
|
||||
while (limitClone->isWith())
|
||||
limitClone = limitClone->getParent();
|
||||
limitClone = limitClone->internalScopeChain();
|
||||
JS_ASSERT(limitClone);
|
||||
|
||||
/*
|
||||
|
@ -342,7 +342,7 @@ GetScopeChainFull(JSContext *cx, StackFrame *fp, JSObject *blockChain)
|
|||
JSObject *newChild = innermostNewChild;
|
||||
for (;;) {
|
||||
JS_ASSERT(newChild->getProto() == sharedBlock);
|
||||
sharedBlock = sharedBlock->getParent();
|
||||
sharedBlock = sharedBlock->getStaticBlockScopeChain();
|
||||
|
||||
/* Sometimes limitBlock will be NULL, so check that first. */
|
||||
if (sharedBlock == limitBlock || !sharedBlock)
|
||||
|
@ -353,10 +353,12 @@ GetScopeChainFull(JSContext *cx, StackFrame *fp, JSObject *blockChain)
|
|||
if (!clone)
|
||||
return NULL;
|
||||
|
||||
newChild->setParent(clone);
|
||||
if (!newChild->setInternalScopeChain(cx, clone))
|
||||
return NULL;
|
||||
newChild = clone;
|
||||
}
|
||||
newChild->setParent(&fp->scopeChain());
|
||||
if (!newChild->setInternalScopeChain(cx, &fp->scopeChain()))
|
||||
return NULL;
|
||||
|
||||
|
||||
/*
|
||||
|
@ -422,7 +424,7 @@ js::BoxNonStrictThis(JSContext *cx, const CallReceiver &call)
|
|||
JS_ASSERT(!thisv.isMagic());
|
||||
|
||||
#ifdef DEBUG
|
||||
JSFunction *fun = call.callee().isFunction() ? call.callee().getFunctionPrivate() : NULL;
|
||||
JSFunction *fun = call.callee().isFunction() ? call.callee().toFunction() : NULL;
|
||||
JS_ASSERT_IF(fun && fun->isInterpreted(), !fun->inStrictMode());
|
||||
#endif
|
||||
|
||||
|
@ -509,12 +511,11 @@ js::OnUnknownMethod(JSContext *cx, Value *vp)
|
|||
vp[0] = IdToValue(id);
|
||||
}
|
||||
#endif
|
||||
obj = js_NewGCObject(cx, FINALIZE_OBJECT2);
|
||||
|
||||
obj = NewObjectWithClassProto(cx, &js_NoSuchMethodClass, NULL, NULL);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
obj->init(cx, &js_NoSuchMethodClass, &emptyTypeObject, NULL, NULL, false);
|
||||
obj->setSharedNonNativeMap();
|
||||
obj->setSlot(JSSLOT_FOUND_FUNCTION, tvr.value());
|
||||
obj->setSlot(JSSLOT_SAVED_ID, vp[0]);
|
||||
vp[0].setObject(*obj);
|
||||
|
@ -619,7 +620,7 @@ js::InvokeKernel(JSContext *cx, CallArgs args, MaybeConstruct construct)
|
|||
}
|
||||
|
||||
/* Invoke native functions. */
|
||||
JSFunction *fun = callee.getFunctionPrivate();
|
||||
JSFunction *fun = callee.toFunction();
|
||||
JS_ASSERT_IF(construct, !fun->isConstructor());
|
||||
if (fun->isNative())
|
||||
return CallJSNative(cx, fun->u.n.native, args);
|
||||
|
@ -766,7 +767,7 @@ js::ExecuteKernel(JSContext *cx, JSScript *script, JSObject &scopeChain, const V
|
|||
|
||||
Probes::startExecution(cx, script);
|
||||
|
||||
if (!script->ensureRanAnalysis(cx, NULL, &scopeChain))
|
||||
if (!script->ensureRanAnalysis(cx, &scopeChain))
|
||||
return false;
|
||||
|
||||
TypeScript::SetThis(cx, script, fp->thisValue());
|
||||
|
@ -801,8 +802,10 @@ js::Execute(JSContext *cx, JSScript *script, JSObject &scopeChainArg, Value *rva
|
|||
JS_ASSERT(!scopeChain->getOps()->defineProperty);
|
||||
|
||||
/* The VAROBJFIX option makes varObj == globalObj in global code. */
|
||||
if (!cx->hasRunOption(JSOPTION_VAROBJFIX))
|
||||
scopeChain->makeVarObj();
|
||||
if (!cx->hasRunOption(JSOPTION_VAROBJFIX)) {
|
||||
if (!scopeChain->setVarObj(cx))
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Use the scope chain as 'this', modulo outerization. */
|
||||
JSObject *thisObj = scopeChain->thisObject(cx);
|
||||
|
@ -922,7 +925,6 @@ js::LooselyEqual(JSContext *cx, const Value &lval, const Value &rval, JSBool *re
|
|||
if (lval.isObject()) {
|
||||
JSObject *l = &lval.toObject();
|
||||
JSObject *r = &rval.toObject();
|
||||
l->assertSpecialEqualitySynced();
|
||||
|
||||
if (JSEqualityOp eq = l->getClass()->ext.equality) {
|
||||
return eq(cx, l, &rval, result);
|
||||
|
@ -1073,7 +1075,7 @@ js::InvokeConstructorKernel(JSContext *cx, const CallArgs &argsRef)
|
|||
JSObject *callee = &args.callee();
|
||||
Class *clasp = callee->getClass();
|
||||
if (clasp == &FunctionClass) {
|
||||
JSFunction *fun = callee->getFunctionPrivate();
|
||||
JSFunction *fun = callee->toFunction();
|
||||
|
||||
if (fun->isConstructor()) {
|
||||
Probes::calloutBegin(cx, fun);
|
||||
|
@ -1117,7 +1119,7 @@ js::InvokeConstructorWithGivenThis(JSContext *cx, JSObject *thisobj, const Value
|
|||
Class *clasp = callee.getClass();
|
||||
JSFunction *fun;
|
||||
bool ok;
|
||||
if (clasp == &FunctionClass && (fun = callee.getFunctionPrivate())->isConstructor()) {
|
||||
if (clasp == &FunctionClass && (fun = callee.toFunction())->isConstructor()) {
|
||||
args.thisv().setMagicWithObjectOrNullPayload(thisobj);
|
||||
Probes::calloutBegin(cx, fun);
|
||||
ok = CallJSNativeConstructor(cx, fun->u.n.native, args);
|
||||
|
@ -1205,7 +1207,7 @@ LeaveWith(JSContext *cx)
|
|||
JS_ASSERT(withobj->getPrivate() == js_FloatingFrameIfGenerator(cx, cx->fp()));
|
||||
JS_ASSERT(OBJ_BLOCK_DEPTH(cx, withobj) >= 0);
|
||||
withobj->setPrivate(NULL);
|
||||
cx->fp()->setScopeChainNoCallObj(*withobj->getParent());
|
||||
cx->fp()->setScopeChainNoCallObj(*withobj->internalScopeChain());
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -1411,9 +1413,9 @@ inline InterpreterFrames::~InterpreterFrames()
|
|||
*/
|
||||
#if defined DEBUG && !defined JS_THREADSAFE
|
||||
|
||||
# define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) \
|
||||
# define ASSERT_VALID_PROPERTY_CACHE_HIT(obj,pobj,entry) \
|
||||
JS_BEGIN_MACRO \
|
||||
if (!AssertValidPropertyCacheHit(cx, script, regs, pcoff, obj, pobj, \
|
||||
if (!AssertValidPropertyCacheHit(cx, script, regs, obj, pobj, \
|
||||
entry)) { \
|
||||
goto error; \
|
||||
} \
|
||||
|
@ -1421,17 +1423,14 @@ inline InterpreterFrames::~InterpreterFrames()
|
|||
|
||||
static bool
|
||||
AssertValidPropertyCacheHit(JSContext *cx, JSScript *script, FrameRegs& regs,
|
||||
ptrdiff_t pcoff, JSObject *start, JSObject *found,
|
||||
JSObject *start, JSObject *found,
|
||||
PropertyCacheEntry *entry)
|
||||
{
|
||||
uint32 sample = cx->runtime->gcNumber;
|
||||
PropertyCacheEntry savedEntry = *entry;
|
||||
|
||||
JSAtom *atom;
|
||||
if (pcoff >= 0)
|
||||
GET_ATOM_FROM_BYTECODE(script, regs.pc, pcoff, atom);
|
||||
else
|
||||
atom = cx->runtime->atomState.lengthAtom;
|
||||
GET_ATOM_FROM_BYTECODE(script, regs.pc, 0, atom);
|
||||
|
||||
JSObject *obj, *pobj;
|
||||
JSProperty *prop;
|
||||
|
@ -1452,34 +1451,13 @@ AssertValidPropertyCacheHit(JSContext *cx, JSScript *script, FrameRegs& regs,
|
|||
JS_ASSERT(pobj == found);
|
||||
|
||||
const Shape *shape = (Shape *) prop;
|
||||
if (entry->vword.isSlot()) {
|
||||
JS_ASSERT(entry->vword.toSlot() == shape->slot);
|
||||
JS_ASSERT(!shape->isMethod());
|
||||
} else if (entry->vword.isShape()) {
|
||||
JS_ASSERT(entry->vword.toShape() == shape);
|
||||
JS_ASSERT_IF(shape->isMethod(),
|
||||
shape->methodObject() == pobj->nativeGetSlot(shape->slot).toObject());
|
||||
} else {
|
||||
Value v;
|
||||
JS_ASSERT(entry->vword.isFunObj());
|
||||
JS_ASSERT(!entry->vword.isNull());
|
||||
JS_ASSERT(pobj->brandedOrHasMethodBarrier());
|
||||
JS_ASSERT(shape->hasDefaultGetterOrIsMethod());
|
||||
JS_ASSERT(pobj->containsSlot(shape->slot));
|
||||
v = pobj->nativeGetSlot(shape->slot);
|
||||
JS_ASSERT(entry->vword.toFunObj() == v.toObject());
|
||||
|
||||
if (shape->isMethod()) {
|
||||
JS_ASSERT(js_CodeSpec[*regs.pc].format & JOF_CALLOP);
|
||||
JS_ASSERT(shape->methodObject() == v.toObject());
|
||||
}
|
||||
}
|
||||
JS_ASSERT(entry->prop == shape);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
# define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) ((void) 0)
|
||||
# define ASSERT_VALID_PROPERTY_CACHE_HIT(obj,pobj,entry) ((void) 0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -1995,7 +1973,7 @@ BEGIN_CASE(JSOP_POPN)
|
|||
JS_ASSERT_IF(obj,
|
||||
OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj)
|
||||
<= (size_t) (regs.sp - regs.fp()->base()));
|
||||
for (obj = ®s.fp()->scopeChain(); obj; obj = obj->getParent()) {
|
||||
for (obj = ®s.fp()->scopeChain(); obj; obj = obj->scopeChain()) {
|
||||
if (!obj->isBlock() || !obj->isWith())
|
||||
continue;
|
||||
if (obj->getPrivate() != js_FloatingFrameIfGenerator(cx, regs.fp()))
|
||||
|
@ -2353,10 +2331,10 @@ END_CASE(JSOP_PICK)
|
|||
JS_BEGIN_MACRO \
|
||||
if (shape->isDataDescriptor() && shape->hasDefaultGetter()) { \
|
||||
/* Fast path for Object instance properties. */ \
|
||||
JS_ASSERT((shape)->slot != SHAPE_INVALID_SLOT || \
|
||||
JS_ASSERT((shape)->slot() != SHAPE_INVALID_SLOT || \
|
||||
!shape->hasDefaultSetter()); \
|
||||
if (((shape)->slot != SHAPE_INVALID_SLOT)) \
|
||||
*(vp) = (pobj)->nativeGetSlot((shape)->slot); \
|
||||
if (((shape)->slot() != SHAPE_INVALID_SLOT)) \
|
||||
*(vp) = (pobj)->nativeGetSlot((shape)->slot()); \
|
||||
else \
|
||||
(vp)->setUndefined(); \
|
||||
} else { \
|
||||
|
@ -2368,8 +2346,8 @@ END_CASE(JSOP_PICK)
|
|||
#define NATIVE_SET(cx,obj,shape,entry,strict,vp) \
|
||||
JS_BEGIN_MACRO \
|
||||
if (shape->hasDefaultSetter() && \
|
||||
(shape)->slot != SHAPE_INVALID_SLOT && \
|
||||
!(obj)->brandedOrHasMethodBarrier()) { \
|
||||
(shape)->hasSlot() && \
|
||||
!(shape)->isMethod()) { \
|
||||
/* Fast path for, e.g., plain Object instance properties. */ \
|
||||
(obj)->nativeSetSlotWithType(cx, shape, *vp); \
|
||||
} else { \
|
||||
|
@ -2435,7 +2413,7 @@ BEGIN_CASE(JSOP_BINDNAME)
|
|||
* forms.
|
||||
*/
|
||||
obj = ®s.fp()->scopeChain();
|
||||
if (!obj->getParent())
|
||||
if (obj->isGlobal())
|
||||
break;
|
||||
|
||||
PropertyCacheEntry *entry;
|
||||
|
@ -2443,7 +2421,7 @@ BEGIN_CASE(JSOP_BINDNAME)
|
|||
JSAtom *atom;
|
||||
JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
|
||||
if (!atom) {
|
||||
ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
|
||||
ASSERT_VALID_PROPERTY_CACHE_HIT(obj, obj2, entry);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -2933,57 +2911,17 @@ BEGIN_CASE(JSOP_VOID)
|
|||
regs.sp[-1].setUndefined();
|
||||
END_CASE(JSOP_VOID)
|
||||
|
||||
{
|
||||
/*
|
||||
* Property incops are followed by an equivalent decomposed version,
|
||||
* and we have the option of running either. If type inference is enabled
|
||||
* we run the decomposed version to accumulate observed types and
|
||||
* overflows which inference can process, otherwise we run the fat opcode
|
||||
* as doing so is faster and is what the tracer needs while recording.
|
||||
*/
|
||||
JSObject *obj;
|
||||
JSAtom *atom;
|
||||
jsid id;
|
||||
jsint i;
|
||||
|
||||
BEGIN_CASE(JSOP_INCELEM)
|
||||
BEGIN_CASE(JSOP_DECELEM)
|
||||
BEGIN_CASE(JSOP_ELEMINC)
|
||||
BEGIN_CASE(JSOP_ELEMDEC)
|
||||
|
||||
if (cx->typeInferenceEnabled()) {
|
||||
len = JSOP_INCELEM_LENGTH;
|
||||
DO_NEXT_OP(len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Delay fetching of id until we have the object to ensure the proper
|
||||
* evaluation order. See bug 372331.
|
||||
*/
|
||||
id = JSID_VOID;
|
||||
i = -2;
|
||||
goto fetch_incop_obj;
|
||||
/* No-op */
|
||||
END_CASE(JSOP_INCELEM)
|
||||
|
||||
BEGIN_CASE(JSOP_INCPROP)
|
||||
BEGIN_CASE(JSOP_DECPROP)
|
||||
BEGIN_CASE(JSOP_PROPINC)
|
||||
BEGIN_CASE(JSOP_PROPDEC)
|
||||
|
||||
if (cx->typeInferenceEnabled()) {
|
||||
len = JSOP_INCPROP_LENGTH;
|
||||
DO_NEXT_OP(len);
|
||||
}
|
||||
|
||||
LOAD_ATOM(0, atom);
|
||||
id = ATOM_TO_JSID(atom);
|
||||
i = -1;
|
||||
|
||||
fetch_incop_obj:
|
||||
FETCH_OBJECT(cx, i, obj);
|
||||
if (JSID_IS_VOID(id))
|
||||
FETCH_ELEMENT_ID(obj, -1, id);
|
||||
goto do_incop;
|
||||
|
||||
BEGIN_CASE(JSOP_INCNAME)
|
||||
BEGIN_CASE(JSOP_DECNAME)
|
||||
BEGIN_CASE(JSOP_NAMEINC)
|
||||
|
@ -2992,114 +2930,8 @@ BEGIN_CASE(JSOP_INCGNAME)
|
|||
BEGIN_CASE(JSOP_DECGNAME)
|
||||
BEGIN_CASE(JSOP_GNAMEINC)
|
||||
BEGIN_CASE(JSOP_GNAMEDEC)
|
||||
{
|
||||
if (cx->typeInferenceEnabled()) {
|
||||
len = JSOP_INCNAME_LENGTH;
|
||||
DO_NEXT_OP(len);
|
||||
}
|
||||
|
||||
obj = ®s.fp()->scopeChain();
|
||||
|
||||
bool global = (js_CodeSpec[op].format & JOF_GNAME);
|
||||
if (global)
|
||||
obj = obj->getGlobal();
|
||||
|
||||
JSObject *obj2;
|
||||
PropertyCacheEntry *entry;
|
||||
JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
|
||||
if (!atom) {
|
||||
ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
|
||||
if (obj == obj2 && entry->vword.isSlot()) {
|
||||
uint32 slot = entry->vword.toSlot();
|
||||
const Value &rref = obj->nativeGetSlot(slot);
|
||||
int32_t tmp;
|
||||
if (JS_LIKELY(rref.isInt32() && CanIncDecWithoutOverflow(tmp = rref.toInt32()))) {
|
||||
int32_t inc = tmp + ((js_CodeSpec[op].format & JOF_INC) ? 1 : -1);
|
||||
if (!(js_CodeSpec[op].format & JOF_POST))
|
||||
tmp = inc;
|
||||
obj->nativeSetSlot(slot, Int32Value(inc));
|
||||
PUSH_INT32(tmp);
|
||||
len = JSOP_INCNAME_LENGTH + GetDecomposeLength(regs.pc, JSOP_INCNAME_LENGTH);
|
||||
DO_NEXT_OP(len);
|
||||
}
|
||||
}
|
||||
LOAD_ATOM(0, atom);
|
||||
}
|
||||
|
||||
id = ATOM_TO_JSID(atom);
|
||||
JSProperty *prop;
|
||||
if (!js_FindPropertyHelper(cx, id, true, global, &obj, &obj2, &prop))
|
||||
goto error;
|
||||
if (!prop) {
|
||||
atomNotDefined = atom;
|
||||
goto atom_not_defined;
|
||||
}
|
||||
}
|
||||
|
||||
do_incop:
|
||||
{
|
||||
/*
|
||||
* We need a root to store the value to leave on the stack until
|
||||
* we have done with obj->setProperty.
|
||||
*/
|
||||
PUSH_NULL();
|
||||
if (!obj->getGeneric(cx, id, ®s.sp[-1]))
|
||||
goto error;
|
||||
|
||||
const JSCodeSpec *cs = &js_CodeSpec[op];
|
||||
JS_ASSERT(cs->ndefs == 1);
|
||||
JS_ASSERT((cs->format & JOF_TMPSLOT_MASK) >= JOF_TMPSLOT2);
|
||||
|
||||
uint32 format = cs->format;
|
||||
uint32 setPropFlags = (JOF_MODE(format) == JOF_NAME)
|
||||
? JSRESOLVE_ASSIGNING
|
||||
: JSRESOLVE_ASSIGNING | JSRESOLVE_QUALIFIED;
|
||||
|
||||
Value &ref = regs.sp[-1];
|
||||
int32_t tmp;
|
||||
if (JS_LIKELY(ref.isInt32() && CanIncDecWithoutOverflow(tmp = ref.toInt32()))) {
|
||||
int incr = (format & JOF_INC) ? 1 : -1;
|
||||
if (format & JOF_POST)
|
||||
ref.getInt32Ref() = tmp + incr;
|
||||
else
|
||||
ref.getInt32Ref() = tmp += incr;
|
||||
|
||||
{
|
||||
JSAutoResolveFlags rf(cx, setPropFlags);
|
||||
if (!obj->setGeneric(cx, id, &ref, script->strictModeCode))
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* We must set regs.sp[-1] to tmp for both post and pre increments
|
||||
* as the setter overwrites regs.sp[-1].
|
||||
*/
|
||||
ref.setInt32(tmp);
|
||||
} else {
|
||||
/* We need an extra root for the result. */
|
||||
PUSH_NULL();
|
||||
if (!DoIncDec(cx, cs, ®s.sp[-2], ®s.sp[-1]))
|
||||
goto error;
|
||||
|
||||
{
|
||||
JSAutoResolveFlags rf(cx, setPropFlags);
|
||||
if (!obj->setGeneric(cx, id, ®s.sp[-1], script->strictModeCode))
|
||||
goto error;
|
||||
}
|
||||
|
||||
regs.sp--;
|
||||
}
|
||||
|
||||
if (cs->nuses == 0) {
|
||||
/* regs.sp[-1] already contains the result of name increment. */
|
||||
} else {
|
||||
regs.sp[-1 - cs->nuses] = regs.sp[-1];
|
||||
regs.sp -= cs->nuses;
|
||||
}
|
||||
len = cs->length + GetDecomposeLength(regs.pc, cs->length);
|
||||
DO_NEXT_OP(len);
|
||||
}
|
||||
}
|
||||
/* No-op */
|
||||
END_CASE(JSOP_INCPROP)
|
||||
|
||||
{
|
||||
int incr, incr2;
|
||||
|
@ -3164,19 +2996,6 @@ BEGIN_CASE(JSOP_THIS)
|
|||
PUSH_COPY(regs.fp()->thisValue());
|
||||
END_CASE(JSOP_THIS)
|
||||
|
||||
BEGIN_CASE(JSOP_UNBRANDTHIS)
|
||||
{
|
||||
if (!ComputeThis(cx, regs.fp()))
|
||||
goto error;
|
||||
Value &thisv = regs.fp()->thisValue();
|
||||
if (thisv.isObject()) {
|
||||
JSObject *obj = &thisv.toObject();
|
||||
if (obj->isNative())
|
||||
obj->unbrand(cx);
|
||||
}
|
||||
}
|
||||
END_CASE(JSOP_UNBRANDTHIS)
|
||||
|
||||
BEGIN_CASE(JSOP_GETPROP)
|
||||
BEGIN_CASE(JSOP_GETXPROP)
|
||||
BEGIN_CASE(JSOP_LENGTH)
|
||||
|
@ -3235,17 +3054,8 @@ BEGIN_CASE(JSOP_LENGTH)
|
|||
JSAtom *atom;
|
||||
JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom);
|
||||
if (!atom) {
|
||||
ASSERT_VALID_PROPERTY_CACHE_HIT(0, aobj, obj2, entry);
|
||||
if (entry->vword.isFunObj()) {
|
||||
rval.setObject(entry->vword.toFunObj());
|
||||
} else if (entry->vword.isSlot()) {
|
||||
uint32 slot = entry->vword.toSlot();
|
||||
rval = obj2->nativeGetSlot(slot);
|
||||
} else {
|
||||
JS_ASSERT(entry->vword.isShape());
|
||||
const Shape *shape = entry->vword.toShape();
|
||||
NATIVE_GET(cx, obj, obj2, shape, JSGET_METHOD_BARRIER, &rval);
|
||||
}
|
||||
ASSERT_VALID_PROPERTY_CACHE_HIT(aobj, obj2, entry);
|
||||
NATIVE_GET(cx, obj, obj2, entry->prop, JSGET_METHOD_BARRIER, &rval);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -3303,17 +3113,8 @@ BEGIN_CASE(JSOP_CALLPROP)
|
|||
JSAtom *atom;
|
||||
JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom);
|
||||
if (!atom) {
|
||||
ASSERT_VALID_PROPERTY_CACHE_HIT(0, aobj, obj2, entry);
|
||||
if (entry->vword.isFunObj()) {
|
||||
rval.setObject(entry->vword.toFunObj());
|
||||
} else if (entry->vword.isSlot()) {
|
||||
uint32 slot = entry->vword.toSlot();
|
||||
rval = obj2->nativeGetSlot(slot);
|
||||
} else {
|
||||
JS_ASSERT(entry->vword.isShape());
|
||||
const Shape *shape = entry->vword.toShape();
|
||||
NATIVE_GET(cx, &objv.toObject(), obj2, shape, JSGET_NO_METHOD_BARRIER, &rval);
|
||||
}
|
||||
ASSERT_VALID_PROPERTY_CACHE_HIT(aobj, obj2, entry);
|
||||
NATIVE_GET(cx, &objv.toObject(), obj2, entry->prop, JSGET_NO_METHOD_BARRIER, &rval);
|
||||
regs.sp[-1] = rval;
|
||||
assertSameCompartment(cx, regs.sp[-1]);
|
||||
PUSH_COPY(lval);
|
||||
|
@ -3361,11 +3162,6 @@ BEGIN_CASE(JSOP_CALLPROP)
|
|||
}
|
||||
END_CASE(JSOP_CALLPROP)
|
||||
|
||||
BEGIN_CASE(JSOP_UNBRAND)
|
||||
JS_ASSERT(regs.sp - regs.fp()->slots() >= 1);
|
||||
regs.sp[-1].toObject().unbrand(cx);
|
||||
END_CASE(JSOP_UNBRAND)
|
||||
|
||||
BEGIN_CASE(JSOP_SETGNAME)
|
||||
BEGIN_CASE(JSOP_SETNAME)
|
||||
BEGIN_CASE(JSOP_SETPROP)
|
||||
|
@ -3411,89 +3207,30 @@ BEGIN_CASE(JSOP_SETMETHOD)
|
|||
* know that the entry applies to regs.pc and that obj's shape
|
||||
* matches.
|
||||
*
|
||||
* The entry predicts either a new property to be added directly to
|
||||
* obj by this set, or on an existing "own" property, or on a
|
||||
* prototype property that has a setter.
|
||||
* The entry predicts a set either an existing "own" property, or
|
||||
* on a prototype property that has a setter.
|
||||
*/
|
||||
const Shape *shape = entry->vword.toShape();
|
||||
const Shape *shape = entry->prop;
|
||||
JS_ASSERT_IF(shape->isDataDescriptor(), shape->writable());
|
||||
JS_ASSERT_IF(shape->hasSlot(), entry->vcapTag() == 0);
|
||||
JS_ASSERT_IF(shape->hasSlot(), !entry->vindex);
|
||||
|
||||
/*
|
||||
* Fastest path: check whether obj already has the cached shape and
|
||||
* call NATIVE_SET and break to get out of the do-while(0). But we
|
||||
* can call NATIVE_SET only for a direct or proto-setter hit.
|
||||
*/
|
||||
if (!entry->adding()) {
|
||||
if (entry->vcapTag() == 0 ||
|
||||
((obj2 = obj->getProto()) && obj2->shape() == entry->vshape()))
|
||||
{
|
||||
if (entry->vindex == 0 ||
|
||||
((obj2 = obj->getProto()) && obj2->lastProperty() == entry->pshape)) {
|
||||
#ifdef DEBUG
|
||||
if (entry->directHit()) {
|
||||
JS_ASSERT(obj->nativeContains(cx, *shape));
|
||||
} else {
|
||||
JS_ASSERT(obj2->nativeContains(cx, *shape));
|
||||
JS_ASSERT(entry->vcapTag() == 1);
|
||||
JS_ASSERT(entry->kshape != entry->vshape());
|
||||
JS_ASSERT(!shape->hasSlot());
|
||||
}
|
||||
if (entry->directHit()) {
|
||||
JS_ASSERT(obj->nativeContains(cx, *shape));
|
||||
} else {
|
||||
JS_ASSERT(obj2->nativeContains(cx, *shape));
|
||||
JS_ASSERT(entry->vindex == 1);
|
||||
JS_ASSERT(entry->kshape != entry->pshape);
|
||||
JS_ASSERT(!shape->hasSlot());
|
||||
}
|
||||
#endif
|
||||
|
||||
PCMETER(cache->pchits++);
|
||||
PCMETER(cache->setpchits++);
|
||||
NATIVE_SET(cx, obj, shape, entry, script->strictModeCode, &rval);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
JS_ASSERT(obj->isExtensible());
|
||||
|
||||
if (obj->nativeEmpty()) {
|
||||
if (!obj->ensureClassReservedSlotsForEmptyObject(cx))
|
||||
goto error;
|
||||
}
|
||||
|
||||
uint32 slot;
|
||||
if (shape->previous() == obj->lastProperty() &&
|
||||
entry->vshape() == rt->protoHazardShape &&
|
||||
shape->hasDefaultSetter() &&
|
||||
obj->getClass()->addProperty == JS_PropertyStub) {
|
||||
slot = shape->slot;
|
||||
JS_ASSERT(slot == obj->slotSpan());
|
||||
|
||||
/*
|
||||
* Fast path: adding a plain old property that was once at
|
||||
* the frontier of the property tree, whose slot is next to
|
||||
* claim among the already-allocated slots in obj, where
|
||||
* shape->table has not been created yet.
|
||||
*/
|
||||
PCMETER(cache->pchits++);
|
||||
PCMETER(cache->addpchits++);
|
||||
|
||||
if (slot < obj->numSlots()) {
|
||||
JS_ASSERT(obj->getSlot(slot).isUndefined());
|
||||
} else {
|
||||
if (!obj->allocSlot(cx, &slot))
|
||||
goto error;
|
||||
JS_ASSERT(slot == shape->slot);
|
||||
}
|
||||
|
||||
/* Simply extend obj's property tree path with shape! */
|
||||
obj->extend(cx, shape);
|
||||
|
||||
/*
|
||||
* No method change check here because here we are adding a
|
||||
* new property, not updating an existing slot's value that
|
||||
* might contain a method of a branded shape.
|
||||
*/
|
||||
obj->nativeSetSlotWithType(cx, shape, rval);
|
||||
|
||||
/*
|
||||
* Purge the property cache of the id we may have just
|
||||
* shadowed in obj's scope and proto chains.
|
||||
*/
|
||||
js_PurgeScopeChain(cx, obj, shape->propid);
|
||||
break;
|
||||
}
|
||||
PCMETER(cache->pchits++);
|
||||
PCMETER(cache->setpchits++);
|
||||
NATIVE_SET(cx, obj, shape, entry, script->strictModeCode, &rval);
|
||||
break;
|
||||
}
|
||||
PCMETER(cache->setpcmisses++);
|
||||
|
||||
|
@ -3713,11 +3450,10 @@ BEGIN_CASE(JSOP_FUNAPPLY)
|
|||
|
||||
bool construct = (*regs.pc == JSOP_NEW);
|
||||
|
||||
JSObject *callee;
|
||||
JSFunction *fun;
|
||||
|
||||
/* Don't bother trying to fast-path calls to scripted non-constructors. */
|
||||
if (!IsFunctionObject(args.calleev(), &callee, &fun) || !fun->isInterpretedConstructor()) {
|
||||
if (!IsFunctionObject(args.calleev(), &fun) || !fun->isInterpretedConstructor()) {
|
||||
if (construct) {
|
||||
if (!InvokeConstructorKernel(cx, args))
|
||||
goto error;
|
||||
|
@ -3737,7 +3473,7 @@ BEGIN_CASE(JSOP_FUNAPPLY)
|
|||
InitialFrameFlags initial = construct ? INITIAL_CONSTRUCT : INITIAL_NONE;
|
||||
|
||||
JSScript *newScript = fun->script();
|
||||
if (!cx->stack.pushInlineFrame(cx, regs, args, *callee, fun, newScript, initial))
|
||||
if (!cx->stack.pushInlineFrame(cx, regs, args, *fun, newScript, initial))
|
||||
goto error;
|
||||
|
||||
RESTORE_INTERP_VARS();
|
||||
|
@ -3804,7 +3540,6 @@ BEGIN_CASE(JSOP_CALLNAME)
|
|||
if (global)
|
||||
obj = obj->getGlobal();
|
||||
|
||||
const Shape *shape;
|
||||
Value rval;
|
||||
|
||||
PropertyCacheEntry *entry;
|
||||
|
@ -3812,18 +3547,9 @@ BEGIN_CASE(JSOP_CALLNAME)
|
|||
JSAtom *atom;
|
||||
JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
|
||||
if (!atom) {
|
||||
ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
|
||||
if (entry->vword.isFunObj()) {
|
||||
PUSH_OBJECT(entry->vword.toFunObj());
|
||||
} else if (entry->vword.isSlot()) {
|
||||
uintN slot = entry->vword.toSlot();
|
||||
PUSH_COPY(obj2->nativeGetSlot(slot));
|
||||
} else {
|
||||
JS_ASSERT(entry->vword.isShape());
|
||||
shape = entry->vword.toShape();
|
||||
NATIVE_GET(cx, obj, obj2, shape, JSGET_METHOD_BARRIER, &rval);
|
||||
PUSH_COPY(rval);
|
||||
}
|
||||
ASSERT_VALID_PROPERTY_CACHE_HIT(obj, obj2, entry);
|
||||
NATIVE_GET(cx, obj, obj2, entry->prop, JSGET_METHOD_BARRIER, &rval);
|
||||
PUSH_COPY(rval);
|
||||
|
||||
TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
|
||||
|
||||
|
@ -3856,7 +3582,7 @@ BEGIN_CASE(JSOP_CALLNAME)
|
|||
if (!obj->getGeneric(cx, id, &rval))
|
||||
goto error;
|
||||
} else {
|
||||
shape = (Shape *)prop;
|
||||
Shape *shape = (Shape *)prop;
|
||||
JSObject *normalized = obj;
|
||||
if (normalized->getClass() == &WithClass && !shape->hasDefaultGetter())
|
||||
normalized = js_UnwrapWithObject(cx, normalized);
|
||||
|
@ -4232,8 +3958,7 @@ BEGIN_CASE(JSOP_CALLFCSLOT)
|
|||
uintN index = GET_UINT16(regs.pc);
|
||||
JSObject *obj = &argv[-2].toObject();
|
||||
|
||||
JS_ASSERT(index < obj->getFunctionPrivate()->script()->bindings.countUpvars());
|
||||
PUSH_COPY(obj->getFlatClosureUpvar(index));
|
||||
PUSH_COPY(obj->toFunction()->getFlatClosureUpvar(index));
|
||||
TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
|
||||
if (op == JSOP_CALLFCSLOT)
|
||||
PUSH_UNDEFINED();
|
||||
|
@ -4324,8 +4049,8 @@ BEGIN_CASE(JSOP_DEFFUN)
|
|||
* windows, and user-defined JS functions precompiled and then shared among
|
||||
* requests in server-side JS.
|
||||
*/
|
||||
if (obj->getParent() != obj2) {
|
||||
obj = CloneFunctionObject(cx, fun, obj2, true);
|
||||
if (obj->toFunction()->environment() != obj2) {
|
||||
obj = CloneFunctionObjectIfNotSingleton(cx, fun, obj2);
|
||||
if (!obj)
|
||||
goto error;
|
||||
JS_ASSERT_IF(script->hasGlobal(), obj->getProto() == fun->getProto());
|
||||
|
@ -4451,7 +4176,7 @@ BEGIN_CASE(JSOP_DEFLOCALFUN)
|
|||
JSObject *obj = fun;
|
||||
|
||||
if (fun->isNullClosure()) {
|
||||
obj = CloneFunctionObject(cx, fun, ®s.fp()->scopeChain(), true);
|
||||
obj = CloneFunctionObjectIfNotSingleton(cx, fun, ®s.fp()->scopeChain());
|
||||
if (!obj)
|
||||
goto error;
|
||||
} else {
|
||||
|
@ -4460,8 +4185,8 @@ BEGIN_CASE(JSOP_DEFLOCALFUN)
|
|||
if (!parent)
|
||||
goto error;
|
||||
|
||||
if (obj->getParent() != parent) {
|
||||
obj = CloneFunctionObject(cx, fun, parent, true);
|
||||
if (obj->toFunction()->environment() != parent) {
|
||||
obj = CloneFunctionObjectIfNotSingleton(cx, fun, parent);
|
||||
if (!obj)
|
||||
goto error;
|
||||
}
|
||||
|
@ -4517,8 +4242,7 @@ BEGIN_CASE(JSOP_LAMBDA)
|
|||
JSObject *obj2 = &lref.toObject();
|
||||
JS_ASSERT(obj2->isObject());
|
||||
#endif
|
||||
|
||||
fun->setMethodAtom(script->getAtom(GET_FULL_INDEX(pc2 - regs.pc)));
|
||||
JS_ASSERT(fun->methodAtom() == script->getAtom(GET_FULL_INDEX(pc2 - regs.pc)));
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -4529,7 +4253,7 @@ BEGIN_CASE(JSOP_LAMBDA)
|
|||
#endif
|
||||
const Value &lref = regs.sp[-1];
|
||||
if (lref.isObject() && lref.toObject().canHaveMethodBarrier()) {
|
||||
fun->setMethodAtom(script->getAtom(GET_FULL_INDEX(pc2 - regs.pc)));
|
||||
JS_ASSERT(fun->methodAtom() == script->getAtom(GET_FULL_INDEX(pc2 - regs.pc)));
|
||||
break;
|
||||
}
|
||||
} else if (op2 == JSOP_CALL) {
|
||||
|
@ -4548,11 +4272,10 @@ BEGIN_CASE(JSOP_LAMBDA)
|
|||
* is the callee for this JSOP_CALL.
|
||||
*/
|
||||
const Value &cref = regs.sp[1 - (iargc + 2)];
|
||||
JSObject *callee;
|
||||
JSFunction *fun;
|
||||
|
||||
if (IsFunctionObject(cref, &callee)) {
|
||||
JSFunction *calleeFun = callee->getFunctionPrivate();
|
||||
if (Native native = calleeFun->maybeNative()) {
|
||||
if (IsFunctionObject(cref, &fun)) {
|
||||
if (Native native = fun->maybeNative()) {
|
||||
if ((iargc == 1 && native == array_sort) ||
|
||||
(iargc == 2 && native == str_replace)) {
|
||||
break;
|
||||
|
@ -4573,7 +4296,7 @@ BEGIN_CASE(JSOP_LAMBDA)
|
|||
goto error;
|
||||
}
|
||||
|
||||
obj = CloneFunctionObject(cx, fun, parent, true);
|
||||
obj = CloneFunctionObjectIfNotSingleton(cx, fun, parent);
|
||||
if (!obj)
|
||||
goto error;
|
||||
} while (0);
|
||||
|
@ -4734,7 +4457,7 @@ BEGIN_CASE(JSOP_NEWINIT)
|
|||
if (i == JSProto_Array) {
|
||||
obj = NewDenseEmptyArray(cx);
|
||||
} else {
|
||||
gc::AllocKind kind = GuessObjectGCKind(0, false);
|
||||
gc::AllocKind kind = GuessObjectGCKind(0);
|
||||
obj = NewBuiltinClassInstance(cx, &ObjectClass, kind);
|
||||
}
|
||||
|
||||
|
@ -4806,48 +4529,24 @@ BEGIN_CASE(JSOP_INITMETHOD)
|
|||
JS_ASSERT(obj->isObject());
|
||||
|
||||
/*
|
||||
* Probe the property cache.
|
||||
*
|
||||
* On a hit, if the cached shape has a non-default setter, it must be
|
||||
* __proto__. If shape->previous() != obj->lastProperty(), there must be a
|
||||
* repeated property name. The fast path does not handle these two cases.
|
||||
* Probe the property cache to see if this is a set on an existing property
|
||||
* added by a NEWOBJECT or a previous INITPROP. If the cached shape has a
|
||||
* non-default setter, it must be __proto__, so don't handle this.
|
||||
*/
|
||||
PropertyCacheEntry *entry;
|
||||
const Shape *shape;
|
||||
if (JS_PROPERTY_CACHE(cx).testForInit(rt, regs.pc, obj, &shape, &entry) &&
|
||||
shape->hasDefaultSetter() &&
|
||||
shape->previous() == obj->lastProperty())
|
||||
{
|
||||
JSObject *obj2;
|
||||
JSAtom *atom;
|
||||
if (JS_PROPERTY_CACHE(cx).testForSet(cx, regs.pc, obj, &entry, &obj2, &atom) &&
|
||||
entry->prop->hasDefaultSetter() &&
|
||||
entry->vindex == 0) {
|
||||
JS_ASSERT(obj == obj2);
|
||||
/* Fast path. Property cache hit. */
|
||||
uint32 slot = shape->slot;
|
||||
|
||||
JS_ASSERT(slot == obj->slotSpan());
|
||||
JS_ASSERT(slot >= JSSLOT_FREE(obj->getClass()));
|
||||
if (slot < obj->numSlots()) {
|
||||
JS_ASSERT(obj->getSlot(slot).isUndefined());
|
||||
} else {
|
||||
if (!obj->allocSlot(cx, &slot))
|
||||
goto error;
|
||||
JS_ASSERT(slot == shape->slot);
|
||||
}
|
||||
|
||||
/* A new object, or one we just extended in a recent initprop op. */
|
||||
JS_ASSERT(!obj->lastProperty() ||
|
||||
obj->shape() == obj->lastProperty()->shapeid);
|
||||
obj->extend(cx, shape);
|
||||
|
||||
/*
|
||||
* No method change check here because here we are adding a new
|
||||
* property, not updating an existing slot's value that might
|
||||
* contain a method of a branded shape.
|
||||
*/
|
||||
obj->nativeSetSlotWithType(cx, shape, rval);
|
||||
obj->nativeSetSlotWithType(cx, entry->prop, rval);
|
||||
} else {
|
||||
PCMETER(JS_PROPERTY_CACHE(cx).inipcmisses++);
|
||||
LOAD_ATOM(0, atom);
|
||||
|
||||
/* Get the immediate property name into id. */
|
||||
JSAtom *atom;
|
||||
LOAD_ATOM(0, atom);
|
||||
jsid id = ATOM_TO_JSID(atom);
|
||||
|
||||
uintN defineHow = (op == JSOP_INITMETHOD)
|
||||
|
@ -5500,13 +5199,13 @@ BEGIN_CASE(JSOP_ENTERBLOCK)
|
|||
*/
|
||||
JSObject *obj2 = ®s.fp()->scopeChain();
|
||||
while (obj2->isWith())
|
||||
obj2 = obj2->getParent();
|
||||
obj2 = obj2->internalScopeChain();
|
||||
if (obj2->isBlock() &&
|
||||
obj2->getPrivate() == js_FloatingFrameIfGenerator(cx, regs.fp())) {
|
||||
JSObject *youngestProto = obj2->getProto();
|
||||
JS_ASSERT(youngestProto->isStaticBlock());
|
||||
JSObject *parent = obj;
|
||||
while ((parent = parent->getParent()) != youngestProto)
|
||||
while ((parent = parent->scopeChain()) != youngestProto)
|
||||
JS_ASSERT(parent);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -348,6 +348,14 @@ Debug_SetValueRangeToCrashOnTouch(Value *vec, size_t len)
|
|||
#endif
|
||||
}
|
||||
|
||||
static JS_ALWAYS_INLINE void
|
||||
Debug_SetValueRangeToCrashOnTouch(HeapValue *vec, size_t len)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug_SetValueRangeToCrashOnTouch((Value *) vec, len);
|
||||
#endif
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* jsinterp_h___ */
|
||||
|
|
|
@ -92,7 +92,6 @@ static JSObject *iterator_iterator(JSContext *cx, JSObject *obj, JSBool keysonly
|
|||
Class js::IteratorClass = {
|
||||
"Iterator",
|
||||
JSCLASS_HAS_PRIVATE |
|
||||
JSCLASS_CONCURRENT_FINALIZER |
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator),
|
||||
JS_PropertyStub, /* addProperty */
|
||||
JS_PropertyStub, /* delProperty */
|
||||
|
@ -118,6 +117,8 @@ Class js::IteratorClass = {
|
|||
}
|
||||
};
|
||||
|
||||
static const gc::AllocKind ITERATOR_FINALIZE_KIND = gc::FINALIZE_OBJECT2;
|
||||
|
||||
void
|
||||
NativeIterator::mark(JSTracer *trc)
|
||||
{
|
||||
|
@ -220,8 +221,8 @@ EnumerateNativeProperties(JSContext *cx, JSObject *obj, JSObject *pobj, uintN fl
|
|||
for (Shape::Range r = pobj->lastProperty()->all(); !r.empty(); r.popFront()) {
|
||||
const Shape &shape = r.front();
|
||||
|
||||
if (!JSID_IS_DEFAULT_XML_NAMESPACE(shape.propid) &&
|
||||
!Enumerate(cx, obj, pobj, shape.propid, shape.enumerable(), flags, ht, props))
|
||||
if (!JSID_IS_DEFAULT_XML_NAMESPACE(shape.propid()) &&
|
||||
!Enumerate(cx, obj, pobj, shape.propid(), shape.enumerable(), flags, ht, props))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -412,23 +413,21 @@ static inline JSObject *
|
|||
NewIteratorObject(JSContext *cx, uintN flags)
|
||||
{
|
||||
if (flags & JSITER_ENUMERATE) {
|
||||
/*
|
||||
* Non-escaping native enumerator objects do not need map, proto, or
|
||||
* parent. However, code in jstracer.cpp and elsewhere may find such a
|
||||
* native enumerator object via the stack and (as for all objects that
|
||||
* are not stillborn, with the exception of "NoSuchMethod" internal
|
||||
* helper objects) expect it to have a non-null map pointer, so we
|
||||
* share an empty Enumerator scope in the runtime.
|
||||
*/
|
||||
JSObject *obj = js_NewGCObject(cx, FINALIZE_OBJECT0);
|
||||
types::TypeObject *type = cx->compartment->getEmptyType(cx);
|
||||
if (!type)
|
||||
return NULL;
|
||||
|
||||
Shape *emptyEnumeratorShape = EmptyShape::getInitialShape(cx, &IteratorClass, NULL, NULL,
|
||||
ITERATOR_FINALIZE_KIND);
|
||||
if (!emptyEnumeratorShape)
|
||||
return NULL;
|
||||
|
||||
JSObject *obj = JSObject::create(cx, ITERATOR_FINALIZE_KIND,
|
||||
emptyEnumeratorShape, type, NULL);
|
||||
if (!obj)
|
||||
return NULL;
|
||||
|
||||
EmptyShape *emptyEnumeratorShape = EmptyShape::getEmptyEnumeratorShape(cx);
|
||||
if (!emptyEnumeratorShape)
|
||||
return NULL;
|
||||
obj->init(cx, &IteratorClass, &types::emptyTypeObject, NULL, NULL, false);
|
||||
obj->setMap(emptyEnumeratorShape);
|
||||
JS_ASSERT(obj->numFixedSlots() == JSObject::ITER_CLASS_NFIXED_SLOTS);
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
@ -440,7 +439,7 @@ NativeIterator::allocateIterator(JSContext *cx, uint32 slength, const AutoIdVect
|
|||
{
|
||||
size_t plength = props.length();
|
||||
NativeIterator *ni = (NativeIterator *)
|
||||
cx->malloc_(sizeof(NativeIterator) + plength * sizeof(jsid) + slength * sizeof(uint32));
|
||||
cx->malloc_(sizeof(NativeIterator) + plength * sizeof(jsid) + slength * sizeof(Shape *));
|
||||
if (!ni)
|
||||
return NULL;
|
||||
ni->props_array = ni->props_cursor = (HeapId *) (ni + 1);
|
||||
|
@ -457,7 +456,7 @@ NativeIterator::init(JSObject *obj, uintN flags, uint32 slength, uint32 key)
|
|||
{
|
||||
this->obj.init(obj);
|
||||
this->flags = flags;
|
||||
this->shapes_array = (uint32 *) this->props_end;
|
||||
this->shapes_array = (const Shape **) this->props_end;
|
||||
this->shapes_length = slength;
|
||||
this->shapes_key = key;
|
||||
}
|
||||
|
@ -482,7 +481,8 @@ VectorToKeyIterator(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector &key
|
|||
JS_ASSERT(!(flags & JSITER_FOREACH));
|
||||
|
||||
if (obj) {
|
||||
obj->flags |= JSObject::ITERATED;
|
||||
if (obj->hasSingletonType() && !obj->setIteratedSingleton(cx))
|
||||
return false;
|
||||
types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED);
|
||||
}
|
||||
|
||||
|
@ -506,7 +506,7 @@ VectorToKeyIterator(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector &key
|
|||
JSObject *pobj = obj;
|
||||
size_t ind = 0;
|
||||
do {
|
||||
ni->shapes_array[ind++] = pobj->shape();
|
||||
ni->shapes_array[ind++] = pobj->lastProperty();
|
||||
pobj = pobj->getProto();
|
||||
} while (pobj);
|
||||
JS_ASSERT(ind == slength);
|
||||
|
@ -534,7 +534,8 @@ VectorToValueIterator(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector &k
|
|||
JS_ASSERT(flags & JSITER_FOREACH);
|
||||
|
||||
if (obj) {
|
||||
obj->flags |= JSObject::ITERATED;
|
||||
if (obj->hasSingletonType() && !obj->setIteratedSingleton(cx))
|
||||
return false;
|
||||
types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED);
|
||||
}
|
||||
|
||||
|
@ -574,7 +575,7 @@ UpdateNativeIterator(NativeIterator *ni, JSObject *obj)
|
|||
bool
|
||||
GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp)
|
||||
{
|
||||
Vector<uint32, 8> shapes(cx);
|
||||
Vector<const Shape *, 8> shapes(cx);
|
||||
uint32 key = 0;
|
||||
|
||||
bool keysOnly = (flags == JSITER_ENUMERATE);
|
||||
|
@ -603,9 +604,9 @@ GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp)
|
|||
NativeIterator *lastni = last->getNativeIterator();
|
||||
if (!(lastni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) &&
|
||||
obj->isNative() &&
|
||||
obj->shape() == lastni->shapes_array[0] &&
|
||||
obj->lastProperty() == lastni->shapes_array[0] &&
|
||||
proto && proto->isNative() &&
|
||||
proto->shape() == lastni->shapes_array[1] &&
|
||||
proto->lastProperty() == lastni->shapes_array[1] &&
|
||||
!proto->getProto()) {
|
||||
vp->setObject(*last);
|
||||
UpdateNativeIterator(lastni, obj);
|
||||
|
@ -623,14 +624,15 @@ GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp)
|
|||
JSObject *pobj = obj;
|
||||
do {
|
||||
if (!pobj->isNative() ||
|
||||
pobj->hasUncacheableProto() ||
|
||||
obj->getOps()->enumerate ||
|
||||
pobj->getClass()->enumerate != JS_EnumerateStub) {
|
||||
shapes.clear();
|
||||
goto miss;
|
||||
}
|
||||
uint32 shape = pobj->shape();
|
||||
key = (key + (key << 16)) ^ shape;
|
||||
if (!shapes.append(shape))
|
||||
const Shape *shape = pobj->lastProperty();
|
||||
key = (key + (key << 16)) ^ ((jsuword)shape >> 3);
|
||||
if (!shapes.append((Shape *) shape))
|
||||
return false;
|
||||
pobj = pobj->getProto();
|
||||
} while (pobj);
|
||||
|
@ -1210,7 +1212,7 @@ js_NewGenerator(JSContext *cx)
|
|||
JSObject *proto = global->getOrCreateGeneratorPrototype(cx);
|
||||
if (!proto)
|
||||
return NULL;
|
||||
JSObject *obj = NewNonFunction<WithProto::Given>(cx, &GeneratorClass, proto, global);
|
||||
JSObject *obj = NewObjectWithGivenProto(cx, &GeneratorClass, proto, global);
|
||||
if (!obj)
|
||||
return NULL;
|
||||
|
||||
|
|
|
@ -61,11 +61,11 @@
|
|||
namespace js {
|
||||
|
||||
struct NativeIterator {
|
||||
HeapPtrObject obj;
|
||||
HeapPtrObject obj;
|
||||
HeapId *props_array;
|
||||
HeapId *props_cursor;
|
||||
HeapId *props_end;
|
||||
uint32 *shapes_array;
|
||||
const Shape **shapes_array;
|
||||
uint32 shapes_length;
|
||||
uint32 shapes_key;
|
||||
uint32 flags;
|
||||
|
@ -223,16 +223,6 @@ js_LiveFrameIfGenerator(js::StackFrame *fp)
|
|||
|
||||
#endif
|
||||
|
||||
namespace js {
|
||||
|
||||
static inline bool
|
||||
IsStopIteration(const js::Value &v)
|
||||
{
|
||||
return v.isObject() && v.toObject().isStopIteration();
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
extern JSObject *
|
||||
js_InitIteratorClasses(JSContext *cx, JSObject *obj);
|
||||
|
||||
|
|
|
@ -703,7 +703,7 @@ js_IsMathFunction(Native native)
|
|||
JSObject *
|
||||
js_InitMathClass(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JSObject *Math = NewNonFunction<WithProto::Class>(cx, &MathClass, NULL, obj);
|
||||
JSObject *Math = NewObjectWithClassProto(cx, &MathClass, NULL, obj);
|
||||
if (!Math || !Math->setSingletonType(cx))
|
||||
return NULL;
|
||||
|
||||
|
|
1374
js/src/jsobj.cpp
1374
js/src/jsobj.cpp
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
1206
js/src/jsobj.h
1206
js/src/jsobj.h
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -930,7 +930,7 @@ static JSFunctionSpec json_static_methods[] = {
|
|||
JSObject *
|
||||
js_InitJSONClass(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JSObject *JSON = NewNonFunction<WithProto::Class>(cx, &JSONClass, NULL, obj);
|
||||
JSObject *JSON = NewObjectWithClassProto(cx, &JSONClass, NULL, obj);
|
||||
if (!JSON || !JSON->setSingletonType(cx))
|
||||
return NULL;
|
||||
|
||||
|
|
|
@ -537,12 +537,12 @@ ToDisassemblySource(JSContext *cx, jsval v, JSAutoByteString *bytes)
|
|||
while (!r.empty()) {
|
||||
const Shape &shape = r.front();
|
||||
JSAutoByteString bytes;
|
||||
if (!js_AtomToPrintableString(cx, JSID_TO_ATOM(shape.propid), &bytes))
|
||||
if (!js_AtomToPrintableString(cx, JSID_TO_ATOM(shape.propid()), &bytes))
|
||||
return false;
|
||||
|
||||
r.popFront();
|
||||
source = JS_sprintf_append(source, "%s: %d%s",
|
||||
bytes.ptr(), shape.shortid,
|
||||
bytes.ptr(), shape.shortid(),
|
||||
!r.empty() ? ", " : "");
|
||||
if (!source)
|
||||
return false;
|
||||
|
@ -556,8 +556,7 @@ ToDisassemblySource(JSContext *cx, jsval v, JSAutoByteString *bytes)
|
|||
}
|
||||
|
||||
if (clasp == &FunctionClass) {
|
||||
JSFunction *fun = obj->getFunctionPrivate();
|
||||
JSString *str = JS_DecompileFunction(cx, fun, JS_DONT_PRETTY_PRINT);
|
||||
JSString *str = JS_DecompileFunction(cx, obj->toFunction(), JS_DONT_PRETTY_PRINT);
|
||||
if (!str)
|
||||
return false;
|
||||
return bytes->encode(cx, str);
|
||||
|
@ -1554,10 +1553,10 @@ GetLocalInSlot(SprintStack *ss, jsint i, jsint slot, JSObject *obj)
|
|||
for (Shape::Range r(obj->lastProperty()); !r.empty(); r.popFront()) {
|
||||
const Shape &shape = r.front();
|
||||
|
||||
if (shape.shortid == slot) {
|
||||
LOCAL_ASSERT(JSID_IS_ATOM(shape.propid));
|
||||
|
||||
JSAtom *atom = JSID_TO_ATOM(shape.propid);
|
||||
if (shape.shortid() == slot) {
|
||||
LOCAL_ASSERT(JSID_IS_ATOM(shape.propid()));
|
||||
|
||||
JSAtom *atom = JSID_TO_ATOM(shape.propid());
|
||||
const char *rval = QuoteString(&ss->sprinter, atom, 0);
|
||||
if (!rval)
|
||||
return NULL;
|
||||
|
@ -2049,8 +2048,8 @@ GetBlockNames(JSContext *cx, JSObject *blockObj, AtomVector *atoms)
|
|||
const Shape &shape = r.front();
|
||||
LOCAL_ASSERT(shape.hasShortID());
|
||||
--i;
|
||||
LOCAL_ASSERT((uintN)shape.shortid == i);
|
||||
(*atoms)[i] = JSID_TO_ATOM(shape.propid);
|
||||
LOCAL_ASSERT((uintN)shape.shortid() == i);
|
||||
(*atoms)[i] = JSID_TO_ATOM(shape.propid());
|
||||
}
|
||||
|
||||
LOCAL_ASSERT(i == 0);
|
||||
|
@ -2320,7 +2319,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
|||
uint32 format = cs->format;
|
||||
if (((fp && pc == fp->pcQuadratic(cx)) ||
|
||||
(pc == startpc && nuses != 0)) &&
|
||||
format & (JOF_SET|JOF_DEL|JOF_INCDEC|JOF_FOR|JOF_VARPROP)) {
|
||||
format & (JOF_SET|JOF_DEL|JOF_INCDEC|JOF_VARPROP)) {
|
||||
uint32 mode = JOF_MODE(format);
|
||||
if (mode == JOF_NAME) {
|
||||
/*
|
||||
|
|
|
@ -109,7 +109,6 @@ typedef enum JSOp {
|
|||
#define JOF_INC (2U<<10) /* increment (++, not --) opcode */
|
||||
#define JOF_INCDEC (3U<<10) /* increment or decrement opcode */
|
||||
#define JOF_POST (1U<<12) /* postorder increment or decrement */
|
||||
#define JOF_FOR (1U<<13) /* for-in property op (akin to JOF_SET) */
|
||||
#define JOF_ASSIGNING JOF_SET /* hint for Class.resolve, used for ops
|
||||
that do simplex assignment */
|
||||
#define JOF_DETECTING (1U<<14) /* object detection for JSNewResolveOp */
|
||||
|
|
|
@ -578,10 +578,8 @@ OPDEF(JSOP_OBJTOP, 222,"objtop", NULL, 3, 0, 0, 0, JOF_UINT16
|
|||
*/
|
||||
OPDEF(JSOP_SETMETHOD, 223,"setmethod", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)
|
||||
OPDEF(JSOP_INITMETHOD, 224,"initmethod", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)
|
||||
OPDEF(JSOP_UNBRAND, 225,"unbrand", NULL, 1, 1, 1, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_UNBRANDTHIS, 226,"unbrandthis", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
|
||||
OPDEF(JSOP_SHARPINIT, 227,"sharpinit", NULL, 3, 0, 0, 0, JOF_UINT16|JOF_SHARPSLOT)
|
||||
OPDEF(JSOP_SHARPINIT, 225,"sharpinit", NULL, 3, 0, 0, 0, JOF_UINT16|JOF_SHARPSLOT)
|
||||
|
||||
/* Pop the stack, convert to a jsid (int or string), and push back. */
|
||||
OPDEF(JSOP_TOID, 228, "toid", NULL, 1, 1, 1, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_TOID, 226, "toid", NULL, 1, 1, 1, 0, JOF_BYTE)
|
||||
|
|
|
@ -137,6 +137,9 @@ bool resizeHeap(JSCompartment *compartment, size_t oldSize, size_t newSize);
|
|||
*/
|
||||
bool createObject(JSContext *cx, JSObject *obj);
|
||||
|
||||
/* Resize events are being tracked. */
|
||||
bool objectResizeActive();
|
||||
|
||||
/* Object has been resized */
|
||||
bool resizeObject(JSContext *cx, JSObject *obj, size_t oldSize, size_t newSize);
|
||||
|
||||
|
@ -489,6 +492,17 @@ Probes::finalizeObject(JSObject *obj)
|
|||
return ok;
|
||||
}
|
||||
|
||||
inline bool
|
||||
Probes::objectResizeActive()
|
||||
{
|
||||
#ifdef MOZ_ETW
|
||||
if (ProfilingActive)
|
||||
return true;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool
|
||||
Probes::resizeObject(JSContext *cx, JSObject *obj, size_t oldSize, size_t newSize)
|
||||
{
|
||||
|
|
|
@ -46,26 +46,17 @@
|
|||
|
||||
using namespace js;
|
||||
|
||||
JS_STATIC_ASSERT(sizeof(PCVal) == sizeof(jsuword));
|
||||
|
||||
JS_REQUIRES_STACK PropertyCacheEntry *
|
||||
PropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, JSObject *pobj,
|
||||
const Shape *shape, JSBool adding)
|
||||
const Shape *shape)
|
||||
{
|
||||
jsuword kshape, vshape;
|
||||
JSOp op;
|
||||
const JSCodeSpec *cs;
|
||||
PCVal vword;
|
||||
PropertyCacheEntry *entry;
|
||||
|
||||
JS_ASSERT(this == &JS_PROPERTY_CACHE(cx));
|
||||
JS_ASSERT(!cx->runtime->gcRunning);
|
||||
|
||||
if (js_IsPropertyCacheDisabled(cx)) {
|
||||
PCMETER(disfills++);
|
||||
return JS_NO_PROP_CACHE_FILL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for fill from js_SetPropertyHelper where the setter removed shape
|
||||
* from pobj (via unwatch or delete, e.g.).
|
||||
|
@ -75,15 +66,6 @@ PropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, JSObject *po
|
|||
return JS_NO_PROP_CACHE_FILL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dictionary-mode objects have unique shapes, so there is no way to cache
|
||||
* a prediction of the next shape when adding.
|
||||
*/
|
||||
if (adding && obj->inDictionaryMode()) {
|
||||
PCMETER(add2dictfills++);
|
||||
return JS_NO_PROP_CACHE_FILL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for overdeep scope and prototype chain. Because resolve, getter,
|
||||
* and setter hooks can change the prototype chain using JS_SetPrototype
|
||||
|
@ -93,16 +75,26 @@ PropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, JSObject *po
|
|||
* The scopeIndex can't be wrong. We require JS_SetParent calls to happen
|
||||
* before any running script might consult a parent-linked scope chain. If
|
||||
* this requirement is not satisfied, the fill in progress will never hit,
|
||||
* but vcap vs. scope shape tests ensure nothing malfunctions.
|
||||
* but scope shape tests ensure nothing malfunctions.
|
||||
*/
|
||||
JS_ASSERT_IF(obj == pobj, scopeIndex == 0);
|
||||
|
||||
JSObject *tmp = obj;
|
||||
for (uintN i = 0; i != scopeIndex; i++)
|
||||
tmp = tmp->getParent();
|
||||
tmp = tmp->internalScopeChain();
|
||||
|
||||
uintN protoIndex = 0;
|
||||
while (tmp != pobj) {
|
||||
|
||||
/*
|
||||
* Don't cache entries across prototype lookups which can mutate in
|
||||
* arbitrary ways without a shape change.
|
||||
*/
|
||||
if (tmp->hasUncacheableProto()) {
|
||||
PCMETER(noprotos++);
|
||||
return JS_NO_PROP_CACHE_FILL;
|
||||
}
|
||||
|
||||
tmp = tmp->getProto();
|
||||
|
||||
/*
|
||||
|
@ -117,7 +109,7 @@ PropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, JSObject *po
|
|||
++protoIndex;
|
||||
}
|
||||
|
||||
if (scopeIndex > PCVCAP_SCOPEMASK || protoIndex > PCVCAP_PROTOMASK) {
|
||||
if (scopeIndex > PCINDEX_SCOPEMASK || protoIndex > PCINDEX_PROTOMASK) {
|
||||
PCMETER(longchains++);
|
||||
return JS_NO_PROP_CACHE_FILL;
|
||||
}
|
||||
|
@ -130,133 +122,9 @@ PropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, JSObject *po
|
|||
JSScript *script = cx->stack.currentScript(&pc);
|
||||
op = js_GetOpcode(cx, script, pc);
|
||||
cs = &js_CodeSpec[op];
|
||||
kshape = 0;
|
||||
|
||||
do {
|
||||
/*
|
||||
* Check for a prototype "plain old method" callee computation. What
|
||||
* is a plain old method? It's a function-valued property with stub
|
||||
* getter, so get of a function is idempotent.
|
||||
*/
|
||||
if (cs->format & JOF_CALLOP) {
|
||||
if (shape->isMethod()) {
|
||||
/*
|
||||
* A compiler-created function object, AKA a method, already
|
||||
* memoized in the property tree.
|
||||
*/
|
||||
JS_ASSERT(pobj->hasMethodBarrier());
|
||||
JSObject &funobj = shape->methodObject();
|
||||
JS_ASSERT(funobj == pobj->nativeGetSlot(shape->slot).toObject());
|
||||
vword.setFunObj(funobj);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* N.B. Objects are not branded if type inference is enabled, to
|
||||
* allow property accesses without shape checks in JIT code.
|
||||
*/
|
||||
if (!pobj->generic() && shape->hasDefaultGetter() && pobj->containsSlot(shape->slot) &&
|
||||
!cx->typeInferenceEnabled()) {
|
||||
const Value &v = pobj->nativeGetSlot(shape->slot);
|
||||
JSObject *funobj;
|
||||
|
||||
if (IsFunctionObject(v, &funobj)) {
|
||||
/*
|
||||
* Great, we have a function-valued prototype property
|
||||
* where the getter is JS_PropertyStub. The type id in
|
||||
* pobj does not evolve with changes to property values,
|
||||
* however.
|
||||
*
|
||||
* So here, on first cache fill for this method, we brand
|
||||
* obj with a new shape and set the JSObject::BRANDED flag.
|
||||
* Once this flag is set, any property assignment that
|
||||
* changes the value from or to a different function object
|
||||
* will result in shape being regenerated.
|
||||
*/
|
||||
if (!pobj->branded()) {
|
||||
PCMETER(brandfills++);
|
||||
#ifdef DEBUG_notme
|
||||
JSFunction *fun = JSVAL_TO_OBJECT(v)->getFunctionPrivate();
|
||||
JSAutoByteString funNameBytes;
|
||||
if (const char *funName = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
|
||||
fprintf(stderr,
|
||||
"branding %p (%s) for funobj %p (%s), shape %lu\n",
|
||||
pobj, pobj->getClass()->name, JSVAL_TO_OBJECT(v), funName,
|
||||
obj->shape());
|
||||
}
|
||||
#endif
|
||||
if (!pobj->brand(cx))
|
||||
return JS_NO_PROP_CACHE_FILL;
|
||||
}
|
||||
vword.setFunObj(*funobj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if ((cs->format & (JOF_SET | JOF_FOR | JOF_INCDEC)) && obj->watched()) {
|
||||
return JS_NO_PROP_CACHE_FILL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If getting a value via a stub getter, or doing an INCDEC op
|
||||
* with stub getters and setters, we can cache the slot.
|
||||
*/
|
||||
if (!(cs->format & (JOF_SET | JOF_FOR)) &&
|
||||
(!(cs->format & JOF_INCDEC) || (shape->hasDefaultSetter() && shape->writable())) &&
|
||||
shape->hasDefaultGetter() &&
|
||||
pobj->containsSlot(shape->slot)) {
|
||||
/* Great, let's cache shape's slot and use it on cache hit. */
|
||||
vword.setSlot(shape->slot);
|
||||
} else {
|
||||
/* Best we can do is to cache shape (still a nice speedup). */
|
||||
vword.setShape(shape);
|
||||
if (adding &&
|
||||
pobj->shape() == shape->shapeid) {
|
||||
/*
|
||||
* Our caller added a new property. We also know that a setter
|
||||
* that js_NativeSet might have run has not mutated pobj, so
|
||||
* the added property is still the last one added, and pobj is
|
||||
* not branded.
|
||||
*
|
||||
* We want to cache under pobj's shape before the property
|
||||
* addition to bias for the case when the mutator opcode
|
||||
* always adds the same property. This allows us to optimize
|
||||
* periodic execution of object initializers or other explicit
|
||||
* initialization sequences such as
|
||||
*
|
||||
* obj = {}; obj.x = 1; obj.y = 2;
|
||||
*
|
||||
* We assume that on average the win from this optimization is
|
||||
* greater than the cost of an extra mismatch per loop owing to
|
||||
* the bias for the following case:
|
||||
*
|
||||
* obj = {}; ... for (...) { ... obj.x = ... }
|
||||
*
|
||||
* On the first iteration of such a for loop, JSOP_SETPROP
|
||||
* fills the cache with the shape of the newly created object
|
||||
* obj, not the shape of obj after obj.x has been assigned.
|
||||
* That mismatches obj's shape on the second iteration. Note
|
||||
* that on the third and subsequent iterations the cache will
|
||||
* be hit because the shape is no longer updated.
|
||||
*/
|
||||
JS_ASSERT(shape == pobj->lastProperty());
|
||||
JS_ASSERT(!pobj->nativeEmpty());
|
||||
|
||||
kshape = shape->previous()->shapeid;
|
||||
|
||||
/*
|
||||
* When adding we predict no prototype object will later gain a
|
||||
* readonly property or setter.
|
||||
*/
|
||||
vshape = cx->runtime->protoHazardShape;
|
||||
}
|
||||
}
|
||||
} while (0);
|
||||
|
||||
if (kshape == 0) {
|
||||
kshape = obj->shape();
|
||||
vshape = pobj->shape();
|
||||
}
|
||||
JS_ASSERT(kshape < SHAPE_OVERFLOW_BIT);
|
||||
if ((cs->format & JOF_SET) && obj->watched())
|
||||
return JS_NO_PROP_CACHE_FILL;
|
||||
|
||||
if (obj == pobj) {
|
||||
JS_ASSERT(scopeIndex == 0 && protoIndex == 0);
|
||||
|
@ -272,20 +140,15 @@ PropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, JSObject *po
|
|||
/*
|
||||
* Make sure that a later shadowing assignment will enter
|
||||
* PurgeProtoChain and invalidate this entry, bug 479198.
|
||||
*
|
||||
* This is not thread-safe but we are about to make all objects
|
||||
* except multi-threaded wrappers (bug 566951) single-threaded.
|
||||
* And multi-threaded wrappers are non-native Proxy instances, so
|
||||
* they won't use the property cache.
|
||||
*/
|
||||
obj->setDelegate();
|
||||
if (!obj->isDelegate())
|
||||
return JS_NO_PROP_CACHE_FILL;
|
||||
}
|
||||
}
|
||||
JS_ASSERT(vshape < SHAPE_OVERFLOW_BIT);
|
||||
|
||||
entry = &table[hash(pc, kshape)];
|
||||
entry = &table[hash(pc, obj->lastProperty())];
|
||||
PCMETER(entry->vword.isNull() || recycles++);
|
||||
entry->assign(pc, kshape, vshape, scopeIndex, protoIndex, vword);
|
||||
entry->assign(pc, obj->lastProperty(), pobj->lastProperty(), shape, scopeIndex, protoIndex);
|
||||
|
||||
empty = false;
|
||||
PCMETER(fills++);
|
||||
|
@ -322,8 +185,6 @@ PropertyCache::fullTest(JSContext *cx, jsbytecode *pc, JSObject **objp, JSObject
|
|||
PropertyCacheEntry *entry)
|
||||
{
|
||||
JSObject *obj, *pobj, *tmp;
|
||||
uint32 vcap;
|
||||
|
||||
JSScript *script = cx->stack.currentScript();
|
||||
|
||||
JS_ASSERT(this == &JS_PROPERTY_CACHE(cx));
|
||||
|
@ -333,25 +194,24 @@ PropertyCache::fullTest(JSContext *cx, jsbytecode *pc, JSObject **objp, JSObject
|
|||
const JSCodeSpec &cs = js_CodeSpec[op];
|
||||
|
||||
obj = *objp;
|
||||
vcap = entry->vcap;
|
||||
uint32 vindex = entry->vindex;
|
||||
|
||||
if (entry->kpc != pc) {
|
||||
PCMETER(kpcmisses++);
|
||||
|
||||
JSAtom *atom = GetAtomFromBytecode(cx, pc, op, cs);
|
||||
#ifdef DEBUG_notme
|
||||
JSScript *script = cx->fp()->getScript();
|
||||
JSAutoByteString printable;
|
||||
fprintf(stderr,
|
||||
"id miss for %s from %s:%u"
|
||||
" (pc %u, kpc %u, kshape %u, shape %u)\n",
|
||||
" (pc %u, kpc %u, kshape %p, shape %p)\n",
|
||||
js_AtomToPrintableString(cx, atom, &printable),
|
||||
script->filename,
|
||||
js_PCToLineNumber(cx, script, pc),
|
||||
pc - script->code,
|
||||
entry->kpc - script->code,
|
||||
entry->kshape,
|
||||
obj->shape());
|
||||
obj->lastProperty());
|
||||
js_Disassemble1(cx, script, pc,
|
||||
pc - script->code,
|
||||
JS_FALSE, stderr);
|
||||
|
@ -360,39 +220,38 @@ PropertyCache::fullTest(JSContext *cx, jsbytecode *pc, JSObject **objp, JSObject
|
|||
return atom;
|
||||
}
|
||||
|
||||
if (entry->kshape != obj->shape()) {
|
||||
if (entry->kshape != obj->lastProperty()) {
|
||||
PCMETER(kshapemisses++);
|
||||
return GetAtomFromBytecode(cx, pc, op, cs);
|
||||
}
|
||||
|
||||
/*
|
||||
* PropertyCache::test handles only the direct and immediate-prototype hit
|
||||
* cases. All others go here. We could embed the target object in the cache
|
||||
* entry but then entry size would be 5 words. Instead we traverse chains.
|
||||
* cases. All others go here.
|
||||
*/
|
||||
pobj = obj;
|
||||
|
||||
if (JOF_MODE(cs.format) == JOF_NAME) {
|
||||
while (vcap & (PCVCAP_SCOPEMASK << PCVCAP_PROTOBITS)) {
|
||||
tmp = pobj->getParent();
|
||||
while (vindex & (PCINDEX_SCOPEMASK << PCINDEX_PROTOBITS)) {
|
||||
tmp = pobj->scopeChain();
|
||||
if (!tmp || !tmp->isNative())
|
||||
break;
|
||||
pobj = tmp;
|
||||
vcap -= PCVCAP_PROTOSIZE;
|
||||
vindex -= PCINDEX_PROTOSIZE;
|
||||
}
|
||||
|
||||
*objp = pobj;
|
||||
}
|
||||
|
||||
while (vcap & PCVCAP_PROTOMASK) {
|
||||
while (vindex & PCINDEX_PROTOMASK) {
|
||||
tmp = pobj->getProto();
|
||||
if (!tmp || !tmp->isNative())
|
||||
break;
|
||||
pobj = tmp;
|
||||
--vcap;
|
||||
--vindex;
|
||||
}
|
||||
|
||||
if (matchShape(cx, pobj, vcap >> PCVCAP_TAGBITS)) {
|
||||
if (pobj->lastProperty() == entry->pshape) {
|
||||
#ifdef DEBUG
|
||||
JSAtom *atom = GetAtomFromBytecode(cx, pc, op, cs);
|
||||
jsid id = ATOM_TO_JSID(atom);
|
||||
|
@ -416,8 +275,9 @@ PropertyCache::assertEmpty()
|
|||
for (uintN i = 0; i < SIZE; i++) {
|
||||
JS_ASSERT(!table[i].kpc);
|
||||
JS_ASSERT(!table[i].kshape);
|
||||
JS_ASSERT(!table[i].vcap);
|
||||
JS_ASSERT(table[i].vword.isNull());
|
||||
JS_ASSERT(!table[i].pshape);
|
||||
JS_ASSERT(!table[i].prop);
|
||||
JS_ASSERT(!table[i].vindex);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -431,7 +291,6 @@ PropertyCache::purge(JSContext *cx)
|
|||
}
|
||||
|
||||
PodArrayZero(table);
|
||||
JS_ASSERT(table[0].vword.isNull());
|
||||
empty = true;
|
||||
|
||||
#ifdef JS_PROPERTY_CACHE_METERING
|
||||
|
@ -491,22 +350,6 @@ PropertyCache::purge(JSContext *cx)
|
|||
PCMETER(flushes++);
|
||||
}
|
||||
|
||||
void
|
||||
PropertyCache::purgeForScript(JSContext *cx, JSScript *script)
|
||||
{
|
||||
JS_ASSERT(!cx->runtime->gcRunning);
|
||||
|
||||
for (PropertyCacheEntry *entry = table; entry < table + SIZE; entry++) {
|
||||
if (UnsignedPtrDiff(entry->kpc, script->code) < script->length) {
|
||||
entry->kpc = NULL;
|
||||
#ifdef DEBUG
|
||||
entry->kshape = entry->vcap = 0;
|
||||
entry->vword.setNull();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PropertyCache::restore(PropertyCacheEntry *entry)
|
||||
{
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче