/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/jsinterp.cpp
C++ | 2138 lines | 1569 code | 225 blank | 344 comment | 373 complexity | 00b5af12134531fe470209df5d04e58b MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause
- /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sw=4 et tw=79:
- *
- * ***** 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 ***** */
- /*
- * JavaScript bytecode interpreter.
- */
- #include "jsstddef.h"
- #include <stdio.h>
- #include <string.h>
- #include <math.h>
- #include "jstypes.h"
- #include "jsarena.h" /* Added by JSIFY */
- #include "jsutil.h" /* Added by JSIFY */
- #include "jsprf.h"
- #include "jsapi.h"
- #include "jsarray.h"
- #include "jsatom.h"
- #include "jsbool.h"
- #include "jscntxt.h"
- #include "jsversion.h"
- #include "jsdbgapi.h"
- #include "jsfun.h"
- #include "jsgc.h"
- #include "jsinterp.h"
- #include "jsiter.h"
- #include "jslock.h"
- #include "jsnum.h"
- #include "jsobj.h"
- #include "jsopcode.h"
- #include "jsscan.h"
- #include "jsscope.h"
- #include "jsscript.h"
- #include "jsstr.h"
- #include "jsstaticcheck.h"
- #include "jstracer.h"
- #ifdef INCLUDE_MOZILLA_DTRACE
- #include "jsdtracef.h"
- #endif
- #if JS_HAS_XML_SUPPORT
- #include "jsxml.h"
- #endif
- #include "jsautooplen.h"
- /* jsinvoke_cpp___ indicates inclusion from jsinvoke.cpp. */
- #if !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___
- uint32
- js_GenerateShape(JSContext *cx, JSBool gcLocked, JSScopeProperty *sprop)
- {
- JSRuntime *rt;
- uint32 shape;
- JSTempValueRooter tvr;
- rt = cx->runtime;
- shape = JS_ATOMIC_INCREMENT(&rt->shapeGen);
- JS_ASSERT(shape != 0);
- if (shape & SHAPE_OVERFLOW_BIT) {
- rt->gcPoke = JS_TRUE;
- if (sprop)
- JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr);
- js_GC(cx, gcLocked ? GC_LOCK_HELD : GC_NORMAL);
- if (sprop)
- JS_POP_TEMP_ROOT(cx, &tvr);
- shape = JS_ATOMIC_INCREMENT(&rt->shapeGen);
- JS_ASSERT(shape != 0);
- JS_ASSERT_IF(shape & SHAPE_OVERFLOW_BIT,
- JS_PROPERTY_CACHE(cx).disabled);
- }
- return shape;
- }
- void
- js_FillPropertyCache(JSContext *cx, JSObject *obj, jsuword kshape,
- uintN scopeIndex, uintN protoIndex,
- JSObject *pobj, JSScopeProperty *sprop,
- JSPropCacheEntry **entryp)
- {
- JSPropertyCache *cache;
- jsbytecode *pc;
- JSScope *scope;
- JSOp op;
- const JSCodeSpec *cs;
- jsuword vword;
- ptrdiff_t pcoff;
- jsuword khash;
- JSAtom *atom;
- JSPropCacheEntry *entry;
- JS_ASSERT(!cx->runtime->gcRunning);
- cache = &JS_PROPERTY_CACHE(cx);
- pc = cx->fp->regs->pc;
- if (cache->disabled || (cx->fp->flags & JSFRAME_EVAL)) {
- PCMETER(cache->disfills++);
- *entryp = NULL;
- return;
- }
- /*
- * Check for fill from js_SetPropertyHelper where the setter removed sprop
- * from pobj's scope (via unwatch or delete, e.g.).
- */
- scope = OBJ_SCOPE(pobj);
- JS_ASSERT(scope->object == pobj);
- if (!SCOPE_HAS_PROPERTY(scope, sprop)) {
- PCMETER(cache->oddfills++);
- *entryp = NULL;
- return;
- }
- /*
- * Check for overdeep scope and prototype chain. Because resolve, getter,
- * and setter hooks can change the prototype chain using JS_SetPrototype
- * after js_LookupPropertyWithFlags has returned the nominal protoIndex,
- * we have to validate protoIndex if it is non-zero. If it is zero, then
- * we know thanks to the SCOPE_HAS_PROPERTY test above, and from the fact
- * that obj == pobj, that protoIndex is invariant.
- *
- * 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.
- */
- JS_ASSERT_IF(scopeIndex == 0 && protoIndex == 0, obj == pobj);
- if (protoIndex != 0) {
- JSObject *tmp;
- JS_ASSERT(pobj != obj);
- protoIndex = 1;
- tmp = obj;
- for (;;) {
- tmp = OBJ_GET_PROTO(cx, tmp);
- if (!tmp) {
- PCMETER(cache->noprotos++);
- *entryp = NULL;
- return;
- }
- if (tmp == pobj)
- break;
- ++protoIndex;
- }
- }
- if (scopeIndex > PCVCAP_SCOPEMASK || protoIndex > PCVCAP_PROTOMASK) {
- PCMETER(cache->longchains++);
- *entryp = NULL;
- return;
- }
- /*
- * Optimize the cached vword based on our parameters and the current pc's
- * opcode format flags.
- */
- op = (JSOp) *pc;
- cs = &js_CodeSpec[op];
- 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 and setter, so get of a function is idempotent and set is
- * transparent.
- */
- if (cs->format & JOF_CALLOP) {
- if (SPROP_HAS_STUB_GETTER(sprop) &&
- SPROP_HAS_VALID_SLOT(sprop, scope)) {
- jsval v;
- v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot);
- if (VALUE_IS_FUNCTION(cx, v)) {
- /*
- * Great, we have a function-valued prototype property
- * where the getter is JS_PropertyStub. The type id in
- * pobj's scope does not evolve with changes to property
- * values, however.
- *
- * So here, on first cache fill for this method, we brand
- * the scope with a new shape and set the SCOPE_BRANDED
- * flag. Once this scope flag is set, any write that adds
- * or deletes a function-valued plain old property in
- * scope->object will result in shape being regenerated.
- */
- if (!SCOPE_IS_BRANDED(scope)) {
- PCMETER(cache->brandfills++);
- #ifdef DEBUG_notme
- fprintf(stderr,
- "branding %p (%s) for funobj %p (%s), kshape %lu\n",
- pobj, LOCKED_OBJ_GET_CLASS(pobj)->name,
- JSVAL_TO_OBJECT(v),
- JS_GetFunctionName(GET_FUNCTION_PRIVATE(cx,
- JSVAL_TO_OBJECT(v))),
- kshape);
- #endif
- SCOPE_MAKE_UNIQUE_SHAPE(cx, scope);
- SCOPE_SET_BRANDED(scope);
- kshape = scope->shape;
- }
- vword = JSVAL_OBJECT_TO_PCVAL(v);
- break;
- }
- }
- }
- /* If getting a value via a stub getter, we can cache the slot. */
- if (!(cs->format & JOF_SET) &&
- SPROP_HAS_STUB_GETTER(sprop) &&
- SPROP_HAS_VALID_SLOT(sprop, scope)) {
- /* Great, let's cache sprop's slot and use it on cache hit. */
- vword = SLOT_TO_PCVAL(sprop->slot);
- } else {
- /* Best we can do is to cache sprop (still a nice speedup). */
- vword = SPROP_TO_PCVAL(sprop);
- }
- } while (0);
- /*
- * Our caller preserved the scope shape prior to the js_GetPropertyHelper
- * or similar call out of the interpreter. We want to cache under that
- * shape if op is overtly mutating, to bias for the case where the mutator
- * udpates shape predictably.
- *
- * Note that an apparently non-mutating op such as JSOP_NAME may still
- * mutate the base object via, e.g., lazy standard class initialization,
- * but that is a one-time event and we'll have to miss the old shape and
- * re-fill under the new one.
- */
- if (!(cs->format & (JOF_SET | JOF_INCDEC)) && obj == pobj)
- kshape = scope->shape;
- khash = PROPERTY_CACHE_HASH_PC(pc, kshape);
- if (obj == pobj) {
- JS_ASSERT(kshape != 0 || scope->shape != 0);
- JS_ASSERT(scopeIndex == 0 && protoIndex == 0);
- JS_ASSERT(OBJ_SCOPE(obj)->object == obj);
- } else {
- if (op == JSOP_LENGTH) {
- atom = cx->runtime->atomState.lengthAtom;
- } else {
- pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? 2 : 0;
- GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);
- }
- JS_ASSERT_IF(scopeIndex == 0,
- protoIndex != 1 || OBJ_GET_PROTO(cx, obj) == pobj);
- if (scopeIndex != 0 || protoIndex != 1) {
- khash = PROPERTY_CACHE_HASH_ATOM(atom, obj, pobj);
- PCMETER(if (PCVCAP_TAG(cache->table[khash].vcap) <= 1)
- cache->pcrecycles++);
- pc = (jsbytecode *) atom;
- kshape = (jsuword) obj;
- }
- }
- entry = &cache->table[khash];
- PCMETER(if (entry != *entryp) cache->modfills++);
- PCMETER(if (!PCVAL_IS_NULL(entry->vword)) cache->recycles++);
- entry->kpc = pc;
- entry->kshape = kshape;
- entry->vcap = PCVCAP_MAKE(scope->shape, scopeIndex, protoIndex);
- entry->vword = vword;
- *entryp = entry;
- cache->empty = JS_FALSE;
- PCMETER(cache->fills++);
- }
- JSAtom *
- js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
- JSObject **objp, JSObject **pobjp,
- JSPropCacheEntry **entryp)
- {
- JSOp op;
- const JSCodeSpec *cs;
- ptrdiff_t pcoff;
- JSAtom *atom;
- JSObject *obj, *pobj, *tmp;
- JSPropCacheEntry *entry;
- uint32 vcap;
- JS_ASSERT(uintN((cx->fp->imacpc ? cx->fp->imacpc : pc) - cx->fp->script->code)
- < cx->fp->script->length);
- op = (JSOp) *pc;
- cs = &js_CodeSpec[op];
- if (op == JSOP_LENGTH) {
- atom = cx->runtime->atomState.lengthAtom;
- } else {
- pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? 2 : 0;
- GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);
- }
- obj = *objp;
- JS_ASSERT(OBJ_IS_NATIVE(obj));
- entry = &JS_PROPERTY_CACHE(cx).table[PROPERTY_CACHE_HASH_ATOM(atom, obj, NULL)];
- *entryp = entry;
- vcap = entry->vcap;
- if (entry->kpc != (jsbytecode *) atom) {
- PCMETER(JS_PROPERTY_CACHE(cx).idmisses++);
- #ifdef DEBUG_notme
- entry = &JS_PROPERTY_CACHE(cx).table[PROPERTY_CACHE_HASH_PC(pc, OBJ_SHAPE(obj))];
- fprintf(stderr,
- "id miss for %s from %s:%u"
- " (pc %u, kpc %u, kshape %u, shape %u)\n",
- js_AtomToPrintableString(cx, atom),
- cx->fp->script->filename,
- js_PCToLineNumber(cx, cx->fp->script, pc),
- pc - cx->fp->script->code,
- entry->kpc - cx->fp->script->code,
- entry->kshape,
- OBJ_SHAPE(obj));
- js_Disassemble1(cx, cx->fp->script, pc,
- PTRDIFF(pc, cx->fp->script->code, jsbytecode),
- JS_FALSE, stderr);
- #endif
- return atom;
- }
- if (entry->kshape != (jsuword) obj) {
- PCMETER(JS_PROPERTY_CACHE(cx).komisses++);
- return atom;
- }
- pobj = obj;
- JS_LOCK_OBJ(cx, pobj);
- if (JOF_MODE(cs->format) == JOF_NAME) {
- while (vcap & (PCVCAP_SCOPEMASK << PCVCAP_PROTOBITS)) {
- tmp = LOCKED_OBJ_GET_PARENT(pobj);
- if (!tmp || !OBJ_IS_NATIVE(tmp))
- break;
- JS_UNLOCK_OBJ(cx, pobj);
- pobj = tmp;
- JS_LOCK_OBJ(cx, pobj);
- vcap -= PCVCAP_PROTOSIZE;
- }
- *objp = pobj;
- }
- while (vcap & PCVCAP_PROTOMASK) {
- tmp = LOCKED_OBJ_GET_PROTO(pobj);
- if (!tmp || !OBJ_IS_NATIVE(tmp))
- break;
- JS_UNLOCK_OBJ(cx, pobj);
- pobj = tmp;
- JS_LOCK_OBJ(cx, pobj);
- --vcap;
- }
- if (PCVCAP_SHAPE(vcap) == OBJ_SHAPE(pobj)) {
- #ifdef DEBUG
- jsid id = ATOM_TO_JSID(atom);
- CHECK_FOR_STRING_INDEX(id);
- JS_ASSERT(SCOPE_GET_PROPERTY(OBJ_SCOPE(pobj), id));
- JS_ASSERT(OBJ_SCOPE(pobj)->object == pobj);
- #endif
- *pobjp = pobj;
- return NULL;
- }
- PCMETER(JS_PROPERTY_CACHE(cx).vcmisses++);
- JS_UNLOCK_OBJ(cx, pobj);
- return atom;
- }
- #ifdef DEBUG
- #define ASSERT_CACHE_IS_EMPTY(cache) \
- JS_BEGIN_MACRO \
- JSPropertyCache *cache_ = (cache); \
- uintN i_; \
- JS_ASSERT(cache_->empty); \
- for (i_ = 0; i_ < PROPERTY_CACHE_SIZE; i_++) { \
- JS_ASSERT(!cache_->table[i_].kpc); \
- JS_ASSERT(!cache_->table[i_].kshape); \
- JS_ASSERT(!cache_->table[i_].vcap); \
- JS_ASSERT(!cache_->table[i_].vword); \
- } \
- JS_END_MACRO
- #else
- #define ASSERT_CACHE_IS_EMPTY(cache) ((void)0)
- #endif
- JS_STATIC_ASSERT(PCVAL_NULL == 0);
- void
- js_FlushPropertyCache(JSContext *cx)
- {
- JSPropertyCache *cache;
- cache = &JS_PROPERTY_CACHE(cx);
- if (cache->empty) {
- ASSERT_CACHE_IS_EMPTY(cache);
- return;
- }
- memset(cache->table, 0, sizeof cache->table);
- cache->empty = JS_TRUE;
- #ifdef JS_PROPERTY_CACHE_METERING
- { static FILE *fp;
- if (!fp)
- fp = fopen("/tmp/propcache.stats", "w");
- if (fp) {
- fputs("Property cache stats for ", fp);
- #ifdef JS_THREADSAFE
- fprintf(fp, "thread %lu, ", (unsigned long) cx->thread->id);
- #endif
- fprintf(fp, "GC %u\n", cx->runtime->gcNumber);
- # define P(mem) fprintf(fp, "%11s %10lu\n", #mem, (unsigned long)cache->mem)
- P(fills);
- P(nofills);
- P(rofills);
- P(disfills);
- P(oddfills);
- P(modfills);
- P(brandfills);
- P(noprotos);
- P(longchains);
- P(recycles);
- P(pcrecycles);
- P(tests);
- P(pchits);
- P(protopchits);
- P(initests);
- P(inipchits);
- P(inipcmisses);
- P(settests);
- P(addpchits);
- P(setpchits);
- P(setpcmisses);
- P(slotchanges);
- P(setmisses);
- P(idmisses);
- P(komisses);
- P(vcmisses);
- P(misses);
- P(flushes);
- P(pcpurges);
- # undef P
- fprintf(fp, "hit rates: pc %g%% (proto %g%%), set %g%%, ini %g%%, full %g%%\n",
- (100. * cache->pchits) / cache->tests,
- (100. * cache->protopchits) / cache->tests,
- (100. * (cache->addpchits + cache->setpchits))
- / cache->settests,
- (100. * cache->inipchits) / cache->initests,
- (100. * (cache->tests - cache->misses)) / cache->tests);
- fflush(fp);
- }
- }
- #endif
- PCMETER(cache->flushes++);
- }
- void
- js_FlushPropertyCacheForScript(JSContext *cx, JSScript *script)
- {
- JSPropertyCache *cache;
- JSPropCacheEntry *entry;
- cache = &JS_PROPERTY_CACHE(cx);
- for (entry = cache->table; entry < cache->table + PROPERTY_CACHE_SIZE;
- entry++) {
- if (JS_UPTRDIFF(entry->kpc, script->code) < script->length) {
- entry->kpc = NULL;
- entry->kshape = 0;
- #ifdef DEBUG
- entry->vcap = entry->vword = 0;
- #endif
- }
- }
- }
- void
- js_DisablePropertyCache(JSContext *cx)
- {
- JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled >= 0);
- ++JS_PROPERTY_CACHE(cx).disabled;
- }
- void
- js_EnablePropertyCache(JSContext *cx)
- {
- --JS_PROPERTY_CACHE(cx).disabled;
- JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled >= 0);
- }
- /*
- * Check if the current arena has enough space to fit nslots after sp and, if
- * so, reserve the necessary space.
- */
- static JSBool
- AllocateAfterSP(JSContext *cx, jsval *sp, uintN nslots)
- {
- uintN surplus;
- jsval *sp2;
- JS_ASSERT((jsval *) cx->stackPool.current->base <= sp);
- JS_ASSERT(sp <= (jsval *) cx->stackPool.current->avail);
- surplus = (jsval *) cx->stackPool.current->avail - sp;
- if (nslots <= surplus)
- return JS_TRUE;
- /*
- * No room before current->avail, check if the arena has enough space to
- * fit the missing slots before the limit.
- */
- if (nslots > (size_t) ((jsval *) cx->stackPool.current->limit - sp))
- return JS_FALSE;
- JS_ARENA_ALLOCATE_CAST(sp2, jsval *, &cx->stackPool,
- (nslots - surplus) * sizeof(jsval));
- JS_ASSERT(sp2 == sp + surplus);
- return JS_TRUE;
- }
- JS_STATIC_INTERPRET jsval *
- js_AllocRawStack(JSContext *cx, uintN nslots, void **markp)
- {
- jsval *sp;
- if (!cx->stackPool.first.next) {
- int64 *timestamp;
- JS_ARENA_ALLOCATE_CAST(timestamp, int64 *,
- &cx->stackPool, sizeof *timestamp);
- if (!timestamp) {
- js_ReportOutOfScriptQuota(cx);
- return NULL;
- }
- *timestamp = JS_Now();
- }
- if (markp)
- *markp = JS_ARENA_MARK(&cx->stackPool);
- JS_ARENA_ALLOCATE_CAST(sp, jsval *, &cx->stackPool, nslots * sizeof(jsval));
- if (!sp)
- js_ReportOutOfScriptQuota(cx);
- return sp;
- }
- JS_STATIC_INTERPRET void
- js_FreeRawStack(JSContext *cx, void *mark)
- {
- JS_ARENA_RELEASE(&cx->stackPool, mark);
- }
- JS_FRIEND_API(jsval *)
- js_AllocStack(JSContext *cx, uintN nslots, void **markp)
- {
- jsval *sp;
- JSArena *a;
- JSStackHeader *sh;
- /* Callers don't check for zero nslots: we do to avoid empty segments. */
- if (nslots == 0) {
- *markp = NULL;
- return (jsval *) JS_ARENA_MARK(&cx->stackPool);
- }
- /* Allocate 2 extra slots for the stack segment header we'll likely need. */
- sp = js_AllocRawStack(cx, 2 + nslots, markp);
- if (!sp)
- return NULL;
- /* Try to avoid another header if we can piggyback on the last segment. */
- a = cx->stackPool.current;
- sh = cx->stackHeaders;
- if (sh && JS_STACK_SEGMENT(sh) + sh->nslots == sp) {
- /* Extend the last stack segment, give back the 2 header slots. */
- sh->nslots += nslots;
- a->avail -= 2 * sizeof(jsval);
- } else {
- /*
- * Need a new stack segment, so allocate and push a stack segment
- * header from the 2 extra slots.
- */
- sh = (JSStackHeader *)sp;
- sh->nslots = nslots;
- sh->down = cx->stackHeaders;
- cx->stackHeaders = sh;
- sp += 2;
- }
- /*
- * Store JSVAL_NULL using memset, to let compilers optimize as they see
- * fit, in case a caller allocates and pushes GC-things one by one, which
- * could nest a last-ditch GC that will scan this segment.
- */
- memset(sp, 0, nslots * sizeof(jsval));
- return sp;
- }
- JS_FRIEND_API(void)
- js_FreeStack(JSContext *cx, void *mark)
- {
- JSStackHeader *sh;
- jsuword slotdiff;
- /* Check for zero nslots allocation special case. */
- if (!mark)
- return;
- /* We can assert because js_FreeStack always balances js_AllocStack. */
- sh = cx->stackHeaders;
- JS_ASSERT(sh);
- /* If mark is in the current segment, reduce sh->nslots, else pop sh. */
- slotdiff = JS_UPTRDIFF(mark, JS_STACK_SEGMENT(sh)) / sizeof(jsval);
- if (slotdiff < (jsuword)sh->nslots)
- sh->nslots = slotdiff;
- else
- cx->stackHeaders = sh->down;
- /* Release the stackPool space allocated since mark was set. */
- JS_ARENA_RELEASE(&cx->stackPool, mark);
- }
- JSObject *
- js_GetScopeChain(JSContext *cx, JSStackFrame *fp)
- {
- JSObject *obj, *cursor, *clonedChild, *parent;
- JSTempValueRooter tvr;
- obj = fp->blockChain;
- if (!obj) {
- /*
- * Don't force a call object for a lightweight function call, but do
- * insist that there is a call object for a heavyweight function call.
- */
- JS_ASSERT(!fp->fun ||
- !(fp->fun->flags & JSFUN_HEAVYWEIGHT) ||
- fp->callobj);
- JS_ASSERT(fp->scopeChain);
- return fp->scopeChain;
- }
- /*
- * We have one or more lexical scopes to reflect into fp->scopeChain, so
- * make sure there's a call object at the current head of the scope chain,
- * if this frame is a call frame.
- */
- if (fp->fun && !fp->callobj) {
- JS_ASSERT(OBJ_GET_CLASS(cx, fp->scopeChain) != &js_BlockClass ||
- OBJ_GET_PRIVATE(cx, fp->scopeChain) != fp);
- if (!js_GetCallObject(cx, fp, fp->scopeChain))
- return NULL;
- }
- /*
- * Clone the block chain. To avoid recursive cloning we set the parent of
- * the cloned child after we clone the parent. In the following loop when
- * clonedChild is null it indicates the first iteration when no special GC
- * rooting is necessary. On the second and the following iterations we
- * have to protect cloned so far chain against the GC during cloning of
- * the cursor object.
- */
- cursor = obj;
- clonedChild = NULL;
- for (;;) {
- parent = OBJ_GET_PARENT(cx, cursor);
- /*
- * We pass fp->scopeChain and not null even if we override the parent
- * slot later as null triggers useless calculations of slot's value in
- * js_NewObject that js_CloneBlockObject calls.
- */
- cursor = js_CloneBlockObject(cx, cursor, fp->scopeChain, fp);
- if (!cursor) {
- if (clonedChild)
- JS_POP_TEMP_ROOT(cx, &tvr);
- return NULL;
- }
- if (!clonedChild) {
- /*
- * The first iteration. Check if other follow and root obj if so
- * to protect the whole cloned chain against GC.
- */
- obj = cursor;
- if (!parent)
- break;
- JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr);
- } else {
- /*
- * Avoid OBJ_SET_PARENT overhead as clonedChild cannot escape to
- * other threads.
- */
- STOBJ_SET_PARENT(clonedChild, cursor);
- if (!parent) {
- JS_ASSERT(tvr.u.value == OBJECT_TO_JSVAL(obj));
- JS_POP_TEMP_ROOT(cx, &tvr);
- break;
- }
- }
- clonedChild = cursor;
- cursor = parent;
- }
- fp->flags |= JSFRAME_POP_BLOCKS;
- fp->scopeChain = obj;
- fp->blockChain = NULL;
- return obj;
- }
- JSBool
- js_GetPrimitiveThis(JSContext *cx, jsval *vp, JSClass *clasp, jsval *thisvp)
- {
- jsval v;
- JSObject *obj;
- v = vp[1];
- if (JSVAL_IS_OBJECT(v)) {
- obj = JS_THIS_OBJECT(cx, vp);
- if (!JS_InstanceOf(cx, obj, clasp, vp + 2))
- return JS_FALSE;
- v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
- }
- *thisvp = v;
- return JS_TRUE;
- }
- /*
- * ECMA requires "the global object", but in embeddings such as the browser,
- * which have multiple top-level objects (windows, frames, etc. in the DOM),
- * we prefer fun's parent. An example that causes this code to run:
- *
- * // in window w1
- * function f() { return this }
- * function g() { return f }
- *
- * // in window w2
- * var h = w1.g()
- * alert(h() == w1)
- *
- * The alert should display "true".
- */
- JS_STATIC_INTERPRET JSObject *
- js_ComputeGlobalThis(JSContext *cx, JSBool lazy, jsval *argv)
- {
- JSObject *thisp;
- if (JSVAL_IS_PRIMITIVE(argv[-2]) ||
- !OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2]))) {
- thisp = cx->globalObject;
- } else {
- JSStackFrame *fp;
- jsid id;
- jsval v;
- uintN attrs;
- JSBool ok;
- JSObject *parent;
- /*
- * Walk up the parent chain, first checking that the running script
- * has access to the callee's parent object. Note that if lazy, the
- * running script whose principals we want to check is the script
- * associated with fp->down, not with fp.
- *
- * FIXME: 417851 -- this access check should not be required, as it
- * imposes a performance penalty on all js_ComputeGlobalThis calls,
- * and it represents a maintenance hazard.
- */
- fp = cx->fp; /* quell GCC overwarning */
- if (lazy) {
- JS_ASSERT(fp->argv == argv);
- fp->dormantNext = cx->dormantFrameChain;
- cx->dormantFrameChain = fp;
- cx->fp = fp->down;
- fp->down = NULL;
- }
- thisp = JSVAL_TO_OBJECT(argv[-2]);
- id = ATOM_TO_JSID(cx->runtime->atomState.parentAtom);
- ok = OBJ_CHECK_ACCESS(cx, thisp, id, JSACC_PARENT, &v, &attrs);
- if (lazy) {
- cx->dormantFrameChain = fp->dormantNext;
- fp->dormantNext = NULL;
- fp->down = cx->fp;
- cx->fp = fp;
- }
- if (!ok)
- return NULL;
- thisp = JSVAL_IS_VOID(v)
- ? OBJ_GET_PARENT(cx, thisp)
- : JSVAL_TO_OBJECT(v);
- while ((parent = OBJ_GET_PARENT(cx, thisp)) != NULL)
- thisp = parent;
- }
- OBJ_TO_OUTER_OBJECT(cx, thisp);
- if (!thisp)
- return NULL;
- argv[-1] = OBJECT_TO_JSVAL(thisp);
- return thisp;
- }
- static JSObject *
- ComputeThis(JSContext *cx, JSBool lazy, jsval *argv)
- {
- JSObject *thisp;
- JS_ASSERT(!JSVAL_IS_NULL(argv[-1]));
- if (!JSVAL_IS_OBJECT(argv[-1])) {
- if (!js_PrimitiveToObject(cx, &argv[-1]))
- return NULL;
- thisp = JSVAL_TO_OBJECT(argv[-1]);
- } else {
- thisp = JSVAL_TO_OBJECT(argv[-1]);
- if (OBJ_GET_CLASS(cx, thisp) == &js_CallClass)
- return js_ComputeGlobalThis(cx, lazy, argv);
- if (thisp->map->ops->thisObject) {
- /* Some objects (e.g., With) delegate 'this' to another object. */
- thisp = thisp->map->ops->thisObject(cx, thisp);
- if (!thisp)
- return NULL;
- }
- OBJ_TO_OUTER_OBJECT(cx, thisp);
- if (!thisp)
- return NULL;
- argv[-1] = OBJECT_TO_JSVAL(thisp);
- }
- return thisp;
- }
- JSObject *
- js_ComputeThis(JSContext *cx, JSBool lazy, jsval *argv)
- {
- if (JSVAL_IS_NULL(argv[-1]))
- return js_ComputeGlobalThis(cx, lazy, argv);
- return ComputeThis(cx, lazy, argv);
- }
- #if JS_HAS_NO_SUCH_METHOD
- #define JSSLOT_FOUND_FUNCTION JSSLOT_PRIVATE
- #define JSSLOT_SAVED_ID (JSSLOT_PRIVATE + 1)
- JSClass js_NoSuchMethodClass = {
- "NoSuchMethod",
- JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS |
- JSCLASS_HAS_CACHED_PROTO(JSProto_NoSuchMethod),
- JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
- JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
- };
- JS_BEGIN_EXTERN_C
- JSObject*
- js_InitNoSuchMethodClass(JSContext *cx, JSObject* obj);
- JS_END_EXTERN_C
- JSObject*
- js_InitNoSuchMethodClass(JSContext *cx, JSObject* obj)
- {
- JSObject *proto;
- proto = JS_InitClass(cx, obj, NULL, &js_NoSuchMethodClass, NULL, 0, NULL,
- NULL, NULL, NULL);
- if (!proto)
- return NULL;
- OBJ_CLEAR_PROTO(cx, proto);
- return proto;
- }
- /*
- * When JSOP_CALLPROP or JSOP_CALLELEM does not find the method property of
- * the base object, we search for the __noSuchMethod__ method in the base.
- * If it exists, we store the method and the property's id into an object of
- * NoSuchMethod class and store this object into the callee's stack slot.
- * Later, js_Invoke will recognise such an object and transfer control to
- * NoSuchMethod that invokes the method like:
- *
- * this.__noSuchMethod__(id, args)
- *
- * where id is the name of the method that this invocation attempted to
- * call by name, and args is an Array containing this invocation's actual
- * parameters.
- */
- JS_STATIC_INTERPRET JSBool
- js_OnUnknownMethod(JSContext *cx, jsval *vp)
- {
- JSObject *obj;
- jsid id;
- JSTempValueRooter tvr;
- JSBool ok;
- JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
- obj = JSVAL_TO_OBJECT(vp[1]);
- JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
- MUST_FLOW_THROUGH("out");
- id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom);
- #if JS_HAS_XML_SUPPORT
- if (OBJECT_IS_XML(cx, obj)) {
- JSXMLObjectOps *ops;
- ops = (JSXMLObjectOps *) obj->map->ops;
- obj = ops->getMethod(cx, obj, id, &tvr.u.value);
- if (!obj) {
- ok = JS_FALSE;
- goto out;
- }
- vp[1] = OBJECT_TO_JSVAL(obj);
- } else
- #endif
- {
- ok = OBJ_GET_PROPERTY(cx, obj, id, &tvr.u.value);
- if (!ok)
- goto out;
- }
- if (JSVAL_IS_PRIMITIVE(tvr.u.value)) {
- vp[0] = tvr.u.value;
- } else {
- #if JS_HAS_XML_SUPPORT
- /* Extract the function name from function::name qname. */
- if (!JSVAL_IS_PRIMITIVE(vp[0])) {
- obj = JSVAL_TO_OBJECT(vp[0]);
- ok = js_IsFunctionQName(cx, obj, &id);
- if (!ok)
- goto out;
- if (id != 0)
- vp[0] = ID_TO_VALUE(id);
- }
- #endif
- obj = js_NewObject(cx, &js_NoSuchMethodClass, NULL, NULL, 0);
- if (!obj) {
- ok = JS_FALSE;
- goto out;
- }
- obj->fslots[JSSLOT_FOUND_FUNCTION] = tvr.u.value;
- obj->fslots[JSSLOT_SAVED_ID] = vp[0];
- vp[0] = OBJECT_TO_JSVAL(obj);
- }
- ok = JS_TRUE;
- out:
- JS_POP_TEMP_ROOT(cx, &tvr);
- return ok;
- }
- static JSBool
- NoSuchMethod(JSContext *cx, uintN argc, jsval *vp, uint32 flags)
- {
- jsval *invokevp;
- void *mark;
- JSBool ok;
- JSObject *obj, *argsobj;
- invokevp = js_AllocStack(cx, 2 + 2, &mark);
- if (!invokevp)
- return JS_FALSE;
- JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[0]));
- JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
- obj = JSVAL_TO_OBJECT(vp[0]);
- JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_NoSuchMethodClass);
- invokevp[0] = obj->fslots[JSSLOT_FOUND_FUNCTION];
- invokevp[1] = vp[1];
- invokevp[2] = obj->fslots[JSSLOT_SAVED_ID];
- argsobj = js_NewArrayObject(cx, argc, vp + 2);
- if (!argsobj) {
- ok = JS_FALSE;
- } else {
- invokevp[3] = OBJECT_TO_JSVAL(argsobj);
- ok = (flags & JSINVOKE_CONSTRUCT)
- ? js_InvokeConstructor(cx, 2, JS_TRUE, invokevp)
- : js_Invoke(cx, 2, invokevp, flags);
- vp[0] = invokevp[0];
- }
- js_FreeStack(cx, mark);
- return ok;
- }
- #endif /* JS_HAS_NO_SUCH_METHOD */
- /*
- * We check if the function accepts a primitive value as |this|. For that we
- * use a table that maps value's tag into the corresponding function flag.
- */
- JS_STATIC_ASSERT(JSVAL_INT == 1);
- JS_STATIC_ASSERT(JSVAL_DOUBLE == 2);
- JS_STATIC_ASSERT(JSVAL_STRING == 4);
- JS_STATIC_ASSERT(JSVAL_BOOLEAN == 6);
- const uint16 js_PrimitiveTestFlags[] = {
- JSFUN_THISP_NUMBER, /* INT */
- JSFUN_THISP_NUMBER, /* DOUBLE */
- JSFUN_THISP_NUMBER, /* INT */
- JSFUN_THISP_STRING, /* STRING */
- JSFUN_THISP_NUMBER, /* INT */
- JSFUN_THISP_BOOLEAN, /* BOOLEAN */
- JSFUN_THISP_NUMBER /* INT */
- };
- /*
- * Find a function reference and its 'this' object implicit first parameter
- * under argc arguments on cx's stack, and call the function. Push missing
- * required arguments, allocate declared local variables, and pop everything
- * when done. Then push the return value.
- */
- JS_FRIEND_API(JSBool)
- js_Invoke(JSContext *cx, uintN argc, jsval *vp, uintN flags)
- {
- void *mark;
- JSStackFrame frame;
- jsval *sp, *argv, *newvp;
- jsval v;
- JSObject *funobj, *parent;
- JSBool ok;
- JSClass *clasp;
- JSObjectOps *ops;
- JSNative native;
- JSFunction *fun;
- JSScript *script;
- uintN nslots, i;
- uint32 rootedArgsFlag;
- JSInterpreterHook hook;
- void *hookData;
- /* [vp .. vp + 2 + argc) must belong to the last JS stack arena. */
- JS_ASSERT((jsval *) cx->stackPool.current->base <= vp);
- JS_ASSERT(vp + 2 + argc <= (jsval *) cx->stackPool.current->avail);
- /*
- * Mark the top of stack and load frequently-used registers. After this
- * point the control should flow through label out2: to return.
- */
- mark = JS_ARENA_MARK(&cx->stackPool);
- v = *vp;
- if (JSVAL_IS_PRIMITIVE(v))
- goto bad;
- funobj = JSVAL_TO_OBJECT(v);
- parent = OBJ_GET_PARENT(cx, funobj);
- clasp = OBJ_GET_CLASS(cx, funobj);
- if (clasp != &js_FunctionClass) {
- #if JS_HAS_NO_SUCH_METHOD
- if (clasp == &js_NoSuchMethodClass) {
- ok = NoSuchMethod(cx, argc, vp, flags);
- goto out2;
- }
- #endif
- /* Function is inlined, all other classes use object ops. */
- ops = funobj->map->ops;
- /*
- * XXX this makes no sense -- why convert to function if clasp->call?
- * XXX better to call that hook without converting
- * XXX the only thing that needs fixing is liveconnect
- *
- * Try converting to function, for closure and API compatibility.
- * We attempt the conversion under all circumstances for 1.2, but
- * only if there is a call op defined otherwise.
- */
- if ((ops == &js_ObjectOps) ? clasp->call : ops->call) {
- ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v);
- if (!ok)
- goto out2;
- if (VALUE_IS_FUNCTION(cx, v)) {
- /* Make vp refer to funobj to keep it available as argv[-2]. */
- *vp = v;
- funobj = JSVAL_TO_OBJECT(v);
- parent = OBJ_GET_PARENT(cx, funobj);
- goto have_fun;
- }
- }
- fun = NULL;
- script = NULL;
- nslots = 0;
- /* Try a call or construct native object op. */
- if (flags & JSINVOKE_CONSTRUCT) {
- if (!JSVAL_IS_OBJECT(vp[1])) {
- ok = js_PrimitiveToObject(cx, &vp[1]);
- if (!ok)
- goto out2;
- }
- native = ops->construct;
- } else {
- native = ops->call;
- }
- if (!native)
- goto bad;
- } else {
- have_fun:
- /* Get private data and set derived locals from it. */
- fun = GET_FUNCTION_PRIVATE(cx, funobj);
- nslots = FUN_MINARGS(fun);
- nslots = (nslots > argc) ? nslots - argc : 0;
- if (FUN_INTERPRETED(fun)) {
- native = NULL;
- script = fun->u.i.script;
- } else {
- native = fun->u.n.native;
- script = NULL;
- nslots += fun->u.n.extra;
- }
- if (JSFUN_BOUND_METHOD_TEST(fun->flags)) {
- /* Handle bound method special case. */
- vp[1] = OBJECT_TO_JSVAL(parent);
- } else if (!JSVAL_IS_OBJECT(vp[1])) {
- JS_ASSERT(!(flags & JSINVOKE_CONSTRUCT));
- if (PRIMITIVE_THIS_TEST(fun, vp[1]))
- goto start_call;
- }
- }
- if (flags & JSINVOKE_CONSTRUCT) {
- JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
- } else {
- /*
- * We must call js_ComputeThis in case we are not called from the
- * interpreter, where a prior bytecode has computed an appropriate
- * |this| already.
- *
- * But we need to compute |this| eagerly only for so-called "slow"
- * (i.e., not fast) native functions. Fast natives must use either
- * JS_THIS or JS_THIS_OBJECT, and scripted functions will go through
- * the appropriate this-computing bytecode, e.g., JSOP_THIS.
- */
- if (native && (!fun || !(fun->flags & JSFUN_FAST_NATIVE))) {
- if (!js_ComputeThis(cx, JS_FALSE, vp + 2)) {
- ok = JS_FALSE;
- goto out2;
- }
- flags |= JSFRAME_COMPUTED_THIS;
- }
- }
- start_call:
- if (native && fun && (fun->flags & JSFUN_FAST_NATIVE)) {
- #ifdef DEBUG_NOT_THROWING
- JSBool alreadyThrowing = cx->throwing;
- #endif
- JS_ASSERT(nslots == 0);
- #if JS_HAS_LVALUE_RETURN
- /* Set by JS_SetCallReturnValue2, used to return reference types. */
- cx->rval2set = JS_FALSE;
- #endif
- ok = ((JSFastNative) native)(cx, argc, vp);
- JS_RUNTIME_METER(cx->runtime, nativeCalls);
- #ifdef DEBUG_NOT_THROWING
- if (ok && !alreadyThrowing)
- ASSERT_NOT_THROWING(cx);
- #endif
- goto out2;
- }
- argv = vp + 2;
- sp = argv + argc;
- rootedArgsFlag = JSFRAME_ROOTED_ARGV;
- if (nslots != 0) {
- /*
- * The extra slots required by the function continue with argument
- * slots. Thus, when the last stack pool arena does not have room to
- * fit nslots right after sp and AllocateAfterSP fails, we have to copy
- * [vp..vp+2+argc) slots and clear rootedArgsFlag to root the copy.
- */
- if (!AllocateAfterSP(cx, sp, nslots)) {
- rootedArgsFlag = 0;
- newvp = js_AllocRawStack(cx, 2 + argc + nslots, NULL);
- if (!newvp) {
- ok = JS_FALSE;
- goto out2;
- }
- memcpy(newvp, vp, (2 + argc) * sizeof(jsval));
- argv = newvp + 2;
- sp = argv + argc;
- }
- /* Push void to initialize missing args. */
- i = nslots;
- do {
- *sp++ = JSVAL_VOID;
- } while (--i != 0);
- }
- /* Allocate space for local variables and stack of interpreted function. */
- if (script && script->nslots != 0) {
- if (!AllocateAfterSP(cx, sp, script->nslots)) {
- /* NB: Discontinuity between argv and slots, stack slots. */
- sp = js_AllocRawStack(cx, script->nslots, NULL);
- if (!sp) {
- ok = JS_FALSE;
- goto out2;
- }
- }
- /* Push void to initialize local variables. */
- for (jsval *end = sp + fun->u.i.nvars; sp != end; ++sp)
- *sp = JSVAL_VOID;
- }
- /*
- * Initialize the frame.
- *
- * To set thisp we use an explicit cast and not JSVAL_TO_OBJECT, as vp[1]
- * can be a primitive value here for those native functions specified with
- * JSFUN_THISP_(NUMBER|STRING|BOOLEAN) flags.
- */
- frame.thisp = (JSObject *)vp[1];
- frame.varobj = NULL;
- frame.callobj = frame.argsobj = NULL;
- frame.script = script;
- frame.callee = funobj;
- frame.fun = fun;
- frame.argc = argc;
- frame.argv = argv;
- /* Default return value for a constructor is the new object. */
- frame.rval = (flags & JSINVOKE_CONSTRUCT) ? vp[1] : JSVAL_VOID;
- frame.down = cx->fp;
- frame.annotation = NULL;
- frame.scopeChain = NULL; /* set below for real, after cx->fp is set */
- frame.regs = NULL;
- frame.imacpc = NULL;
- frame.slots = NULL;
- frame.sharpDepth = 0;
- frame.sharpArray = NULL;
- frame.flags = flags | rootedArgsFlag;
- frame.dormantNext = NULL;
- frame.xmlNamespace = NULL;
- frame.blockChain = NULL;
- MUST_FLOW_THROUGH("out");
- cx->fp = &frame;
- /* Init these now in case we goto out before first hook call. */
- hook = cx->debugHooks->callHook;
- hookData = NULL;
- /* call the hook if present */
- if (hook && (native || script))
- hookData = hook(cx, &frame, JS_TRUE, 0, cx->debugHooks->callHookData);
- /* Call the function, either a native method or an interpreted script. */
- if (native) {
- #ifdef DEBUG_NOT_THROWING
- JSBool alreadyThrowing = cx->throwing;
- #endif
- #if JS_HAS_LVALUE_RETURN
- /* Set by JS_SetCallReturnValue2, used to return reference types. */
- cx->rval2set = JS_FALSE;
- #endif
- /* If native, use caller varobj and scopeChain for eval. */
- JS_ASSERT(!frame.varobj);
- JS_ASSERT(!frame.scopeChain);
- if (frame.down) {
- frame.varobj = frame.down->varobj;
- frame.scopeChain = frame.down->scopeChain;
- }
- /* But ensure that we have a scope chain. */
- if (!frame.scopeChain)
- frame.scopeChain = parent;
- frame.displaySave = NULL;
- ok = native(cx, frame.thisp, argc, frame.argv, &frame.rval);
- JS_RUNTIME_METER(cx->runtime, nativeCalls);
- #ifdef DEBUG_NOT_THROWING
- if (ok && !alreadyThrowing)
- ASSERT_NOT_THROWING(cx);
- #endif
- } else if (script) {
- /* Use parent scope so js_GetCallObject can find the right "Call". */
- frame.scopeChain = parent;
- if (JSFUN_HEAVYWEIGHT_TEST(fun->flags)) {
- /* Scope with a call object parented by the callee's parent. */
- if (!js_GetCallObject(cx, &frame, parent)) {
- ok = JS_FALSE;
- goto out;
- }
- }
- frame.slots = sp - fun->u.i.nvars;
- ok = js_Interpret(cx);
- } else {
- /* fun might be onerror trying to report a syntax error in itself. */
- frame.scopeChain = NULL;
- frame.displaySave = NULL;
- ok = JS_TRUE;
- }
- out:
- if (hookData) {
- hook = cx->debugHooks->callHook;
- if (hook)
- hook(cx, &frame, JS_FALSE, &ok, hookData);
- }
- /* If frame has a call object, sync values and clear back-pointer. */
- if (frame.callobj)
- ok &= js_PutCallObject(cx, &frame);
- /* If frame has an arguments object, sync values and clear back-pointer. */
- if (frame.argsobj)
- ok &= js_PutArgsObject(cx, &frame);
- *vp = frame.rval;
- /* Restore cx->fp now that we're done releasing frame objects. */
- cx->fp = frame.down;
- out2:
- /* Pop everything we may have allocated off the stack. */
- JS_ARENA_RELEASE(&cx->stackPool, mark);
- if (!ok)
- *vp = JSVAL_NULL;
- return ok;
- bad:
- js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS);
- ok = JS_FALSE;
- goto out2;
- }
- JSBool
- js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags,
- uintN argc, jsval *argv, jsval *rval)
- {
- jsval *invokevp;
- void *mark;
- JSBool ok;
- invokevp = js_AllocStack(cx, 2 + argc, &mark);
- if (!invokevp)
- return JS_FALSE;
- invokevp[0] = fval;
- invokevp[1] = OBJECT_TO_JSVAL(obj);
- memcpy(invokevp + 2, argv, argc * sizeof *argv);
- ok = js_Invoke(cx, argc, invokevp, flags);
- if (ok) {
- /*
- * Store *rval in the a scoped local root if a scope is open, else in
- * the lastInternalResult pigeon-hole GC root, solely so users of
- * js_InternalInvoke and its direct and indirect (js_ValueToString for
- * example) callers do not need to manage roots for local, temporary
- * references to such results.
- */
- *rval = *invokevp;
- if (JSVAL_IS_GCTHING(*rval) && *rval != JSVAL_NULL) {
- if (cx->localRootStack) {
- if (js_PushLocalRoot(cx, cx->localRootStack, *rval) < 0)
- ok = JS_FALSE;
- } else {
- cx->weakRoots.lastInternalResult = *rval;
- }
- }
- }
- js_FreeStack(cx, mark);
- return ok;
- }
- JSBool
- js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval,
- JSAccessMode mode, uintN argc, jsval *argv, jsval *rval)
- {
- JSSecurityCallbacks *callbacks;
- /*
- * js_InternalInvoke could result in another try to get or set the same id
- * again, see bug 355497.
- */
- JS_CHECK_RECURSION(cx, return JS_FALSE);
- /*
- * Check general (not object-ops/class-specific) access from the running
- * script to obj.id only if id has a scripted getter or setter that we're
- * about to invoke. If we don't check this case, nothing else will -- no
- * other native code has the chance to check.
- *
- * Contrast this non-native (scripted) case with native getter and setter
- * accesses, where the native itself must do an access check, if security
- * policies requires it. We make a checkAccess or checkObjectAccess call
- * back to the embedding program only in those cases where we're not going
- * to call an embedding-defined native function, getter, setter, or class
- * hook anyway. Where we do call such a native, there's no need for the
- * engine to impose a separate access check callback on all embeddings --
- * many embeddings have no security policy at all.
- */
- JS_ASSERT(mode == JSACC_READ || mode == JSACC_WRITE);
- callbacks = JS_GetSecurityCallbacks(cx);
- if (callbacks &&
- callbacks->checkObjectAccess &&
- VALUE_IS_FUNCTION(cx, fval) &&
- FUN_INTERPRETED(GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(fval))) &&
- !callbacks->checkObjectAccess(cx, obj, ID_TO_VALUE(id), mode, &fval)) {
- return JS_FALSE;
- }
- return js_InternalCall(cx, obj, fval, argc, argv, rval);
- }
- JSBool
- js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
- JSStackFrame *down, uintN flags, jsval *result)
- {
- JSInterpreterHook hook;
- void *hookData, *mark;
- JSStackFrame *oldfp, frame;
- JSObject *obj, *tmp;
- JSBool ok;
- #ifdef INCLUDE_MOZILLA_DTRACE
- if (JAVASCRIPT_EXECUTE_START_ENABLED())
- jsdtrace_execute_start(script);
- #endif
- hook = cx->debugHooks->executeHook;
- hookData = mark = NULL;
- oldfp = cx->fp;
- frame.script = script;
- if (down) {
- /* Propagate arg state for eval and the debugger API. */
- frame.callobj = down->callobj;
- frame.argsobj = down->argsobj;
- frame.varobj = down->varobj;
- frame.callee = down->callee;
- frame.fun = down->fun;
- frame.thisp = down->thisp;
- if (down->flags & JSFRAME_COMPUTED_THIS)
- flags |= JSFRAME_COMPUTED_THIS;
- frame.argc = down->argc;
- frame.argv = down->argv;
- frame.annotation = down->annotation;
- frame.sharpArray = down->sharpArray;
- JS_ASSERT(script->nfixed == 0);
- } else {
- frame.callobj = frame.argsobj = NULL;
- obj = chain;
- if (cx->options & JSOPTION_VAROBJFIX) {
- while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
- obj = tmp;
- }
- frame.varobj = obj;
- frame.callee = NULL;
- frame.fun = NULL;
- frame.thisp = chain;
- frame.argc = 0;
- frame.argv = NULL;
- frame.annotation = NULL;
- frame.sharpArray = NULL;
- }
- frame.imacpc = NULL;
- if (script->nslots != 0) {
- frame.slots = js_AllocRawStack(cx, script->nslots, &mark);
- if (!frame.slots) {
- ok = JS_FALSE;
- goto out;
- }
- memset(frame.slots, 0, script->nfixed * sizeof(jsval));
- } else {
- frame.slots = NULL;
- }
- frame.rval = JSVAL_VOID;
- frame.down = down;
- frame.scopeChain = chain;
- frame.regs = NULL;
- frame.sharpDepth = 0;
- frame.flags = flags;
- frame.dormantNext = NULL;
- frame.xmlNamespace = NULL;
- frame.blockChain = NULL;
- /*
- * Here we wrap the call to js_Interpret with code to (conditionally)
- * save and restore the old stack frame chain into a chain of 'dormant'
- * frame chains. Since we are replacing cx->fp, we were running into
- * the problem that if GC was called under this frame, some of the GC
- * things associated with the old frame chain (available here only in
- * the C variable 'oldfp') were not rooted and were being collected.
- *
- * So, now we preserve the links to these 'dormant' frame chains in cx
- * before calling js_Interpret and cleanup afterwards. The GC walks
- * these dormant chains and marks objects in the same way that it marks
- * objects in the primary cx->fp chain.
- */
- if (oldfp && oldfp != down) {
- JS_ASSERT(!oldfp->dormantNext);
- oldfp->dormantNext = cx->dormantFrameChain;
- cx->dormantFrameChain = oldfp;
- }
- cx->fp = &frame;
- if (!down) {
- OBJ_TO_OUTER_OBJECT(cx, frame.thisp);
- if (!frame.thisp) {
- ok = JS_FALSE;
- goto out2;
- }
- frame.flags |= JSFRAME_COMPUTED_THIS;
- }
- if (hook) {
- hookData = hook(cx, &frame, JS_TRUE, 0,
- cx->debugHooks->executeHookData);
- }
- ok = js_Interpret(cx);
- if (result)
- *result = frame.rval;
- if (hookData) {
- hook = cx->debugHooks->executeHook;
- if (hook)
- hook(cx, &frame, JS_FALSE, &ok, hookData);
- }
- out2:
- if (mark)
- js_FreeRawStack(cx, mark);
- cx->fp = oldfp;
- if (oldfp && oldfp != down) {
- JS_ASSERT(cx->dormantFrameChain == oldfp);
- cx->dormantFrameChain = oldfp->dormantNext;
- oldfp->dormantNext = NULL;
- }
- out:
- #ifdef INCLUDE_MOZILLA_DTRACE
- if (JAVASCRIPT_EXECUTE_DONE_ENABLED())
- jsdtrace_execute_done(script);
- #endif
- return ok;
- }
- JSBool
- js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
- JSObject **objp, JSProperty **propp)
- {
- JSObject *obj2;
- JSProperty *prop;
- uintN oldAttrs, report;
- JSBool isFunction;
- jsval value;
- const char *type, *name;
- if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
- return JS_FALSE;
- if (propp) {
- *objp = obj2;
- *propp = prop;
- }
- if (!prop)
- return JS_TRUE;
- /*
- * Use prop as a speedup hint to OBJ_GET_ATTRIBUTES, but drop it on error.
- * An assertion at label bad: will insist that it is null.
- */
- if (!OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs)) {
- OBJ_DROP_PROPERTY(cx, obj2, prop);
- #ifdef DEBUG
- prop = NULL;
- #endif
- goto bad;
- }
- /*
- * From here, return true, or else goto bad on failure to null out params.
- * If our caller doesn't want prop, drop it (we don't need it any longer).
- */
- if (!propp) {
- OBJ_DROP_PROPERTY(cx, obj2, prop);
- prop = NULL;
- }
- if (attrs == JSPROP_INITIALIZER) {
- /* Allow the new object to override properties. */
- if (obj2 != obj)
- return JS_TRUE;
- report = JSREPORT_WARNING | JSREPORT_STRICT;
- } else {
- /* We allow redeclaring some non-readonly properties. */
- if (((oldAttrs | attrs) & JSPROP_READONLY) == 0) {
- /*
- * Allow redeclaration of variables and functions, but insist that
- * the new value is not a getter if the old value was, ditto for
- * setters -- unless prop is impermanent (in which case anyone
- * could delete it and redefine it, willy-nilly).
- */
- if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
- return JS_TRUE;
- if ((~(oldAttrs ^ attrs) & (JSPROP_GETTER | JSPROP_SETTER)) == 0)
- return JS_TRUE;
- if (!(oldAttrs & JSPROP_PERMANENT))
- return JS_TRUE;
- }
- report = JSREPORT_ERROR;
- isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0;
- if (!isFunction) {
- if (!OBJ_GET_PROPERTY(cx, obj, id, &value))
- goto bad;
- isFunction = VALUE_IS_FUNCTION(cx, value);
- }
- }
- type = (attrs == JSPROP_INITIALIZER)
- ? "property"
- : (oldAttrs & attrs & JSPROP_GETTER)
- ? js_getter_str
- : (oldAttrs & attrs & JSPROP_SETTER)
- ? js_setter_str
- : (oldAttrs & JSPROP_READONLY)
- ? js_const_str
- : isFunction
- ? js_function_str
- : js_var_str;
- name = js_ValueToPrintableString(cx, ID_TO_VALUE(id));
- if (!name)
- goto bad;
- return JS_ReportErrorFlagsAndNumber(cx, report,
- js_GetErrorMessage, NULL,
- JSMSG_REDECLARED_VAR,
- type, name);
- bad:
- if (propp) {
- *objp = NULL;
- *propp = NULL;
- }
- JS_ASSERT(!prop);
- return JS_FALSE;
- }
- JSBool
- js_StrictlyEqual(JSContext *cx, jsval lval, jsval rval)
- {
- jsval ltag = JSVAL_TAG(lval), rtag = JSVAL_TAG(rval);
- jsdouble ld, rd;
- if (ltag == rtag) {
- if (ltag == JSVAL_STRING) {
- JSString *lstr = JSVAL_TO_STRING(lval),
- *rstr = JSVAL_TO_STRING(rval);
- return js_EqualStrings(lstr, rstr);
- }
- if (ltag == JSVAL_DOUBLE) {
- ld = *JSVAL_TO_DOUBLE(lval);
- rd = *JSVAL_TO_DOUBLE(rval);
- return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
- }
- if (ltag == JSVAL_OBJECT &&
- lval != rval &&
- !JSVAL_IS_NULL(lval) &&
- !JSVAL_IS_NULL(rval)) {
- JSObject *lobj, *robj;
- lobj = js_GetWrappedObject(cx, JSVAL_TO_OBJECT(lval));
- robj = js_GetWrappedObject(cx, JSVAL_TO_OBJECT(rval));
- lval = OBJECT_TO_JSVAL(lobj);
- rval = OBJECT_TO_JSVAL(robj);
- }
- return lval == rval;
- }
- if (ltag == JSVAL_DOUBLE && JSVAL_IS_INT(rval)) {
- ld = *JSVAL_TO_DOUBLE(lval);
- rd = JSVAL_TO_INT(rval);
- return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
- }
- if (JSVAL_IS_INT(lval) && rtag == JSVAL_DOUBLE) {
- ld = JSVAL_TO_INT(lval);
- rd = *JSVAL_TO_DOUBLE(rval);
- return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
- }
- return lval == rval;
- }
- JSBool
- js_InvokeConstructor(JSContext *cx, uintN argc, JSBool clampReturn, jsval *vp)
- {
- JSFunction *fun, *fun2;
- JSObject *obj, *obj2, *proto, *parent;
- jsval lval, rval;
- JSClass *clasp;
- fun = NULL;
- obj2 = NULL;
- lval = *vp;
- if (!JSVAL_IS_OBJECT(lval) ||
- (obj2 = JSVAL_TO_OBJECT(lval)) == NULL ||
- /* XXX clean up to avoid special cases above ObjectOps layer */
- OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass ||
- !obj2->map->ops->construct)
- {
- fun = js_ValueToFunction(cx, vp, JSV2F_CONSTRUCT);
- if (!fun)
- return JS_FALSE;
- }
- clasp = &js_ObjectClass;
- if (!obj2) {
- proto = parent = NULL;
- fun = NULL;
- } else {
- /*
- * Get the constructor prototype object for this function.
- * Use the nominal 'this' parameter slot, vp[1], as a local
- * root to protect this prototype, in case it has no other
- * strong refs.
- */
- if (!OBJ_GET_PROPERTY(cx, obj2,
- ATOM_TO_JSID(cx->runtime->atomState
- .classPrototypeAtom),
- &vp[1])) {
- return JS_FALSE;
- }
- rval = vp[1];
- proto = JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : NULL;
- parent = OBJ_GET_PARENT(cx, obj2);
- if (OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass) {
- fun2 = GET_FUNCTION_PRIVATE(cx, obj2);
- if (!FUN_INTERPRETED(fun2) &&
- !(fun2->flags & JSFUN_TRACEABLE) &&
- fun2->u.n.u.clasp) {
- clasp = fun2->u.n.u.clasp;
- }
- }
- }
- obj = js_NewObject(cx, clasp, proto, parent, 0);
- if (!obj)
- return JS_FALSE;
- /* Now we have an object with a constructor method; call it. */
- vp[1] = OBJECT_TO_JSVAL(obj);
- if (!js_Invoke(cx, argc, vp, JSINVOKE_CONSTRUCT)) {
- cx->weakRoots.newborn[GCX_OBJECT] = NULL;
- return JS_FALSE;
- }
- /* Check the return value and if it's primitive, force it to be obj. */
- rval = *vp;
- if (clampReturn && JSVAL_IS_PRIMITIVE(rval)) {
- if (!fun) {
- /* native [[Construct]] returning primitive is error */
- JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
- JSMSG_BAD_NEW_RESULT,
- js_ValueToPrintableString(cx, rval));
- return JS_FALSE;
- }
- *vp = OBJECT_TO_JSVAL(obj);
- }
- JS_RUNTIME_METER(cx->runtime, constructs);
- return JS_TRUE;
- }
- JSBool
- js_InternNonIntElementId(JSContext *cx, JSObject *obj, jsval idval, jsid *idp)
- {
- JS_ASSERT(!JSVAL_IS_INT(idval));
- #if JS_HAS_XML_SUPPORT
- if (!JSVAL_IS_PRIMITIVE(idval)) {
- if (OBJECT_IS_XML(cx, obj)) {
- *idp = OBJECT_JSVAL_TO_JSID(idval);
- return JS_TRUE;
- }
- if (!js_IsFunctionQName(cx, JSVAL_TO_OBJECT(idval), idp))
- return JS_FALSE;
- if (*idp != 0)
- return JS_TRUE;
- }
- #endif
- return js_ValueToStringId(cx, idval, idp);
- }
- /*
- * Enter the new with scope using an object at sp[-1] and associate the depth
- * of the with block with sp + stackIndex.
- */
- JS_STATIC_INTERPRET JSBool
- js_EnterWith(JSContext *cx, jsint stackIndex)
- {
- JSStackFrame *fp;
- jsval *sp;
- JSObject *obj, *parent, *withobj;
- fp = cx->fp;
- sp = fp->regs->sp;
- JS_ASSERT(stackIndex < 0);
- JS_ASSERT(StackBase(fp) <= sp + stackIndex);
- if (!JSVAL_IS_PRIMITIVE(sp[-1])) {
- obj = JSVAL_TO_OBJECT(sp[-1]);
- } else {
- obj = js_ValueToNonNullObject(cx, sp[-1]);
- if (!obj)
- return JS_FALSE;
- sp[-1] = OBJECT_TO_JSVAL(obj);
- }
- parent = js_GetScopeChain(cx, fp);
- if (!parent)
- return JS_FALSE;
- OBJ_TO_INNER_OBJECT(cx, obj);
- if (!obj)
- return JS_FALSE;
- withobj = js_NewWithObject(cx, obj, parent,
- sp + stackIndex - StackBase(fp));
- if (!withobj)
- return JS_FALSE;
- fp->scopeChain = withobj;
- js_DisablePropertyCache(cx);
- return JS_TRUE;
- }
- JS_STATIC_INTERPRET void
- js_LeaveWith(JSContext *cx)
- {
- JSObject *withobj;
- withobj = cx->fp->scopeChain;
- JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass);
- JS_ASSERT(OBJ_GET_PRIVATE(cx, withobj) == cx->fp);
- JS_ASSERT(OBJ_BLOCK_DEPTH(cx, withobj) >= 0);
- cx->fp->scopeChain = OBJ_GET_PARENT(cx, withobj);
- JS_SetPrivate(cx, withobj, NULL);
- js_EnablePropertyCache(cx);
- }
- JSClass *
- js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth)
- {
- JSClass *clasp;
- clasp = OBJ_GET_CLASS(cx, obj);
- if ((clasp == &js_WithClass || clasp == &js_BlockClass) &&
- OBJ_GET_PRIVATE(cx, obj) == cx->fp &&
- OBJ_BLOCK_DEPTH(cx, obj) >= stackDepth) {
- return clasp;
- }
- return NULL;
- }
- JS_STATIC_INTERPRET jsint
- js_CountWithBlocks(JSContext *cx, JSStackFrame *fp)
- {
- jsint n;
- JSObject *obj;
- JSClass *clasp;
- n = 0;
- for (obj = fp->scopeChain;
- (clasp = js_IsActiveWithOrBlock(cx, obj, 0)) != NULL;
- obj = OBJ_GET_PARENT(cx, obj)) {
- if (clasp == &js_WithClass)
- ++n;
- }
- return n;
- }
- /*
- * Unwind block and scope chains to match the given depth. The function sets
- * fp->sp on return to stackDepth.
- */
- JSBool
- js_UnwindScope(JSContext *cx, JSStackFrame *fp, jsint stackDepth,
- JSBool normalUnwind)
- {
- JSObject *obj;
- JSClass *clasp;
- JS_ASSERT(stackDepth >= 0);
- JS_ASSERT(StackBase(fp) + stackDepth <= fp->regs->sp);
- for (obj = fp->blockChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {
- JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass);
- if (OBJ_BLOCK_DEPTH(cx, obj) < stackDepth)
- break;
- }
- fp->blockChain = obj;
- for (;;) {
- obj = fp->scopeChain;
- clasp = js_IsActiveWithOrBlock(cx, obj, stackDepth);
- if (!clasp)
- break;
- if (clasp == &js_BlockClass) {
- /* Don't fail until after we've updated all stacks. */
- normalUnwind &= js_PutBlockObject(cx, normalUnwind);
- } else {
- js_LeaveWith(cx);
- }
- }
- fp->regs->sp = StackBase(fp) + stackDepth;
- return normalUnwind;
- }
- JS_STATIC_INTERPRET JSBool
- js_DoIncDec(JSContext *cx, const JSCodeSpec *cs, jsval *vp, jsval *vp2)
- {
- jsval v;
- jsdouble d;
- v = *vp;
- if (JSVAL_IS_DOUBLE(v)) {
- d = *JSVAL_TO_DOUBLE(v);
- } else if (JSVAL_IS_INT(v)) {
- d = JSVAL_TO_INT(v);
- } else {
- d = js_ValueToNumber(cx, vp);
- if (JSVAL_IS_NULL(*vp))
- return JS_FALSE;
- JS_ASSERT(JSVAL_IS_NUMBER(*vp) || *vp == JSVAL_TRUE);
- /* Store the result of v conversion back in vp for post increments. */
- if ((cs->format & JOF_POST) &&
- *vp == JSVAL_TRUE
- && !js_NewNumberInRootedValue(cx, d, vp)) {
- return JS_FALSE;
- }
- }
- (cs->format & JOF_INC) ? d++ : d--;
- if (!js_NewNumberInRootedValue(cx, d, vp2))
- return JS_FALSE;
- if (!(cs->format & JOF_POST))
- *vp = *vp2;
- return JS_TRUE;
- }
- #ifdef DEBUG
- JS_STATIC_INTERPRET void
- js_TraceOpcode(JSContext *cx, jsint len)
- {
- FILE *tracefp;
- JSStackFrame *fp;
- JSFrameRegs *regs;
- JSOp prevop;
- intN ndefs, n, nuses;
- jsval *siter;
- JSString *str;
- JSOp op;
- tracefp = (FILE *) cx->tracefp;
- JS_ASSERT(tracefp);
- fp = cx->fp;
- regs = fp->regs;
- if (len != 0) {
- prevop = (JSOp) regs->pc[-len];
- ndefs = js_CodeSpec[prevop].ndefs;
- if (ndefs != 0) {
- for (n = -ndefs; n < 0; n++) {
- char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n],
- NULL);
- if (bytes) {
- fprintf(tracefp, "%s %s",
- (n == -ndefs) ? " output:" : ",",
- bytes);
- JS_free(cx, bytes);
- }
- }
- fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp)));
- }
- fprintf(tracefp, " stack: ");
- for (siter = StackBase(fp); siter < regs->sp; siter++) {
- str = js_ValueToString(cx, *siter);
- if (!str)
- fputs("<null>", tracefp);
- else
- js_FileEscapedString(tracefp, str, 0);
- fputc(' ', tracefp);
- }
- fputc('\n', tracefp);
- }
- fprintf(tracefp, "%4u: ",
- js_PCToLineNumber(cx, fp->script, fp->imacpc ? fp->imacpc : regs->pc));
- js_Disassemble1(cx, fp->script, regs->pc,
- PTRDIFF(regs->pc, fp->script->code, jsbytecode),
- JS_FALSE, tracefp);
- op = (JSOp) *regs->pc;
- nuses = js_CodeSpec[op].nuses;
- if (nuses != 0) {
- for (n = -nuses; n < 0; n++) {
- char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n],
- NULL);
- if (bytes) {
- fprintf(tracefp, "%s %s",
- (n == -nuses) ? " inputs:" : ",",
- bytes);
- JS_free(cx, bytes);
- }
- }
- fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp)));
- }
- }
- #endif /* DEBUG */
- #ifdef JS_OPMETER
- # include <stdlib.h>
- # define HIST_NSLOTS 8
- /*
- * The second dimension is hardcoded at 256 because we know that many bits fit
- * in a byte, and mainly to optimize away multiplying by JSOP_LIMIT to address
- * any particular row.
- */
- static uint32 succeeds[JSOP_LIMIT][256];
- static uint32 slot_ops[JSOP_LIMIT][HIST_NSLOTS];
- JS_STATIC_INTERPRET void
- js_MeterOpcodePair(JSOp op1, JSOp op2)
- {
- if (op1 != JSOP_STOP)
- ++succeeds[op1][op2];
- }
- JS_STATIC_INTERPRET void
- js_MeterSlotOpcode(JSOp op, uint32 slot)
- {
- if (slot < HIST_NSLOTS)
- ++slot_ops[op][slot];
- }
- typedef struct Edge {
- const char *from;
- const char *to;
- uint32 count;
- } Edge;
- static int
- compare_edges(const void *a, const void *b)
- {
- const Edge *ea = (const Edge *) a;
- const Edge *eb = (const Edge *) b;
- return (int32)eb->count - (int32)ea->count;
- }
- void
- js_DumpOpMeters()
- {
- const char *name, *from, *style;
- FILE *fp;
- uint32 total, count;
- uint32 i, j, nedges;
- Edge *graph;
- name = getenv("JS_OPMETER_FILE");
- if (!name)
- name = "/tmp/ops.dot";
- fp = fopen(name, "w");
- if (!fp) {
- perror(name);
- return;
- }
- total = nedges = 0;
- for (i = 0; i < JSOP_LIMIT; i++) {
- for (j = 0; j < JSOP_LIMIT; j++) {
- count = succeeds[i][j];
- if (count != 0) {
- total += count;
- ++nedges;
- }
- }
- }
- # define SIGNIFICANT(count,total) (200. * (count) >= (total))
- graph = (Edge *) calloc(nedges, sizeof graph[0]);
- for (i = nedges = 0; i < JSOP_LIMIT; i++) {
- from = js_CodeName[