2009-06-10 18:29:44 -07:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
2007-03-22 10:30:00 -07:00
|
|
|
* vim: set ts=8 sw=4 et tw=80:
|
|
|
|
*
|
|
|
|
* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is Mozilla Communicator client code, released
|
|
|
|
* March 31, 1998.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Netscape Communications Corporation.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
|
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* JS execution context.
|
|
|
|
*/
|
2009-09-14 17:29:46 -07:00
|
|
|
#include <new>
|
2007-03-22 10:30:00 -07:00
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2010-10-01 01:45:27 -07:00
|
|
|
#ifdef ANDROID
|
2010-11-18 18:27:44 -08:00
|
|
|
# include <android/log.h>
|
2010-10-01 01:45:27 -07:00
|
|
|
# include <fstream>
|
|
|
|
# include <string>
|
|
|
|
#endif // ANDROID
|
2010-05-26 17:49:04 -07:00
|
|
|
|
2009-03-18 11:38:16 -07:00
|
|
|
#include "jsstdint.h"
|
2010-05-26 17:49:04 -07:00
|
|
|
|
|
|
|
#include "jstypes.h"
|
2010-10-01 16:46:54 -07:00
|
|
|
#include "jsarena.h"
|
|
|
|
#include "jsutil.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "jsclist.h"
|
|
|
|
#include "jsprf.h"
|
|
|
|
#include "jsatom.h"
|
|
|
|
#include "jscntxt.h"
|
2008-09-05 10:19:17 -07:00
|
|
|
#include "jsversion.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "jsdbgapi.h"
|
|
|
|
#include "jsexn.h"
|
2008-08-08 09:02:50 -07:00
|
|
|
#include "jsfun.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "jsgc.h"
|
2011-04-15 16:56:08 -07:00
|
|
|
#include "jsgcmark.h"
|
2010-03-03 17:52:26 -08:00
|
|
|
#include "jsiter.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "jslock.h"
|
2009-08-19 15:23:54 -07:00
|
|
|
#include "jsmath.h"
|
2010-06-04 07:22:28 -07:00
|
|
|
#include "jsnativestack.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "jsnum.h"
|
|
|
|
#include "jsobj.h"
|
|
|
|
#include "jsopcode.h"
|
2009-02-07 23:23:01 -08:00
|
|
|
#include "jspubtd.h"
|
2011-04-13 09:27:37 -07:00
|
|
|
#include "jsscan.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "jsscope.h"
|
|
|
|
#include "jsscript.h"
|
2009-01-30 15:40:05 -08:00
|
|
|
#include "jsstaticcheck.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "jsstr.h"
|
2008-07-10 14:29:16 -07:00
|
|
|
#include "jstracer.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-09-15 22:27:17 -07:00
|
|
|
#ifdef JS_METHODJIT
|
|
|
|
# include "assembler/assembler/MacroAssembler.h"
|
|
|
|
#endif
|
|
|
|
|
2010-03-03 17:52:26 -08:00
|
|
|
#include "jscntxtinlines.h"
|
2010-09-24 10:54:39 -07:00
|
|
|
#include "jscompartment.h"
|
2010-08-09 22:43:33 -07:00
|
|
|
#include "jsobjinlines.h"
|
2010-03-03 17:52:26 -08:00
|
|
|
|
2010-01-22 14:49:18 -08:00
|
|
|
using namespace js;
|
2010-09-24 10:54:39 -07:00
|
|
|
using namespace js::gc;
|
2010-01-22 14:49:18 -08:00
|
|
|
|
2011-03-13 07:45:02 -07:00
|
|
|
namespace js {
|
2009-03-22 02:07:14 -07:00
|
|
|
|
2011-03-13 07:45:02 -07:00
|
|
|
ThreadData::ThreadData()
|
|
|
|
: interruptFlags(0),
|
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
requestDepth(0),
|
|
|
|
#endif
|
2011-01-06 06:13:48 -08:00
|
|
|
#ifdef JS_TRACER
|
2011-03-13 07:45:02 -07:00
|
|
|
onTraceCompartment(NULL),
|
|
|
|
recordingCompartment(NULL),
|
|
|
|
profilingCompartment(NULL),
|
|
|
|
maxCodeCacheBytes(DEFAULT_JIT_CACHE_SIZE),
|
2011-01-06 06:13:48 -08:00
|
|
|
#endif
|
2011-03-13 07:45:02 -07:00
|
|
|
waiveGCQuota(false),
|
|
|
|
dtoaState(NULL),
|
|
|
|
nativeStackBase(GetNativeStackBase()),
|
|
|
|
pendingProxyOperation(NULL)
|
|
|
|
{
|
2010-10-06 12:13:20 -07:00
|
|
|
}
|
|
|
|
|
2011-03-13 07:45:02 -07:00
|
|
|
ThreadData::~ThreadData()
|
2009-03-24 05:07:35 -07:00
|
|
|
{
|
2010-03-22 16:21:10 -07:00
|
|
|
if (dtoaState)
|
|
|
|
js_DestroyDtoaState(dtoaState);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2011-03-13 07:45:02 -07:00
|
|
|
bool
|
|
|
|
ThreadData::init()
|
2009-11-12 03:53:25 -08:00
|
|
|
{
|
2011-03-13 07:45:02 -07:00
|
|
|
return stackSpace.init() && !!(dtoaState = js_NewDtoaState());
|
2009-11-12 03:53:25 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2011-03-13 07:45:02 -07:00
|
|
|
ThreadData::triggerOperationCallback(JSRuntime *rt)
|
2009-03-22 02:07:14 -07:00
|
|
|
{
|
2011-03-13 07:45:02 -07:00
|
|
|
/*
|
|
|
|
* Use JS_ATOMIC_SET and JS_ATOMIC_INCREMENT in the hope that it ensures
|
|
|
|
* the write will become immediately visible to other processors polling
|
|
|
|
* the flag. Note that we only care about visibility here, not read/write
|
|
|
|
* ordering: this field can only be written with the GC lock held.
|
|
|
|
*/
|
|
|
|
if (interruptFlags)
|
|
|
|
return;
|
|
|
|
JS_ATOMIC_SET(&interruptFlags, 1);
|
2009-11-12 03:53:25 -08:00
|
|
|
|
2009-03-24 05:07:35 -07:00
|
|
|
#ifdef JS_THREADSAFE
|
2011-03-13 07:45:02 -07:00
|
|
|
/* rt->interruptCounter does not reflect suspended threads. */
|
|
|
|
if (requestDepth != 0)
|
|
|
|
JS_ATOMIC_INCREMENT(&rt->interruptCounter);
|
|
|
|
#endif
|
2009-03-24 05:07:35 -07:00
|
|
|
}
|
2009-03-22 02:15:27 -07:00
|
|
|
|
2011-03-13 07:45:02 -07:00
|
|
|
} /* namespace js */
|
2010-08-30 11:46:18 -07:00
|
|
|
|
2011-03-13 07:45:02 -07:00
|
|
|
#ifdef JS_THREADSAFE
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2009-10-27 17:55:34 -07:00
|
|
|
JSThread *
|
|
|
|
js_CurrentThread(JSRuntime *rt)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2010-05-13 10:50:43 -07:00
|
|
|
void *id = js_CurrentThreadId();
|
2009-03-24 05:07:35 -07:00
|
|
|
JS_LOCK_GC(rt);
|
2009-03-22 02:15:27 -07:00
|
|
|
|
2009-03-24 05:07:35 -07:00
|
|
|
/*
|
|
|
|
* We must not race with a GC that accesses cx->thread for JSContext
|
|
|
|
* instances on all threads, see bug 476934.
|
|
|
|
*/
|
|
|
|
js_WaitForGC(rt);
|
2010-05-13 10:50:43 -07:00
|
|
|
|
2009-03-24 05:07:35 -07:00
|
|
|
JSThread *thread;
|
2010-05-13 10:50:43 -07:00
|
|
|
JSThread::Map::AddPtr p = rt->threads.lookupForAdd(id);
|
|
|
|
if (p) {
|
|
|
|
thread = p->value;
|
2010-10-20 16:15:39 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If thread has no contexts, it might be left over from a previous
|
|
|
|
* thread with the same id but a different stack address.
|
|
|
|
*/
|
|
|
|
if (JS_CLIST_IS_EMPTY(&thread->contextList))
|
|
|
|
thread->data.nativeStackBase = GetNativeStackBase();
|
2009-03-24 05:07:35 -07:00
|
|
|
} else {
|
|
|
|
JS_UNLOCK_GC(rt);
|
2011-03-13 07:45:02 -07:00
|
|
|
|
|
|
|
thread = OffTheBooks::new_<JSThread>(id);
|
|
|
|
if (!thread || !thread->init()) {
|
|
|
|
Foreground::delete_(thread);
|
2009-10-27 17:55:34 -07:00
|
|
|
return NULL;
|
2011-03-13 07:45:02 -07:00
|
|
|
}
|
2009-03-24 05:07:35 -07:00
|
|
|
JS_LOCK_GC(rt);
|
|
|
|
js_WaitForGC(rt);
|
2010-05-13 10:50:43 -07:00
|
|
|
if (!rt->threads.relookupOrAdd(p, id, thread)) {
|
2009-03-24 05:07:35 -07:00
|
|
|
JS_UNLOCK_GC(rt);
|
2011-03-13 07:45:02 -07:00
|
|
|
Foreground::delete_(thread);
|
2009-10-27 17:55:34 -07:00
|
|
|
return NULL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2010-05-13 10:50:43 -07:00
|
|
|
/* Another thread cannot add an entry for the current thread id. */
|
|
|
|
JS_ASSERT(p->value == thread);
|
2009-03-22 02:15:27 -07:00
|
|
|
}
|
2010-05-13 10:50:43 -07:00
|
|
|
JS_ASSERT(thread->id == id);
|
2010-11-25 05:59:27 -08:00
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
char* gnsb = (char*) GetNativeStackBase();
|
|
|
|
JS_ASSERT(gnsb + 0 == (char*) thread->data.nativeStackBase ||
|
|
|
|
/* Work around apparent glibc bug; see bug 608526. */
|
|
|
|
gnsb + 0x1000 == (char*) thread->data.nativeStackBase ||
|
|
|
|
gnsb + 0x2000 == (char*) thread->data.nativeStackBase ||
|
|
|
|
gnsb + 0x3000 == (char*) thread->data.nativeStackBase);
|
|
|
|
#endif
|
2009-03-24 05:07:35 -07:00
|
|
|
|
2009-10-27 17:55:34 -07:00
|
|
|
return thread;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSBool
|
|
|
|
js_InitContextThread(JSContext *cx)
|
|
|
|
{
|
|
|
|
JSThread *thread = js_CurrentThread(cx->runtime);
|
|
|
|
if (!thread)
|
|
|
|
return false;
|
|
|
|
|
2009-03-24 05:07:35 -07:00
|
|
|
JS_APPEND_LINK(&cx->threadLinks, &thread->contextList);
|
2011-04-13 09:27:37 -07:00
|
|
|
cx->setThread(thread);
|
2009-03-24 05:07:35 -07:00
|
|
|
return true;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2011-04-13 09:27:37 -07:00
|
|
|
void
|
|
|
|
JSContext::setThread(JSThread *thread)
|
|
|
|
{
|
|
|
|
thread_ = thread;
|
|
|
|
stack.threadReset();
|
|
|
|
}
|
|
|
|
|
2009-02-06 20:05:32 -08:00
|
|
|
void
|
2009-03-24 05:07:35 -07:00
|
|
|
js_ClearContextThread(JSContext *cx)
|
2009-03-22 02:07:14 -07:00
|
|
|
{
|
2011-04-13 09:27:37 -07:00
|
|
|
JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread()));
|
2009-03-24 05:07:35 -07:00
|
|
|
JS_REMOVE_AND_INIT_LINK(&cx->threadLinks);
|
2011-04-13 09:27:37 -07:00
|
|
|
cx->setThread(NULL);
|
2009-03-24 05:07:35 -07:00
|
|
|
}
|
|
|
|
|
2009-03-22 02:15:27 -07:00
|
|
|
#endif /* JS_THREADSAFE */
|
2009-03-22 02:07:14 -07:00
|
|
|
|
2011-03-13 07:45:02 -07:00
|
|
|
ThreadData *
|
2009-10-27 17:55:34 -07:00
|
|
|
js_CurrentThreadData(JSRuntime *rt)
|
|
|
|
{
|
|
|
|
#ifdef JS_THREADSAFE
|
2009-10-27 18:15:37 -07:00
|
|
|
JSThread *thread = js_CurrentThread(rt);
|
2009-10-27 17:55:34 -07:00
|
|
|
if (!thread)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return &thread->data;
|
|
|
|
#else
|
|
|
|
return &rt->threadData;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2009-03-24 05:07:35 -07:00
|
|
|
JSBool
|
|
|
|
js_InitThreads(JSRuntime *rt)
|
|
|
|
{
|
|
|
|
#ifdef JS_THREADSAFE
|
2011-03-13 07:45:02 -07:00
|
|
|
return rt->threads.init(4);
|
2009-03-24 05:07:35 -07:00
|
|
|
#else
|
2011-03-13 07:45:02 -07:00
|
|
|
return rt->threadData.init();
|
2009-03-24 05:07:35 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
js_FinishThreads(JSRuntime *rt)
|
|
|
|
{
|
|
|
|
#ifdef JS_THREADSAFE
|
2010-05-13 10:50:43 -07:00
|
|
|
if (!rt->threads.initialized())
|
2009-03-24 05:07:35 -07:00
|
|
|
return;
|
2010-05-13 10:50:43 -07:00
|
|
|
for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) {
|
|
|
|
JSThread *thread = r.front().value;
|
2011-03-13 07:45:02 -07:00
|
|
|
Foreground::delete_(thread);
|
2010-05-13 10:50:43 -07:00
|
|
|
}
|
|
|
|
rt->threads.clear();
|
2009-03-24 05:07:35 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
js_PurgeThreads(JSContext *cx)
|
|
|
|
{
|
|
|
|
#ifdef JS_THREADSAFE
|
2010-05-13 10:50:43 -07:00
|
|
|
for (JSThread::Map::Enum e(cx->runtime->threads);
|
|
|
|
!e.empty();
|
|
|
|
e.popFront()) {
|
|
|
|
JSThread *thread = e.front().value;
|
|
|
|
|
|
|
|
if (JS_CLIST_IS_EMPTY(&thread->contextList)) {
|
2011-04-13 09:27:37 -07:00
|
|
|
JS_ASSERT(cx->thread() != thread);
|
2011-03-13 07:45:02 -07:00
|
|
|
Foreground::delete_(thread);
|
2010-05-13 10:50:43 -07:00
|
|
|
e.removeFront();
|
|
|
|
} else {
|
|
|
|
thread->data.purge(cx);
|
|
|
|
}
|
|
|
|
}
|
2009-03-24 05:07:35 -07:00
|
|
|
#else
|
2009-11-12 03:53:25 -08:00
|
|
|
cx->runtime->threadData.purge(cx);
|
2009-03-24 05:07:35 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2011-03-13 07:45:02 -07:00
|
|
|
static const size_t ARENA_HEADER_SIZE_HACK = 40;
|
|
|
|
static const size_t TEMP_POOL_CHUNK_SIZE = 4096 - ARENA_HEADER_SIZE_HACK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
JSContext *
|
|
|
|
js_NewContext(JSRuntime *rt, size_t stackChunkSize)
|
|
|
|
{
|
|
|
|
JSContext *cx;
|
|
|
|
JSBool ok, first;
|
|
|
|
JSContextCallback cxCallback;
|
|
|
|
|
2009-02-07 03:39:57 -08:00
|
|
|
/*
|
|
|
|
* We need to initialize the new context fully before adding it to the
|
|
|
|
* runtime list. After that it can be accessed from another thread via
|
|
|
|
* js_ContextIterator.
|
|
|
|
*/
|
2011-03-31 01:14:12 -07:00
|
|
|
void *mem = OffTheBooks::calloc_(sizeof *cx);
|
2009-09-14 17:29:46 -07:00
|
|
|
if (!mem)
|
2007-03-22 10:30:00 -07:00
|
|
|
return NULL;
|
|
|
|
|
2009-09-14 17:29:46 -07:00
|
|
|
cx = new (mem) JSContext(rt);
|
2007-06-14 23:44:18 -07:00
|
|
|
cx->debugHooks = &rt->globalDebugHooks;
|
2007-03-22 10:30:00 -07:00
|
|
|
#if JS_STACK_GROWTH_DIRECTION > 0
|
2009-02-07 03:39:57 -08:00
|
|
|
cx->stackLimit = (jsuword) -1;
|
2007-03-22 10:30:00 -07:00
|
|
|
#endif
|
2007-08-27 15:21:55 -07:00
|
|
|
cx->scriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;
|
2009-02-07 03:39:57 -08:00
|
|
|
JS_STATIC_ASSERT(JSVERSION_DEFAULT == 0);
|
2010-09-13 09:38:22 -07:00
|
|
|
JS_ASSERT(cx->findVersion() == JSVERSION_DEFAULT);
|
2009-02-07 03:39:57 -08:00
|
|
|
VOUCH_DOES_NOT_REQUIRE_STACK();
|
2010-05-11 08:52:17 -07:00
|
|
|
|
2010-07-11 00:09:34 -07:00
|
|
|
JS_InitArenaPool(&cx->tempPool, "temp", TEMP_POOL_CHUNK_SIZE, sizeof(jsdouble),
|
|
|
|
&cx->scriptStackQuota);
|
2010-08-11 13:30:07 -07:00
|
|
|
JS_InitArenaPool(&cx->regExpPool, "regExp", TEMP_POOL_CHUNK_SIZE, sizeof(int),
|
|
|
|
&cx->scriptStackQuota);
|
2009-02-07 03:39:57 -08:00
|
|
|
|
|
|
|
JS_ASSERT(cx->resolveFlags == 0);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2011-01-24 09:47:25 -08:00
|
|
|
if (!cx->busyArrays.init()) {
|
2011-03-13 07:45:02 -07:00
|
|
|
Foreground::delete_(cx);
|
2011-01-24 09:47:25 -08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-03-24 05:07:35 -07:00
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
if (!js_InitContextThread(cx)) {
|
2011-03-13 07:45:02 -07:00
|
|
|
Foreground::delete_(cx);
|
2009-03-24 05:07:35 -07:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Here the GC lock is still held after js_InitContextThread took it and
|
|
|
|
* the GC is not running on another thread.
|
|
|
|
*/
|
2007-03-22 10:30:00 -07:00
|
|
|
for (;;) {
|
|
|
|
if (rt->state == JSRTS_UP) {
|
2009-03-11 03:54:49 -07:00
|
|
|
JS_ASSERT(!JS_CLIST_IS_EMPTY(&rt->contextList));
|
|
|
|
first = JS_FALSE;
|
2007-03-22 10:30:00 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (rt->state == JSRTS_DOWN) {
|
2009-03-11 03:54:49 -07:00
|
|
|
JS_ASSERT(JS_CLIST_IS_EMPTY(&rt->contextList));
|
|
|
|
first = JS_TRUE;
|
2007-03-22 10:30:00 -07:00
|
|
|
rt->state = JSRTS_LAUNCHING;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT);
|
2009-03-24 05:07:35 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* During the above wait after we are notified about the state change
|
|
|
|
* but before we wake up, another thread could enter the GC from
|
|
|
|
* js_DestroyContext, bug 478336. So we must wait here to ensure that
|
|
|
|
* when we exit the loop with the first flag set to true, that GC is
|
|
|
|
* finished.
|
|
|
|
*/
|
|
|
|
js_WaitForGC(rt);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2008-12-18 07:24:34 -08:00
|
|
|
JS_APPEND_LINK(&cx->link, &rt->contextList);
|
2007-03-22 10:30:00 -07:00
|
|
|
JS_UNLOCK_GC(rt);
|
|
|
|
|
2010-03-18 08:27:26 -07:00
|
|
|
js_InitRandom(cx);
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
/*
|
|
|
|
* If cx is the first context on this runtime, initialize well-known atoms,
|
|
|
|
* keywords, numbers, and strings. If one of these steps should fail, the
|
|
|
|
* runtime will be left in a partially initialized state, with zeroes and
|
|
|
|
* nulls stored in the default-initialized remainder of the struct. We'll
|
|
|
|
* clean the runtime up under js_DestroyContext, because cx will be "last"
|
|
|
|
* as well as "first".
|
|
|
|
*/
|
|
|
|
if (first) {
|
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
JS_BeginRequest(cx);
|
|
|
|
#endif
|
2007-08-07 00:29:32 -07:00
|
|
|
ok = js_InitCommonAtoms(cx);
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
/*
|
2007-08-07 00:29:32 -07:00
|
|
|
* scriptFilenameTable may be left over from a previous episode of
|
|
|
|
* non-zero contexts alive in rt, so don't re-init the table if it's
|
|
|
|
* not necessary.
|
2007-03-22 10:30:00 -07:00
|
|
|
*/
|
|
|
|
if (ok && !rt->scriptFilenameTable)
|
|
|
|
ok = js_InitRuntimeScriptState(rt);
|
|
|
|
if (ok)
|
|
|
|
ok = js_InitRuntimeNumberState(cx);
|
2010-01-26 22:50:17 -08:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
JS_EndRequest(cx);
|
|
|
|
#endif
|
|
|
|
if (!ok) {
|
|
|
|
js_DestroyContext(cx, JSDCM_NEW_FAILED);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-04-08 05:54:18 -07:00
|
|
|
AutoLockGC lock(rt);
|
2007-03-22 10:30:00 -07:00
|
|
|
rt->state = JSRTS_UP;
|
|
|
|
JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
|
|
|
|
}
|
|
|
|
|
|
|
|
cxCallback = rt->cxCallback;
|
|
|
|
if (cxCallback && !cxCallback(cx, JSCONTEXT_NEW)) {
|
|
|
|
js_DestroyContext(cx, JSDCM_NEW_FAILED);
|
|
|
|
return NULL;
|
|
|
|
}
|
2008-08-11 19:10:24 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
return cx;
|
|
|
|
}
|
|
|
|
|
2009-01-27 16:40:40 -08:00
|
|
|
#if defined DEBUG && defined XP_UNIX
|
|
|
|
# include <stdio.h>
|
|
|
|
|
2009-04-05 21:17:22 -07:00
|
|
|
class JSAutoFile {
|
2009-02-06 14:28:48 -08:00
|
|
|
public:
|
2009-04-05 21:17:22 -07:00
|
|
|
JSAutoFile() : mFile(NULL) {}
|
2009-02-06 14:28:48 -08:00
|
|
|
|
2009-04-05 21:17:22 -07:00
|
|
|
~JSAutoFile() {
|
|
|
|
if (mFile)
|
|
|
|
fclose(mFile);
|
2009-02-06 14:28:48 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
FILE *open(const char *fname, const char *mode) {
|
2009-04-05 21:17:22 -07:00
|
|
|
return mFile = fopen(fname, mode);
|
2009-02-06 14:28:48 -08:00
|
|
|
}
|
|
|
|
operator FILE *() {
|
2009-04-05 21:17:22 -07:00
|
|
|
return mFile;
|
2009-02-06 14:28:48 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2009-04-05 21:17:22 -07:00
|
|
|
FILE *mFile;
|
2009-02-06 14:28:48 -08:00
|
|
|
};
|
|
|
|
|
2009-01-27 16:40:40 -08:00
|
|
|
static void
|
|
|
|
DumpEvalCacheMeter(JSContext *cx)
|
|
|
|
{
|
2010-08-01 09:58:03 -07:00
|
|
|
if (const char *filename = getenv("JS_EVALCACHE_STATFILE")) {
|
|
|
|
struct {
|
|
|
|
const char *name;
|
|
|
|
ptrdiff_t offset;
|
|
|
|
} table[] = {
|
2009-01-27 16:40:40 -08:00
|
|
|
#define frob(x) { #x, offsetof(JSEvalCacheMeter, x) }
|
2010-08-01 09:58:03 -07:00
|
|
|
EVAL_CACHE_METER_LIST(frob)
|
2009-01-27 16:40:40 -08:00
|
|
|
#undef frob
|
2010-08-01 09:58:03 -07:00
|
|
|
};
|
2010-12-22 12:02:25 -08:00
|
|
|
JSEvalCacheMeter *ecm = &cx->compartment->evalCacheMeter;
|
2009-01-27 16:40:40 -08:00
|
|
|
|
2010-08-01 09:58:03 -07:00
|
|
|
static JSAutoFile fp;
|
|
|
|
if (!fp && !fp.open(filename, "w"))
|
2009-01-27 16:40:40 -08:00
|
|
|
return;
|
|
|
|
|
2010-08-01 09:58:03 -07:00
|
|
|
fprintf(fp, "eval cache meter (%p):\n",
|
2009-01-27 16:40:40 -08:00
|
|
|
#ifdef JS_THREADSAFE
|
2011-04-13 09:27:37 -07:00
|
|
|
(void *) cx->thread()
|
2009-01-27 16:40:40 -08:00
|
|
|
#else
|
2010-08-01 09:58:03 -07:00
|
|
|
(void *) cx->runtime
|
2009-01-27 16:40:40 -08:00
|
|
|
#endif
|
2010-08-01 09:58:03 -07:00
|
|
|
);
|
|
|
|
for (uintN i = 0; i < JS_ARRAY_LENGTH(table); ++i) {
|
|
|
|
fprintf(fp, "%-8.8s %llu\n",
|
|
|
|
table[i].name,
|
|
|
|
(unsigned long long int) *(uint64 *)((uint8 *)ecm + table[i].offset));
|
|
|
|
}
|
|
|
|
fprintf(fp, "hit ratio %g%%\n", ecm->hit * 100. / ecm->probe);
|
|
|
|
fprintf(fp, "avg steps %g\n", double(ecm->step) / ecm->probe);
|
|
|
|
fflush(fp);
|
2009-01-27 16:40:40 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
# define DUMP_EVAL_CACHE_METER(cx) DumpEvalCacheMeter(cx)
|
2009-04-05 21:17:22 -07:00
|
|
|
|
2010-08-01 09:58:03 -07:00
|
|
|
static void
|
|
|
|
DumpFunctionCountMap(const char *title, JSRuntime::FunctionCountMap &map, FILE *fp)
|
|
|
|
{
|
|
|
|
fprintf(fp, "\n%s count map:\n", title);
|
|
|
|
|
|
|
|
for (JSRuntime::FunctionCountMap::Range r = map.all(); !r.empty(); r.popFront()) {
|
|
|
|
JSFunction *fun = r.front().key;
|
|
|
|
int32 count = r.front().value;
|
|
|
|
|
|
|
|
fprintf(fp, "%10d %s:%u\n", count, fun->u.i.script->filename, fun->u.i.script->lineno);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-05 21:17:22 -07:00
|
|
|
static void
|
|
|
|
DumpFunctionMeter(JSContext *cx)
|
|
|
|
{
|
2010-08-01 09:58:03 -07:00
|
|
|
if (const char *filename = cx->runtime->functionMeterFilename) {
|
|
|
|
struct {
|
|
|
|
const char *name;
|
|
|
|
ptrdiff_t offset;
|
|
|
|
} table[] = {
|
2009-04-05 21:17:22 -07:00
|
|
|
#define frob(x) { #x, offsetof(JSFunctionMeter, x) }
|
2010-08-01 09:58:03 -07:00
|
|
|
FUNCTION_KIND_METER_LIST(frob)
|
2009-04-05 21:17:22 -07:00
|
|
|
#undef frob
|
2010-08-01 09:58:03 -07:00
|
|
|
};
|
|
|
|
JSFunctionMeter *fm = &cx->runtime->functionMeter;
|
2009-04-05 21:17:22 -07:00
|
|
|
|
2010-08-01 09:58:03 -07:00
|
|
|
static JSAutoFile fp;
|
|
|
|
if (!fp && !fp.open(filename, "w"))
|
2009-04-05 21:17:22 -07:00
|
|
|
return;
|
|
|
|
|
2010-08-01 09:58:03 -07:00
|
|
|
fprintf(fp, "function meter (%s):\n", cx->runtime->lastScriptFilename);
|
|
|
|
for (uintN i = 0; i < JS_ARRAY_LENGTH(table); ++i)
|
|
|
|
fprintf(fp, "%-19.19s %d\n", table[i].name, *(int32 *)((uint8 *)fm + table[i].offset));
|
|
|
|
|
|
|
|
DumpFunctionCountMap("method read barrier", cx->runtime->methodReadBarrierCountMap, fp);
|
|
|
|
DumpFunctionCountMap("unjoined function", cx->runtime->unjoinedFunctionCountMap, fp);
|
|
|
|
|
|
|
|
putc('\n', fp);
|
|
|
|
fflush(fp);
|
2009-04-05 21:17:22 -07:00
|
|
|
}
|
|
|
|
}
|
2010-08-01 09:58:03 -07:00
|
|
|
|
2009-04-05 21:17:22 -07:00
|
|
|
# define DUMP_FUNCTION_METER(cx) DumpFunctionMeter(cx)
|
|
|
|
|
|
|
|
#endif /* DEBUG && XP_UNIX */
|
|
|
|
|
|
|
|
#ifndef DUMP_EVAL_CACHE_METER
|
2009-01-27 16:40:40 -08:00
|
|
|
# define DUMP_EVAL_CACHE_METER(cx) ((void) 0)
|
|
|
|
#endif
|
|
|
|
|
2009-04-05 21:17:22 -07:00
|
|
|
#ifndef DUMP_FUNCTION_METER
|
|
|
|
# define DUMP_FUNCTION_METER(cx) ((void) 0)
|
|
|
|
#endif
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
void
|
|
|
|
js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
|
|
|
|
{
|
|
|
|
JSRuntime *rt;
|
|
|
|
JSContextCallback cxCallback;
|
|
|
|
JSBool last;
|
|
|
|
|
2010-06-03 21:41:01 -07:00
|
|
|
JS_ASSERT(!cx->enumerators);
|
|
|
|
|
2009-05-14 09:39:44 -07:00
|
|
|
rt = cx->runtime;
|
2009-02-06 20:05:32 -08:00
|
|
|
#ifdef JS_THREADSAFE
|
2009-03-25 08:41:17 -07:00
|
|
|
/*
|
|
|
|
* For API compatibility we allow to destroy contexts without a thread in
|
|
|
|
* optimized builds. We assume that the embedding knows that an OOM error
|
|
|
|
* cannot happen in JS_SetContextThread.
|
|
|
|
*/
|
2011-04-13 09:27:37 -07:00
|
|
|
JS_ASSERT(cx->thread() && CURRENT_THREAD_IS_ME(cx->thread()));
|
|
|
|
if (!cx->thread())
|
2009-03-25 08:41:17 -07:00
|
|
|
JS_SetContextThread(cx);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-08-30 11:46:18 -07:00
|
|
|
/*
|
|
|
|
* For API compatibility we support destroying contexts with non-zero
|
|
|
|
* cx->outstandingRequests but we assume that all JS_BeginRequest calls
|
2010-10-22 10:48:06 -07:00
|
|
|
* on this cx contributes to cx->thread->data.requestDepth and there is no
|
2010-08-30 11:46:18 -07:00
|
|
|
* JS_SuspendRequest calls that set aside the counter.
|
|
|
|
*/
|
2011-04-13 09:27:37 -07:00
|
|
|
JS_ASSERT(cx->outstandingRequests <= cx->thread()->data.requestDepth);
|
2009-05-14 09:39:44 -07:00
|
|
|
#endif
|
2009-05-07 11:19:36 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (mode != JSDCM_NEW_FAILED) {
|
|
|
|
cxCallback = rt->cxCallback;
|
|
|
|
if (cxCallback) {
|
|
|
|
/*
|
|
|
|
* JSCONTEXT_DESTROY callback is not allowed to fail and must
|
|
|
|
* return true.
|
|
|
|
*/
|
2011-04-20 16:27:30 -07:00
|
|
|
DebugOnly<JSBool> callbackStatus = cxCallback(cx, JSCONTEXT_DESTROY);
|
2007-03-22 10:30:00 -07:00
|
|
|
JS_ASSERT(callbackStatus);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
JS_LOCK_GC(rt);
|
|
|
|
JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING);
|
2009-02-07 03:39:57 -08:00
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
/*
|
|
|
|
* Typically we are called outside a request, so ensure that the GC is not
|
|
|
|
* running before removing the context from rt->contextList, see bug 477021.
|
|
|
|
*/
|
2011-04-13 09:27:37 -07:00
|
|
|
if (cx->thread()->data.requestDepth == 0)
|
2009-02-07 03:39:57 -08:00
|
|
|
js_WaitForGC(rt);
|
|
|
|
#endif
|
2008-12-18 07:24:34 -08:00
|
|
|
JS_REMOVE_LINK(&cx->link);
|
2007-03-22 10:30:00 -07:00
|
|
|
last = (rt->contextList.next == &rt->contextList);
|
|
|
|
if (last)
|
|
|
|
rt->state = JSRTS_LANDING;
|
2009-03-24 05:07:35 -07:00
|
|
|
if (last || mode == JSDCM_FORCE_GC || mode == JSDCM_MAYBE_GC
|
|
|
|
#ifdef JS_THREADSAFE
|
2010-08-30 11:46:18 -07:00
|
|
|
|| cx->outstandingRequests != 0
|
2009-03-24 05:07:35 -07:00
|
|
|
#endif
|
|
|
|
) {
|
2009-05-20 01:23:56 -07:00
|
|
|
JS_ASSERT(!rt->gcRunning);
|
2009-05-07 11:19:36 -07:00
|
|
|
|
2009-03-24 05:07:35 -07:00
|
|
|
JS_UNLOCK_GC(rt);
|
2011-04-13 13:43:33 -07:00
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
rt->gcHelperThread.waitBackgroundSweepEnd(rt);
|
|
|
|
#endif
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2009-03-24 05:07:35 -07:00
|
|
|
if (last) {
|
2007-03-22 10:30:00 -07:00
|
|
|
#ifdef JS_THREADSAFE
|
2009-03-24 05:07:35 -07:00
|
|
|
/*
|
2010-08-30 11:46:18 -07:00
|
|
|
* If this thread is not in a request already, begin one now so
|
|
|
|
* that we wait for any racing GC started on a not-last context to
|
|
|
|
* finish, before we plow ahead and unpin atoms. Note that even
|
|
|
|
* though we begin a request here if necessary, we end all
|
|
|
|
* thread's requests before forcing a final GC. This lets any
|
|
|
|
* not-last context destruction racing in another thread try to
|
|
|
|
* force or maybe run the GC, but by that point, rt->state will
|
|
|
|
* not be JSRTS_UP, and that GC attempt will return early.
|
2009-03-24 05:07:35 -07:00
|
|
|
*/
|
2011-04-13 09:27:37 -07:00
|
|
|
if (cx->thread()->data.requestDepth == 0)
|
2009-03-24 05:07:35 -07:00
|
|
|
JS_BeginRequest(cx);
|
2007-03-22 10:30:00 -07:00
|
|
|
#endif
|
|
|
|
|
2011-03-03 14:07:48 -08:00
|
|
|
/*
|
|
|
|
* Dump remaining type inference results first. This printing
|
|
|
|
* depends on atoms still existing.
|
|
|
|
*/
|
|
|
|
AutoLockGC lock(rt);
|
|
|
|
JSCompartment **compartment = rt->compartments.begin();
|
|
|
|
JSCompartment **end = rt->compartments.end();
|
|
|
|
while (compartment < end) {
|
2011-03-10 16:17:39 -08:00
|
|
|
(*compartment)->types.print(cx, *compartment);
|
2011-03-03 14:07:48 -08:00
|
|
|
compartment++;
|
2010-10-29 08:05:55 -07:00
|
|
|
}
|
|
|
|
|
2009-03-24 05:07:35 -07:00
|
|
|
js_FinishRuntimeNumberState(cx);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2009-03-24 05:07:35 -07:00
|
|
|
/* Unpin all common atoms before final GC. */
|
|
|
|
js_FinishCommonAtoms(cx);
|
2007-07-28 09:57:30 -07:00
|
|
|
|
2009-03-24 05:07:35 -07:00
|
|
|
/* Clear debugging state to remove GC roots. */
|
|
|
|
JS_ClearAllTraps(cx);
|
|
|
|
JS_ClearAllWatchPoints(cx);
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
#ifdef JS_THREADSAFE
|
2009-03-22 02:15:27 -07:00
|
|
|
/*
|
2009-03-24 05:07:35 -07:00
|
|
|
* Destroying a context implicitly calls JS_EndRequest(). Also, we must
|
|
|
|
* end our request here in case we are "last" -- in that event, another
|
|
|
|
* js_DestroyContext that was not last might be waiting in the GC for our
|
|
|
|
* request to end. We'll let it run below, just before we do the truly
|
|
|
|
* final GC and then free atom state.
|
2009-03-22 02:15:27 -07:00
|
|
|
*/
|
2010-08-30 11:46:18 -07:00
|
|
|
while (cx->outstandingRequests != 0)
|
2009-03-24 05:07:35 -07:00
|
|
|
JS_EndRequest(cx);
|
|
|
|
#endif
|
2009-03-22 02:07:14 -07:00
|
|
|
|
2009-03-24 05:07:35 -07:00
|
|
|
if (last) {
|
2011-01-07 23:44:57 -08:00
|
|
|
js_GC(cx, NULL, GC_LAST_CONTEXT);
|
2009-03-24 05:07:35 -07:00
|
|
|
DUMP_EVAL_CACHE_METER(cx);
|
2009-04-05 21:17:22 -07:00
|
|
|
DUMP_FUNCTION_METER(cx);
|
2009-03-24 05:07:35 -07:00
|
|
|
|
|
|
|
/* Take the runtime down, now that it has no contexts or atoms. */
|
|
|
|
JS_LOCK_GC(rt);
|
|
|
|
rt->state = JSRTS_DOWN;
|
|
|
|
JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
|
|
|
|
} else {
|
|
|
|
if (mode == JSDCM_FORCE_GC)
|
2011-01-07 23:44:57 -08:00
|
|
|
js_GC(cx, NULL, GC_NORMAL);
|
2009-03-24 05:07:35 -07:00
|
|
|
else if (mode == JSDCM_MAYBE_GC)
|
|
|
|
JS_MaybeGC(cx);
|
|
|
|
JS_LOCK_GC(rt);
|
|
|
|
js_WaitForGC(rt);
|
|
|
|
}
|
2009-03-22 02:15:27 -07:00
|
|
|
}
|
2009-03-24 05:07:35 -07:00
|
|
|
#ifdef JS_THREADSAFE
|
2010-08-30 11:46:18 -07:00
|
|
|
#ifdef DEBUG
|
2011-04-13 09:27:37 -07:00
|
|
|
JSThread *t = cx->thread();
|
2010-08-30 11:46:18 -07:00
|
|
|
#endif
|
2009-03-24 05:07:35 -07:00
|
|
|
js_ClearContextThread(cx);
|
2010-10-22 10:48:06 -07:00
|
|
|
JS_ASSERT_IF(JS_CLIST_IS_EMPTY(&t->contextList), !t->data.requestDepth);
|
2010-05-26 17:49:04 -07:00
|
|
|
#endif
|
|
|
|
#ifdef JS_METER_DST_OFFSET_CACHING
|
|
|
|
cx->dstOffsetCache.dumpStats();
|
2009-03-24 05:07:35 -07:00
|
|
|
#endif
|
|
|
|
JS_UNLOCK_GC(rt);
|
2011-04-13 13:43:33 -07:00
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
rt->gcHelperThread.waitBackgroundSweepEnd(rt);
|
|
|
|
#endif
|
Bug 634155: Account for NewCompartment's memory, and change allocation APIs (r=nnethercote)
This changes the allocation API, in the following way:
js_malloc -> {cx->,rt->,OffTheBooks::}malloc
js_calloc -> {cx->,rt->,OffTheBooks::}calloc
js_realloc -> {cx->,rt->,OffTheBooks::}realloc
js_free -> {cx->,rt->,Foreground::,UnwantedForeground::}free
js_new -> {cx->,rt->,OffTheBooks::}new_
js_new_array -> {cx->,rt->,OffTheBooks::}new_array
js_delete -> {cx->,rt->,Foreground::,UnwantedForeground::}delete_
This is to move as many allocations as possible through a JSContext (so that they may be aken into account by gcMallocBytes) and to move as many deallocations to the background as possible (except on error paths).
2011-03-31 01:13:49 -07:00
|
|
|
Foreground::delete_(cx);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2009-01-05 16:22:42 -08:00
|
|
|
JSContext *
|
2007-03-22 10:30:00 -07:00
|
|
|
js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp)
|
|
|
|
{
|
|
|
|
JSContext *cx = *iterp;
|
|
|
|
|
2011-04-28 15:48:51 -07:00
|
|
|
Maybe<AutoLockGC> lockIf;
|
2011-04-28 15:48:51 -07:00
|
|
|
if (unlocked)
|
|
|
|
lockIf.construct(rt);
|
2008-12-18 07:24:34 -08:00
|
|
|
cx = js_ContextFromLinkField(cx ? cx->link.next : rt->contextList.next);
|
|
|
|
if (&cx->link == &rt->contextList)
|
2007-03-22 10:30:00 -07:00
|
|
|
cx = NULL;
|
|
|
|
*iterp = cx;
|
|
|
|
return cx;
|
|
|
|
}
|
|
|
|
|
2009-02-10 03:45:36 -08:00
|
|
|
JS_FRIEND_API(JSContext *)
|
|
|
|
js_NextActiveContext(JSRuntime *rt, JSContext *cx)
|
|
|
|
{
|
|
|
|
JSContext *iter = cx;
|
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
while ((cx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) {
|
2011-04-13 09:27:37 -07:00
|
|
|
if (cx->outstandingRequests && cx->thread()->data.requestDepth)
|
2009-02-10 03:45:36 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return cx;
|
|
|
|
#else
|
|
|
|
return js_ContextIterator(rt, JS_FALSE, &iter);
|
2009-07-27 18:40:12 -07:00
|
|
|
#endif
|
2009-02-10 03:45:36 -08:00
|
|
|
}
|
|
|
|
|
2011-03-07 14:00:00 -08:00
|
|
|
namespace js {
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2011-03-07 14:00:00 -08:00
|
|
|
bool
|
|
|
|
AutoResolving::alreadyStartedSlow() const
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2011-03-07 14:00:00 -08:00
|
|
|
JS_ASSERT(link);
|
|
|
|
AutoResolving *cursor = link;
|
|
|
|
do {
|
|
|
|
JS_ASSERT(this != cursor);
|
|
|
|
if (object == cursor->object && id == cursor->id && kind == cursor->kind)
|
|
|
|
return true;
|
|
|
|
} while (!!(cursor = cursor->link));
|
|
|
|
return false;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2011-03-07 14:00:00 -08:00
|
|
|
} /* namespace js */
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
static void
|
2009-12-15 14:20:48 -08:00
|
|
|
ReportError(JSContext *cx, const char *message, JSErrorReport *reportp,
|
|
|
|
JSErrorCallback callback, void *userRef)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Check the error report, and set a JavaScript-catchable exception
|
|
|
|
* if the error is defined to have an associated exception. If an
|
|
|
|
* exception is thrown, then the JSREPORT_EXCEPTION flag will be set
|
|
|
|
* on the error report, and exception-aware hosts should ignore it.
|
|
|
|
*/
|
|
|
|
JS_ASSERT(reportp);
|
2009-12-15 14:20:48 -08:00
|
|
|
if ((!callback || callback == js_GetErrorMessage) &&
|
|
|
|
reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION)
|
2007-03-22 10:30:00 -07:00
|
|
|
reportp->flags |= JSREPORT_EXCEPTION;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Call the error reporter only if an exception wasn't raised.
|
|
|
|
*
|
|
|
|
* If an exception was raised, then we call the debugErrorHook
|
|
|
|
* (if present) to give it a chance to see the error before it
|
|
|
|
* propagates out of scope. This is needed for compatability
|
|
|
|
* with the old scheme.
|
|
|
|
*/
|
2009-12-15 14:20:48 -08:00
|
|
|
if (!JS_IsRunning(cx) ||
|
|
|
|
!js_ErrorToException(cx, message, reportp, callback, userRef)) {
|
2007-03-22 10:30:00 -07:00
|
|
|
js_ReportErrorAgain(cx, message, reportp);
|
2007-06-14 23:44:18 -07:00
|
|
|
} else if (cx->debugHooks->debugErrorHook && cx->errorReporter) {
|
|
|
|
JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
|
2007-03-22 10:30:00 -07:00
|
|
|
/* test local in case debugErrorHook changed on another thread */
|
|
|
|
if (hook)
|
2007-06-14 23:44:18 -07:00
|
|
|
hook(cx, message, reportp, cx->debugHooks->debugErrorHookData);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-09 08:38:32 -08:00
|
|
|
/* The report must be initially zeroed. */
|
|
|
|
static void
|
|
|
|
PopulateReportBlame(JSContext *cx, JSErrorReport *report)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Walk stack until we find a frame that is associated with some script
|
|
|
|
* rather than a native frame.
|
|
|
|
*/
|
2011-04-28 13:02:47 -07:00
|
|
|
for (StackFrame *fp = js_GetTopStackFrame(cx, FRAME_EXPAND_TOP); fp; fp = fp->prev()) {
|
2010-03-03 18:10:13 -08:00
|
|
|
if (fp->pc(cx)) {
|
2010-08-09 22:43:33 -07:00
|
|
|
report->filename = fp->script()->filename;
|
2008-12-09 08:38:32 -08:00
|
|
|
report->lineno = js_FramePCToLineNumber(cx, fp);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
/*
|
|
|
|
* We don't post an exception in this case, since doing so runs into
|
|
|
|
* complications of pre-allocating an exception object which required
|
|
|
|
* running the Exception class initializer early etc.
|
|
|
|
* Instead we just invoke the errorReporter with an "Out Of Memory"
|
|
|
|
* type message, and then hope the process ends swiftly.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
js_ReportOutOfMemory(JSContext *cx)
|
|
|
|
{
|
2009-04-07 14:26:13 -07:00
|
|
|
#ifdef JS_TRACER
|
|
|
|
/*
|
|
|
|
* If we are in a builtin called directly from trace, don't report an
|
|
|
|
* error. We will retry in the interpreter instead.
|
|
|
|
*/
|
2011-02-01 10:18:06 -08:00
|
|
|
if (JS_ON_TRACE(cx) && !JS_TRACE_MONITOR_ON_TRACE(cx)->bailExit)
|
2009-04-07 14:26:13 -07:00
|
|
|
return;
|
|
|
|
#endif
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
JSErrorReport report;
|
|
|
|
JSErrorReporter onError = cx->errorReporter;
|
|
|
|
|
|
|
|
/* Get the message for this error, but we won't expand any arguments. */
|
|
|
|
const JSErrorFormatString *efs =
|
|
|
|
js_GetLocalizedErrorMessage(cx, NULL, NULL, JSMSG_OUT_OF_MEMORY);
|
|
|
|
const char *msg = efs ? efs->format : "Out of memory";
|
|
|
|
|
|
|
|
/* Fill out the report, but don't do anything that requires allocation. */
|
2010-03-10 15:34:12 -08:00
|
|
|
PodZero(&report);
|
2007-03-22 10:30:00 -07:00
|
|
|
report.flags = JSREPORT_ERROR;
|
|
|
|
report.errorNumber = JSMSG_OUT_OF_MEMORY;
|
2008-12-09 08:38:32 -08:00
|
|
|
PopulateReportBlame(cx, &report);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
/*
|
2008-02-15 03:38:40 -08:00
|
|
|
* If debugErrorHook is present then we give it a chance to veto sending
|
|
|
|
* the error on to the regular ErrorReporter. We also clear a pending
|
|
|
|
* exception if any now so the hooks can replace the out-of-memory error
|
|
|
|
* by a script-catchable exception.
|
2007-03-22 10:30:00 -07:00
|
|
|
*/
|
2011-01-07 02:03:14 -08:00
|
|
|
cx->clearPendingException();
|
2007-03-22 10:30:00 -07:00
|
|
|
if (onError) {
|
2007-06-14 23:44:18 -07:00
|
|
|
JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
|
2007-03-22 10:30:00 -07:00
|
|
|
if (hook &&
|
2007-06-14 23:44:18 -07:00
|
|
|
!hook(cx, msg, &report, cx->debugHooks->debugErrorHookData)) {
|
2007-03-22 10:30:00 -07:00
|
|
|
onError = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (onError)
|
|
|
|
onError(cx, msg, &report);
|
|
|
|
}
|
|
|
|
|
2007-11-22 14:21:18 -08:00
|
|
|
void
|
2011-04-13 09:27:37 -07:00
|
|
|
js_ReportOutOfScriptQuota(JSContext *maybecx)
|
2007-11-22 14:21:18 -08:00
|
|
|
{
|
2011-04-13 09:27:37 -07:00
|
|
|
if (maybecx)
|
|
|
|
JS_ReportErrorNumber(maybecx, js_GetErrorMessage, NULL, JSMSG_SCRIPT_STACK_QUOTA);
|
2007-11-22 14:21:18 -08:00
|
|
|
}
|
|
|
|
|
2010-08-24 06:50:15 -07:00
|
|
|
JS_FRIEND_API(void)
|
2011-04-13 09:27:37 -07:00
|
|
|
js_ReportOverRecursed(JSContext *maybecx)
|
2007-11-22 14:21:18 -08:00
|
|
|
{
|
2011-04-13 09:27:37 -07:00
|
|
|
if (maybecx)
|
|
|
|
JS_ReportErrorNumber(maybecx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
|
2007-11-22 14:21:18 -08:00
|
|
|
}
|
|
|
|
|
2008-03-12 16:07:47 -07:00
|
|
|
void
|
2011-04-13 09:27:37 -07:00
|
|
|
js_ReportAllocationOverflow(JSContext *maybecx)
|
2008-03-12 16:07:47 -07:00
|
|
|
{
|
2011-04-13 09:27:37 -07:00
|
|
|
if (maybecx)
|
|
|
|
JS_ReportErrorNumber(maybecx, js_GetErrorMessage, NULL, JSMSG_ALLOC_OVERFLOW);
|
2008-03-12 16:07:47 -07:00
|
|
|
}
|
|
|
|
|
2009-11-19 09:23:20 -08:00
|
|
|
/*
|
|
|
|
* Given flags and the state of cx, decide whether we should report an
|
|
|
|
* error, a warning, or just continue execution normally. Return
|
|
|
|
* true if we should continue normally, without reporting anything;
|
|
|
|
* otherwise, adjust *flags as appropriate and return false.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
checkReportFlags(JSContext *cx, uintN *flags)
|
|
|
|
{
|
|
|
|
if (JSREPORT_IS_STRICT_MODE_ERROR(*flags)) {
|
2010-03-11 22:50:10 -08:00
|
|
|
/*
|
|
|
|
* Error in strict code; warning with strict option; okay otherwise.
|
|
|
|
* We assume that if the top frame is a native, then it is strict if
|
|
|
|
* the nearest scripted frame is strict, see bug 536306.
|
|
|
|
*/
|
2011-04-13 09:27:37 -07:00
|
|
|
StackFrame *fp = js_GetScriptedCaller(cx, NULL);
|
2010-08-09 22:43:33 -07:00
|
|
|
if (fp && fp->script()->strictModeCode)
|
2009-11-19 09:23:20 -08:00
|
|
|
*flags &= ~JSREPORT_WARNING;
|
2011-01-27 02:54:58 -08:00
|
|
|
else if (cx->hasStrictOption())
|
2009-11-19 09:23:20 -08:00
|
|
|
*flags |= JSREPORT_WARNING;
|
|
|
|
else
|
|
|
|
return true;
|
|
|
|
} else if (JSREPORT_IS_STRICT(*flags)) {
|
|
|
|
/* Warning/error only when JSOPTION_STRICT is set. */
|
2011-01-27 02:54:58 -08:00
|
|
|
if (!cx->hasStrictOption())
|
2009-11-19 09:23:20 -08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Warnings become errors when JSOPTION_WERROR is set. */
|
2011-01-27 02:54:58 -08:00
|
|
|
if (JSREPORT_IS_WARNING(*flags) && cx->hasWErrorOption())
|
2009-11-19 09:23:20 -08:00
|
|
|
*flags &= ~JSREPORT_WARNING;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
JSBool
|
|
|
|
js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap)
|
|
|
|
{
|
|
|
|
char *message;
|
|
|
|
jschar *ucmessage;
|
|
|
|
size_t messagelen;
|
|
|
|
JSErrorReport report;
|
|
|
|
JSBool warning;
|
|
|
|
|
2009-11-19 09:23:20 -08:00
|
|
|
if (checkReportFlags(cx, &flags))
|
2007-03-22 10:30:00 -07:00
|
|
|
return JS_TRUE;
|
|
|
|
|
|
|
|
message = JS_vsmprintf(format, ap);
|
|
|
|
if (!message)
|
|
|
|
return JS_FALSE;
|
|
|
|
messagelen = strlen(message);
|
|
|
|
|
2010-03-10 15:34:12 -08:00
|
|
|
PodZero(&report);
|
2007-03-22 10:30:00 -07:00
|
|
|
report.flags = flags;
|
|
|
|
report.errorNumber = JSMSG_USER_DEFINED_ERROR;
|
|
|
|
report.ucmessage = ucmessage = js_InflateString(cx, message, &messagelen);
|
2008-12-09 08:38:32 -08:00
|
|
|
PopulateReportBlame(cx, &report);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
warning = JSREPORT_IS_WARNING(report.flags);
|
|
|
|
|
2009-12-15 14:20:48 -08:00
|
|
|
ReportError(cx, message, &report, NULL, NULL);
|
2011-03-31 01:14:12 -07:00
|
|
|
Foreground::free_(message);
|
|
|
|
Foreground::free_(ucmessage);
|
2007-03-22 10:30:00 -07:00
|
|
|
return warning;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The arguments from ap need to be packaged up into an array and stored
|
|
|
|
* into the report struct.
|
|
|
|
*
|
|
|
|
* The format string addressed by the error number may contain operands
|
|
|
|
* identified by the format {N}, where N is a decimal digit. Each of these
|
|
|
|
* is to be replaced by the Nth argument from the va_list. The complete
|
|
|
|
* message is placed into reportp->ucmessage converted to a JSString.
|
|
|
|
*
|
|
|
|
* Returns true if the expansion succeeds (can fail if out of memory).
|
|
|
|
*/
|
|
|
|
JSBool
|
|
|
|
js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,
|
|
|
|
void *userRef, const uintN errorNumber,
|
|
|
|
char **messagep, JSErrorReport *reportp,
|
2009-11-19 09:23:20 -08:00
|
|
|
bool charArgs, va_list ap)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
const JSErrorFormatString *efs;
|
|
|
|
int i;
|
|
|
|
int argCount;
|
|
|
|
|
|
|
|
*messagep = NULL;
|
|
|
|
|
|
|
|
/* Most calls supply js_GetErrorMessage; if this is so, assume NULL. */
|
|
|
|
if (!callback || callback == js_GetErrorMessage)
|
|
|
|
efs = js_GetLocalizedErrorMessage(cx, userRef, NULL, errorNumber);
|
|
|
|
else
|
|
|
|
efs = callback(userRef, NULL, errorNumber);
|
|
|
|
if (efs) {
|
|
|
|
size_t totalArgsLength = 0;
|
|
|
|
size_t argLengths[10]; /* only {0} thru {9} supported */
|
|
|
|
argCount = efs->argCount;
|
|
|
|
JS_ASSERT(argCount <= 10);
|
|
|
|
if (argCount > 0) {
|
|
|
|
/*
|
|
|
|
* Gather the arguments into an array, and accumulate
|
|
|
|
* their sizes. We allocate 1 more than necessary and
|
|
|
|
* null it out to act as the caboose when we free the
|
|
|
|
* pointers later.
|
|
|
|
*/
|
|
|
|
reportp->messageArgs = (const jschar **)
|
2011-03-31 01:14:12 -07:00
|
|
|
cx->malloc_(sizeof(jschar *) * (argCount + 1));
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!reportp->messageArgs)
|
|
|
|
return JS_FALSE;
|
|
|
|
reportp->messageArgs[argCount] = NULL;
|
|
|
|
for (i = 0; i < argCount; i++) {
|
|
|
|
if (charArgs) {
|
|
|
|
char *charArg = va_arg(ap, char *);
|
|
|
|
size_t charArgLength = strlen(charArg);
|
|
|
|
reportp->messageArgs[i]
|
|
|
|
= js_InflateString(cx, charArg, &charArgLength);
|
|
|
|
if (!reportp->messageArgs[i])
|
|
|
|
goto error;
|
|
|
|
} else {
|
|
|
|
reportp->messageArgs[i] = va_arg(ap, jschar *);
|
|
|
|
}
|
|
|
|
argLengths[i] = js_strlen(reportp->messageArgs[i]);
|
|
|
|
totalArgsLength += argLengths[i];
|
|
|
|
}
|
|
|
|
/* NULL-terminate for easy copying. */
|
|
|
|
reportp->messageArgs[i] = NULL;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Parse the error format, substituting the argument X
|
|
|
|
* for {X} in the format.
|
|
|
|
*/
|
|
|
|
if (argCount > 0) {
|
|
|
|
if (efs->format) {
|
|
|
|
jschar *buffer, *fmt, *out;
|
|
|
|
int expandedArgs = 0;
|
|
|
|
size_t expandedLength;
|
|
|
|
size_t len = strlen(efs->format);
|
|
|
|
|
|
|
|
buffer = fmt = js_InflateString (cx, efs->format, &len);
|
|
|
|
if (!buffer)
|
|
|
|
goto error;
|
|
|
|
expandedLength = len
|
|
|
|
- (3 * argCount) /* exclude the {n} */
|
|
|
|
+ totalArgsLength;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note - the above calculation assumes that each argument
|
|
|
|
* is used once and only once in the expansion !!!
|
|
|
|
*/
|
|
|
|
reportp->ucmessage = out = (jschar *)
|
2011-03-31 01:14:12 -07:00
|
|
|
cx->malloc_((expandedLength + 1) * sizeof(jschar));
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!out) {
|
2011-03-31 01:14:12 -07:00
|
|
|
cx->free_(buffer);
|
2007-03-22 10:30:00 -07:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
while (*fmt) {
|
|
|
|
if (*fmt == '{') {
|
|
|
|
if (isdigit(fmt[1])) {
|
|
|
|
int d = JS7_UNDEC(fmt[1]);
|
|
|
|
JS_ASSERT(d < argCount);
|
|
|
|
js_strncpy(out, reportp->messageArgs[d],
|
|
|
|
argLengths[d]);
|
|
|
|
out += argLengths[d];
|
|
|
|
fmt += 3;
|
|
|
|
expandedArgs++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*out++ = *fmt++;
|
|
|
|
}
|
|
|
|
JS_ASSERT(expandedArgs == argCount);
|
|
|
|
*out = 0;
|
2011-03-31 01:14:12 -07:00
|
|
|
cx->free_(buffer);
|
2007-03-22 10:30:00 -07:00
|
|
|
*messagep =
|
|
|
|
js_DeflateString(cx, reportp->ucmessage,
|
|
|
|
(size_t)(out - reportp->ucmessage));
|
|
|
|
if (!*messagep)
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Zero arguments: the format string (if it exists) is the
|
|
|
|
* entire message.
|
|
|
|
*/
|
|
|
|
if (efs->format) {
|
|
|
|
size_t len;
|
|
|
|
*messagep = JS_strdup(cx, efs->format);
|
|
|
|
if (!*messagep)
|
|
|
|
goto error;
|
|
|
|
len = strlen(*messagep);
|
|
|
|
reportp->ucmessage = js_InflateString(cx, *messagep, &len);
|
|
|
|
if (!reportp->ucmessage)
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (*messagep == NULL) {
|
|
|
|
/* where's the right place for this ??? */
|
|
|
|
const char *defaultErrorMessage
|
|
|
|
= "No error message available for error number %d";
|
|
|
|
size_t nbytes = strlen(defaultErrorMessage) + 16;
|
2011-03-31 01:14:12 -07:00
|
|
|
*messagep = (char *)cx->malloc_(nbytes);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!*messagep)
|
|
|
|
goto error;
|
|
|
|
JS_snprintf(*messagep, nbytes, defaultErrorMessage, errorNumber);
|
|
|
|
}
|
|
|
|
return JS_TRUE;
|
|
|
|
|
|
|
|
error:
|
|
|
|
if (reportp->messageArgs) {
|
|
|
|
/* free the arguments only if we allocated them */
|
|
|
|
if (charArgs) {
|
|
|
|
i = 0;
|
|
|
|
while (reportp->messageArgs[i])
|
2011-03-31 01:14:12 -07:00
|
|
|
cx->free_((void *)reportp->messageArgs[i++]);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2011-03-31 01:14:12 -07:00
|
|
|
cx->free_((void *)reportp->messageArgs);
|
2007-03-22 10:30:00 -07:00
|
|
|
reportp->messageArgs = NULL;
|
|
|
|
}
|
|
|
|
if (reportp->ucmessage) {
|
2011-03-31 01:14:12 -07:00
|
|
|
cx->free_((void *)reportp->ucmessage);
|
2007-03-22 10:30:00 -07:00
|
|
|
reportp->ucmessage = NULL;
|
|
|
|
}
|
|
|
|
if (*messagep) {
|
2011-03-31 01:14:12 -07:00
|
|
|
cx->free_((void *)*messagep);
|
2007-03-22 10:30:00 -07:00
|
|
|
*messagep = NULL;
|
|
|
|
}
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSBool
|
|
|
|
js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback,
|
|
|
|
void *userRef, const uintN errorNumber,
|
|
|
|
JSBool charArgs, va_list ap)
|
|
|
|
{
|
|
|
|
JSErrorReport report;
|
|
|
|
char *message;
|
|
|
|
JSBool warning;
|
|
|
|
|
2009-11-19 09:23:20 -08:00
|
|
|
if (checkReportFlags(cx, &flags))
|
2007-03-22 10:30:00 -07:00
|
|
|
return JS_TRUE;
|
2009-11-19 09:23:20 -08:00
|
|
|
warning = JSREPORT_IS_WARNING(flags);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-03-10 15:34:12 -08:00
|
|
|
PodZero(&report);
|
2007-03-22 10:30:00 -07:00
|
|
|
report.flags = flags;
|
|
|
|
report.errorNumber = errorNumber;
|
2008-12-09 08:38:32 -08:00
|
|
|
PopulateReportBlame(cx, &report);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,
|
2010-03-04 20:44:09 -08:00
|
|
|
&message, &report, !!charArgs, ap)) {
|
2007-03-22 10:30:00 -07:00
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
|
2009-12-15 14:20:48 -08:00
|
|
|
ReportError(cx, message, &report, callback, userRef);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (message)
|
2011-03-31 01:14:12 -07:00
|
|
|
cx->free_(message);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (report.messageArgs) {
|
|
|
|
/*
|
|
|
|
* js_ExpandErrorArguments owns its messageArgs only if it had to
|
|
|
|
* inflate the arguments (from regular |char *|s).
|
|
|
|
*/
|
|
|
|
if (charArgs) {
|
|
|
|
int i = 0;
|
|
|
|
while (report.messageArgs[i])
|
2011-03-31 01:14:12 -07:00
|
|
|
cx->free_((void *)report.messageArgs[i++]);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2011-03-31 01:14:12 -07:00
|
|
|
cx->free_((void *)report.messageArgs);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
if (report.ucmessage)
|
2011-03-31 01:14:12 -07:00
|
|
|
cx->free_((void *)report.ucmessage);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
return warning;
|
|
|
|
}
|
|
|
|
|
|
|
|
JS_FRIEND_API(void)
|
|
|
|
js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *reportp)
|
|
|
|
{
|
|
|
|
JSErrorReporter onError;
|
|
|
|
|
|
|
|
if (!message)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (cx->lastMessage)
|
2011-03-31 01:14:12 -07:00
|
|
|
Foreground::free_(cx->lastMessage);
|
2007-03-22 10:30:00 -07:00
|
|
|
cx->lastMessage = JS_strdup(cx, message);
|
|
|
|
if (!cx->lastMessage)
|
|
|
|
return;
|
|
|
|
onError = cx->errorReporter;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If debugErrorHook is present then we give it a chance to veto
|
|
|
|
* sending the error on to the regular ErrorReporter.
|
|
|
|
*/
|
|
|
|
if (onError) {
|
2007-06-14 23:44:18 -07:00
|
|
|
JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
|
2007-03-22 10:30:00 -07:00
|
|
|
if (hook &&
|
|
|
|
!hook(cx, cx->lastMessage, reportp,
|
2007-06-14 23:44:18 -07:00
|
|
|
cx->debugHooks->debugErrorHookData)) {
|
2007-03-22 10:30:00 -07:00
|
|
|
onError = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (onError)
|
|
|
|
onError(cx, cx->lastMessage, reportp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
js_ReportIsNotDefined(JSContext *cx, const char *name)
|
|
|
|
{
|
|
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_DEFINED, name);
|
|
|
|
}
|
|
|
|
|
2007-11-12 21:23:22 -08:00
|
|
|
JSBool
|
2010-07-14 23:19:36 -07:00
|
|
|
js_ReportIsNullOrUndefined(JSContext *cx, intN spindex, const Value &v,
|
2007-11-12 21:23:22 -08:00
|
|
|
JSString *fallback)
|
|
|
|
{
|
|
|
|
char *bytes;
|
|
|
|
JSBool ok;
|
|
|
|
|
2010-07-14 23:19:36 -07:00
|
|
|
bytes = DecompileValueGenerator(cx, spindex, v, fallback);
|
2007-11-12 21:23:22 -08:00
|
|
|
if (!bytes)
|
|
|
|
return JS_FALSE;
|
|
|
|
|
|
|
|
if (strcmp(bytes, js_undefined_str) == 0 ||
|
|
|
|
strcmp(bytes, js_null_str) == 0) {
|
|
|
|
ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
|
|
|
|
js_GetErrorMessage, NULL,
|
|
|
|
JSMSG_NO_PROPERTIES, bytes,
|
|
|
|
NULL, NULL);
|
2010-07-14 23:19:36 -07:00
|
|
|
} else if (v.isUndefined()) {
|
2007-11-12 21:23:22 -08:00
|
|
|
ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
|
|
|
|
js_GetErrorMessage, NULL,
|
2009-08-17 14:54:52 -07:00
|
|
|
JSMSG_UNEXPECTED_TYPE, bytes,
|
2007-11-12 21:23:22 -08:00
|
|
|
js_undefined_str, NULL);
|
|
|
|
} else {
|
2010-07-14 23:19:36 -07:00
|
|
|
JS_ASSERT(v.isNull());
|
2007-11-12 21:23:22 -08:00
|
|
|
ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
|
|
|
|
js_GetErrorMessage, NULL,
|
2009-08-17 14:54:52 -07:00
|
|
|
JSMSG_UNEXPECTED_TYPE, bytes,
|
2007-11-12 21:23:22 -08:00
|
|
|
js_null_str, NULL);
|
|
|
|
}
|
|
|
|
|
2011-03-31 01:14:12 -07:00
|
|
|
cx->free_(bytes);
|
2007-11-12 21:23:22 -08:00
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2008-08-08 09:02:50 -07:00
|
|
|
void
|
2010-07-14 23:19:36 -07:00
|
|
|
js_ReportMissingArg(JSContext *cx, const Value &v, uintN arg)
|
2008-08-08 09:02:50 -07:00
|
|
|
{
|
|
|
|
char argbuf[11];
|
|
|
|
char *bytes;
|
|
|
|
JSAtom *atom;
|
|
|
|
|
|
|
|
JS_snprintf(argbuf, sizeof argbuf, "%u", arg);
|
|
|
|
bytes = NULL;
|
2010-07-14 23:19:36 -07:00
|
|
|
if (IsFunctionObject(v)) {
|
|
|
|
atom = GET_FUNCTION_PRIVATE(cx, &v.toObject())->atom;
|
|
|
|
bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK,
|
2011-03-14 13:59:53 -07:00
|
|
|
v, atom);
|
2008-08-08 09:02:50 -07:00
|
|
|
if (!bytes)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
|
|
|
JSMSG_MISSING_FUN_ARG, argbuf,
|
|
|
|
bytes ? bytes : "");
|
2011-03-31 01:14:12 -07:00
|
|
|
cx->free_(bytes);
|
2008-08-08 09:02:50 -07:00
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
JSBool
|
|
|
|
js_ReportValueErrorFlags(JSContext *cx, uintN flags, const uintN errorNumber,
|
2010-07-14 23:19:36 -07:00
|
|
|
intN spindex, const Value &v, JSString *fallback,
|
2007-03-22 10:30:00 -07:00
|
|
|
const char *arg1, const char *arg2)
|
|
|
|
{
|
|
|
|
char *bytes;
|
|
|
|
JSBool ok;
|
|
|
|
|
|
|
|
JS_ASSERT(js_ErrorFormatString[errorNumber].argCount >= 1);
|
|
|
|
JS_ASSERT(js_ErrorFormatString[errorNumber].argCount <= 3);
|
2010-07-14 23:19:36 -07:00
|
|
|
bytes = DecompileValueGenerator(cx, spindex, v, fallback);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!bytes)
|
|
|
|
return JS_FALSE;
|
|
|
|
|
|
|
|
ok = JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage,
|
|
|
|
NULL, errorNumber, bytes, arg1, arg2);
|
2011-03-31 01:14:12 -07:00
|
|
|
cx->free_(bytes);
|
2007-03-22 10:30:00 -07:00
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined DEBUG && defined XP_UNIX
|
|
|
|
/* For gdb usage. */
|
2010-10-26 12:07:26 -07:00
|
|
|
void js_logon(JSContext *cx) { cx->logfp = stderr; cx->logPrevPc = NULL; }
|
|
|
|
void js_logoff(JSContext *cx) { cx->logfp = NULL; }
|
2007-03-22 10:30:00 -07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = {
|
|
|
|
#define MSG_DEF(name, number, count, exception, format) \
|
|
|
|
{ format, count, exception } ,
|
|
|
|
#include "js.msg"
|
|
|
|
#undef MSG_DEF
|
|
|
|
};
|
|
|
|
|
2008-09-02 15:30:42 -07:00
|
|
|
JS_FRIEND_API(const JSErrorFormatString *)
|
2007-03-22 10:30:00 -07:00
|
|
|
js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
|
|
|
|
{
|
|
|
|
if ((errorNumber > 0) && (errorNumber < JSErr_Limit))
|
|
|
|
return &js_ErrorFormatString[errorNumber];
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-04-20 09:28:57 -07:00
|
|
|
bool
|
|
|
|
checkOutOfMemory(JSRuntime *rt)
|
|
|
|
{
|
|
|
|
AutoLockGC lock(rt);
|
|
|
|
return rt->gcBytes > rt->gcMaxBytes;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
JSBool
|
2009-02-10 03:45:36 -08:00
|
|
|
js_InvokeOperationCallback(JSContext *cx)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2010-10-22 10:48:06 -07:00
|
|
|
JSRuntime *rt = cx->runtime;
|
2011-03-13 07:45:02 -07:00
|
|
|
ThreadData *td = JS_THREAD_DATA(cx);
|
2010-10-22 10:48:06 -07:00
|
|
|
|
2010-07-23 09:32:50 -07:00
|
|
|
JS_ASSERT_REQUEST_DEPTH(cx);
|
2010-10-22 10:48:06 -07:00
|
|
|
JS_ASSERT(td->interruptFlags != 0);
|
2009-07-27 18:40:12 -07:00
|
|
|
|
2009-02-10 03:45:36 -08:00
|
|
|
/*
|
2010-12-04 08:04:10 -08:00
|
|
|
* Reset the callback counter first, then run GC and yield. If another
|
|
|
|
* thread is racing us here we will accumulate another callback request
|
|
|
|
* which will be serviced at the next opportunity.
|
2009-02-10 03:45:36 -08:00
|
|
|
*/
|
2010-10-22 10:48:06 -07:00
|
|
|
JS_LOCK_GC(rt);
|
|
|
|
td->interruptFlags = 0;
|
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
JS_ATOMIC_DECREMENT(&rt->interruptCounter);
|
|
|
|
#endif
|
|
|
|
JS_UNLOCK_GC(rt);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-07-28 11:20:19 -07:00
|
|
|
if (rt->gcIsNeeded) {
|
2011-01-07 23:44:57 -08:00
|
|
|
js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL);
|
2010-07-28 11:20:19 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* On trace we can exceed the GC quota, see comments in NewGCArena. So
|
|
|
|
* we check the quota and report OOM here when we are off trace.
|
|
|
|
*/
|
2011-04-20 09:28:57 -07:00
|
|
|
if (checkOutOfMemory(rt)) {
|
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
/*
|
|
|
|
* We have to wait until the background thread is done in order
|
|
|
|
* to get a correct answer.
|
|
|
|
*/
|
|
|
|
rt->gcHelperThread.waitBackgroundSweepEnd(rt);
|
|
|
|
if (checkOutOfMemory(rt)) {
|
|
|
|
js_ReportOutOfMemory(cx);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#else
|
2010-04-16 05:31:17 -07:00
|
|
|
js_ReportOutOfMemory(cx);
|
|
|
|
return false;
|
2011-04-20 09:28:57 -07:00
|
|
|
#endif
|
2010-04-16 05:31:17 -07:00
|
|
|
}
|
|
|
|
}
|
2011-04-20 09:28:57 -07:00
|
|
|
|
2009-07-27 18:40:12 -07:00
|
|
|
#ifdef JS_THREADSAFE
|
2010-12-04 08:04:10 -08:00
|
|
|
/*
|
|
|
|
* We automatically yield the current context every time the operation
|
|
|
|
* callback is hit since we might be called as a result of an impending
|
|
|
|
* GC on another thread, which would deadlock if we do not yield.
|
|
|
|
* Operation callbacks are supposed to happen rarely (seconds, not
|
|
|
|
* milliseconds) so it is acceptable to yield at every callback.
|
|
|
|
*
|
|
|
|
* As the GC can be canceled before it does any request checks we yield
|
|
|
|
* even if rt->gcIsNeeded was true above. See bug 590533.
|
|
|
|
*/
|
|
|
|
JS_YieldRequest(cx);
|
2009-02-10 03:45:36 -08:00
|
|
|
#endif
|
2009-01-05 16:22:42 -08:00
|
|
|
|
2010-07-28 11:20:19 -07:00
|
|
|
JSOperationCallback cb = cx->operationCallback;
|
|
|
|
|
2009-02-10 03:45:36 -08:00
|
|
|
/*
|
|
|
|
* Important: Additional callbacks can occur inside the callback handler
|
|
|
|
* if it re-enters the JS engine. The embedding must ensure that the
|
|
|
|
* callback is disconnected before attempting such re-entry.
|
|
|
|
*/
|
2010-07-28 11:20:19 -07:00
|
|
|
|
2009-02-10 03:45:36 -08:00
|
|
|
return !cb || cb(cx);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2008-12-09 08:38:32 -08:00
|
|
|
|
2010-06-07 23:55:13 -07:00
|
|
|
JSBool
|
|
|
|
js_HandleExecutionInterrupt(JSContext *cx)
|
|
|
|
{
|
|
|
|
JSBool result = JS_TRUE;
|
2010-10-22 10:48:06 -07:00
|
|
|
if (JS_THREAD_DATA(cx)->interruptFlags)
|
2010-06-07 23:55:13 -07:00
|
|
|
result = js_InvokeOperationCallback(cx) && result;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2010-09-07 14:08:20 -07:00
|
|
|
namespace js {
|
|
|
|
|
2010-10-22 10:48:06 -07:00
|
|
|
void
|
|
|
|
TriggerOperationCallback(JSContext *cx)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* We allow for cx to come from another thread. Thus we must deal with
|
|
|
|
* possible JS_ClearContextThread calls when accessing cx->thread. But we
|
|
|
|
* assume that the calling thread is in a request so JSThread cannot be
|
|
|
|
* GC-ed.
|
|
|
|
*/
|
2011-03-13 07:45:02 -07:00
|
|
|
ThreadData *td;
|
2010-10-22 10:48:06 -07:00
|
|
|
#ifdef JS_THREADSAFE
|
2011-04-13 09:27:37 -07:00
|
|
|
JSThread *thread = cx->thread();
|
2010-10-22 10:48:06 -07:00
|
|
|
if (!thread)
|
|
|
|
return;
|
|
|
|
td = &thread->data;
|
|
|
|
#else
|
|
|
|
td = JS_THREAD_DATA(cx);
|
|
|
|
#endif
|
|
|
|
td->triggerOperationCallback(cx->runtime);
|
|
|
|
}
|
|
|
|
|
2009-04-20 03:10:29 -07:00
|
|
|
void
|
2010-09-07 14:08:20 -07:00
|
|
|
TriggerAllOperationCallbacks(JSRuntime *rt)
|
2009-04-20 03:10:29 -07:00
|
|
|
{
|
2010-07-22 13:59:59 -07:00
|
|
|
for (ThreadDataIter i(rt); !i.empty(); i.popFront())
|
2010-10-22 10:48:06 -07:00
|
|
|
i.threadData()->triggerOperationCallback(rt);
|
2009-04-20 03:10:29 -07:00
|
|
|
}
|
|
|
|
|
2010-09-07 14:08:20 -07:00
|
|
|
} /* namespace js */
|
|
|
|
|
2011-04-13 09:27:37 -07:00
|
|
|
StackFrame *
|
|
|
|
js_GetScriptedCaller(JSContext *cx, StackFrame *fp)
|
2008-12-09 08:38:32 -08:00
|
|
|
{
|
|
|
|
if (!fp)
|
2011-03-26 19:07:13 -07:00
|
|
|
fp = js_GetTopStackFrame(cx, FRAME_EXPAND_NONE);
|
2010-08-16 12:35:04 -07:00
|
|
|
while (fp && fp->isDummyFrame())
|
2010-08-09 22:43:33 -07:00
|
|
|
fp = fp->prev();
|
|
|
|
JS_ASSERT_IF(fp, fp->isScriptFrame());
|
2010-08-16 12:35:04 -07:00
|
|
|
return fp;
|
2008-12-09 08:38:32 -08:00
|
|
|
}
|
2009-03-20 17:07:30 -07:00
|
|
|
|
|
|
|
jsbytecode*
|
|
|
|
js_GetCurrentBytecodePC(JSContext* cx)
|
|
|
|
{
|
|
|
|
jsbytecode *pc, *imacpc;
|
|
|
|
|
|
|
|
#ifdef JS_TRACER
|
|
|
|
if (JS_ON_TRACE(cx)) {
|
2011-02-01 10:18:06 -08:00
|
|
|
pc = JS_TRACE_MONITOR_ON_TRACE(cx)->bailExit->pc;
|
|
|
|
imacpc = JS_TRACE_MONITOR_ON_TRACE(cx)->bailExit->imacpc;
|
2009-03-20 17:07:30 -07:00
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
JS_ASSERT_NOT_ON_TRACE(cx); /* for static analysis */
|
2011-04-13 09:27:37 -07:00
|
|
|
pc = cx->running() ? cx->regs().pc : NULL;
|
2010-03-03 18:10:13 -08:00
|
|
|
if (!pc)
|
2009-03-20 17:07:30 -07:00
|
|
|
return NULL;
|
2010-08-09 22:43:33 -07:00
|
|
|
imacpc = cx->fp()->maybeImacropc();
|
2009-03-20 17:07:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we are inside GetProperty_tn or similar, return a pointer to the
|
|
|
|
* current instruction in the script, not the CALL instruction in the
|
|
|
|
* imacro, for the benefit of callers doing bytecode inspection.
|
|
|
|
*/
|
|
|
|
return (*pc == JSOP_CALL && imacpc) ? imacpc : pc;
|
|
|
|
}
|
2009-08-20 12:13:21 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
js_CurrentPCIsInImacro(JSContext *cx)
|
|
|
|
{
|
|
|
|
#ifdef JS_TRACER
|
|
|
|
VOUCH_DOES_NOT_REQUIRE_STACK();
|
2010-08-15 04:16:54 -07:00
|
|
|
if (JS_ON_TRACE(cx))
|
2011-02-01 10:18:06 -08:00
|
|
|
return JS_TRACE_MONITOR_ON_TRACE(cx)->bailExit->imacpc != NULL;
|
2010-08-09 22:43:33 -07:00
|
|
|
return cx->fp()->hasImacropc();
|
2009-08-20 12:13:21 -07:00
|
|
|
#else
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
}
|
2009-10-18 08:40:19 -07:00
|
|
|
|
2010-05-26 17:49:04 -07:00
|
|
|
void
|
|
|
|
DSTOffsetCache::purge()
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* NB: The initial range values are carefully chosen to result in a cache
|
|
|
|
* miss on first use given the range of possible values. Be careful
|
|
|
|
* to keep these values and the caching algorithm in sync!
|
|
|
|
*/
|
|
|
|
offsetMilliseconds = 0;
|
|
|
|
rangeStartSeconds = rangeEndSeconds = INT64_MIN;
|
2010-08-17 10:42:57 -07:00
|
|
|
oldOffsetMilliseconds = 0;
|
|
|
|
oldRangeStartSeconds = oldRangeEndSeconds = INT64_MIN;
|
2010-05-26 17:49:04 -07:00
|
|
|
|
|
|
|
#ifdef JS_METER_DST_OFFSET_CACHING
|
|
|
|
totalCalculations = 0;
|
|
|
|
hit = 0;
|
|
|
|
missIncreasing = missDecreasing = 0;
|
|
|
|
missIncreasingOffsetChangeExpand = missIncreasingOffsetChangeUpper = 0;
|
|
|
|
missDecreasingOffsetChangeExpand = missDecreasingOffsetChangeLower = 0;
|
|
|
|
missLargeIncrease = missLargeDecrease = 0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
sanityCheck();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Since getDSTOffsetMilliseconds guarantees that all times seen will be
|
|
|
|
* positive, we can initialize the range at construction time with large
|
|
|
|
* negative numbers to ensure the first computation is always a cache miss and
|
|
|
|
* doesn't return a bogus offset.
|
|
|
|
*/
|
|
|
|
DSTOffsetCache::DSTOffsetCache()
|
|
|
|
{
|
|
|
|
purge();
|
|
|
|
}
|
|
|
|
|
2010-03-03 17:52:26 -08:00
|
|
|
JSContext::JSContext(JSRuntime *rt)
|
2011-01-27 02:54:58 -08:00
|
|
|
: hasVersionOverride(false),
|
|
|
|
runtime(rt),
|
2011-01-13 14:11:15 -08:00
|
|
|
compartment(NULL),
|
2011-04-13 09:27:37 -07:00
|
|
|
stack(thisDuringConstruction()),
|
2011-01-24 09:47:25 -08:00
|
|
|
busyArrays()
|
2010-03-03 17:52:26 -08:00
|
|
|
{}
|
|
|
|
|
2011-03-13 07:45:02 -07:00
|
|
|
JSContext::~JSContext()
|
|
|
|
{
|
|
|
|
#ifdef JS_THREADSAFE
|
2011-04-13 09:27:37 -07:00
|
|
|
JS_ASSERT(!thread_);
|
2011-03-13 07:45:02 -07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Free the stuff hanging off of cx. */
|
|
|
|
VOUCH_DOES_NOT_REQUIRE_STACK();
|
|
|
|
JS_FinishArenaPool(&tempPool);
|
|
|
|
JS_FinishArenaPool(®ExpPool);
|
|
|
|
|
|
|
|
if (lastMessage)
|
|
|
|
Foreground::free_(lastMessage);
|
|
|
|
|
|
|
|
/* Remove any argument formatters. */
|
|
|
|
JSArgumentFormatMap *map = argumentFormatMap;
|
|
|
|
while (map) {
|
|
|
|
JSArgumentFormatMap *temp = map;
|
|
|
|
map = map->next;
|
|
|
|
Foreground::free_(temp);
|
|
|
|
}
|
|
|
|
|
|
|
|
JS_ASSERT(!resolvingList);
|
|
|
|
}
|
|
|
|
|
2010-09-27 18:24:24 -07:00
|
|
|
void
|
|
|
|
JSContext::resetCompartment()
|
|
|
|
{
|
|
|
|
JSObject *scopeobj;
|
2011-04-13 09:27:37 -07:00
|
|
|
if (stack.running()) {
|
2010-09-27 18:24:24 -07:00
|
|
|
scopeobj = &fp()->scopeChain();
|
|
|
|
} else {
|
|
|
|
scopeobj = globalObject;
|
2011-01-07 02:03:14 -08:00
|
|
|
if (!scopeobj)
|
|
|
|
goto error;
|
2010-09-27 18:24:24 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Innerize. Assert, but check anyway, that this succeeds. (It
|
|
|
|
* can only fail due to bugs in the engine or embedding.)
|
|
|
|
*/
|
|
|
|
OBJ_TO_INNER_OBJECT(this, scopeobj);
|
2011-01-07 02:03:14 -08:00
|
|
|
if (!scopeobj)
|
|
|
|
goto error;
|
2010-09-27 18:24:24 -07:00
|
|
|
}
|
2011-01-07 02:03:14 -08:00
|
|
|
|
|
|
|
compartment = scopeobj->compartment();
|
2011-04-01 17:26:34 -07:00
|
|
|
inferenceEnabled = compartment->types.inferenceEnabled;
|
2011-01-07 02:03:14 -08:00
|
|
|
|
|
|
|
if (isExceptionPending())
|
2011-01-07 09:13:00 -08:00
|
|
|
wrapPendingException();
|
2011-01-07 02:03:14 -08:00
|
|
|
return;
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we try to use the context without a selected compartment,
|
|
|
|
* we will crash.
|
|
|
|
*/
|
|
|
|
compartment = NULL;
|
2010-09-27 18:24:24 -07:00
|
|
|
}
|
|
|
|
|
2011-01-07 09:13:00 -08:00
|
|
|
/*
|
|
|
|
* Since this function is only called in the context of a pending exception,
|
2011-01-24 23:16:48 -08:00
|
|
|
* the caller must subsequently take an error path. If wrapping fails, it will
|
|
|
|
* set a new (uncatchable) exception to be used in place of the original.
|
2011-01-07 09:13:00 -08:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
JSContext::wrapPendingException()
|
|
|
|
{
|
|
|
|
Value v = getPendingException();
|
|
|
|
clearPendingException();
|
|
|
|
if (compartment->wrap(this, &v))
|
|
|
|
setPendingException(v);
|
2010-09-27 18:24:24 -07:00
|
|
|
}
|
|
|
|
|
2010-03-03 17:52:26 -08:00
|
|
|
JSGenerator *
|
2011-04-13 09:27:37 -07:00
|
|
|
JSContext::generatorFor(StackFrame *fp) const
|
2010-03-03 17:52:26 -08:00
|
|
|
{
|
2011-04-13 09:27:37 -07:00
|
|
|
JS_ASSERT(stack.contains(fp) && fp->isGeneratorFrame());
|
2010-03-03 17:52:26 -08:00
|
|
|
JS_ASSERT(!fp->isFloatingGenerator());
|
|
|
|
JS_ASSERT(!genStack.empty());
|
|
|
|
|
2010-08-09 22:43:33 -07:00
|
|
|
if (JS_LIKELY(fp == genStack.back()->liveFrame()))
|
2010-03-03 17:52:26 -08:00
|
|
|
return genStack.back();
|
|
|
|
|
|
|
|
/* General case; should only be needed for debug APIs. */
|
|
|
|
for (size_t i = 0; i < genStack.length(); ++i) {
|
2010-08-09 22:43:33 -07:00
|
|
|
if (genStack[i]->liveFrame() == fp)
|
2010-03-03 17:52:26 -08:00
|
|
|
return genStack[i];
|
|
|
|
}
|
|
|
|
JS_NOT_REACHED("no matching generator");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-09-07 14:08:20 -07:00
|
|
|
JS_FRIEND_API(void)
|
|
|
|
JSRuntime::onTooMuchMalloc()
|
2010-07-28 11:20:19 -07:00
|
|
|
{
|
|
|
|
#ifdef JS_THREADSAFE
|
2010-09-07 14:08:20 -07:00
|
|
|
AutoLockGC lock(this);
|
2010-07-28 11:20:19 -07:00
|
|
|
|
|
|
|
/*
|
2010-09-07 14:08:20 -07:00
|
|
|
* We can be called outside a request and can race against a GC that
|
|
|
|
* mutates the JSThread set during the sweeping phase.
|
2010-07-28 11:20:19 -07:00
|
|
|
*/
|
2010-09-07 14:08:20 -07:00
|
|
|
js_WaitForGC(this);
|
2010-07-28 11:20:19 -07:00
|
|
|
#endif
|
2010-09-07 14:08:20 -07:00
|
|
|
TriggerGC(this);
|
|
|
|
}
|
2010-07-28 11:20:19 -07:00
|
|
|
|
2010-09-07 14:08:20 -07:00
|
|
|
JS_FRIEND_API(void *)
|
|
|
|
JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx)
|
|
|
|
{
|
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
gcHelperThread.waitBackgroundSweepEnd(this);
|
|
|
|
if (!p)
|
2011-03-31 01:14:12 -07:00
|
|
|
p = OffTheBooks::malloc_(nbytes);
|
2010-09-07 14:08:20 -07:00
|
|
|
else if (p == reinterpret_cast<void *>(1))
|
2011-03-31 01:14:12 -07:00
|
|
|
p = OffTheBooks::calloc_(nbytes);
|
2010-09-07 14:08:20 -07:00
|
|
|
else
|
2011-03-31 01:14:12 -07:00
|
|
|
p = OffTheBooks::realloc_(p, nbytes);
|
2010-09-07 14:08:20 -07:00
|
|
|
if (p)
|
|
|
|
return p;
|
|
|
|
#endif
|
|
|
|
if (cx)
|
|
|
|
js_ReportOutOfMemory(cx);
|
|
|
|
return NULL;
|
2010-07-28 11:20:19 -07:00
|
|
|
}
|
|
|
|
|
2010-02-19 09:44:23 -08:00
|
|
|
/*
|
|
|
|
* Release pool's arenas if the stackPool has existed for longer than the
|
|
|
|
* limit specified by gcEmptyArenaPoolLifespan.
|
|
|
|
*/
|
|
|
|
inline void
|
|
|
|
FreeOldArenas(JSRuntime *rt, JSArenaPool *pool)
|
|
|
|
{
|
2010-07-11 00:09:34 -07:00
|
|
|
JSArena *a = pool->current;
|
|
|
|
if (a == pool->first.next && a->avail == a->base + sizeof(int64)) {
|
|
|
|
int64 age = JS_Now() - *(int64 *) a->base;
|
2010-02-19 09:44:23 -08:00
|
|
|
if (age > int64(rt->gcEmptyArenaPoolLifespan) * 1000)
|
2010-07-11 00:09:34 -07:00
|
|
|
JS_FreeArenaPool(pool);
|
2010-02-19 09:44:23 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
JSContext::purge()
|
|
|
|
{
|
2010-08-11 13:30:07 -07:00
|
|
|
FreeOldArenas(runtime, ®ExpPool);
|
2010-02-19 09:44:23 -08:00
|
|
|
}
|
2010-06-04 16:32:10 -07:00
|
|
|
|
2010-10-01 01:45:27 -07:00
|
|
|
static bool
|
|
|
|
ComputeIsJITBroken()
|
|
|
|
{
|
|
|
|
#ifndef ANDROID
|
|
|
|
return false;
|
|
|
|
#else // ANDROID
|
|
|
|
if (getenv("JS_IGNORE_JIT_BROKENNESS")) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string line;
|
2010-11-16 16:27:21 -08:00
|
|
|
|
|
|
|
// Check for the known-bad kernel version (2.6.29).
|
|
|
|
std::ifstream osrelease("/proc/sys/kernel/osrelease");
|
|
|
|
std::getline(osrelease, line);
|
2010-11-18 18:27:44 -08:00
|
|
|
__android_log_print(ANDROID_LOG_INFO, "Gecko", "Detected osrelease `%s'",
|
|
|
|
line.c_str());
|
|
|
|
|
2010-11-16 16:27:21 -08:00
|
|
|
if (line.npos == line.find("2.6.29")) {
|
|
|
|
// We're using something other than 2.6.29, so the JITs should work.
|
2010-11-18 18:27:44 -08:00
|
|
|
__android_log_print(ANDROID_LOG_INFO, "Gecko", "JITs are not broken");
|
2010-11-16 16:27:21 -08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We're using 2.6.29, and this causes trouble with the JITs on i9000.
|
2010-11-18 18:27:44 -08:00
|
|
|
line = "";
|
2010-11-16 16:27:21 -08:00
|
|
|
bool broken = false;
|
2010-10-01 01:45:27 -07:00
|
|
|
std::ifstream cpuinfo("/proc/cpuinfo");
|
|
|
|
do {
|
|
|
|
if (0 == line.find("Hardware")) {
|
|
|
|
const char* blacklist[] = {
|
2011-03-04 17:35:23 -08:00
|
|
|
"SCH-I400", // Samsung Continuum
|
2010-10-01 01:45:27 -07:00
|
|
|
"SGH-T959", // Samsung i9000, Vibrant device
|
|
|
|
"SGH-I897", // Samsung i9000, Captivate device
|
|
|
|
"SCH-I500", // Samsung i9000, Fascinate device
|
|
|
|
"SPH-D700", // Samsung i9000, Epic device
|
2010-10-08 14:22:36 -07:00
|
|
|
"GT-I9000", // Samsung i9000, UK/Europe device
|
2010-10-01 01:45:27 -07:00
|
|
|
NULL
|
|
|
|
};
|
2010-10-06 17:39:19 -07:00
|
|
|
for (const char** hw = &blacklist[0]; *hw; ++hw) {
|
|
|
|
if (line.npos != line.find(*hw)) {
|
2010-11-18 18:27:44 -08:00
|
|
|
__android_log_print(ANDROID_LOG_INFO, "Gecko",
|
|
|
|
"Blacklisted device `%s'", *hw);
|
2010-10-01 01:45:27 -07:00
|
|
|
broken = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
std::getline(cpuinfo, line);
|
|
|
|
} while(!cpuinfo.fail() && !cpuinfo.eof());
|
2010-11-18 18:27:44 -08:00
|
|
|
|
|
|
|
__android_log_print(ANDROID_LOG_INFO, "Gecko", "JITs are %sbroken",
|
|
|
|
broken ? "" : "not ");
|
|
|
|
|
2010-10-01 01:45:27 -07:00
|
|
|
return broken;
|
|
|
|
#endif // ifndef ANDROID
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
IsJITBrokenHere()
|
|
|
|
{
|
|
|
|
static bool computedIsBroken = false;
|
|
|
|
static bool isBroken = false;
|
|
|
|
if (!computedIsBroken) {
|
|
|
|
isBroken = ComputeIsJITBroken();
|
|
|
|
computedIsBroken = true;
|
|
|
|
}
|
|
|
|
return isBroken;
|
|
|
|
}
|
|
|
|
|
2010-09-15 22:27:17 -07:00
|
|
|
void
|
|
|
|
JSContext::updateJITEnabled()
|
|
|
|
{
|
|
|
|
#ifdef JS_TRACER
|
2011-01-27 02:54:58 -08:00
|
|
|
traceJitEnabled = ((runOptions & JSOPTION_JIT) &&
|
2010-10-01 01:45:27 -07:00
|
|
|
!IsJITBrokenHere() &&
|
2010-09-15 22:27:17 -07:00
|
|
|
(debugHooks == &js_NullDebugHooks ||
|
|
|
|
(debugHooks == &runtime->globalDebugHooks &&
|
|
|
|
!runtime->debuggerInhibitsJIT())));
|
|
|
|
#endif
|
|
|
|
#ifdef JS_METHODJIT
|
2011-01-27 02:54:58 -08:00
|
|
|
methodJitEnabled = (runOptions & JSOPTION_METHODJIT) &&
|
2010-10-01 01:45:27 -07:00
|
|
|
!IsJITBrokenHere()
|
2010-09-30 18:45:22 -07:00
|
|
|
# if defined JS_CPU_X86 || defined JS_CPU_X64
|
2010-09-15 22:27:17 -07:00
|
|
|
&& JSC::MacroAssemblerX86Common::getSSEState() >=
|
|
|
|
JSC::MacroAssemblerX86Common::HasSSE2
|
|
|
|
# endif
|
|
|
|
;
|
2010-10-21 09:36:39 -07:00
|
|
|
#ifdef JS_TRACER
|
2011-01-27 02:54:58 -08:00
|
|
|
profilingEnabled = (runOptions & JSOPTION_PROFILING) && traceJitEnabled && methodJitEnabled;
|
2010-10-21 09:36:39 -07:00
|
|
|
#endif
|
2010-09-15 22:27:17 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2010-07-14 23:19:36 -07:00
|
|
|
namespace js {
|
|
|
|
|
2010-12-22 12:02:25 -08:00
|
|
|
JS_FORCES_STACK JS_FRIEND_API(void)
|
|
|
|
LeaveTrace(JSContext *cx)
|
2010-07-14 23:19:36 -07:00
|
|
|
{
|
2010-12-22 12:02:25 -08:00
|
|
|
#ifdef JS_TRACER
|
|
|
|
if (JS_ON_TRACE(cx))
|
|
|
|
DeepBail(cx);
|
|
|
|
#endif
|
2010-07-14 23:19:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
} /* namespace js */
|