Merge backouts

This commit is contained in:
Ehsan Akhgari 2012-07-04 19:44:26 -04:00
commit 5842de901d
88 changed files with 1425 additions and 2715 deletions

View File

@ -63,8 +63,6 @@ _TEST_FILES = \
test_lookupGetter.html \
test_InstanceOf.html \
test_traceProtos.html \
test_forOf.html \
forOf_iframe.html \
$(NULL)

View File

@ -1,13 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>iframe content for test_forOf_iframe.html</title>
</head>
<body>
<div id="basket">
<span id="egg0"></span>
<span id="egg1"><span id="duckling1"></span></span>
<span id="egg2"></span>
</div>
</body>
</html>

View File

@ -1,94 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=725907
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 725907</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=725907">Mozilla Bug 725907</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<div id="basket">
<span id="egg0"></span>
<span id="egg1"><span id="duckling1"></span></span>
<span id="egg2"></span>
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 725907 **/
function runTestsForDocument(document, msgSuffix) {
function is(a, b, msg) { SimpleTest.is(a, b, msg + msgSuffix); }
function isnot(a, b, msg) { SimpleTest.isnot(a, b, msg + msgSuffix); }
var basket = document.getElementById("basket");
var egg3 = document.createElement("span");
egg3.id = "egg3";
var log = '';
for (var x of basket.childNodes) {
if (x.nodeType != x.TEXT_NODE)
log += x.id + ";";
}
is(log, "egg0;egg1;egg2;", "'for (x of div.childNodes)' should iterate over child nodes");
log = '';
for (var x of basket.childNodes) {
if (x.nodeType != x.TEXT_NODE) {
log += x.id + ";";
if (x.id == "egg1")
basket.appendChild(egg3);
}
}
is(log, "egg0;egg1;egg2;egg3;", "'for (x of div.childNodes)' should see elements added during iteration");
var iter1 = basket.childNodes.iterator();
var iter2 = basket.childNodes.iterator();
isnot(iter1, iter2, "nodelist.iterator() returns a new iterator each time");
log = '';
basket.appendChild(document.createTextNode("some text"));
for (var x of basket.children)
log += x.id + ";";
is(log, "egg0;egg1;egg2;egg3;", "'for (x of div.children)' should iterate over child elements");
var iter1 = basket.children.iterator();
var iter2 = basket.children.iterator();
isnot(iter1, iter2, ".iterator() returns a new iterator each time");
var count = 0;
for (var x of document.getElementsByClassName("hazardous-materials"))
count++;
is(count, 0, "'for (x of emptyNodeList)' loop should run zero times");
var log = '';
for (var x of document.querySelectorAll("span"))
log += x.id + ";";
is(log, "egg0;egg1;duckling1;egg2;egg3;", "for-of loop should work with a querySelectorAll() NodeList");
}
/* All the tests run twice. First, in this document, so without any wrappers. */
runTestsForDocument(document, "");
/* And once using the document of an iframe, so working with cross-compartment wrappers. */
SimpleTest.waitForExplicitFinish();
function iframeLoaded(iframe) {
runTestsForDocument(iframe.contentWindow.document, " (in iframe)");
SimpleTest.finish();
}
</script>
<iframe src="forOf_iframe.html" onload="iframeLoaded(this)"></iframe>
</pre>
</body>
</html>

View File

@ -15,6 +15,9 @@ namespace js {
class TempAllocPolicy;
/* Integral types for all hash functions. */
typedef uint32_t HashNumber;
/*****************************************************************************/
namespace detail {
@ -285,6 +288,7 @@ class HashTable : private AllocPolicy
static const uint8_t sMinAlphaFrac = 64; /* (0x100 * .25) taken from jsdhash.h */
static const uint8_t sMaxAlphaFrac = 192; /* (0x100 * .75) taken from jsdhash.h */
static const uint8_t sInvMaxAlpha = 171; /* (ceil(0x100 / .75) >> 1) */
static const HashNumber sGoldenRatio = 0x9E3779B9U; /* taken from jsdhash.h */
static const HashNumber sFreeKey = Entry::sFreeKey;
static const HashNumber sRemovedKey = Entry::sRemovedKey;
static const HashNumber sCollisionBit = Entry::sCollisionBit;
@ -304,7 +308,10 @@ class HashTable : private AllocPolicy
static HashNumber prepareHash(const Lookup& l)
{
HashNumber keyHash = ScrambleHashCode(HashPolicy::hash(l));
HashNumber keyHash = HashPolicy::hash(l);
/* Improve keyHash distribution. */
keyHash *= sGoldenRatio;
/* Avoid reserved hash codes. */
if (!isLiveHash(keyHash))

View File

@ -355,26 +355,6 @@ JS_FLOOR_LOG2W(size_t n)
return js_FloorLog2wImpl(n);
}
/*
* JS_ROTATE_LEFT32
*
* There is no rotate operation in the C Language so the construct (a << 4) |
* (a >> 28) is used instead. Most compilers convert this to a rotate
* instruction but some versions of MSVC don't without a little help. To get
* MSVC to generate a rotate instruction, we have to use the _rotl intrinsic
* and use a pragma to make _rotl inline.
*
* MSVC in VS2005 will do an inline rotate instruction on the above construct.
*/
#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64) || \
defined(_M_X64))
#include <stdlib.h>
#pragma intrinsic(_rotl)
#define JS_ROTATE_LEFT32(a, bits) _rotl(a, bits)
#else
#define JS_ROTATE_LEFT32(a, bits) (((a) << (bits)) | ((a) >> (32 - (bits))))
#endif
JS_END_EXTERN_C
#ifdef __cplusplus
@ -915,61 +895,6 @@ RoundUpPow2(size_t x)
return size_t(1) << JS_CEILING_LOG2W(x);
}
/* Integral types for all hash functions. */
typedef uint32_t HashNumber;
namespace detail {
/*
* Given a raw hash code, h, return a number to be used as an index into a
* power-of-two-sized hash table.
*
* This function aims to produce as uniform an output distribution as possible,
* especially in the rightmost bits, even though the input distribution may be
* highly nonrandom, given the constraints that this must be deterministic and
* quick to compute.
*/
inline HashNumber
ScrambleHashCode(HashNumber h)
{
/*
* Simply returning h would not cause any hash tables to produce wrong
* answers. But it can produce pathologically bad performance: The caller
* bitmasks the result, keeping only the lowest bits. The low bits of
* hash codes are often low-entropy.
*
* Multiplying by a constant is a good start. It mixes the bits such that
* each bit of the output is a combination of several bits of the
* input--except for the lowest few bits which are not mixed at all
* (shuffleBits * X has the same three low bits as X, for all X). So after
* multiplication, we rotate those least-mixed low bits out toward the end
* where they are likely to be masked away. The number of bits rotated
* toward the end is shiftBits, which is 12 so that hash tables with size
* up to 2^(32-12) = 2^20 = about a million buckets will get the best-mixed
* bits we have.
*
* The particular value of shuffleBits is taken from the hex expansion of
* the golden ratio, which starts 1.9E3779B9.... This value has been
* cargo-culted around since the original jshash.h. I doubt there is
* anything particularly magical about it, except:
*
* - It's odd. Multiplying by any odd number modulo 2^32 loses no entropy;
* multiplying by any even number zeroes out a bit (or more).
*
* - It has a fairly even mix of one bits and zero bits. Too few one-bits
* and each bit of the output ends up being a combination of a very small
* number of input bits. Too many one-bits and you end up with the same
* problem; in the limit, multiplying by 0xffffffff modulo 2^32 is the
* same as multiplying by -1, which hardly mixes the bits at all.
*/
static const HashNumber shuffleBits = 0x9E3779B9U;
h *= shuffleBits;
static const int bitsToRotate = 20;
return JS_ROTATE_LEFT32(h, bitsToRotate);
}
} /* namespace detail */
} /* namespace js */
namespace JS {

View File

@ -130,7 +130,6 @@ CPPSRCS = \
TreeContext.cpp \
TestingFunctions.cpp \
LifoAlloc.cpp \
Eval.cpp \
MapObject.cpp \
MemoryMetrics.cpp \
RegExpObject.cpp \

View File

@ -1,433 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=79:
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jscntxt.h"
#include "jsonparser.h"
#include "builtin/Eval.h"
#include "frontend/BytecodeCompiler.h"
#include "vm/GlobalObject.h"
#include "jsinterpinlines.h"
using namespace js;
// We should be able to assert this for *any* fp->scopeChain().
static void
AssertInnerizedScopeChain(JSContext *cx, JSObject &scopeobj)
{
#ifdef DEBUG
for (JSObject *o = &scopeobj; o; o = o->enclosingScope()) {
if (JSObjectOp op = o->getClass()->ext.innerObject) {
Rooted<JSObject*> obj(cx, o);
JS_ASSERT(op(cx, obj) == o);
}
}
#endif
}
void
EvalCache::purge()
{
// Purge all scripts from the eval cache. In addition to removing them from
// table_, null out the evalHashLink field of any script removed. Since
// evalHashLink is in a union with globalObject, this allows the GC to
// indiscriminately use the union as a nullable globalObject pointer.
for (size_t i = 0; i < ArrayLength(table_); ++i) {
for (JSScript **listHeadp = &table_[i]; *listHeadp; ) {
JSScript *script = *listHeadp;
JS_ASSERT(GetGCThingTraceKind(script) == JSTRACE_SCRIPT);
*listHeadp = script->evalHashLink();
script->evalHashLink() = NULL;
}
}
}
JSScript **
EvalCache::bucket(JSLinearString *str)
{
const jschar *s = str->chars();
size_t n = str->length();
if (n > 100)
n = 100;
uint32_t h;
for (h = 0; n; s++, n--)
h = JS_ROTATE_LEFT32(h, 4) ^ *s;
h *= JS_GOLDEN_RATIO;
h >>= 32 - SHIFT;
JS_ASSERT(h < ArrayLength(table_));
return &table_[h];
}
static JSScript *
EvalCacheLookup(JSContext *cx, JSLinearString *str, StackFrame *caller, unsigned staticLevel,
JSPrincipals *principals, JSObject &scopeobj, JSScript **bucket)
{
// Cache local eval scripts indexed by source qualified by scope.
//
// An eval cache entry should never be considered a hit unless its
// strictness matches that of the new eval code. The existing code takes
// care of this, because hits are qualified by the function from which
// eval was called, whose strictness doesn't change. (We don't cache evals
// in eval code, so the calling function corresponds to the calling script,
// and its strictness never varies.) Scripts produced by calls to eval from
// global code aren't cached.
//
// FIXME bug 620141: Qualify hits by calling script rather than function.
// Then we wouldn't need the unintuitive !isEvalFrame() hack in EvalKernel
// to avoid caching nested evals in functions (thus potentially mismatching
// on strict mode), and we could cache evals in global code if desired.
unsigned count = 0;
JSScript **scriptp = bucket;
JSVersion version = cx->findVersion();
JSScript *script;
JSSubsumePrincipalsOp subsume = cx->runtime->securityCallbacks->subsumePrincipals;
while ((script = *scriptp) != NULL) {
if (script->savedCallerFun &&
script->staticLevel == staticLevel &&
script->getVersion() == version &&
!script->hasSingletons &&
(!subsume || script->principals == principals ||
(subsume(principals, script->principals) &&
subsume(script->principals, principals))))
{
// Get the prior (cache-filling) eval's saved caller function.
// See frontend::CompileScript.
JSFunction *fun = script->getCallerFunction();
if (fun == caller->fun()) {
/*
* Get the source string passed for safekeeping in the atom map
* by the prior eval to frontend::CompileScript.
*/
JSAtom *src = script->atoms[0];
if (src == str || EqualStrings(src, str)) {
// Source matches. Make sure there are no inner objects
// which might use the wrong parent and/or call scope by
// reusing the previous eval's script. Skip the script's
// first object, which entrains the eval's scope.
JS_ASSERT(script->objects()->length >= 1);
if (script->objects()->length == 1 &&
!script->hasRegexps()) {
JS_ASSERT(staticLevel == script->staticLevel);
*scriptp = script->evalHashLink();
script->evalHashLink() = NULL;
return script;
}
}
}
}
static const unsigned EVAL_CACHE_CHAIN_LIMIT = 4;
if (++count == EVAL_CACHE_CHAIN_LIMIT)
return NULL;
scriptp = &script->evalHashLink();
}
return NULL;
}
// There are two things we want to do with each script executed in EvalKernel:
// 1. notify jsdbgapi about script creation/destruction
// 2. add the script to the eval cache when EvalKernel is finished
//
// NB: Although the eval cache keeps a script alive wrt to the JS engine, from
// a jsdbgapi user's perspective, we want each eval() to create and destroy a
// script. This hides implementation details and means we don't have to deal
// with calls to JS_GetScriptObject for scripts in the eval cache (currently,
// script->object aliases script->evalHashLink()).
class EvalScriptGuard
{
JSContext *cx_;
JSLinearString *str_;
JSScript **bucket_;
Rooted<JSScript*> script_;
public:
EvalScriptGuard(JSContext *cx, JSLinearString *str)
: cx_(cx),
str_(str),
script_(cx) {
bucket_ = cx->runtime->evalCache.bucket(str);
}
~EvalScriptGuard() {
if (script_) {
CallDestroyScriptHook(cx_->runtime->defaultFreeOp(), script_);
script_->isActiveEval = false;
script_->isCachedEval = true;
script_->evalHashLink() = *bucket_;
*bucket_ = script_;
}
}
void lookupInEvalCache(StackFrame *caller, unsigned staticLevel,
JSPrincipals *principals, JSObject &scopeobj) {
if (JSScript *found = EvalCacheLookup(cx_, str_, caller, staticLevel,
principals, scopeobj, bucket_)) {
js_CallNewScriptHook(cx_, found, NULL);
script_ = found;
script_->isCachedEval = false;
script_->isActiveEval = true;
}
}
void setNewScript(JSScript *script) {
// JSScript::initFromEmitter has already called js_CallNewScriptHook.
JS_ASSERT(!script_ && script);
script_ = script;
script_->isActiveEval = true;
}
bool foundScript() {
return !!script_;
}
JSScript *script() const {
JS_ASSERT(script_);
return script_;
}
};
// Define subset of ExecuteType so that casting performs the injection.
enum EvalType { DIRECT_EVAL = EXECUTE_DIRECT_EVAL, INDIRECT_EVAL = EXECUTE_INDIRECT_EVAL };
// Common code implementing direct and indirect eval.
//
// Evaluate call.argv[2], if it is a string, in the context of the given calling
// frame, with the provided scope chain, with the semantics of either a direct
// or indirect eval (see ES5 10.4.2). If this is an indirect eval, scopeobj
// must be a global object.
//
// On success, store the completion value in call.rval and return true.
static bool
EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, StackFrame *caller,
HandleObject scopeobj)
{
JS_ASSERT((evalType == INDIRECT_EVAL) == (caller == NULL));
JS_ASSERT_IF(evalType == INDIRECT_EVAL, scopeobj->isGlobal());
AssertInnerizedScopeChain(cx, *scopeobj);
if (!scopeobj->global().isRuntimeCodeGenEnabled(cx)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CSP_BLOCKED_EVAL);
return false;
}
// ES5 15.1.2.1 step 1.
if (args.length() < 1) {
args.rval().setUndefined();
return true;
}
if (!args[0].isString()) {
args.rval() = args[0];
return true;
}
JSString *str = args[0].toString();
// ES5 15.1.2.1 steps 2-8.
// Per ES5, indirect eval runs in the global scope. (eval is specified this
// way so that the compiler can make assumptions about what bindings may or
// may not exist in the current frame if it doesn't see 'eval'.)
unsigned staticLevel;
RootedValue thisv(cx);
if (evalType == DIRECT_EVAL) {
staticLevel = caller->script()->staticLevel + 1;
// Direct calls to eval are supposed to see the caller's |this|. If we
// haven't wrapped that yet, do so now, before we make a copy of it for
// the eval code to use.
if (!ComputeThis(cx, caller))
return false;
thisv = caller->thisValue();
#ifdef DEBUG
jsbytecode *callerPC = caller->pcQuadratic(cx);
JS_ASSERT(callerPC && JSOp(*callerPC) == JSOP_EVAL);
#endif
} else {
JS_ASSERT(args.callee().global() == *scopeobj);
staticLevel = 0;
// Use the global as 'this', modulo outerization.
JSObject *thisobj = scopeobj->thisObject(cx);
if (!thisobj)
return false;
thisv = ObjectValue(*thisobj);
}
Rooted<JSLinearString*> linearStr(cx, str->ensureLinear(cx));
if (!linearStr)
return false;
const jschar *chars = linearStr->chars();
size_t length = linearStr->length();
SkipRoot skip(cx, &chars);
// If the eval string starts with '(' or '[' and ends with ')' or ']', it may be JSON.
// Try the JSON parser first because it's much faster. If the eval string
// isn't JSON, JSON parsing will probably fail quickly, so little time
// will be lost.
//
// Don't use the JSON parser if the caller is strict mode code, because in
// strict mode object literals must not have repeated properties, and the
// JSON parser cheerfully (and correctly) accepts them. If you're parsing
// JSON with eval and using strict mode, you deserve to be slow.
if (length > 2 &&
((chars[0] == '[' && chars[length - 1] == ']') ||
(chars[0] == '(' && chars[length - 1] == ')')) &&
(!caller || !caller->script()->strictModeCode))
{
// Remarkably, JavaScript syntax is not a superset of JSON syntax:
// strings in JavaScript cannot contain the Unicode line and paragraph
// terminator characters U+2028 and U+2029, but strings in JSON can.
// Rather than force the JSON parser to handle this quirk when used by
// eval, we simply don't use the JSON parser when either character
// appears in the provided string. See bug 657367.
for (const jschar *cp = &chars[1], *end = &chars[length - 2]; ; cp++) {
if (*cp == 0x2028 || *cp == 0x2029)
break;
if (cp == end) {
bool isArray = (chars[0] == '[');
JSONParser parser(cx, isArray ? chars : chars + 1, isArray ? length : length - 2,
JSONParser::StrictJSON, JSONParser::NoError);
Value tmp;
if (!parser.parse(&tmp))
return false;
if (tmp.isUndefined())
break;
args.rval() = tmp;
return true;
}
}
}
EvalScriptGuard esg(cx, linearStr);
JSPrincipals *principals = PrincipalsForCompiledCode(args, cx);
if (evalType == DIRECT_EVAL && caller->isNonEvalFunctionFrame())
esg.lookupInEvalCache(caller, staticLevel, principals, *scopeobj);
if (!esg.foundScript()) {
unsigned lineno;
const char *filename;
JSPrincipals *originPrincipals;
CurrentScriptFileLineOrigin(cx, &filename, &lineno, &originPrincipals,
evalType == DIRECT_EVAL ? CALLED_FROM_JSOP_EVAL
: NOT_CALLED_FROM_JSOP_EVAL);
bool compileAndGo = true;
bool noScriptRval = false;
bool needScriptGlobal = false;
JSScript *compiled = frontend::CompileScript(cx, scopeobj, caller,
principals, originPrincipals,
compileAndGo, noScriptRval, needScriptGlobal,
chars, length, filename,
lineno, cx->findVersion(), linearStr,
staticLevel);
if (!compiled)
return false;
esg.setNewScript(compiled);
}
return ExecuteKernel(cx, esg.script(), *scopeobj, thisv, ExecuteType(evalType),
NULL /* evalInFrame */, &args.rval());
}
// We once supported a second argument to eval to use as the scope chain
// when evaluating the code string. Warn when such uses are seen so that
// authors will know that support for eval(s, o) has been removed.
static inline bool
WarnOnTooManyArgs(JSContext *cx, const CallArgs &args)
{
if (args.length() > 1) {
if (JSScript *script = cx->stack.currentScript()) {
if (!script->warnedAboutTwoArgumentEval) {
static const char TWO_ARGUMENT_WARNING[] =
"Support for eval(code, scopeObject) has been removed. "
"Use |with (scopeObject) eval(code);| instead.";
if (!JS_ReportWarning(cx, TWO_ARGUMENT_WARNING))
return false;
script->warnedAboutTwoArgumentEval = true;
}
} else {
// In the case of an indirect call without a caller frame, avoid a
// potential warning-flood by doing nothing.
}
}
return true;
}
JSBool
js::IndirectEval(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (!WarnOnTooManyArgs(cx, args))
return false;
Rooted<GlobalObject*> global(cx, &args.callee().global());
return EvalKernel(cx, args, INDIRECT_EVAL, NULL, global);
}
bool
js::DirectEval(JSContext *cx, const CallArgs &args)
{
// Direct eval can assume it was called from an interpreted frame.
StackFrame *caller = cx->fp();
JS_ASSERT(caller->isScriptFrame());
JS_ASSERT(IsBuiltinEvalForScope(caller->scopeChain(), args.calleev()));
JS_ASSERT(JSOp(*cx->regs().pc) == JSOP_EVAL);
AutoFunctionCallProbe callProbe(cx, args.callee().toFunction(), caller->script());
if (!WarnOnTooManyArgs(cx, args))
return false;
return EvalKernel(cx, args, DIRECT_EVAL, caller, caller->scopeChain());
}
bool
js::IsBuiltinEvalForScope(JSObject *scopeChain, const Value &v)
{
return scopeChain->global().getOriginalEval() == v;
}
bool
js::IsAnyBuiltinEval(JSFunction *fun)
{
return fun->maybeNative() == IndirectEval;
}
JSPrincipals *
js::PrincipalsForCompiledCode(const CallReceiver &call, JSContext *cx)
{
JS_ASSERT(IsAnyBuiltinEval(call.callee().toFunction()) ||
IsBuiltinFunctionConstructor(call.callee().toFunction()));
// To compute the principals of the compiled eval/Function code, we simply
// use the callee's principals. To see why the caller's principals are
// ignored, consider first that, in the capability-model we assume, the
// high-privileged eval/Function should never have escaped to the
// low-privileged caller. (For the Mozilla embedding, this is brute-enforced
// by explicit filtering by wrappers.) Thus, the caller's privileges should
// subsume the callee's.
//
// In the converse situation, where the callee has lower privileges than the
// caller, we might initially guess that the caller would want to retain
// their higher privileges in the generated code. However, since the
// compiled code will be run with the callee's scope chain, this would make
// fp->script()->compartment() != fp->compartment().
return call.callee().principals(cx);
}

View File

@ -1,44 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=99 ft=cpp:
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "vm/Stack.h"
#ifndef Eval_h__
#define Eval_h__
namespace js {
// The C++ native for 'eval' (ES5 15.1.2.1). The function is named "indirect
// eval" because "direct eval" calls (as defined by the spec) will emit
// JSOP_EVAL which in turn calls DirectEval. Thus, even though IndirectEval is
// the callee function object for *all* calls to eval, it is by construction
// only ever called in the case indirect eval.
extern JSBool
IndirectEval(JSContext *cx, unsigned argc, Value *vp);
// Performs a direct eval for the given arguments, which must correspond to the
// currently-executing stack frame, which must be a script frame. On completion
// the result is returned in args.rval.
extern bool
DirectEval(JSContext *cx, const CallArgs &args);
// True iff 'v' is the built-in eval function for the global object that
// corresponds to 'scopeChain'.
extern bool
IsBuiltinEvalForScope(JSObject *scopeChain, const Value &v);
// True iff fun is a built-in eval function.
extern bool
IsAnyBuiltinEval(JSFunction *fun);
// Return the principals to assign to code compiled for a call to
// eval or the Function constructor.
extern JSPrincipals *
PrincipalsForCompiledCode(const CallReceiver &call, JSContext *cx);
} // namespace js
#endif // Eval_h__

View File

@ -1,38 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef Iterator_inl_h_
#define Iterator_inl_h_
#include "jsiter.h"
#include "jsobjinlines.h"
inline bool
JSObject::isPropertyIterator() const
{
return hasClass(&js::PropertyIteratorObject::class_);
}
inline js::PropertyIteratorObject &
JSObject::asPropertyIterator()
{
JS_ASSERT(isPropertyIterator());
return *static_cast<js::PropertyIteratorObject *>(this);
}
js::NativeIterator *
js::PropertyIteratorObject::getNativeIterator() const
{
JS_ASSERT(isPropertyIterator());
return static_cast<js::NativeIterator *>(getPrivate());
}
inline void
js::PropertyIteratorObject::setNativeIterator(js::NativeIterator *ni)
{
setPrivate(ni);
}
#endif // Iterator_inl_h_

View File

@ -5,6 +5,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/FloatingPoint.h"
#include "builtin/MapObject.h"
#include "jscntxt.h"
@ -12,7 +14,6 @@
#include "jsobj.h"
#include "gc/Marking.h"
#include "js/Utility.h"
#include "vm/GlobalObject.h"
#include "vm/MethodGuard.h"
#include "vm/Stack.h"
@ -21,622 +22,26 @@
using namespace js;
/*** OrderedHashTable ****************************************************************************/
/*
* Define two collection templates, js::OrderedHashMap and js::OrderedHashSet.
* They are like js::HashMap and js::HashSet except that:
*
* - Iterating over an Ordered hash table visits the entries in the order in
* which they were inserted. This means that unlike a HashMap, the behavior
* of an OrderedHashMap is deterministic (as long as the HashPolicy methods
* are effect-free and consistent); the hashing is a pure performance
* optimization.
*
* - Range objects over Ordered tables remain valid even when entries are
* added or removed or the table is resized. (However in the case of
* removing entries, note the warning on class Range below.)
*
* - The API is a little different, so it's not a drop-in replacement.
* In particular, the hash policy is a little different.
* Also, the Ordered templates lack the Ptr and AddPtr types.
*
* Hash policies
*
* See the comment about "Hash policy" in HashTable.h for general features that
* hash policy classes must provide. Hash policies for OrderedHashMaps and Sets
* must additionally provide a distinguished "empty" key value and the
* following static member functions:
* bool isEmpty(const Key &);
* void makeEmpty(Key *);
*/
namespace js {
namespace detail {
/*
* detail::OrderedHashTable is the underlying data structure used to implement both
* OrderedHashMap and OrderedHashSet. Programs should use one of those two
* templates rather than OrderedHashTable.
*/
template <class T, class Ops, class AllocPolicy>
class OrderedHashTable
static JSObject *
InitClass(JSContext *cx, Handle<GlobalObject*> global, Class *clasp, JSProtoKey key, Native construct,
JSFunctionSpec *methods)
{
public:
typedef typename Ops::KeyType Key;
typedef typename Ops::Lookup Lookup;
RootedObject proto(cx, global->createBlankPrototype(cx, clasp));
if (!proto)
return NULL;
proto->setPrivate(NULL);
struct Data
JSAtom *atom = cx->runtime->atomState.classAtoms[key];
RootedFunction ctor(cx, global->createConstructor(cx, construct, atom, 1));
if (!ctor ||
!LinkConstructorAndPrototype(cx, ctor, proto) ||
!DefinePropertiesAndBrand(cx, proto, NULL, methods) ||
!DefineConstructorAndPrototype(cx, global, key, ctor, proto))
{
T element;
Data *chain;
Data(const T &e, Data *c) : element(e), chain(c) {}
Data(MoveRef<T> e, Data *c) : element(e), chain(c) {}
};
class Range;
friend class Range;
private:
Data **hashTable; // power-of-2-sized hash table
Data *data; // data vector, an array of Data objects
// data[0:dataLength] are constructed
uint32_t dataLength; // number of constructed elements in data
uint32_t dataCapacity; // size of data, in elements
uint32_t liveCount; // dataLength less empty (removed) entries
uint32_t hashTableMask; // size of hashTable, in elements, minus one
Range *ranges; // list of all live Ranges on this table
AllocPolicy alloc;
public:
OrderedHashTable(AllocPolicy &ap)
: hashTable(NULL), data(NULL), dataLength(0), ranges(NULL), alloc(ap) {}
bool init() {
MOZ_ASSERT(!hashTable, "init must be called at most once");
uint32_t buckets = initialBuckets();
Data **tableAlloc = static_cast<Data **>(alloc.malloc_(buckets * sizeof(Data *)));
if (!tableAlloc)
return false;
for (uint32_t i = 0; i < buckets; i++)
tableAlloc[i] = NULL;
uint32_t capacity = uint32_t(buckets * fillFactor());
Data *dataAlloc = static_cast<Data *>(alloc.malloc_(capacity * sizeof(Data)));
if (!dataAlloc) {
alloc.free_(tableAlloc);
return false;
}
hashTable = tableAlloc;
data = dataAlloc;
dataLength = 0;
dataCapacity = capacity;
liveCount = 0;
hashTableMask = buckets - 1;
return true;
}
~OrderedHashTable() {
alloc.free_(hashTable);
freeData(data, dataLength);
}
/* Return the number of elements in the table. */
uint32_t count() const { return liveCount; }
/* True if any element matches l. */
bool has(const Lookup &l) const {
return lookup(l) != NULL;
}
/* Return a pointer to the element, if any, that matches l, else NULL. */
T *get(const Lookup &l) {
Data *e = lookup(l, prepareHash(l));
return e ? &e->element : NULL;
}
/* Return a pointer to the element, if any, that matches l, else NULL. */
const T *get(const Lookup &l) const {
return const_cast<OrderedHashTable *>(this)->get(l);
}
/*
* If the table already contains an entry that matches |element|,
* replace that entry with |element|. Otherwise add a new entry.
*
* On success, return true, whether there was already a matching element or
* not. On allocation failure, return false. If this returns false, it
* means the element was not added to the table.
*/
bool put(const T &element) {
HashNumber h = prepareHash(Ops::getKey(element));
if (Data *e = lookup(Ops::getKey(element), h)) {
e->element = element;
return true;
}
if (dataLength == dataCapacity) {
// If the hashTable is more than 1/4 deleted data, simply rehash in
// place to free up some space. Otherwise, grow the table.
uint32_t newMask = liveCount >= dataCapacity * 0.75
? (hashTableMask << 1) | 1
: hashTableMask;
if (!rehash(newMask))
return false;
}
h &= hashTableMask;
liveCount++;
Data *e = &data[dataLength++];
new (e) Data(element, hashTable[h]);
hashTable[h] = e;
return true;
}
/*
* If the table contains an element matching l, remove it and set *foundp
* to true. Otherwise set *foundp to false.
*
* Return true on success, false if we tried to shrink the table and hit an
* allocation failure. Even if this returns false, *foundp is set correctly
* and the matching element was removed. Shrinking is an optimization and
* it's OK for it to fail.
*/
bool remove(const Lookup &l, bool *foundp) {
// Note: This could be optimized so that removing the last entry,
// data[dataLength - 1], decrements dataLength. LIFO use cases would
// benefit.
// If a matching entry exists, empty it.
Data *e = lookup(l, prepareHash(l));
if (e == NULL) {
*foundp = false;
return true;
}
*foundp = true;
liveCount--;
Ops::makeEmpty(&e->element);
// Update active Ranges.
uint32_t pos = e - data;
for (Range *r = ranges; r; r = r->next)
r->onRemove(pos);
// If many entries have been removed, try to shrink the table.
if (hashTableMask > initialBuckets() && liveCount < dataLength * minDataFill()) {
if (!rehash(hashTableMask >> 1))
return false;
}
return true;
}
/*
* Ranges are used to iterate over OrderedHashTables.
*
* Suppose 'Map' is some instance of OrderedHashMap, and 'map' is a Map.
* Then you can walk all the key-value pairs like this:
*
* for (Map::Range r = map.all(); !r.empty(); r.popFront()) {
* Map::Entry &pair = r.front();
* ... do something with pair ...
* }
*
* Ranges remain valid for the lifetime of the OrderedHashTable, even if
* entries are added or removed or the table is resized.
*
* Warning: The behavior when the current front() entry is removed from the
* table is subtly different from js::HashTable<>::Enum::removeFront()!
* HashTable::Enum doesn't skip any entries when you removeFront() and then
* popFront(). OrderedHashTable::Range does! (This is useful for using a
* Range to implement JS Map.prototype.iterator.)
*
* The workaround is to call popFront() as soon as possible,
* before there's any possibility of modifying the table:
*
* for (Map::Range r = map.all(); !r.empty(); ) {
* Key key = r.front().key; // this won't modify map
* Value val = r.front().value; // this won't modify map
* r.popFront();
* // ...do things that might modify map...
* }
*/
class Range {
friend class OrderedHashTable;
OrderedHashTable &ht;
/* The index of front() within ht.data. */
uint32_t i;
/*
* The number of nonempty entries in ht.data to the left of front().
* This is used when the table is resized or compacted.
*/
uint32_t count;
/*
* Links in the doubly-linked list of active Ranges on ht.
*
* prevp points to the previous Range's .next field;
* or to ht.ranges if this is the first Range in the list.
* next points to the next Range;
* or NULL if this is the last Range in the list.
*
* Invariant: *prevp == this.
*/
Range **prevp;
Range *next;
/*
* Create a Range over all the entries in ht.
* (This is private on purpose. End users must use ht.all().)
*/
Range(OrderedHashTable &ht) : ht(ht), i(0), count(0), prevp(&ht.ranges), next(ht.ranges) {
*prevp = this;
if (next)
next->prevp = &next;
seek();
}
public:
Range(const Range &other)
: ht(other.ht), i(other.i), count(other.count), prevp(&ht.ranges), next(ht.ranges)
{
*prevp = this;
if (next)
next->prevp = &next;
}
~Range() {
*prevp = next;
if (next)
next->prevp = prevp;
}
private:
// Prohibit copy assignment.
Range &operator=(const Range &other) MOZ_DELETE;
void seek() {
while (i < ht.dataLength && Ops::isEmpty(Ops::getKey(ht.data[i].element)))
i++;
}
/*
* The hash table calls this when an entry is removed.
* j is the index of the removed entry.
*/
void onRemove(uint32_t j) {
if (j < i)
count--;
if (j == i)
seek();
}
/*
* The hash table calls this when the table is resized or compacted.
* Since |count| is the number of nonempty entries to the left of
* front(), discarding the empty entries will not affect count, and it
* will make i and count equal.
*/
void onCompact() {
i = count;
}
public:
bool empty() const { return i >= ht.dataLength; }
/*
* Return the first element in the range. This must not be called if
* this->empty().
*
* Warning: Removing an entry from the table also removes it from any
* live Ranges, and a Range can become empty that way, rendering
* front() invalid. If in doubt, check empty() before calling front().
*/
T &front() {
MOZ_ASSERT(!empty());
return ht.data[i].element;
}
/*
* Remove the first element from this range.
* This must not be called if this->empty().
*
* Warning: Removing an entry from the table also removes it from any
* live Ranges, and a Range can become empty that way, rendering
* popFront() invalid. If in doubt, check empty() before calling
* popFront().
*/
void popFront() {
MOZ_ASSERT(!empty());
MOZ_ASSERT(!Ops::isEmpty(Ops::getKey(ht.data[i].element)));
count++;
i++;
seek();
}
/*
* Change the key of the front entry.
*
* This calls Ops::hash on both the current key and the new key.
* Ops::hash on the current key must return the same hash code as
* when the entry was added to the table.
*/
void rekeyFront(const Key &k) {
Data &entry = ht.data[i];
HashNumber oldHash = prepareHash(Ops::getKey(entry.element)) & ht.hashTableMask;
HashNumber newHash = prepareHash(k) & ht.hashTableMask;
Ops::setKey(entry.element, k);
if (newHash != oldHash) {
// Remove this entry from its old hash chain. (If this crashes
// reading NULL, it would mean we did not find this entry on
// the hash chain where we expected it. That probably means the
// key's hash code changed since it was inserted, breaking the
// hash code invariant.)
Data **ep = &ht.hashTable[oldHash];
while (*ep != &entry)
ep = &(*ep)->chain;
*ep = entry.chain;
// Add it to the new hash chain. We could just insert it at the
// beginning of the chain. Instead, we do a bit of work to
// preserve the invariant that hash chains always go in reverse
// insertion order (descending memory order). No code currently
// depends on this invariant, so it's fine to kill it if
// needed.
ep = &ht.hashTable[newHash];
while (*ep && *ep > &entry)
ep = &(*ep)->chain;
entry.chain = *ep;
*ep = &entry;
}
}
/*
* Change the key of the front entry without calling Ops::hash on the
* entry's current key. The caller must ensure that k has the same hash
* code that the current key had when it was inserted.
*/
void rekeyFrontWithSameHashCode(const Key &k) {
#ifdef DEBUG
// Assert that k falls in the same hash bucket as the old key.
HashNumber h = Ops::hash(k) & ht.hashTableMask;
Data *e = ht.hashTable[h];
while (e && e != &ht.data[i])
e = e->chain;
JS_ASSERT(e == &ht.data[i]);
#endif
Ops::setKey(ht.data[i].element, k);
}
};
Range all() { return Range(*this); }
private:
/*
* The number of buckets in the hash table initially.
* This must be a power of two.
*/
static uint32_t initialBuckets() { return 2; }
/*
* The maximum load factor (mean number of entries per bucket).
* It is an invariant that
* dataCapacity == floor((hashTableMask + 1) * fillFactor()).
*
* The fill factor should be between 2 and 4, and it should be chosen so that
* the fill factor times sizeof(Data) is close to but <= a power of 2.
* This fixed fill factor was chosen to make the size of the data
* array, in bytes, close to a power of two when sizeof(T) is 16.
*/
static double fillFactor() { return 8.0 / 3.0; }
/*
* The minimum permitted value of (liveCount / dataLength).
* If that ratio drops below this value, we shrink the table.
*/
static double minDataFill() { return 0.25; }
static HashNumber prepareHash(const Lookup &l) {
return ScrambleHashCode(Ops::hash(l));
}
void freeData(Data *data, uint32_t length) {
for (Data *p = data + length; p != data; )
(--p)->~Data();
alloc.free_(data);
}
Data *lookup(const Lookup &l, HashNumber h) {
for (Data *e = hashTable[h & hashTableMask]; e; e = e->chain) {
if (Ops::match(Ops::getKey(e->element), l))
return e;
}
return NULL;
}
const Data *lookup(const Lookup &l) const {
return const_cast<OrderedHashTable *>(this)->lookup(l, prepareHash(l));
}
/* This is called after rehashing the table. */
void compacted() {
// If we had any empty entries, compacting may have moved live entries
// to the left within |data|. Notify all live Ranges of the change.
for (Range *r = ranges; r; r = r->next)
r->onCompact();
}
/* Compact the entries in |data| and rehash them. */
void rehashInPlace() {
for (uint32_t i = 0; i <= hashTableMask; i++)
hashTable[i] = NULL;
Data *wp = data, *end = data + dataLength;
for (Data *rp = data; rp != end; rp++) {
if (!Ops::isEmpty(Ops::getKey(rp->element))) {
HashNumber h = prepareHash(Ops::getKey(rp->element)) & hashTableMask;
if (rp != wp)
wp->element = Move(rp->element);
wp->chain = hashTable[h];
hashTable[h] = wp;
wp++;
}
}
MOZ_ASSERT(wp == data + liveCount);
while (wp != end)
(--end)->~Data();
dataLength = liveCount;
compacted();
}
/*
* Grow, shrink, or compact both |hashTable| and |data|.
*
* On success, this returns true, dataLength == liveCount, and there are no
* empty elements in data[0:dataLength]. On allocation failure, this
* leaves everything as it was and returns false.
*/
bool rehash(uint32_t newMask) {
// If the size of the table is not changing, rehash in place to avoid
// allocating memory.
if (newMask == hashTableMask) {
rehashInPlace();
return true;
}
Data **newHashTable = static_cast<Data **>(alloc.malloc_((newMask + 1) * sizeof(Data *)));
if (!newHashTable)
return false;
for (uint32_t i = 0; i <= newMask; i++)
newHashTable[i] = NULL;
uint32_t newCapacity = uint32_t((newMask + 1) * fillFactor());
Data *newData = static_cast<Data *>(alloc.malloc_(newCapacity * sizeof(Data)));
if (!newData) {
alloc.free_(newHashTable);
return false;
}
Data *wp = newData;
for (Data *p = data, *end = data + dataLength; p != end; p++) {
if (!Ops::isEmpty(Ops::getKey(p->element))) {
HashNumber h = prepareHash(Ops::getKey(p->element)) & newMask;
new (wp) Data(Move(p->element), newHashTable[h]);
newHashTable[h] = wp;
wp++;
}
}
MOZ_ASSERT(wp == newData + liveCount);
alloc.free_(hashTable);
freeData(data, dataLength);
hashTable = newHashTable;
data = newData;
dataLength = liveCount;
dataCapacity = newCapacity;
hashTableMask = newMask;
compacted();
return true;
}
// Not copyable.
OrderedHashTable &operator=(const OrderedHashTable &) MOZ_DELETE;
OrderedHashTable(const OrderedHashTable &) MOZ_DELETE;
};
} // namespace detail
template <class Key, class Value, class OrderedHashPolicy, class AllocPolicy>
class OrderedHashMap
{
public:
class Entry
{
template <class, class, class> friend class detail::OrderedHashTable;
void operator=(const Entry &rhs) {
const_cast<Key &>(key) = rhs.key;
value = rhs.value;
}
void operator=(MoveRef<Entry> rhs) {
const_cast<Key &>(key) = Move(rhs->key);
value = Move(rhs->value);
}
public:
Entry() : key(), value() {}
Entry(const Key &k, const Value &v) : key(k), value(v) {}
Entry(MoveRef<Entry> rhs) : key(Move(rhs->key)), value(Move(rhs->value)) {}
const Key key;
Value value;
};
private:
struct MapOps : OrderedHashPolicy
{
typedef Key KeyType;
static void makeEmpty(Entry *e) {
OrderedHashPolicy::makeEmpty(const_cast<Key *>(&e->key));
}
static const Key &getKey(const Entry &e) { return e.key; }
static void setKey(Entry &e, const Key &k) { const_cast<Key &>(e.key) = k; }
};
typedef detail::OrderedHashTable<Entry, MapOps, AllocPolicy> Impl;
Impl impl;
public:
typedef typename Impl::Range Range;
OrderedHashMap(AllocPolicy ap = AllocPolicy()) : impl(ap) {}
bool init() { return impl.init(); }
uint32_t count() const { return impl.count(); }
bool has(const Key &key) const { return impl.has(key); }
Range all() { return impl.all(); }
const Entry *get(const Key &key) const { return impl.get(key); }
Entry *get(const Key &key) { return impl.get(key); }
bool put(const Key &key, const Value &value) { return impl.put(Entry(key, value)); }
bool remove(const Key &key, bool *foundp) { return impl.remove(key, foundp); }
};
template <class T, class OrderedHashPolicy, class AllocPolicy>
class OrderedHashSet
{
private:
struct SetOps : OrderedHashPolicy
{
typedef const T KeyType;
static const T &getKey(const T &v) { return v; }
static void setKey(const T &e, const T &v) { const_cast<T &>(e) = v; }
};
typedef detail::OrderedHashTable<T, SetOps, AllocPolicy> Impl;
Impl impl;
public:
typedef typename Impl::Range Range;
OrderedHashSet(AllocPolicy ap = AllocPolicy()) : impl(ap) {}
bool init() { return impl.init(); }
uint32_t count() const { return impl.count(); }
bool has(const T &value) const { return impl.has(value); }
Range all() { return impl.all(); }
bool put(const T &value) { return impl.put(value); }
bool remove(const T &value, bool *foundp) { return impl.remove(value, foundp); }
};
} // namespace js
return proto;
}
/*** HashableValue *******************************************************************************/
@ -745,27 +150,6 @@ JSFunctionSpec MapObject::methods[] = {
JS_FS_END
};
static JSObject *
InitClass(JSContext *cx, Handle<GlobalObject*> global, Class *clasp, JSProtoKey key, Native construct,
JSFunctionSpec *methods)
{
Rooted<JSObject*> proto(cx, global->createBlankPrototype(cx, clasp));
if (!proto)
return NULL;
proto->setPrivate(NULL);
JSAtom *atom = cx->runtime->atomState.classAtoms[key];
Rooted<JSFunction*> ctor(cx, global->createConstructor(cx, construct, atom, 1));
if (!ctor ||
!LinkConstructorAndPrototype(cx, ctor, proto) ||
!DefinePropertiesAndBrand(cx, proto, NULL, methods) ||
!DefineConstructorAndPrototype(cx, global, key, ctor, proto))
{
return NULL;
}
return proto;
}
JSObject *
MapObject::initClass(JSContext *cx, JSObject *obj)
{
@ -773,41 +157,14 @@ MapObject::initClass(JSContext *cx, JSObject *obj)
return InitClass(cx, global, &class_, JSProto_Map, construct, methods);
}
template <class Range>
static void
MarkKey(Range &r, const HashableValue &key, JSTracer *trc)
{
HashableValue newKey = key.mark(trc);
if (newKey.get() != key.get()) {
if (newKey.get().isString()) {
// GC moved a string. The key stored in the OrderedHashTable must
// be updated to point to the string's new location, but rekeyFront
// would not work because it would access the string's old
// location.
//
// So as a specially gruesome hack, overwrite the key in place.
// FIXME bug 769504.
r.rekeyFrontWithSameHashCode(newKey);
} else {
// GC moved an object. It must be rekeyed, and rekeying is safe
// because the old key's hash() method is still safe to call (it
// does not access the object's old location).
JS_ASSERT(newKey.get().isObject());
r.rekeyFront(newKey);
}
}
}
void
MapObject::mark(JSTracer *trc, JSObject *obj)
{
MapObject *mapobj = static_cast<MapObject *>(obj);
if (ValueMap *map = mapobj->getData()) {
for (ValueMap::Range r = map->all(); !r.empty(); r.popFront()) {
MarkKey(r, r.front().key, trc);
gc::MarkValue(trc, &r.front().value, "value");
for (ValueMap::Enum iter(*map); !iter.empty(); iter.popFront()) {
gc::MarkValue(trc, &iter.front().value, "value");
iter.rekeyFront(iter.front().key.mark(trc));
}
}
}
@ -857,8 +214,7 @@ MapObject::construct(JSContext *cx, unsigned argc, Value *vp)
if (!pairobj->getElement(cx, 1, &val))
return false;
RelocatableValue rval(val);
if (!map->put(hkey, rval)) {
if (!map->put(hkey, val)) {
js_ReportOutOfMemory(cx);
return false;
}
@ -908,7 +264,8 @@ MapObject::get(JSContext *cx, unsigned argc, Value *vp)
{
THIS_MAP(get, cx, argc, vp, args, map);
ARG0_KEY(cx, args, key);
if (ValueMap::Entry *p = map.get(key))
if (ValueMap::Ptr p = map.lookup(key))
args.rval() = p->value;
else
args.rval().setUndefined();
@ -920,7 +277,7 @@ MapObject::has(JSContext *cx, unsigned argc, Value *vp)
{
THIS_MAP(has, cx, argc, vp, args, map);
ARG0_KEY(cx, args, key);
args.rval().setBoolean(map.has(key));
args.rval().setBoolean(map.lookup(key));
return true;
}
@ -929,8 +286,7 @@ MapObject::set(JSContext *cx, unsigned argc, Value *vp)
{
THIS_MAP(set, cx, argc, vp, args, map);
ARG0_KEY(cx, args, key);
RelocatableValue rval(args.length() > 1 ? args[1] : UndefinedValue());
if (!map.put(key, rval)) {
if (!map.put(key, args.length() > 1 ? args[1] : UndefinedValue())) {
js_ReportOutOfMemory(cx);
return false;
}
@ -943,9 +299,10 @@ MapObject::delete_(JSContext *cx, unsigned argc, Value *vp)
{
THIS_MAP(delete_, cx, argc, vp, args, map);
ARG0_KEY(cx, args, key);
bool found;
if (!map.remove(key, &found))
return false;
ValueMap::Ptr p = map.lookup(key);
bool found = p.found();
if (found)
map.remove(p);
args.rval().setBoolean(found);
return true;
}
@ -998,8 +355,8 @@ SetObject::mark(JSTracer *trc, JSObject *obj)
{
SetObject *setobj = static_cast<SetObject *>(obj);
if (ValueSet *set = setobj->getData()) {
for (ValueSet::Range r = set->all(); !r.empty(); r.popFront())
MarkKey(r, r.front(), trc);
for (ValueSet::Enum iter(*set); !iter.empty(); iter.popFront())
iter.rekeyFront(iter.front().mark(trc));
}
}
@ -1086,9 +443,10 @@ SetObject::delete_(JSContext *cx, unsigned argc, Value *vp)
{
THIS_SET(delete_, cx, argc, vp, args, set);
ARG0_KEY(cx, args, key);
bool found;
if (!set.remove(key, &found))
return false;
ValueSet::Ptr p = set.lookup(key);
bool found = p.found();
if (found)
set.remove(p);
args.rval().setBoolean(found);
return true;
}

View File

@ -12,7 +12,7 @@
#include "jscntxt.h"
#include "jsobj.h"
#include "mozilla/FloatingPoint.h"
#include "js/HashTable.h"
namespace js {
@ -32,8 +32,6 @@ class HashableValue {
typedef HashableValue Lookup;
static HashNumber hash(const Lookup &v) { return v.hash(); }
static bool match(const HashableValue &k, const Lookup &l) { return k.equals(l); }
static bool isEmpty(const HashableValue &v) { return v.value.isMagic(JS_HASH_KEY_EMPTY); }
static void makeEmpty(HashableValue *vp) { vp->value = MagicValue(JS_HASH_KEY_EMPTY); }
};
HashableValue() : value(UndefinedValue()) {}
@ -42,7 +40,6 @@ class HashableValue {
HashNumber hash() const;
bool equals(const HashableValue &other) const;
HashableValue mark(JSTracer *trc) const;
Value get() const { return value.get(); }
class AutoRooter : private AutoGCRooter
{
@ -64,20 +61,13 @@ class HashableValue {
};
};
template <class Key, class Value, class OrderedHashPolicy, class AllocPolicy>
class OrderedHashMap;
template <class T, class OrderedHashPolicy, class AllocPolicy>
class OrderedHashSet;
typedef OrderedHashMap<HashableValue,
RelocatableValue,
HashableValue::Hasher,
RuntimeAllocPolicy> ValueMap;
typedef OrderedHashSet<HashableValue,
HashableValue::Hasher,
RuntimeAllocPolicy> ValueSet;
typedef HashMap<HashableValue,
RelocatableValue,
HashableValue::Hasher,
RuntimeAllocPolicy> ValueMap;
typedef HashSet<HashableValue,
HashableValue::Hasher,
RuntimeAllocPolicy> ValueSet;
class MapObject : public JSObject {
public:

File diff suppressed because it is too large Load Diff

View File

@ -239,19 +239,19 @@ EmitN(JSContext *cx, BytecodeEmitter *bce, JSOp op, size_t extra);
* value other than undefined if the constant was found, true with *vp set to
* JSVAL_VOID if not found, and false on error.
*/
bool
JSBool
DefineCompileTimeConstant(JSContext *cx, BytecodeEmitter *bce, JSAtom *atom, ParseNode *pn);
/*
* Emit code into bce for the tree rooted at pn.
*/
bool
JSBool
EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn);
/*
* Emit function code using bce for the tree rooted at body.
*/
bool
JSBool
EmitFunctionScript(JSContext *cx, BytecodeEmitter *bce, ParseNode *body);
} /* namespace frontend */
@ -421,7 +421,7 @@ NewSrcNote3(JSContext *cx, BytecodeEmitter *bce, SrcNoteType type, ptrdiff_t off
jssrcnote *
AddToSrcNoteDelta(JSContext *cx, BytecodeEmitter *bce, jssrcnote *sn, ptrdiff_t delta);
bool
JSBool
FinishTakingSrcNotes(JSContext *cx, BytecodeEmitter *bce, jssrcnote *notes);
void

View File

@ -72,7 +72,7 @@ ContainsVarOrConst(ParseNode *pn)
* Fold from one constant type to another.
* XXX handles only strings and numbers for now
*/
static bool
static JSBool
FoldType(JSContext *cx, ParseNode *pn, ParseNodeKind kind)
{
if (!pn->isKind(kind)) {
@ -81,7 +81,7 @@ FoldType(JSContext *cx, ParseNode *pn, ParseNodeKind kind)
if (pn->isKind(PNK_STRING)) {
double d;
if (!ToNumber(cx, StringValue(pn->pn_atom), &d))
return false;
return JS_FALSE;
pn->pn_dval = d;
pn->setKind(PNK_NUMBER);
pn->setOp(JSOP_DOUBLE);
@ -92,10 +92,10 @@ FoldType(JSContext *cx, ParseNode *pn, ParseNodeKind kind)
if (pn->isKind(PNK_NUMBER)) {
JSString *str = js_NumberToString(cx, pn->pn_dval);
if (!str)
return false;
return JS_FALSE;
pn->pn_atom = js_AtomizeString(cx, str);
if (!pn->pn_atom)
return false;
return JS_FALSE;
pn->setKind(PNK_STRING);
pn->setOp(JSOP_STRING);
}
@ -104,7 +104,7 @@ FoldType(JSContext *cx, ParseNode *pn, ParseNodeKind kind)
default:;
}
}
return true;
return JS_TRUE;
}
/*
@ -112,7 +112,7 @@ FoldType(JSContext *cx, ParseNode *pn, ParseNodeKind kind)
* one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
* a successful call to this function.
*/
static bool
static JSBool
FoldBinaryNumeric(JSContext *cx, JSOp op, ParseNode *pn1, ParseNode *pn2,
ParseNode *pn, Parser *parser)
{
@ -188,12 +188,12 @@ FoldBinaryNumeric(JSContext *cx, JSOp op, ParseNode *pn1, ParseNode *pn2,
pn->setOp(JSOP_DOUBLE);
pn->setArity(PN_NULLARY);
pn->pn_dval = d;
return true;
return JS_TRUE;
}
#if JS_HAS_XML_SUPPORT
static bool
static JSBool
FoldXMLConstants(JSContext *cx, ParseNode *pn, Parser *parser)
{
JS_ASSERT(pn->isArity(PN_LIST));
@ -238,20 +238,20 @@ FoldXMLConstants(JSContext *cx, ParseNode *pn, Parser *parser)
case PNK_XMLCDATA:
str = js_MakeXMLCDATAString(cx, pn2->pn_atom);
if (!str)
return false;
return JS_FALSE;
break;
case PNK_XMLCOMMENT:
str = js_MakeXMLCommentString(cx, pn2->pn_atom);
if (!str)
return false;
return JS_FALSE;
break;
case PNK_XMLPI: {
XMLProcessingInstruction &pi = pn2->asXMLProcessingInstruction();
str = js_MakeXMLPIString(cx, pi.target(), pi.data());
if (!str)
return false;
return JS_FALSE;
break;
}
@ -278,7 +278,7 @@ FoldXMLConstants(JSContext *cx, ParseNode *pn, Parser *parser)
pn1->setArity(PN_NULLARY);
pn1->pn_atom = js_AtomizeString(cx, accum);
if (!pn1->pn_atom)
return false;
return JS_FALSE;
JS_ASSERT(pnp != &pn1->pn_next);
*pnp = pn1;
}
@ -295,7 +295,7 @@ FoldXMLConstants(JSContext *cx, ParseNode *pn, Parser *parser)
: js_ConcatStrings(cx, accum, str);
}
if (!str)
return false;
return JS_FALSE;
#ifdef DEBUG_brendanXXX
printf("2: %d, %d => ", i, j);
FileEscapedString(stdout, str, 0);
@ -317,7 +317,7 @@ FoldXMLConstants(JSContext *cx, ParseNode *pn, Parser *parser)
if (str) {
accum = js_ConcatStrings(cx, accum, str);
if (!accum)
return false;
return JS_FALSE;
}
JS_ASSERT(*pnp == pn1);
@ -330,7 +330,7 @@ FoldXMLConstants(JSContext *cx, ParseNode *pn, Parser *parser)
pn1->setArity(PN_NULLARY);
pn1->pn_atom = js_AtomizeString(cx, accum);
if (!pn1->pn_atom)
return false;
return JS_FALSE;
JS_ASSERT(pnp != &pn1->pn_next);
*pnp = pn1;
}
@ -350,7 +350,7 @@ FoldXMLConstants(JSContext *cx, ParseNode *pn, Parser *parser)
pn->setOp(JSOP_TOXML);
}
}
return true;
return JS_TRUE;
}
#endif /* JS_HAS_XML_SUPPORT */

View File

@ -600,7 +600,7 @@ struct ParseNode {
} binary;
struct { /* one kid if unary */
ParseNode *kid;
bool hidden; /* hidden genexp-induced JSOP_YIELD
JSBool hidden; /* hidden genexp-induced JSOP_YIELD
or directive prologue member (as
pn_prologue) */
} unary;

View File

@ -403,7 +403,7 @@ HasFinalReturn(ParseNode *pn)
}
}
static bool
static JSBool
ReportBadReturn(JSContext *cx, Parser *parser, ParseNode *pn, Parser::Reporter reporter,
unsigned errnum, unsigned anonerrnum)
{
@ -417,7 +417,7 @@ ReportBadReturn(JSContext *cx, Parser *parser, ParseNode *pn, Parser::Reporter r
return (parser->*reporter)(pn, errnum, name.ptr());
}
static bool
static JSBool
CheckFinalReturn(JSContext *cx, Parser *parser, ParseNode *pn)
{
JS_ASSERT(parser->tc->sc->inFunction());
@ -919,13 +919,13 @@ js::DefineArg(ParseNode *pn, JSAtom *atom, unsigned i, Parser *parser)
* function is called indirectly from the variable declaration parser by way
* of CheckDestructuring and its friends.
*/
typedef bool
typedef JSBool
(*Binder)(JSContext *cx, BindData *data, JSAtom *atom, Parser *parser);
static bool
static JSBool
BindLet(JSContext *cx, BindData *data, JSAtom *atom, Parser *parser);
static bool
static JSBool
BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, Parser *parser);
struct BindData {
@ -961,7 +961,7 @@ struct BindData {
};
#if JS_HAS_DESTRUCTURING
static bool
static JSBool
BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom, Parser *parser)
{
TreeContext *tc = parser->tc;
@ -974,7 +974,7 @@ BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom, Parser *parser
*/
if (tc->decls.lookupFirst(atom)) {
parser->reportError(NULL, JSMSG_DESTRUCT_DUP_ARG);
return false;
return JS_FALSE;
}
ParseNode *pn = data->pn;
@ -1036,20 +1036,20 @@ Parser::newFunction(TreeContext *tc, JSAtom *atom, FunctionSyntaxKind kind)
return fun;
}
static bool
static JSBool
MatchOrInsertSemicolon(JSContext *cx, TokenStream *ts)
{
TokenKind tt = ts->peekTokenSameLine(TSF_OPERAND);
if (tt == TOK_ERROR)
return false;
return JS_FALSE;
if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
/* Advance the scanner for proper error location reporting. */
ts->getToken(TSF_OPERAND);
ts->reportError(JSMSG_SEMI_BEFORE_STMNT);
return false;
return JS_FALSE;
}
(void) ts->matchToken(TOK_SEMI);
return true;
return JS_TRUE;
}
static bool
@ -1959,7 +1959,7 @@ ReportRedeclaration(JSContext *cx, Parser *parser, ParseNode *pn, bool isConst,
* populate data->pn->pn_{op,cookie,defn,dflags}; and stash a pointer to
* data->pn in a slot of the block object.
*/
static bool
static JSBool
BindLet(JSContext *cx, BindData *data, JSAtom *atom, Parser *parser)
{
TreeContext *tc = parser->tc;
@ -2100,7 +2100,7 @@ BindFunctionLocal(JSContext *cx, BindData *data, MultiDeclRange &mdl, TreeContex
return true;
}
static bool
static JSBool
BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, Parser *parser)
{
TreeContext *tc = parser->tc;
@ -2131,14 +2131,14 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, Parser *parser)
if (dn_kind == Definition::ARG) {
JSAutoByteString name;
if (!js_AtomToPrintableString(cx, atom, &name))
return false;
return JS_FALSE;
if (op == JSOP_DEFCONST) {
parser->reportError(pn, JSMSG_REDECLARED_PARAM, name.ptr());
return false;
return JS_FALSE;
}
if (!parser->reportStrictWarning(pn, JSMSG_VAR_HIDES_ARG, name.ptr()))
return false;
return JS_FALSE;
} else {
bool error = (op == JSOP_DEFCONST ||
dn_kind == Definition::CONST ||
@ -2156,7 +2156,7 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, Parser *parser)
!(parser->*reporter)(pn, JSMSG_REDECLARED_VAR,
Definition::kindString(dn_kind), name.ptr()))
{
return false;
return JS_FALSE;
}
}
}
@ -2164,7 +2164,7 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, Parser *parser)
if (mdl.empty()) {
if (!Define(pn, atom, tc))
return false;
return JS_FALSE;
} else {
/*
* A var declaration never recreates an existing binding, it restates
@ -2188,7 +2188,7 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, Parser *parser)
if (pn->isDefn()) {
pnu = NameNode::create(PNK_NAME, atom, parser, parser->tc);
if (!pnu)
return false;
return JS_FALSE;
}
LinkUseToDef(pnu, dn);
@ -2206,7 +2206,7 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, Parser *parser)
if (dn) {
JS_ASSERT_IF(data->op == JSOP_DEFCONST,
dn->kind() == Definition::CONST);
return true;
return JS_TRUE;
}
/*
@ -2220,7 +2220,7 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, Parser *parser)
} else {
ParseNode *pn2 = NameNode::create(PNK_NAME, atom, parser, parser->tc);
if (!pn2)
return false;
return JS_FALSE;
/* The token stream may be past the location for pn. */
pn2->pn_pos = pn->pn_pos;
@ -2230,7 +2230,7 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, Parser *parser)
}
if (!tc->decls.addHoist(atom, (Definition *) pn))
return false;
return JS_FALSE;
pn->setDefn(true);
pn->pn_dflags &= ~PND_PLACEHOLDER;
}
@ -2338,14 +2338,14 @@ NoteNameUse(ParseNode *pn, Parser *parser)
#if JS_HAS_DESTRUCTURING
static bool
static JSBool
BindDestructuringVar(JSContext *cx, BindData *data, ParseNode *pn, Parser *parser)
{
JS_ASSERT(pn->isKind(PNK_NAME));
data->pn = pn;
if (!data->binder(cx, data, pn->pn_atom, parser))
return false;
return JS_FALSE;
/*
* Select the appropriate name-setting opcode, respecting eager selection
@ -2362,7 +2362,7 @@ BindDestructuringVar(JSContext *cx, BindData *data, ParseNode *pn, Parser *parse
pn->pn_dflags |= PND_CONST;
NoteLValue(cx, pn, parser->tc->sc, PND_INITIALIZED);
return true;
return JS_TRUE;
}
/*
@ -2383,7 +2383,7 @@ BindDestructuringVar(JSContext *cx, BindData *data, ParseNode *pn, Parser *parse
* JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var,
* which can be optimized further. So we select JSOP_SETNAME.
*/
static bool
static JSBool
BindDestructuringLHS(JSContext *cx, ParseNode *pn, Parser *parser)
{
switch (pn->getKind()) {
@ -2404,7 +2404,7 @@ BindDestructuringLHS(JSContext *cx, ParseNode *pn, Parser *parser)
case PNK_LP:
if (!MakeSetCall(cx, pn, parser, JSMSG_BAD_LEFTSIDE_OF_ASS))
return false;
return JS_FALSE;
break;
#if JS_HAS_XML_SUPPORT
@ -2416,10 +2416,10 @@ BindDestructuringLHS(JSContext *cx, ParseNode *pn, Parser *parser)
default:
parser->reportError(pn, JSMSG_BAD_LEFTSIDE_OF_ASS);
return false;
return JS_FALSE;
}
return true;
return JS_TRUE;
}
/*
@ -2568,7 +2568,7 @@ Parser::destructuringExpr(BindData *data, TokenKind tt)
JS_ASSERT(tokenStream.isCurrentTokenType(tt));
tc->inDeclDestructuring = true;
ParseNode *pn = primaryExpr(tt, false);
ParseNode *pn = primaryExpr(tt, JS_FALSE);
tc->inDeclDestructuring = false;
if (!pn)
return NULL;
@ -4147,7 +4147,7 @@ Parser::variables(ParseNodeKind kind, StaticBlockObject *blockObj, VarContext va
#if JS_HAS_DESTRUCTURING
if (tt == TOK_LB || tt == TOK_LC) {
tc->inDeclDestructuring = true;
pn2 = primaryExpr(tt, false);
pn2 = primaryExpr(tt, JS_FALSE);
tc->inDeclDestructuring = false;
if (!pn2)
return NULL;
@ -4618,7 +4618,7 @@ SetLvalKid(JSContext *cx, Parser *parser, ParseNode *pn, ParseNode *kid,
static const char incop_name_str[][10] = {"increment", "decrement"};
static bool
static JSBool
SetIncOpKid(JSContext *cx, Parser *parser, ParseNode *pn, ParseNode *kid,
TokenKind tt, bool preorder)
{
@ -4642,7 +4642,7 @@ SetIncOpKid(JSContext *cx, Parser *parser, ParseNode *pn, ParseNode *kid,
case PNK_LP:
if (!MakeSetCall(cx, kid, parser, JSMSG_BAD_INCOP_OPERAND))
return false;
return JS_FALSE;
/* FALL THROUGH */
#if JS_HAS_XML_SUPPORT
case PNK_XMLUNARY:
@ -4661,7 +4661,7 @@ SetIncOpKid(JSContext *cx, Parser *parser, ParseNode *pn, ParseNode *kid,
op = JSOP_NOP;
}
pn->setOp(op);
return true;
return JS_TRUE;
}
ParseNode *
@ -4700,7 +4700,7 @@ Parser::unaryExpr()
pn = UnaryNode::create((tt == TOK_INC) ? PNK_PREINCREMENT : PNK_PREDECREMENT, this);
if (!pn)
return NULL;
pn2 = memberExpr(true);
pn2 = memberExpr(JS_TRUE);
if (!pn2)
return NULL;
if (!SetIncOpKid(context, this, pn, pn2, tt, true))
@ -4752,7 +4752,7 @@ Parser::unaryExpr()
default:
tokenStream.ungetToken();
pn = memberExpr(true);
pn = memberExpr(JS_TRUE);
if (!pn)
return NULL;
@ -5199,7 +5199,7 @@ Parser::comprehensionTail(ParseNode *kid, unsigned blockid, bool isGenexp,
case TOK_LB:
case TOK_LC:
tc->inDeclDestructuring = true;
pn3 = primaryExpr(tt, false);
pn3 = primaryExpr(tt, JS_FALSE);
tc->inDeclDestructuring = false;
if (!pn3)
return NULL;
@ -5462,11 +5462,11 @@ Parser::assignExprWithoutYield(unsigned msg)
return res;
}
bool
JSBool
Parser::argumentList(ParseNode *listNode)
{
if (tokenStream.matchToken(TOK_RP, TSF_OPERAND))
return true;
return JS_TRUE;
GenexpGuard guard(this);
bool arg0 = true;
@ -5474,7 +5474,7 @@ Parser::argumentList(ParseNode *listNode)
do {
ParseNode *argNode = assignExpr();
if (!argNode)
return false;
return JS_FALSE;
if (arg0)
guard.endBody();
@ -5483,25 +5483,25 @@ Parser::argumentList(ParseNode *listNode)
!argNode->isInParens() &&
tokenStream.peekToken() == TOK_COMMA) {
reportError(argNode, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
return false;
return JS_FALSE;
}
#endif
#if JS_HAS_GENERATOR_EXPRS
if (tokenStream.matchToken(TOK_FOR)) {
if (!guard.checkValidBody(argNode))
return false;
return JS_FALSE;
argNode = generatorExpr(argNode);
if (!argNode)
return false;
return JS_FALSE;
if (listNode->pn_count > 1 ||
tokenStream.peekToken() == TOK_COMMA) {
reportError(argNode, JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
return false;
return JS_FALSE;
}
} else
#endif
if (arg0 && !guard.maybeNoteGenerator(argNode))
return false;
return JS_FALSE;
arg0 = false;
@ -5510,13 +5510,13 @@ Parser::argumentList(ParseNode *listNode)
if (tokenStream.getToken() != TOK_RP) {
reportError(NULL, JSMSG_PAREN_AFTER_ARGS);
return false;
return JS_FALSE;
}
return true;
return JS_TRUE;
}
ParseNode *
Parser::memberExpr(bool allowCallSyntax)
Parser::memberExpr(JSBool allowCallSyntax)
{
ParseNode *lhs;
@ -5528,7 +5528,7 @@ Parser::memberExpr(bool allowCallSyntax)
lhs = ListNode::create(PNK_NEW, this);
if (!lhs)
return NULL;
ParseNode *ctorExpr = memberExpr(false);
ParseNode *ctorExpr = memberExpr(JS_FALSE);
if (!ctorExpr)
return NULL;
lhs->setOp(JSOP_NEW);
@ -5544,7 +5544,7 @@ Parser::memberExpr(bool allowCallSyntax)
}
lhs->pn_pos.end = lhs->last()->pn_pos.end;
} else {
lhs = primaryExpr(tt, false);
lhs = primaryExpr(tt, JS_FALSE);
if (!lhs)
return NULL;
@ -5641,7 +5641,7 @@ Parser::memberExpr(bool allowCallSyntax)
if (!nextMember)
return NULL;
tt = tokenStream.getToken(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
ParseNode *pn3 = primaryExpr(tt, true);
ParseNode *pn3 = primaryExpr(tt, JS_TRUE);
if (!pn3)
return NULL;
if (pn3->isKind(PNK_NAME) && !pn3->isInParens()) {
@ -5950,7 +5950,7 @@ Parser::attributeIdentifier()
* Make a TOK_LC unary node whose pn_kid is an expression.
*/
ParseNode *
Parser::xmlExpr(bool inTag)
Parser::xmlExpr(JSBool inTag)
{
JS_ASSERT(allowsXML());
@ -6002,7 +6002,7 @@ Parser::xmlNameExpr()
do {
tt = tokenStream.currentToken().type;
if (tt == TOK_LC) {
pn2 = xmlExpr(true);
pn2 = xmlExpr(JS_TRUE);
if (!pn2)
return NULL;
} else {
@ -6104,7 +6104,7 @@ Parser::xmlTagContent(ParseNodeKind tagkind, JSAtom **namep)
JS_ASSERT(tokenStream.currentToken().t_op == JSOP_STRING);
pn2 = atomNode(PNK_XMLATTR, JSOP_STRING);
} else if (tt == TOK_LC) {
pn2 = xmlExpr(true);
pn2 = xmlExpr(JS_TRUE);
pn->pn_xflags |= PNX_CANTFOLD;
} else {
reportError(NULL, JSMSG_BAD_XML_ATTR_VALUE);
@ -6133,7 +6133,7 @@ Parser::xmlTagContent(ParseNodeKind tagkind, JSAtom **namep)
* Consume XML element tag content, including the TOK_XMLETAGO (</) sequence
* that opens the end tag for the container.
*/
bool
JSBool
Parser::xmlElementContent(ParseNode *pn)
{
JS_ASSERT(allowsXML());
@ -6141,7 +6141,7 @@ Parser::xmlElementContent(ParseNode *pn)
tokenStream.setXMLTagMode(false);
for (;;) {
TokenKind tt = tokenStream.getToken(TSF_XMLTEXTMODE);
XML_CHECK_FOR_ERROR_AND_EOF(tt, false);
XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
JS_ASSERT(tt == TOK_XMLSPACE || tt == TOK_XMLTEXT);
JSAtom *textAtom = tokenStream.currentToken().atom();
@ -6157,18 +6157,18 @@ Parser::xmlElementContent(ParseNode *pn)
}
tt = tokenStream.getToken(TSF_OPERAND);
XML_CHECK_FOR_ERROR_AND_EOF(tt, false);
XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
if (tt == TOK_XMLETAGO)
break;
ParseNode *pn2;
if (tt == TOK_LC) {
pn2 = xmlExpr(false);
pn2 = xmlExpr(JS_FALSE);
if (!pn2)
return false;
pn->pn_xflags |= PNX_CANTFOLD;
} else if (tt == TOK_XMLSTAGO) {
pn2 = xmlElementOrList(false);
pn2 = xmlElementOrList(JS_FALSE);
if (!pn2)
return false;
pn2->pn_xflags &= ~PNX_XMLROOT;
@ -6191,14 +6191,14 @@ Parser::xmlElementContent(ParseNode *pn)
tokenStream.setXMLTagMode(true);
JS_ASSERT(tokenStream.currentToken().type == TOK_XMLETAGO);
return true;
return JS_TRUE;
}
/*
* Return a PN_LIST node containing an XML or XMLList Initialiser.
*/
ParseNode *
Parser::xmlElementOrList(bool allowList)
Parser::xmlElementOrList(JSBool allowList)
{
JS_ASSERT(allowsXML());
@ -6334,7 +6334,7 @@ Parser::xmlElementOrList(bool allowList)
}
ParseNode *
Parser::xmlElementOrListRoot(bool allowList)
Parser::xmlElementOrListRoot(JSBool allowList)
{
JS_ASSERT(allowsXML());
@ -6903,7 +6903,7 @@ Parser::primaryExpr(TokenKind tt, bool afterDoubleDot)
case TOK_LP:
{
bool genexp;
JSBool genexp;
pn = parenExpr(&genexp);
if (!pn)
@ -6927,7 +6927,7 @@ Parser::primaryExpr(TokenKind tt, bool afterDoubleDot)
break;
case TOK_XMLSTAGO:
pn = xmlElementOrListRoot(true);
pn = xmlElementOrListRoot(JS_TRUE);
if (!pn)
return NULL;
break;
@ -7024,7 +7024,7 @@ Parser::primaryExpr(TokenKind tt, bool afterDoubleDot)
}
ParseNode *
Parser::parenExpr(bool *genexp)
Parser::parenExpr(JSBool *genexp)
{
TokenPtr begin;
ParseNode *pn;
@ -7033,7 +7033,7 @@ Parser::parenExpr(bool *genexp)
begin = tokenStream.currentToken().pos.begin;
if (genexp)
*genexp = false;
*genexp = JS_FALSE;
GenexpGuard guard(this);
@ -7061,7 +7061,7 @@ Parser::parenExpr(bool *genexp)
return NULL;
}
pn->pn_pos.end = tokenStream.currentToken().pos.end;
*genexp = true;
*genexp = JS_TRUE;
}
} else
#endif /* JS_HAS_GENERATOR_EXPRS */

View File

@ -200,9 +200,9 @@ struct Parser : private AutoGCRooter
ParseNode *mulExpr1i();
ParseNode *mulExpr1n();
ParseNode *unaryExpr();
ParseNode *memberExpr(bool allowCallSyntax);
ParseNode *memberExpr(JSBool allowCallSyntax);
ParseNode *primaryExpr(TokenKind tt, bool afterDoubleDot);
ParseNode *parenExpr(bool *genexp = NULL);
ParseNode *parenExpr(JSBool *genexp = NULL);
/*
* Additional JS parsers.
@ -218,7 +218,7 @@ struct Parser : private AutoGCRooter
ParseNode *comprehensionTail(ParseNode *kid, unsigned blockid, bool isGenexp,
ParseNodeKind kind = PNK_SEMI, JSOp op = JSOP_NOP);
ParseNode *generatorExpr(ParseNode *kid);
bool argumentList(ParseNode *listNode);
JSBool argumentList(ParseNode *listNode);
ParseNode *bracketedExpr();
ParseNode *letBlock(LetContext letContext);
ParseNode *returnOrYield(bool useAssignExpr);
@ -238,12 +238,12 @@ struct Parser : private AutoGCRooter
ParseNode *qualifiedSuffix(ParseNode *pn);
ParseNode *qualifiedIdentifier();
ParseNode *attributeIdentifier();
ParseNode *xmlExpr(bool inTag);
ParseNode *xmlExpr(JSBool inTag);
ParseNode *xmlNameExpr();
ParseNode *xmlTagContent(ParseNodeKind tagkind, JSAtom **namep);
bool xmlElementContent(ParseNode *pn);
ParseNode *xmlElementOrList(bool allowList);
ParseNode *xmlElementOrListRoot(bool allowList);
JSBool xmlElementContent(ParseNode *pn);
ParseNode *xmlElementOrList(JSBool allowList);
ParseNode *xmlElementOrListRoot(JSBool allowList);
ParseNode *starOrAtPropertyIdentifier(TokenKind tt);
ParseNode *propertyQualifiedIdentifier();

View File

@ -94,24 +94,24 @@ js::FindKeyword(const jschar *s, size_t length)
return NULL;
}
bool
JSBool
js::IsIdentifier(JSLinearString *str)
{
const jschar *chars = str->chars();
size_t length = str->length();
if (length == 0)
return false;
return JS_FALSE;
jschar c = *chars;
if (!IsIdentifierStart(c))
return false;
return JS_FALSE;
const jschar *end = chars + length;
while (++chars != end) {
c = *chars;
if (!IsIdentifierPart(c))
return false;
return JS_FALSE;
}
return true;
return JS_TRUE;
}
#ifdef _MSC_VER
@ -601,7 +601,7 @@ TokenStream::getXMLEntity()
{
ptrdiff_t offset, length, i;
int c, d;
bool ispair;
JSBool ispair;
jschar *bp, digit;
char *bytes;
JSErrNum msg;
@ -782,7 +782,7 @@ TokenStream::getXMLTextOrTag(TokenKind *ttp, Token **tpp)
tokenbuf.clear();
if (IsXMLNamespaceStart(c)) {
bool sawColon = false;
JSBool sawColon = JS_FALSE;
if (!tokenbuf.append(c))
goto error;
@ -797,7 +797,7 @@ TokenStream::getXMLTextOrTag(TokenKind *ttp, Token **tpp)
reportError(JSMSG_BAD_XML_QNAME);
goto error;
}
sawColon = true;
sawColon = JS_TRUE;
}
if (!tokenbuf.append(c))
@ -2127,13 +2127,13 @@ JS_FRIEND_API(int)
js_fgets(char *buf, int size, FILE *file)
{
int n, i, c;
bool crflag;
JSBool crflag;
n = size - 1;
if (n < 0)
return -1;
crflag = false;
crflag = JS_FALSE;
for (i = 0; i < n && (c = fast_getc(file)) != EOF; i++) {
buf[i] = c;
if (c == '\n') { /* any \n ends a line */

View File

@ -829,7 +829,7 @@ FindKeyword(const jschar *s, size_t length);
* Check that str forms a valid JS identifier name. The function does not
* check if str is a JS keyword.
*/
bool
JSBool
IsIdentifier(JSLinearString *str);
/*

View File

@ -36,8 +36,8 @@
* MarkString, etc. These functions check if an object is in the compartment
* currently being GCed. If it is, they call PushMarkStack. Roots are pushed
* this way as well as pointers traversed inside trace hooks (for things like
* PropertyIteratorObjects). It is always valid to call a MarkX function
* instead of PushMarkStack, although it may be slower.
* IteratorClass). It it always valid to call a MarkX function instead of
* PushMarkStack, although it may be slower.
*
* The MarkX functions also handle non-GC object traversal. In this case, they
* call a callback for each object visited. This is a recursive process; the

View File

@ -1,6 +1,6 @@
// Map(x) throws if x is not iterable (unless x is undefined).
load(libdir + "asserts.js");
var nonIterables = [null, true, 1, -0, 3.14, NaN, {}, Math, this];
var nonIterables = [null, true, 1, -0, 3.14, NaN, "", "xyzzy", {}, Math, this];
for (let k of nonIterables)
assertThrowsInstanceOf(function () { Map(k); }, TypeError);

View File

@ -1,9 +1,5 @@
// for-of can iterate arguments objects.
// Arguments objects do not have a .iterator() method by default.
// Install one on Object.prototype.
Object.prototype.iterator = Array.prototype.iterator;
var s;
function test() {
for (var v of arguments)

View File

@ -6,7 +6,6 @@ function f() {
var s = '';
var args = f('a', 'b', 'c');
Object.prototype.iterator = Array.prototype.iterator;
for (var v of args)
s += v;
assertEq(s, 'abc');

View File

@ -1,7 +1,5 @@
// for-of can iterate strict arguments objects.
Object.prototype.iterator = Array.prototype.iterator;
var s;
function test() {
"use strict";

View File

@ -1,7 +1,5 @@
// for-of can iterate arguments objects for other active frames.
Object.prototype.iterator = Array.prototype.iterator;
var s;
function g(obj) {
for (var v of obj)

View File

@ -1,7 +1,5 @@
// for-of can iterate strict arguments objects in non-strict code.
Object.prototype.iterator = Array.prototype.iterator;
var s;
function g(obj) {
for (var v of obj)

View File

@ -1,7 +1,5 @@
// Changing arguments.length affects a for-of loop iterating over arguments.
Object.prototype.iterator = Array.prototype.iterator;
var s;
function f() {
arguments.length = 2;

View File

@ -1,7 +1,5 @@
// Changing arguments.length during a for-of loop iterating over arguments affects the loop.
Object.prototype.iterator = Array.prototype.iterator;
var s;
function f() {
for (var v of arguments) {

View File

@ -4,7 +4,6 @@ var m = {1: 'peek'};
var a = [0, , 2, 3];
a.__proto__ = m;
var log = [];
Object.prototype.iterator = Array.prototype.iterator;
for (var x of a)
log.push(x);
assertEq(log[1], 'peek');

View File

@ -1,11 +0,0 @@
// Array iterators reflect changes to elements of the underlying array.
load(libdir + "asserts.js");
var arr = [0, 1, 2];
var it = arr.iterator();
arr[0] = 1000;
arr[2] = 2000;
assertEq(it.next(), 1000);
assertEq(it.next(), 1);
assertEq(it.next(), 2000);
assertThrowsValue(function () { it.next(); }, StopIteration);

View File

@ -1,12 +0,0 @@
// Array iterators keep the underlying array, arraylike object, or string alive.
load(libdir + "referencesVia.js");
function test(obj) {
var it = Array.prototype.iterator.call(obj);
assertEq(referencesVia(it, "**UNKNOWN SLOT 0**", obj), true);
}
test([]);
test([1, 2, 3, 4]);
test({});

View File

@ -1,27 +0,0 @@
// Array.prototype.iterator is generic.
// That is, it can be applied to arraylike objects and strings, not just arrays.
load(libdir + "asserts.js");
function test(obj) {
var it = Array.prototype.iterator.call(obj);
for (var i = 0; i < (obj.length >>> 0); i++)
assertEq(it.next(), obj[i]);
assertThrowsValue(function () { it.next(); }, StopIteration);
}
test({length: 0});
test({length: 0, 0: 'x', 1: 'y'});
test({length: 2, 0: 'x', 1: 'y'});
test(Object.create(['x', 'y', 'z']));
test(Object.create({length: 2, 0: 'x', 1: 'y'}));
test("");
test("ponies");
// Perverse length values.
test({length: 0x1f00000000});
test({length: -0xfffffffe, 0: 'a', 1: 'b'});
test({length: "011", 9: 9, 10: 10, 11: 11});
test({length: -0});
test({length: 2.7, 0: 0, 1: 1, 2: 2});
test({length: {valueOf: function () { return 3; }}, 0: 0, 1: 1, 2: 2});

View File

@ -1,12 +0,0 @@
// If an array with an active iterator is lengthened, the iterator visits the new elements.
load(libdir + "asserts.js");
var arr = [0, 1];
var it = arr.iterator();
it.next();
it.next();
arr[2] = 2;
arr.length = 4;
assertEq(it.next(), 2);
assertEq(it.next(), undefined);
assertThrowsValue(function () { it.next(); }, StopIteration);

View File

@ -1,9 +0,0 @@
// Array.prototype.iterator applied to undefined or null does not throw (until .next is called).
load(libdir + "asserts.js");
for (var v of [undefined, null]) {
var it = Array.prototype.iterator.call(v);
// This will throw because the iterator is trying to get v.length.
assertThrowsInstanceOf(function () { it.next(); }, TypeError);
}

View File

@ -1,23 +0,0 @@
// An array iterator for a proxy calls the traps in a predictable order.
load(libdir + "asserts.js");
var s = '';
var it = Array.prototype.iterator.call(Proxy.create({
get: function (recipient, name) {
if (name == 'length') {
s += 'L';
return 2;
} else {
s += name;
return name;
}
}
}));
assertEq(it.next(), "0");
s += ' ';
assertEq(it.next(), "1");
s += ' ';
assertThrowsValue(function () { it.next(); }, StopIteration);
assertEq(s, "L0 L1 L");

View File

@ -1,9 +0,0 @@
// If an array is truncated to the left of an iterator it, it.next() throws StopIteration.
load(libdir + "asserts.js");
var arr = [0, 1, 2];
var it = arr.iterator();
it.next();
it.next();
arr.length = 1;
assertThrowsValue(function () { it.next(); }, StopIteration);

View File

@ -1,10 +0,0 @@
// Superficial tests of the Array.prototype.iterator builtin function and its workalikes.
var constructors = [Array, String, Uint8Array, Uint8ClampedArray];
for (var c of constructors) {
assertEq(c.prototype.iterator.length, 0);
var desc = Object.getOwnPropertyDescriptor(c.prototype, "iterator");
assertEq(desc.configurable, true);
assertEq(desc.enumerable, false);
assertEq(desc.writable, true);
}

View File

@ -1,22 +0,0 @@
// Superficial tests for iterators created by Array.prototype.iterator
var proto = Object.getPrototypeOf([].iterator());
assertEq(Object.getPrototypeOf(proto), Iterator.prototype);
function check(it) {
assertEq(typeof it, 'object');
assertEq(Object.getPrototypeOf(it), proto);
assertEq(Object.getOwnPropertyNames(it).length, 0);
assertEq(it.iterator(), it);
// for-in enumerates the iterator's properties.
it.x = 0;
var s = '';
for (var p in it)
s += p + '.';
assertEq(s, 'x.');
}
check([].iterator());
check(Array.prototype.iterator.call({}));
check(Array.prototype.iterator.call(undefined));

View File

@ -1,5 +0,0 @@
// Iterator.prototype.next throws if applied to a value that isn't an iterator.
load(libdir + "asserts.js");
for (var v of [null, undefined, false, 0, "ponies", {}, [], this])
assertThrowsInstanceOf(function () { Iterator.prototype.next.call(v); }, TypeError);

View File

@ -1,6 +0,0 @@
// Iterator.prototype.next throws if applied to a non-iterator that inherits from an iterator.
load(libdir + "asserts.js");
var it = [1, 2].iterator();
var v = Object.create(it);
assertThrowsInstanceOf(function () { Iterator.prototype.next.call(v); }, TypeError);

View File

@ -1,8 +0,0 @@
// The .next method of array iterators works across compartment boundaries.
load(libdir + "asserts.js");
var g = newGlobal('new-compartment');
g.eval("var it = [1, 2].iterator();");
assertEq(g.it.next(), 1);
assertEq([].iterator().next.call(g.it), 2);
assertThrowsValue([].iterator().next.bind(g.it), StopIteration);

View File

@ -1,7 +0,0 @@
// Test superficial features of the Iterator.prototype.next builtin function.
assertEq(Iterator.prototype.next.length, 0);
var desc = Object.getOwnPropertyDescriptor(Iterator.prototype, "next");
assertEq(desc.configurable, true);
assertEq(desc.enumerable, false);
assertEq(desc.writable, true);

View File

@ -2,15 +2,13 @@
load(libdir + "asserts.js");
function argsobj() { return arguments; }
var misc = [
{}, {x: 1}, Math, isNaN,
Object.create(null),
argsobj(0, 1, 2),
Object.create(Array.prototype),
null, undefined,
true, 0, 3.1416,
new Boolean(true), new Number(0)];
true, 0, 3.1416, "", "ponies",
new Boolean(true), new Number(0), new String("ponies")];
for (var i = 0; i < misc.length; i++) {
let v = misc[i];

View File

@ -1,25 +1,25 @@
// Basic for-of test with Proxy.
function iterableProxy(arr) {
return Proxy.create({
getPropertyDescriptor: function (name) {
for (var obj = arr; obj; obj = Object.getPrototypeOf(obj)) {
var desc = Object.getOwnPropertyDescriptor(obj, name);
if (desc)
return desc;
}
return undefined;
function iter(arr) {
var i = 0;
return {
next: function () {
if (i < arr.length)
return arr[i++];
throw StopIteration;
}
});
};
}
function iterableProxy(arr) {
return Proxy.create({iterate: function () { return iter(arr); }});
}
var s = '';
var arr = ['a', 'b', 'c', 'd'];
var p = iterableProxy(arr);
// Test the same proxy twice. Each time through the loop, the proxy handler's
// getPropertyDescriptor method will be called 10 times (once for 'iterator',
// five times for 'length', and once for each of the four elements).
// Test the same proxy twice. Its iterate method should be called each time.
for (var i = 0; i < 2; i++) {
var j = 0;
for (var x of p)

View File

@ -1,27 +1,10 @@
// Basic for-of test with Proxy whose iterator method is a generator.
// Basic for-of test with Proxy whose iterate method is a generator.
var arr = ['a', 'b', 'c', 'd'];
var proxy = Proxy.create({
getPropertyDescriptor: function (name) {
if (name == 'iterator') {
return {
configurable: false,
enumerable: false,
writeable: false,
value: function () {
for (var i = 0; i < arr.length; i++)
yield arr[i];
}
};
}
// Otherwise, inherit the property from arr.
for (var obj = arr; obj; obj = Object.getPrototypeOf(obj)) {
var desc = Object.getOwnPropertyDescriptor(obj, name);
if (desc)
return desc;
}
return undefined;
iterate: function () {
for (var i = 0; i < arr.length; i++)
yield arr[i];
}
});

View File

@ -1,12 +1,6 @@
// An exception thrown from a proxy trap while getting the .iterator method is propagated.
// An exception thrown from an iterate trap is propagated.
load(libdir + "asserts.js");
var p = Proxy.create({
getPropertyDescriptor: function (name) {
if (name == "iterator")
throw "fit";
return undefined;
}
});
var p = Proxy.create({iterate: function () { throw "fit"; }});
assertThrowsValue(function () { for (var v of p) {} }, "fit");

View File

@ -1,11 +0,0 @@
// for-of is defined in terms of basic operations on objects, particularly
// [[Get]] for properties named "iterator" and "next", and [[Call]]. These
// "semantics" tests check that for-of really does appear to be implemented in
// terms of those more basic operations, as required by the spec, even in
// unusual cases.
// Deleting Array.prototype.iterator makes for-of stop working on arrays.
load(libdir + "asserts.js");
delete Array.prototype.iterator;
assertThrowsInstanceOf(function () { for (var x of []) ; }, TypeError);

View File

@ -1,10 +0,0 @@
// Replacing Array.prototype.iterator with something non-callable makes for-of throw.
load(libdir + "asserts.js");
function test(v) {
Array.prototype.iterator = v;
assertThrowsInstanceOf(function () { for (var x of []) ; }, TypeError);
}
test(undefined);
test(null);
test({});

View File

@ -1,11 +0,0 @@
// Replacing Array.prototype.iterator with a generator affects for-of behavior.
Array.prototype.iterator = function () {
for (var i = this.length; --i >= 0; )
yield this[i];
};
var s = '';
for (var v of ['a', 'b', 'c', 'd'])
s += v;
assertEq(s, 'dcba');

View File

@ -1,15 +0,0 @@
// Giving an Array an own .iterator property affects for-of.
var a = [];
a.iterator = function () {
yield 'o';
yield 'k';
};
var s = '';
for (var v of a)
s += v;
assertEq(s, 'ok');
load(libdir + "asserts.js");
a.iterator = undefined;
assertThrowsInstanceOf(function () { for (var v of a) ; }, TypeError);

View File

@ -1,6 +0,0 @@
// Deleting String.prototype.iterator makes for-of stop working on strings.
load(libdir + "asserts.js");
delete String.prototype.iterator;
assertThrowsInstanceOf(function () { for (var v of "abc") ; }, TypeError);
assertThrowsInstanceOf(function () { for (var v of new String("abc")) ; }, TypeError);

View File

@ -1,6 +0,0 @@
// Deleting the .next method makes for-of stop working on arrays.
load(libdir + "asserts.js");
var iterProto = Object.getPrototypeOf([].iterator());
delete iterProto.next;
assertThrowsInstanceOf(function () { for (var v of []) ; }, TypeError);

View File

@ -1,15 +0,0 @@
// Deleting the .next method of an iterator in the middle of a for-of loop
// causes a TypeError at the next iteration.
load(libdir + "asserts.js");
var iterProto = Object.getPrototypeOf([].iterator());
var s = '';
assertThrowsInstanceOf(function () {
for (var v of ['duck', 'duck', 'duck', 'goose', 'FAIL']) {
s += v;
if (v === 'goose')
delete iterProto.next;
s += '.';
}
}, TypeError);
assertEq(s, 'duck.duck.duck.goose.');

View File

@ -1,7 +0,0 @@
// A for-of loop exits if the iterator's .next method throws another compartment's StopIteration.
var g = newGlobal('new-compartment');
var it = g.eval("({ iterator: function () { return this; }, " +
"next: function () { throw StopIteration; } });");
for (x of it)
throw 'FAIL';

View File

@ -1,25 +0,0 @@
// The LHS of a for-of loop is not evaluated until after the .next() method returns.
var s;
function f() {
s += 'f';
return {};
}
// Test 1: .next() throws StopIteration right away. f is never called.
s = '';
for (f().x of [])
s += '.';
assertEq(s, '');
// Test 2: check proper interleaving of f calls, iterator.next() calls, and the loop body.
function g() {
s += 'g';
yield 0;
s += 'g';
yield 1;
s += 'g';
}
for (f().x of g())
s += '.';
assertEq(s, 'gf.gf.g');

View File

@ -1,31 +0,0 @@
// The LHS of a for-loop is not bound to a particular scope until after the .next() method returns.
var obj = {};
// Test 1
function g() {
obj.x = 0;
yield 1;
}
var x = 2, n = 0;
with (obj) {
for (x of g()) // g().next() inserts a binding for x on obj
n++;
}
assertEq(x, 2);
assertEq(obj.x, 1);
assertEq(n, 1);
// Test 2
function h() {
delete obj.x;
yield 3;
}
n = 0;
with (obj) {
for (x of h()) // h().next() deletes the binding for x on obj
n++;
}
assertEq(x, 3);
assertEq("x" in obj, false);
assertEq(n, 1);

View File

@ -1,41 +0,0 @@
// for-of on a proxy causes a predictable sequence of trap calls.
var s = '';
var i = 0;
var next_fn = Proxy.createFunction({}, function () {
s += "n";
if (i == 3)
throw StopIteration;
return i++;
});
var it = Proxy.create({
get: function (receiver, name) {
if (name == 'toSource') {
s += '?';
return function () 'it';
}
assertEq(name, "next");
s += "N";
return next_fn;
}
});
var iterator_fn = Proxy.createFunction({}, function () {
s += 'i';
return it;
});
var obj = Proxy.create({
get: function (receiver, name) {
assertEq(name, "iterator");
s += "I";
return iterator_fn;
}
});
for (var v of obj)
s += v;
assertEq(s, 'IiNn0Nn1Nn2Nn');

View File

@ -1,14 +0,0 @@
// String.prototype.iterator is generic.
load(libdir + "asserts.js");
function test(obj) {
var it = Array.prototype.iterator.call(obj);
for (var i = 0; i < (obj.length >>> 0); i++)
assertEq(it.next(), obj[i]);
assertThrowsValue(function () { it.next(); }, StopIteration);
}
test({length: 0});
test(Object.create(['x', 'y', 'z']));
test(Object.create({length: 2, 0: 'x', 1: 'y'}));

View File

@ -1,26 +0,0 @@
// for-of works on strings and String objects.
function test(s) {
var copy = '';
for (var v of s) {
assertEq(typeof v, 'string');
assertEq(v.length, 1);
copy += v;
}
assertEq(copy, String(s));
}
test('');
test('abc');
test('a \0 \ufffe \ufeff');
// Non-BMP characters are generally passed to JS in UTF-16, as surrogate pairs.
// ES requires that such pairs be treated as two 16-bit "characters" in pretty
// much every circumstance, including string indexing. We anticipate the same
// requirement will be imposed here, though it's not a sure thing.
test('\ud808\udf45');
test(new String(''));
test(new String('abc'));
test(new String('a \0 \ufffe \ufeff'));
test(new String('\ud808\udf45'));

View File

@ -52,20 +52,19 @@
#include "jstypedarray.h"
#include "jsxml.h"
#include "builtin/Eval.h"
#include "ds/LifoAlloc.h"
#include "builtin/MapObject.h"
#include "builtin/RegExp.h"
#include "ds/LifoAlloc.h"
#include "frontend/BytecodeCompiler.h"
#include "frontend/TreeContext.h"
#include "gc/Marking.h"
#include "gc/Memory.h"
#include "js/MemoryMetrics.h"
#include "yarr/BumpPointerAllocator.h"
#include "vm/MethodGuard.h"
#include "vm/NumericConversions.h"
#include "vm/StringBuffer.h"
#include "vm/Xdr.h"
#include "yarr/BumpPointerAllocator.h"
#include "jsatominlines.h"
#include "jsinferinlines.h"
@ -1812,7 +1811,9 @@ static JSStdName standard_class_names[] = {
{js_InitXMLClass, EAGER_ATOM(isXMLName), CLASP(XML)},
#endif
{js_InitIteratorClasses, EAGER_CLASS_ATOM(Iterator), &PropertyIteratorObject::class_},
#if JS_HAS_GENERATORS
{js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(Iterator)},
#endif
/* Typed Arrays */
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(ArrayBuffer), &ArrayBufferClass},
@ -4407,16 +4408,22 @@ JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp)
return JS_TRUE;
}
JS_PUBLIC_API(JSBool)
JS_ArrayIterator(JSContext *cx, unsigned argc, jsval *vp)
JS_PUBLIC_API(JSObject *)
JS_NewElementIterator(JSContext *cx, JSObject *obj_)
{
CallArgs args = CallArgsFromVp(argc, vp);
Rooted<Value> target(cx, args.thisv());
JSObject *iterobj = ElementIteratorObject::create(cx, target);
if (!iterobj)
return false;
vp->setObject(*iterobj);
return true;
AssertNoGC(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, obj_);
Rooted<JSObject*> obj(cx, obj_);
return ElementIteratorObject::create(cx, obj);
}
JS_PUBLIC_API(JSObject *)
JS_ElementIteratorStub(JSContext *cx, JSHandleObject obj, JSBool keysonly)
{
JS_ASSERT(!keysonly);
return JS_NewElementIterator(cx, obj);
}
JS_PUBLIC_API(jsval)

View File

@ -3752,20 +3752,26 @@ struct JSClass {
#define JSCLASS_HIGH_FLAGS_SHIFT (JSCLASS_RESERVED_SLOTS_SHIFT + \
JSCLASS_RESERVED_SLOTS_WIDTH)
#define JSCLASS_IS_ANONYMOUS (1<<(JSCLASS_HIGH_FLAGS_SHIFT+0))
#define JSCLASS_IS_GLOBAL (1<<(JSCLASS_HIGH_FLAGS_SHIFT+1))
#define JSCLASS_INTERNAL_FLAG2 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+2))
#define JSCLASS_INTERNAL_FLAG3 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+3))
/*
* Call the iteratorObject hook only to iterate over contents (for-of), not to
* enumerate properties (for-in, for-each, Object.keys, etc.)
*/
#define JSCLASS_FOR_OF_ITERATION (1<<(JSCLASS_HIGH_FLAGS_SHIFT+0))
#define JSCLASS_IS_ANONYMOUS (1<<(JSCLASS_HIGH_FLAGS_SHIFT+1))
#define JSCLASS_IS_GLOBAL (1<<(JSCLASS_HIGH_FLAGS_SHIFT+2))
#define JSCLASS_INTERNAL_FLAG2 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+3))
#define JSCLASS_INTERNAL_FLAG3 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+4))
/* Indicate whether the proto or ctor should be frozen. */
#define JSCLASS_FREEZE_PROTO (1<<(JSCLASS_HIGH_FLAGS_SHIFT+4))
#define JSCLASS_FREEZE_CTOR (1<<(JSCLASS_HIGH_FLAGS_SHIFT+5))
#define JSCLASS_FREEZE_PROTO (1<<(JSCLASS_HIGH_FLAGS_SHIFT+5))
#define JSCLASS_FREEZE_CTOR (1<<(JSCLASS_HIGH_FLAGS_SHIFT+6))
#define JSCLASS_XPCONNECT_GLOBAL (1<<(JSCLASS_HIGH_FLAGS_SHIFT+6))
#define JSCLASS_XPCONNECT_GLOBAL (1<<(JSCLASS_HIGH_FLAGS_SHIFT+7))
/* Reserved for embeddings. */
#define JSCLASS_USERBIT2 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+7))
#define JSCLASS_USERBIT3 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+8))
#define JSCLASS_USERBIT2 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+8))
#define JSCLASS_USERBIT3 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+9))
/*
* Bits 26 through 31 are reserved for the CACHED_PROTO_KEY mechanism, see
@ -3786,7 +3792,7 @@ struct JSClass {
* with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was
* prevously allowed, but is now an ES5 violation and thus unsupported.
*/
#define JSCLASS_GLOBAL_SLOT_COUNT (JSProto_LIMIT * 3 + 9)
#define JSCLASS_GLOBAL_SLOT_COUNT (JSProto_LIMIT * 3 + 8)
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
#define JSCLASS_GLOBAL_FLAGS \
@ -4353,13 +4359,19 @@ extern JS_PUBLIC_API(JSBool)
JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp);
/*
* A JSNative that creates and returns a new iterator that iterates over the
* elements of |this|, up to |this.length|, in index order. This can be used to
* make any array-like object iterable. Just give the object an obj.iterator()
* method using this JSNative as the implementation.
* Create an object to iterate over the elements of obj in for-of order. This
* can be used to implement the iteratorObject hook for an array-like Class.
*/
extern JS_PUBLIC_API(JSBool)
JS_ArrayIterator(JSContext *cx, unsigned argc, jsval *vp);
extern JS_PUBLIC_API(JSObject *)
JS_NewElementIterator(JSContext *cx, JSObject *obj);
/*
* To make your array-like class iterable using the for-of loop, set the
* JSCLASS_FOR_OF_ITERATION bit in the class's flags field and set its
* .ext.iteratorObject hook to this function.
*/
extern JS_PUBLIC_API(JSObject *)
JS_ElementIteratorStub(JSContext *cx, JSHandleObject obj, JSBool keysonly);
extern JS_PUBLIC_API(JSBool)
JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,

View File

@ -1190,7 +1190,7 @@ array_trace(JSTracer *trc, JSObject *obj)
Class js::ArrayClass = {
"Array",
Class::NON_NATIVE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array),
Class::NON_NATIVE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array) | JSCLASS_FOR_OF_ITERATION,
JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
@ -1208,7 +1208,7 @@ Class js::ArrayClass = {
NULL, /* equality */
NULL, /* outerObject */
NULL, /* innerObject */
NULL, /* iteratorObject */
JS_ElementIteratorStub,
NULL, /* unused */
false, /* isWrappedNative */
},
@ -1251,7 +1251,7 @@ Class js::ArrayClass = {
Class js::SlowArrayClass = {
"Array",
JSCLASS_HAS_CACHED_PROTO(JSProto_Array),
JSCLASS_HAS_CACHED_PROTO(JSProto_Array) | JSCLASS_FOR_OF_ITERATION,
slowarray_addProperty,
JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
@ -1269,7 +1269,7 @@ Class js::SlowArrayClass = {
NULL, /* equality */
NULL, /* outerObject */
NULL, /* innerObject */
NULL, /* iteratorObject */
JS_ElementIteratorStub,
NULL, /* unused */
false, /* isWrappedNative */
}
@ -3621,7 +3621,6 @@ static JSFunctionSpec array_methods[] = {
JS_FN("some", array_some, 1,JSFUN_GENERIC_NATIVE),
JS_FN("every", array_every, 1,JSFUN_GENERIC_NATIVE),
JS_FN("iterator", JS_ArrayIterator, 0,0),
JS_FS_END
};

View File

@ -53,8 +53,7 @@ DEFINE_ATOM(ignoreCase, "ignoreCase")
DEFINE_ATOM(index, "index")
DEFINE_ATOM(input, "input")
DEFINE_ATOM(toISOString, "toISOString")
DEFINE_ATOM(iterator, "iterator")
DEFINE_ATOM(iteratorIntrinsic, "__iterator__")
DEFINE_ATOM(iterator, "__iterator__")
DEFINE_ATOM(join, "join")
DEFINE_ATOM(lastIndex, "lastIndex")
DEFINE_ATOM(length, "length")

View File

@ -189,7 +189,7 @@ class NativeIterCache
static const size_t SIZE = size_t(1) << 8;
/* Cached native iterators. */
PropertyIteratorObject *data[SIZE];
JSObject *data[SIZE];
static size_t getIndex(uint32_t key) {
return size_t(key) % SIZE;
@ -197,7 +197,7 @@ class NativeIterCache
public:
/* Native iterator most recently started. */
PropertyIteratorObject *last;
JSObject *last;
NativeIterCache()
: last(NULL) {
@ -209,11 +209,11 @@ class NativeIterCache
PodArrayZero(data);
}
PropertyIteratorObject *get(uint32_t key) const {
JSObject *get(uint32_t key) const {
return data[getIndex(key)];
}
void set(uint32_t key, PropertyIteratorObject *iterobj) {
void set(uint32_t key, JSObject *iterobj) {
data[getIndex(key)] = iterobj;
}
};
@ -1256,7 +1256,7 @@ struct JSContext : js::ContextFriendFields
DSTOffsetCache dstOffsetCache;
/* List of currently active non-escaping enumerators (for-in). */
js::PropertyIteratorObject *enumerators;
JSObject *enumerators;
private:
/* Innermost-executing generator or null if no generator are executing. */

View File

@ -23,7 +23,6 @@
#include "jsfun.h"
#include "jsgc.h"
#include "jsinterp.h"
#include "jsiter.h"
#include "jslock.h"
#include "jsnum.h"
#include "jsobj.h"
@ -34,7 +33,6 @@
#include "jsscript.h"
#include "jsstr.h"
#include "builtin/Eval.h"
#include "frontend/BytecodeCompiler.h"
#include "frontend/TokenStream.h"
#include "gc/Marking.h"
@ -43,6 +41,10 @@
#include "vm/ScopeObject.h"
#include "vm/Xdr.h"
#if JS_HAS_GENERATORS
# include "jsiter.h"
#endif
#ifdef JS_METHODJIT
#include "methodjit/MethodJIT.h"
#endif
@ -52,7 +54,6 @@
#include "jsinferinlines.h"
#include "jsobjinlines.h"
#include "jsscriptinlines.h"
#include "vm/ArgumentsObject-inl.h"
#include "vm/ScopeObject-inl.h"
#include "vm/Stack-inl.h"

View File

@ -3917,10 +3917,10 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
return false;
}
if (GET_UINT8(pc) == JSITER_ENUMERATE)
state.forTypes->addType(cx, Type::StringType());
else
if (GET_UINT8(pc) & JSITER_FOREACH)
state.forTypes->addType(cx, Type::UnknownType());
else
state.forTypes->addType(cx, Type::StringType());
break;
}

View File

@ -29,7 +29,6 @@
#include "jsgc.h"
#include "jsinterp.h"
#include "jsiter.h"
#include "jslibmath.h"
#include "jslock.h"
#include "jsnum.h"
#include "jsobj.h"
@ -38,15 +37,14 @@
#include "jsscope.h"
#include "jsscript.h"
#include "jsstr.h"
#include "jslibmath.h"
#include "builtin/Eval.h"
#include "gc/Marking.h"
#include "vm/Debugger.h"
#ifdef JS_METHODJIT
#include "methodjit/MethodJIT.h"
#include "methodjit/Logging.h"
#endif
#include "vm/Debugger.h"
#include "jsatominlines.h"
#include "jsinferinlines.h"
@ -59,7 +57,6 @@
#include "jsscriptinlines.h"
#include "jstypedarrayinlines.h"
#include "builtin/Iterator-inl.h"
#include "vm/Stack-inl.h"
#include "vm/String-inl.h"
@ -1004,8 +1001,8 @@ JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEDEC_LENGTH);
static inline bool
IteratorMore(JSContext *cx, JSObject *iterobj, bool *cond, Value *rval)
{
if (iterobj->isPropertyIterator()) {
NativeIterator *ni = iterobj->asPropertyIterator().getNativeIterator();
if (iterobj->isIterator()) {
NativeIterator *ni = iterobj->getNativeIterator();
if (ni->isKeyIter()) {
*cond = (ni->props_cursor < ni->props_end);
return true;
@ -1021,8 +1018,8 @@ IteratorMore(JSContext *cx, JSObject *iterobj, bool *cond, Value *rval)
static inline bool
IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval)
{
if (iterobj->isPropertyIterator()) {
NativeIterator *ni = iterobj->asPropertyIterator().getNativeIterator();
if (iterobj->isIterator()) {
NativeIterator *ni = iterobj->getNativeIterator();
if (ni->isKeyIter()) {
JS_ASSERT(ni->props_cursor < ni->props_end);
rval->setString(*ni->current());

View File

@ -43,7 +43,6 @@
#include "jsinferinlines.h"
#include "jsobjinlines.h"
#include "builtin/Iterator-inl.h"
#include "vm/MethodGuard-inl.h"
#include "vm/Stack-inl.h"
#include "vm/String-inl.h"
@ -52,6 +51,61 @@ using namespace mozilla;
using namespace js;
using namespace js::gc;
static void iterator_finalize(FreeOp *fop, JSObject *obj);
static void iterator_trace(JSTracer *trc, JSObject *obj);
static JSObject *iterator_iterator(JSContext *cx, HandleObject obj, JSBool keysonly);
Class js::IteratorClass = {
"Iterator",
JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator),
JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
JS_StrictPropertyStub, /* setProperty */
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub,
iterator_finalize,
NULL, /* checkAccess */
NULL, /* call */
NULL, /* construct */
NULL, /* hasInstance */
iterator_trace,
{
NULL, /* equality */
NULL, /* outerObject */
NULL, /* innerObject */
iterator_iterator,
NULL /* unused */
}
};
Class js::ElementIteratorClass = {
"ElementIterator",
JSCLASS_HAS_RESERVED_SLOTS(ElementIteratorObject::NumSlots),
JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
JS_StrictPropertyStub, /* setProperty */
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub,
NULL, /* finalize */
NULL, /* checkAccess */
NULL, /* call */
NULL, /* construct */
NULL, /* hasInstance */
NULL, /* trace */
{
NULL, /* equality */
NULL, /* outerObject */
NULL, /* innerObject */
iterator_iterator,
NULL /* unused */
}
};
static const gc::AllocKind ITERATOR_FINALIZE_KIND = gc::FINALIZE_OBJECT2;
void
@ -63,6 +117,27 @@ NativeIterator::mark(JSTracer *trc)
MarkObject(trc, &obj, "obj");
}
static void
iterator_finalize(FreeOp *fop, JSObject *obj)
{
JS_ASSERT(obj->isIterator());
NativeIterator *ni = obj->getNativeIterator();
if (ni) {
obj->setPrivate(NULL);
fop->free_(ni);
}
}
static void
iterator_trace(JSTracer *trc, JSObject *obj)
{
NativeIterator *ni = obj->getNativeIterator();
if (ni)
ni->mark(trc);
}
struct IdHashPolicy {
typedef jsid Lookup;
static HashNumber hash(jsid id) {
@ -342,8 +417,18 @@ GetCustomIterator(JSContext *cx, HandleObject obj, unsigned flags, Value *vp)
{
JS_CHECK_RECURSION(cx, return false);
/*
* for-of iteration does not fall back on __iterator__ or property
* enumeration. This is more conservative than the current proposed spec.
*/
if (flags == JSITER_FOR_OF) {
js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_NOT_ITERABLE,
JSDVG_SEARCH_STACK, ObjectValue(*obj), NULL, NULL, NULL);
return false;
}
/* Check whether we have a valid __iterator__ method. */
PropertyName *name = cx->runtime->atomState.iteratorIntrinsicAtom;
PropertyName *name = cx->runtime->atomState.iteratorAtom;
if (!GetMethod(cx, obj, name, 0, vp))
return false;
@ -394,8 +479,8 @@ Compare(T *a, T *b, size_t c)
return true;
}
static inline PropertyIteratorObject *
NewPropertyIteratorObject(JSContext *cx, unsigned flags)
static inline JSObject *
NewIteratorObject(JSContext *cx, unsigned flags)
{
if (flags & JSITER_ENUMERATE) {
RootedTypeObject type(cx);
@ -404,8 +489,8 @@ NewPropertyIteratorObject(JSContext *cx, unsigned flags)
return NULL;
RootedShape emptyEnumeratorShape(cx);
emptyEnumeratorShape = EmptyShape::getInitialShape(cx, &PropertyIteratorObject::class_,
NULL, NULL, ITERATOR_FINALIZE_KIND);
emptyEnumeratorShape = EmptyShape::getInitialShape(cx, &IteratorClass, NULL, NULL,
ITERATOR_FINALIZE_KIND);
if (!emptyEnumeratorShape)
return NULL;
@ -415,10 +500,10 @@ NewPropertyIteratorObject(JSContext *cx, unsigned flags)
return NULL;
JS_ASSERT(obj->numFixedSlots() == JSObject::ITER_CLASS_NFIXED_SLOTS);
return &obj->asPropertyIterator();
return obj;
}
return &NewBuiltinClassInstance(cx, &PropertyIteratorObject::class_)->asPropertyIterator();
return NewBuiltinClassInstance(cx, &IteratorClass);
}
NativeIterator *
@ -456,7 +541,7 @@ NativeIterator::init(JSObject *obj, unsigned flags, uint32_t slength, uint32_t k
}
static inline void
RegisterEnumerator(JSContext *cx, PropertyIteratorObject *iterobj, NativeIterator *ni)
RegisterEnumerator(JSContext *cx, JSObject *iterobj, NativeIterator *ni)
{
/* Register non-escaping native enumerators (for-in) with the current context. */
if (ni->flags & JSITER_ENUMERATE) {
@ -480,7 +565,7 @@ VectorToKeyIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVecto
types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED);
}
Rooted<PropertyIteratorObject *> iterobj(cx, NewPropertyIteratorObject(cx, flags));
RootedObject iterobj(cx, NewIteratorObject(cx, flags));
if (!iterobj)
return false;
@ -533,7 +618,7 @@ VectorToValueIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVec
types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED);
}
PropertyIteratorObject *iterobj = NewPropertyIteratorObject(cx, flags);
JSObject *iterobj = NewIteratorObject(cx, flags);
if (!iterobj)
return false;
@ -569,42 +654,30 @@ UpdateNativeIterator(NativeIterator *ni, JSObject *obj)
bool
GetIterator(JSContext *cx, HandleObject obj, unsigned flags, Value *vp)
{
if (flags == JSITER_FOR_OF) {
// for-of loop. The iterator is simply |obj.iterator()|.
Value method;
if (!obj->getProperty(cx, obj, cx->runtime->atomState.iteratorAtom, &method))
return false;
// Throw if obj.iterator isn't callable. js::Invoke is about to check
// for this kind of error anyway, but it would throw an inscrutable
// error message about |method| rather than this nice one about |obj|.
if (!method.isObject() || !method.toObject().isCallable()) {
char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, ObjectOrNullValue(obj), NULL);
if (!bytes)
return false;
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_ITERABLE, bytes);
cx->free_(bytes);
return false;
}
if (!Invoke(cx, ObjectOrNullValue(obj), method, 0, NULL, vp))
return false;
return true;
}
Vector<const Shape *, 8> shapes(cx);
uint32_t key = 0;
bool keysOnly = (flags == JSITER_ENUMERATE);
if (obj) {
/* Enumerate Iterator.prototype directly. */
if (JSIteratorOp op = obj->getClass()->ext.iteratorObject) {
JSObject *iterobj = op(cx, obj, !(flags & JSITER_FOREACH));
if (!iterobj)
return false;
vp->setObject(*iterobj);
types::MarkIteratorUnknown(cx);
return true;
/*
* Arrays and other classes representing iterable collections have
* the JSCLASS_FOR_OF_ITERATION flag. This flag means that the
* object responds to all other kinds of enumeration (for-in,
* for-each, Object.keys, Object.getOwnPropertyNames, etc.) in the
* default way, ignoring the hook. The hook is used only when
* iterating in the style of a for-of loop.
*/
if (!(obj->getClass()->flags & JSCLASS_FOR_OF_ITERATION) || flags == JSITER_FOR_OF) {
JSObject *iterobj = op(cx, obj, !(flags & (JSITER_FOREACH | JSITER_FOR_OF)));
if (!iterobj)
return false;
vp->setObject(*iterobj);
types::MarkIteratorUnknown(cx);
return true;
}
}
if (keysOnly) {
@ -614,7 +687,7 @@ GetIterator(JSContext *cx, HandleObject obj, unsigned flags, Value *vp)
* objects here, as they are not inserted into the cache and
* will result in a miss.
*/
PropertyIteratorObject *last = cx->runtime->nativeIterCache.last;
JSObject *last = cx->runtime->nativeIterCache.last;
JSObject *proto = obj->getProto();
if (last) {
NativeIterator *lastni = last->getNativeIterator();
@ -653,7 +726,7 @@ GetIterator(JSContext *cx, HandleObject obj, unsigned flags, Value *vp)
pobj = pobj->getProto();
} while (pobj);
PropertyIteratorObject *iterobj = cx->runtime->nativeIterCache.get(key);
JSObject *iterobj = cx->runtime->nativeIterCache.get(key);
if (iterobj) {
NativeIterator *ni = iterobj->getNativeIterator();
if (!(ni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) &&
@ -700,7 +773,7 @@ GetIterator(JSContext *cx, HandleObject obj, unsigned flags, Value *vp)
return false;
}
PropertyIteratorObject *iterobj = &vp->toObject().asPropertyIterator();
JSObject *iterobj = &vp->toObject();
/* Cache the iterator object if possible. */
if (shapes.length())
@ -713,18 +786,12 @@ GetIterator(JSContext *cx, HandleObject obj, unsigned flags, Value *vp)
}
JSBool
js_ThrowStopIteration(JSContext *cx)
static JSObject *
iterator_iterator(JSContext *cx, HandleObject obj, JSBool keysonly)
{
JS_ASSERT(!cx->isExceptionPending());
Value v;
if (js_FindClassObject(cx, NULL, JSProto_StopIteration, &v))
cx->setPendingException(v);
return false;
return obj;
}
/*** Iterator objects ****************************************************************************/
static JSBool
Iterator(JSContext *cx, unsigned argc, Value *vp)
{
@ -745,12 +812,15 @@ Iterator(JSContext *cx, unsigned argc, Value *vp)
return true;
}
static JSBool
iterator_iterator(JSContext *cx, unsigned argc, Value *vp)
JSBool
js_ThrowStopIteration(JSContext *cx)
{
CallArgs args = CallArgsFromVp(argc, vp);
args.rval() = args.thisv();
return true;
Value v;
JS_ASSERT(!JS_IsExceptionPending(cx));
if (js_FindClassObject(cx, NULL, JSProto_StopIteration, &v))
cx->setPendingException(v);
return JS_FALSE;
}
static JSBool
@ -759,11 +829,8 @@ iterator_next(JSContext *cx, unsigned argc, Value *vp)
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject thisObj(cx);
if (!NonGenericMethodGuard(cx, args, iterator_next, &PropertyIteratorObject::class_,
thisObj.address()))
{
if (!NonGenericMethodGuard(cx, args, iterator_next, &IteratorClass, thisObj.address()))
return false;
}
if (!thisObj)
return true;
@ -778,151 +845,10 @@ iterator_next(JSContext *cx, unsigned argc, Value *vp)
return js_IteratorNext(cx, thisObj, &args.rval());
}
#define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT)
static JSFunctionSpec iterator_methods[] = {
JS_FN("iterator", iterator_iterator, 0, 0),
JS_FN("next", iterator_next, 0, 0),
JS_FS_END
};
static JSObject *
iterator_iteratorObject(JSContext *cx, HandleObject obj, JSBool keysonly)
{
return obj;
}
void
PropertyIteratorObject::trace(JSTracer *trc, JSObject *obj)
{
if (NativeIterator *ni = obj->asPropertyIterator().getNativeIterator())
ni->mark(trc);
}
void
PropertyIteratorObject::finalize(FreeOp *fop, JSObject *obj)
{
if (NativeIterator *ni = obj->asPropertyIterator().getNativeIterator()) {
obj->asPropertyIterator().setNativeIterator(NULL);
fop->free_(ni);
}
}
Class PropertyIteratorObject::class_ = {
"Iterator",
JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator) |
JSCLASS_HAS_PRIVATE,
JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
JS_StrictPropertyStub, /* setProperty */
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub,
finalize,
NULL, /* checkAccess */
NULL, /* call */
NULL, /* construct */
NULL, /* hasInstance */
trace,
{
NULL, /* equality */
NULL, /* outerObject */
NULL, /* innerObject */
iterator_iteratorObject,
NULL /* unused */
}
};
const uint32_t CLOSED_INDEX = UINT32_MAX;
JSObject *
ElementIteratorObject::create(JSContext *cx, Handle<Value> target)
{
GlobalObject *global = GetCurrentGlobal(cx);
Rooted<JSObject*> proto(cx, global->getOrCreateElementIteratorPrototype(cx));
if (!proto)
return NULL;
JSObject *iterobj = NewObjectWithGivenProto(cx, &class_, proto, global);
if (iterobj) {
iterobj->setReservedSlot(TargetSlot, target);
iterobj->setReservedSlot(IndexSlot, Int32Value(0));
}
return iterobj;
}
JSBool
ElementIteratorObject::next(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
JSObject *iterobj;
if (!NonGenericMethodGuard(cx, args, next, &class_, &iterobj))
return false;
if (!iterobj)
return true;
uint32_t i, length;
Value target = iterobj->getReservedSlot(TargetSlot);
Rooted<JSObject*> obj(cx);
// Get target.length.
if (target.isString()) {
length = uint32_t(target.toString()->length());
} else {
obj = ValueToObject(cx, target);
if (!obj)
goto close;
if (!js_GetLengthProperty(cx, obj, &length))
goto close;
}
// Check target.length.
i = uint32_t(iterobj->getReservedSlot(IndexSlot).toInt32());
if (i >= length) {
js_ThrowStopIteration(cx);
goto close;
}
// Get target[i].
JS_ASSERT(i + 1 > i);
if (target.isString()) {
JSString *c = cx->runtime->staticStrings.getUnitStringForElement(cx, target.toString(), i);
if (!c)
goto close;
vp->setString(c);
} else {
if (!obj->getElement(cx, obj, i, vp))
goto close;
}
// On success, bump the index.
iterobj->setReservedSlot(IndexSlot, Int32Value(int32_t(i + 1)));
return true;
close:
// Close the iterator. The TargetSlot will never be used again, so don't keep a
// reference to it.
iterobj->setReservedSlot(TargetSlot, UndefinedValue());
iterobj->setReservedSlot(IndexSlot, Int32Value(int32_t(CLOSED_INDEX)));
return false;
}
Class ElementIteratorObject::class_ = {
"Iterator",
JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_RESERVED_SLOTS(NumSlots),
JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
JS_StrictPropertyStub, /* setProperty */
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub,
NULL /* finalize */
};
JSFunctionSpec ElementIteratorObject::methods[] = {
JS_FN("next", next, 0, 0),
JS_FN(js_next_str, iterator_next, 0,JSPROP_ROPERM),
JS_FS_END
};
@ -931,7 +857,11 @@ static JSBool
CloseGenerator(JSContext *cx, JSObject *genobj);
#endif
bool
/*
* Call ToObject(v).__iterator__(keyonly) if ToObject(v).__iterator__ exists.
* Otherwise construct the default iterator.
*/
JSBool
js::ValueToIterator(JSContext *cx, unsigned flags, Value *vp)
{
/* JSITER_KEYVALUE must always come with JSITER_FOREACH */
@ -975,9 +905,9 @@ js::CloseIterator(JSContext *cx, JSObject *obj)
{
cx->iterValue.setMagic(JS_NO_ITER_VALUE);
if (obj->isPropertyIterator()) {
if (obj->isIterator()) {
/* Remove enumerators from the active list, which is a stack. */
NativeIterator *ni = obj->asPropertyIterator().getNativeIterator();
NativeIterator *ni = obj->getNativeIterator();
if (ni->flags & JSITER_ENUMERATE) {
JS_ASSERT(cx->enumerators == obj);
@ -1015,8 +945,8 @@ js::UnwindIteratorForException(JSContext *cx, JSObject *obj)
void
js::UnwindIteratorForUncatchableException(JSContext *cx, JSObject *obj)
{
if (obj->isPropertyIterator()) {
NativeIterator *ni = obj->asPropertyIterator().getNativeIterator();
if (obj->isIterator()) {
NativeIterator *ni = obj->getNativeIterator();
if (ni->flags & JSITER_ENUMERATE) {
JS_ASSERT(cx->enumerators == obj);
cx->enumerators = ni->next;
@ -1045,7 +975,7 @@ template<typename StringPredicate>
static bool
SuppressDeletedPropertyHelper(JSContext *cx, HandleObject obj, StringPredicate predicate)
{
PropertyIteratorObject *iterobj = cx->enumerators;
JSObject *iterobj = cx->enumerators;
while (iterobj) {
again:
NativeIterator *ni = iterobj->getNativeIterator();
@ -1168,14 +1098,83 @@ js_SuppressDeletedElements(JSContext *cx, HandleObject obj, uint32_t begin, uint
return SuppressDeletedPropertyHelper(cx, obj, IndexRangePredicate(begin, end));
}
const uint32_t CLOSED_INDEX = UINT32_MAX;
JSObject *
ElementIteratorObject::create(JSContext *cx, HandleObject obj)
{
JS_ASSERT(obj);
JSObject *iterobj = NewObjectWithGivenProto(cx, &ElementIteratorClass, NULL, obj);
if (iterobj) {
iterobj->setReservedSlot(TargetSlot, ObjectValue(*obj));
iterobj->setReservedSlot(IndexSlot, Int32Value(0));
}
return iterobj;
}
inline uint32_t
ElementIteratorObject::getIndex() const
{
return uint32_t(getReservedSlot(IndexSlot).toInt32());
}
inline JSObject *
ElementIteratorObject::getTargetObject() const
{
return &getReservedSlot(TargetSlot).toObject();
}
inline void
ElementIteratorObject::setIndex(uint32_t index)
{
setReservedSlot(IndexSlot, Int32Value(int32_t(index)));
}
bool
ElementIteratorObject::iteratorNext(JSContext *cx, Value *vp)
{
Rooted<ElementIteratorObject*> self(cx, this);
uint32_t i, length;
RootedObject obj(cx, getTargetObject());
if (!js_GetLengthProperty(cx, obj, &length))
goto error;
i = self->getIndex();
if (i >= length) {
self->setIndex(CLOSED_INDEX);
vp->setMagic(JS_NO_ITER_VALUE);
return true;
}
JS_ASSERT(i + 1 > i);
if (!obj->getElement(cx, obj, i, vp))
goto error;
/* On success, bump the index. */
self->setIndex(i + 1);
return true;
error:
self->setIndex(CLOSED_INDEX);
return false;
}
inline js::ElementIteratorObject *
JSObject::asElementIterator()
{
JS_ASSERT(isElementIterator());
return static_cast<js::ElementIteratorObject *>(this);
}
JSBool
js_IteratorMore(JSContext *cx, HandleObject iterobj, Value *rval)
{
/* Fast path for native iterators */
NativeIterator *ni = NULL;
if (iterobj->isPropertyIterator()) {
if (iterobj->isIterator()) {
/* Key iterators are handled by fast-paths. */
ni = iterobj->asPropertyIterator().getNativeIterator();
ni = iterobj->getNativeIterator();
bool more = ni->props_cursor < ni->props_end;
if (ni->isKeyIter() || !more) {
rval->setBoolean(more);
@ -1203,6 +1202,25 @@ js_IteratorMore(JSContext *cx, HandleObject iterobj, Value *rval)
return false;
if ((ni->flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, *rval, rval))
return false;
} else if (iterobj->isElementIterator()) {
/*
* Like native iterators, element iterators do not have a .next
* method, so this fast path is necessary for correctness.
*/
if (!iterobj->asElementIterator()->iteratorNext(cx, rval))
return false;
if (rval->isMagic(JS_NO_ITER_VALUE)) {
cx->iterValue.setMagic(JS_NO_ITER_VALUE);
rval->setBoolean(false);
return true;
}
} else if (iterobj->isProxy()) {
if (!Proxy::iteratorNext(cx, iterobj, rval))
return false;
if (rval->isMagic(JS_NO_ITER_VALUE)) {
rval->setBoolean(false);
return true;
}
} else {
/* Call the iterator object's .next method. */
if (!GetMethod(cx, iterobj, cx->runtime->atomState.nextAtom, 0, rval))
@ -1230,12 +1248,12 @@ JSBool
js_IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval)
{
/* Fast path for native iterators */
if (iterobj->isPropertyIterator()) {
if (iterobj->isIterator()) {
/*
* Implement next directly as all the methods of the native iterator are
* read-only and permanent.
*/
NativeIterator *ni = iterobj->asPropertyIterator().getNativeIterator();
NativeIterator *ni = iterobj->getNativeIterator();
if (ni->isKeyIter()) {
JS_ASSERT(ni->props_cursor < ni->props_end);
*rval = StringValue(*ni->current());
@ -1274,7 +1292,7 @@ stopiter_hasInstance(JSContext *cx, HandleObject obj, const Value *v, JSBool *bp
}
Class js::StopIterationClass = {
"StopIteration",
js_StopIteration_str,
JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration) |
JSCLASS_FREEZE_PROTO,
JS_PropertyStub, /* addProperty */
@ -1291,8 +1309,6 @@ Class js::StopIterationClass = {
NULL /* construct */
};
/*** Generators **********************************************************************************/
#if JS_HAS_GENERATORS
static void
@ -1391,7 +1407,7 @@ Class js::GeneratorClass = {
NULL, /* equality */
NULL, /* outerObject */
NULL, /* innerObject */
iterator_iteratorObject,
iterator_iterator,
NULL /* unused */
}
};
@ -1532,7 +1548,7 @@ SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj,
gen->regs = cx->regs();
cx->enterGenerator(gen); /* OOM check above. */
PropertyIteratorObject *enumerators = cx->enumerators;
JSObject *enumerators = cx->enumerators;
cx->enumerators = gen->enumerators;
ok = RunScript(cx, fp->script(), fp);
@ -1674,89 +1690,93 @@ generator_close(JSContext *cx, unsigned argc, Value *vp)
return generator_op(cx, generator_close, JSGENOP_CLOSE, vp, argc);
}
#define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT)
static JSFunctionSpec generator_methods[] = {
JS_FN("iterator", iterator_iterator, 0, 0),
JS_FN("next", generator_next, 0,JSPROP_ROPERM),
JS_FN("send", generator_send, 1,JSPROP_ROPERM),
JS_FN("throw", generator_throw, 1,JSPROP_ROPERM),
JS_FN("close", generator_close, 0,JSPROP_ROPERM),
JS_FN(js_next_str, generator_next, 0,JSPROP_ROPERM),
JS_FN(js_send_str, generator_send, 1,JSPROP_ROPERM),
JS_FN(js_throw_str, generator_throw, 1,JSPROP_ROPERM),
JS_FN(js_close_str, generator_close, 0,JSPROP_ROPERM),
JS_FS_END
};
#endif /* JS_HAS_GENERATORS */
/* static */ bool
GlobalObject::initIteratorClasses(JSContext *cx, Handle<GlobalObject *> global)
static bool
InitIteratorClass(JSContext *cx, Handle<GlobalObject*> global)
{
Rooted<JSObject*> iteratorProto(cx);
Value iteratorProtoVal = global->getPrototype(JSProto_Iterator);
if (iteratorProtoVal.isObject()) {
iteratorProto = &iteratorProtoVal.toObject();
} else {
iteratorProto = global->createBlankPrototype(cx, &PropertyIteratorObject::class_);
if (!iteratorProto)
return false;
RootedObject iteratorProto(cx, global->createBlankPrototype(cx, &IteratorClass));
if (!iteratorProto)
return false;
AutoIdVector blank(cx);
NativeIterator *ni = NativeIterator::allocateIterator(cx, 0, blank);
if (!ni)
return false;
ni->init(NULL, 0 /* flags */, 0, 0);
AutoIdVector blank(cx);
NativeIterator *ni = NativeIterator::allocateIterator(cx, 0, blank);
if (!ni)
return false;
ni->init(NULL, 0 /* flags */, 0, 0);
iteratorProto->asPropertyIterator().setNativeIterator(ni);
iteratorProto->setNativeIterator(ni);
Rooted<JSFunction*> ctor(cx);
ctor = global->createConstructor(cx, Iterator, CLASS_NAME(cx, Iterator), 2);
if (!ctor)
return false;
if (!LinkConstructorAndPrototype(cx, ctor, iteratorProto))
return false;
if (!DefinePropertiesAndBrand(cx, iteratorProto, NULL, iterator_methods))
return false;
if (!DefineConstructorAndPrototype(cx, global, JSProto_Iterator, ctor, iteratorProto))
return false;
}
RootedFunction ctor(cx);
ctor = global->createConstructor(cx, Iterator, CLASS_NAME(cx, Iterator), 2);
if (!ctor)
return false;
Rooted<JSObject*> proto(cx);
if (global->getSlot(ELEMENT_ITERATOR_PROTO).isUndefined()) {
Class *cls = &ElementIteratorObject::class_;
proto = global->createBlankPrototypeInheriting(cx, cls, *iteratorProto);
if (!proto || !DefinePropertiesAndBrand(cx, proto, NULL, ElementIteratorObject::methods))
return false;
global->setReservedSlot(ELEMENT_ITERATOR_PROTO, ObjectValue(*proto));
}
if (!LinkConstructorAndPrototype(cx, ctor, iteratorProto))
return false;
if (!DefinePropertiesAndBrand(cx, iteratorProto, NULL, iterator_methods))
return false;
return DefineConstructorAndPrototype(cx, global, JSProto_Iterator, ctor, iteratorProto);
}
/* static */ bool
GlobalObject::initGeneratorClass(JSContext *cx, Handle<GlobalObject*> global)
{
#if JS_HAS_GENERATORS
if (global->getSlot(GENERATOR_PROTO).isUndefined()) {
proto = global->createBlankPrototype(cx, &GeneratorClass);
if (!proto || !DefinePropertiesAndBrand(cx, proto, NULL, generator_methods))
return false;
global->setReservedSlot(GENERATOR_PROTO, ObjectValue(*proto));
}
RootedObject proto(cx, global->createBlankPrototype(cx, &GeneratorClass));
if (!proto || !DefinePropertiesAndBrand(cx, proto, NULL, generator_methods))
return false;
global->setReservedSlot(GENERATOR_PROTO, ObjectValue(*proto));
#endif
if (global->getPrototype(JSProto_StopIteration).isUndefined()) {
proto = global->createBlankPrototype(cx, &StopIterationClass);
if (!proto || !proto->freeze(cx))
return false;
/* This should use a non-JSProtoKey'd slot, but this is easier for now. */
if (!DefineConstructorAndPrototype(cx, global, JSProto_StopIteration, proto, proto))
return false;
MarkStandardClassInitializedNoProto(global, &StopIterationClass);
}
return true;
}
static JSObject *
InitStopIterationClass(JSContext *cx, Handle<GlobalObject*> global)
{
RootedObject proto(cx, global->createBlankPrototype(cx, &StopIterationClass));
if (!proto || !proto->freeze(cx))
return NULL;
/* This should use a non-JSProtoKey'd slot, but this is easier for now. */
if (!DefineConstructorAndPrototype(cx, global, JSProto_StopIteration, proto, proto))
return NULL;
MarkStandardClassInitializedNoProto(global, &StopIterationClass);
return proto;
}
JSObject *
js_InitIteratorClasses(JSContext *cx, JSObject *obj)
{
JS_ASSERT(obj->isNative());
Rooted<GlobalObject*> global(cx, &obj->asGlobal());
if (!GlobalObject::initIteratorClasses(cx, global))
/*
* Bail if Iterator has already been initialized. We test for Iterator
* rather than for StopIteration because if js_InitIteratorClasses recurs,
* as happens when the StopIteration object is frozen, initializing the
* Iterator class a second time will assert.
*/
JSObject *iter;
if (!js_GetClassObject(cx, global, JSProto_Iterator, &iter))
return NULL;
return global->getIteratorPrototype();
if (iter)
return iter;
if (!InitIteratorClass(cx, global) || !GlobalObject::initGeneratorClass(cx, global))
return NULL;
return InitStopIterationClass(cx, global);
}

View File

@ -28,17 +28,16 @@
namespace js {
struct NativeIterator
{
struct NativeIterator {
HeapPtrObject obj;
HeapPtr<JSFlatString> *props_array;
HeapPtr<JSFlatString> *props_cursor;
HeapPtr<JSFlatString> *props_end;
const Shape **shapes_array;
uint32_t shapes_length;
uint32_t shapes_key;
uint32_t flags;
PropertyIteratorObject *next; /* Forms cx->enumerators list, garbage otherwise. */
uint32_t shapes_length;
uint32_t shapes_key;
uint32_t flags;
JSObject *next; /* Forms cx->enumerators list, garbage otherwise. */
bool isKeyIter() const { return (flags & JSITER_FOREACH) == 0; }
@ -70,45 +69,65 @@ struct NativeIterator
void mark(JSTracer *trc);
};
class PropertyIteratorObject : public JSObject
{
class ElementIteratorObject : public JSObject {
public:
static Class class_;
inline NativeIterator *getNativeIterator() const;
inline void setNativeIterator(js::NativeIterator *ni);
private:
static void trace(JSTracer *trc, JSObject *obj);
static void finalize(FreeOp *fop, JSObject *obj);
};
/*
* Array iterators are roughly like this:
*
* Array.prototype.iterator = function iterator() {
* for (var i = 0; i < (this.length >>> 0); i++)
* yield this[i];
* }
*
* However they are not generators. They are a different class. The semantics
* of Array iterators will be given in the eventual ES6 spec in full detail.
*/
class ElementIteratorObject : public JSObject
{
public:
static JSObject *create(JSContext *cx, Handle<Value> target);
static Class class_;
static JSFunctionSpec methods[];
private:
enum {
TargetSlot,
IndexSlot,
NumSlots
};
static JSBool next(JSContext *cx, unsigned argc, Value *vp);
static JSObject *create(JSContext *cx, HandleObject target);
inline uint32_t getIndex() const;
inline void setIndex(uint32_t index);
inline JSObject *getTargetObject() const;
/*
Array iterators are like this:
Array.prototype[iterate] = function () {
for (var i = 0; i < (this.length >>> 0); i++) {
var desc = Object.getOwnPropertyDescriptor(this, i);
yield desc === undefined ? undefined : this[i];
}
}
This has the following implications:
- Array iterators are generic; Array.prototype[iterate] can be transferred to
any other object to create iterators over it.
- The next() method of an Array iterator is non-reentrant. Trying to reenter,
e.g. by using it on an object with a length getter that calls it.next() on
the same iterator, causes a TypeError.
- The iterator fetches obj.length every time its next() method is called.
- The iterator converts obj.length to a whole number using ToUint32. As a
consequence the iterator can't go on forever; it can yield at most 2^32-1
values. Then i will be 0xffffffff, and no possible length value will be
greater than that.
- The iterator does not skip "array holes". When it encounters a hole, it
yields undefined.
- The iterator never consults the prototype chain.
- If an element has a getter which throws, the exception is propagated, and
the iterator is closed (that is, all future calls to next() will simply
throw StopIteration).
Note that if next() were reentrant, even more details of its inner
workings would be observable.
*/
/*
* If there are any more elements to visit, store the value of the next
* element in *vp, increment the index, and return true. If not, call
* vp->setMagic(JS_NO_ITER_VALUE) and return true. Return false on error.
*/
bool iteratorNext(JSContext *cx, Value *vp);
};
bool
@ -136,16 +155,16 @@ EnumeratedIdVectorToIterator(JSContext *cx, HandleObject obj, unsigned flags, Au
* for-in semantics are required, and when the caller can guarantee that the
* iterator will never be exposed to scripts.
*/
bool
extern JSBool
ValueToIterator(JSContext *cx, unsigned flags, Value *vp);
bool
extern bool
CloseIterator(JSContext *cx, JSObject *iterObj);
bool
extern bool
UnwindIteratorForException(JSContext *cx, JSObject *obj);
void
extern void
UnwindIteratorForUncatchableException(JSContext *cx, JSObject *obj);
}
@ -210,8 +229,7 @@ Next(JSContext *cx, HandleObject iter, Value *vp)
* and the failure is allowed to propagate on cx, as in this example if DoStuff
* fails. In that case, ForOfIterator's destructor does all necessary cleanup.
*/
class ForOfIterator
{
class ForOfIterator {
private:
JSContext *cx;
RootedObject iterator;
@ -287,7 +305,7 @@ struct JSGenerator
js::HeapPtrObject obj;
JSGeneratorState state;
js::FrameRegs regs;
js::PropertyIteratorObject *enumerators;
JSObject *enumerators;
JSGenerator *prevGenerator;
js::StackFrame *fp;
js::HeapValue stackSnapshot[1];

View File

@ -700,6 +700,459 @@ obj_valueOf(JSContext *cx, unsigned argc, Value *vp)
return true;
}
/* We should be able to assert this for *any* fp->scopeChain(). */
static void
AssertInnerizedScopeChain(JSContext *cx, JSObject &scopeobj)
{
#ifdef DEBUG
for (JSObject *o = &scopeobj; o; o = o->enclosingScope()) {
if (JSObjectOp op = o->getClass()->ext.innerObject) {
Rooted<JSObject*> obj(cx, o);
JS_ASSERT(op(cx, obj) == o);
}
}
#endif
}
#ifndef EVAL_CACHE_CHAIN_LIMIT
# define EVAL_CACHE_CHAIN_LIMIT 4
#endif
void
EvalCache::purge()
{
/*
* Purge all scripts from the eval cache. In addition to removing them from
* table_, null out the evalHashLink field of any script removed. Since
* evalHashLink is in a union with globalObject, this allows the GC to
* indiscriminately use the union as a nullable globalObject pointer.
*/
for (size_t i = 0; i < ArrayLength(table_); ++i) {
for (JSScript **listHeadp = &table_[i]; *listHeadp; ) {
JSScript *script = *listHeadp;
JS_ASSERT(GetGCThingTraceKind(script) == JSTRACE_SCRIPT);
*listHeadp = script->evalHashLink();
script->evalHashLink() = NULL;
}
}
}
inline JSScript **
EvalCache::bucket(JSLinearString *str)
{
const jschar *s = str->chars();
size_t n = str->length();
if (n > 100)
n = 100;
uint32_t h;
for (h = 0; n; s++, n--)
h = JS_ROTATE_LEFT32(h, 4) ^ *s;
h *= JS_GOLDEN_RATIO;
h >>= 32 - SHIFT;
JS_ASSERT(h < ArrayLength(table_));
return &table_[h];
}
static JS_ALWAYS_INLINE JSScript *
EvalCacheLookup(JSContext *cx, JSLinearString *str, StackFrame *caller, unsigned staticLevel,
JSPrincipals *principals, JSObject &scopeobj, JSScript **bucket)
{
/*
* Cache local eval scripts indexed by source qualified by scope.
*
* An eval cache entry should never be considered a hit unless its
* strictness matches that of the new eval code. The existing code takes
* care of this, because hits are qualified by the function from which
* eval was called, whose strictness doesn't change. (We don't cache evals
* in eval code, so the calling function corresponds to the calling script,
* and its strictness never varies.) Scripts produced by calls to eval from
* global code aren't cached.
*
* FIXME bug 620141: Qualify hits by calling script rather than function.
* Then we wouldn't need the unintuitive !isEvalFrame() hack in EvalKernel
* to avoid caching nested evals in functions (thus potentially mismatching
* on strict mode), and we could cache evals in global code if desired.
*/
unsigned count = 0;
JSScript **scriptp = bucket;
JSVersion version = cx->findVersion();
JSScript *script;
JSSubsumePrincipalsOp subsume = cx->runtime->securityCallbacks->subsumePrincipals;
while ((script = *scriptp) != NULL) {
if (script->savedCallerFun &&
script->staticLevel == staticLevel &&
script->getVersion() == version &&
!script->hasSingletons &&
(!subsume || script->principals == principals ||
(subsume(principals, script->principals) &&
subsume(script->principals, principals)))) {
/*
* Get the prior (cache-filling) eval's saved caller function.
* See frontend::CompileScript.
*/
JSFunction *fun = script->getCallerFunction();
if (fun == caller->fun()) {
/*
* Get the source string passed for safekeeping in the atom map
* by the prior eval to frontend::CompileScript.
*/
JSAtom *src = script->atoms[0];
if (src == str || EqualStrings(src, str)) {
/*
* Source matches. Make sure there are no inner objects
* which might use the wrong parent and/or call scope by
* reusing the previous eval's script. Skip the script's
* first object, which entrains the eval's scope.
*/
JS_ASSERT(script->objects()->length >= 1);
if (script->objects()->length == 1 &&
!script->hasRegexps()) {
JS_ASSERT(staticLevel == script->staticLevel);
*scriptp = script->evalHashLink();
script->evalHashLink() = NULL;
return script;
}
}
}
}
if (++count == EVAL_CACHE_CHAIN_LIMIT)
return NULL;
scriptp = &script->evalHashLink();
}
return NULL;
}
/*
* There are two things we want to do with each script executed in EvalKernel:
* 1. notify jsdbgapi about script creation/destruction
* 2. add the script to the eval cache when EvalKernel is finished
*
* NB: Although the eval cache keeps a script alive wrt to the JS engine, from
* a jsdbgapi user's perspective, we want each eval() to create and destroy a
* script. This hides implementation details and means we don't have to deal
* with calls to JS_GetScriptObject for scripts in the eval cache (currently,
* script->object aliases script->evalHashLink()).
*/
class EvalScriptGuard
{
JSContext *cx_;
JSLinearString *str_;
JSScript **bucket_;
Rooted<JSScript*> script_;
public:
EvalScriptGuard(JSContext *cx, JSLinearString *str)
: cx_(cx),
str_(str),
script_(cx) {
bucket_ = cx->runtime->evalCache.bucket(str);
}
~EvalScriptGuard() {
if (script_) {
CallDestroyScriptHook(cx_->runtime->defaultFreeOp(), script_);
script_->isActiveEval = false;
script_->isCachedEval = true;
script_->evalHashLink() = *bucket_;
*bucket_ = script_;
}
}
void lookupInEvalCache(StackFrame *caller, unsigned staticLevel,
JSPrincipals *principals, JSObject &scopeobj) {
if (JSScript *found = EvalCacheLookup(cx_, str_, caller, staticLevel,
principals, scopeobj, bucket_)) {
js_CallNewScriptHook(cx_, found, NULL);
script_ = found;
script_->isCachedEval = false;
script_->isActiveEval = true;
}
}
void setNewScript(JSScript *script) {
/* JSScript::initFromEmitter has already called js_CallNewScriptHook. */
JS_ASSERT(!script_ && script);
script_ = script;
script_->isActiveEval = true;
}
bool foundScript() {
return !!script_;
}
JSScript *script() const {
JS_ASSERT(script_);
return script_;
}
};
/* Define subset of ExecuteType so that casting performs the injection. */
enum EvalType { DIRECT_EVAL = EXECUTE_DIRECT_EVAL, INDIRECT_EVAL = EXECUTE_INDIRECT_EVAL };
/*
* Common code implementing direct and indirect eval.
*
* Evaluate call.argv[2], if it is a string, in the context of the given calling
* frame, with the provided scope chain, with the semantics of either a direct
* or indirect eval (see ES5 10.4.2). If this is an indirect eval, scopeobj
* must be a global object.
*
* On success, store the completion value in call.rval and return true.
*/
static bool
EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, StackFrame *caller,
HandleObject scopeobj)
{
JS_ASSERT((evalType == INDIRECT_EVAL) == (caller == NULL));
JS_ASSERT_IF(evalType == INDIRECT_EVAL, scopeobj->isGlobal());
AssertInnerizedScopeChain(cx, *scopeobj);
if (!scopeobj->global().isRuntimeCodeGenEnabled(cx)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CSP_BLOCKED_EVAL);
return false;
}
/* ES5 15.1.2.1 step 1. */
if (args.length() < 1) {
args.rval().setUndefined();
return true;
}
if (!args[0].isString()) {
args.rval() = args[0];
return true;
}
JSString *str = args[0].toString();
/* ES5 15.1.2.1 steps 2-8. */
/*
* Per ES5, indirect eval runs in the global scope. (eval is specified this
* way so that the compiler can make assumptions about what bindings may or
* may not exist in the current frame if it doesn't see 'eval'.)
*/
unsigned staticLevel;
RootedValue thisv(cx);
if (evalType == DIRECT_EVAL) {
staticLevel = caller->script()->staticLevel + 1;
/*
* Direct calls to eval are supposed to see the caller's |this|. If we
* haven't wrapped that yet, do so now, before we make a copy of it for
* the eval code to use.
*/
if (!ComputeThis(cx, caller))
return false;
thisv = caller->thisValue();
#ifdef DEBUG
jsbytecode *callerPC = caller->pcQuadratic(cx);
JS_ASSERT(callerPC && JSOp(*callerPC) == JSOP_EVAL);
#endif
} else {
JS_ASSERT(args.callee().global() == *scopeobj);
staticLevel = 0;
/* Use the global as 'this', modulo outerization. */
JSObject *thisobj = scopeobj->thisObject(cx);
if (!thisobj)
return false;
thisv = ObjectValue(*thisobj);
}
Rooted<JSLinearString*> linearStr(cx, str->ensureLinear(cx));
if (!linearStr)
return false;
const jschar *chars = linearStr->chars();
size_t length = linearStr->length();
SkipRoot skip(cx, &chars);
/*
* If the eval string starts with '(' or '[' and ends with ')' or ']', it may be JSON.
* Try the JSON parser first because it's much faster. If the eval string
* isn't JSON, JSON parsing will probably fail quickly, so little time
* will be lost.
*
* Don't use the JSON parser if the caller is strict mode code, because in
* strict mode object literals must not have repeated properties, and the
* JSON parser cheerfully (and correctly) accepts them. If you're parsing
* JSON with eval and using strict mode, you deserve to be slow.
*/
if (length > 2 &&
((chars[0] == '[' && chars[length - 1] == ']') ||
(chars[0] == '(' && chars[length - 1] == ')')) &&
(!caller || !caller->script()->strictModeCode))
{
/*
* Remarkably, JavaScript syntax is not a superset of JSON syntax:
* strings in JavaScript cannot contain the Unicode line and paragraph
* terminator characters U+2028 and U+2029, but strings in JSON can.
* Rather than force the JSON parser to handle this quirk when used by
* eval, we simply don't use the JSON parser when either character
* appears in the provided string. See bug 657367.
*/
for (const jschar *cp = &chars[1], *end = &chars[length - 2]; ; cp++) {
if (*cp == 0x2028 || *cp == 0x2029)
break;
if (cp == end) {
bool isArray = (chars[0] == '[');
JSONParser parser(cx, isArray ? chars : chars + 1, isArray ? length : length - 2,
JSONParser::StrictJSON, JSONParser::NoError);
Value tmp;
if (!parser.parse(&tmp))
return false;
if (tmp.isUndefined())
break;
args.rval() = tmp;
return true;
}
}
}
EvalScriptGuard esg(cx, linearStr);
JSPrincipals *principals = PrincipalsForCompiledCode(args, cx);
if (evalType == DIRECT_EVAL && caller->isNonEvalFunctionFrame())
esg.lookupInEvalCache(caller, staticLevel, principals, *scopeobj);
if (!esg.foundScript()) {
unsigned lineno;
const char *filename;
JSPrincipals *originPrincipals;
CurrentScriptFileLineOrigin(cx, &filename, &lineno, &originPrincipals,
evalType == DIRECT_EVAL ? CALLED_FROM_JSOP_EVAL
: NOT_CALLED_FROM_JSOP_EVAL);
bool compileAndGo = true;
bool noScriptRval = false;
bool needScriptGlobal = false;
JSScript *compiled = frontend::CompileScript(cx, scopeobj, caller,
principals, originPrincipals,
compileAndGo, noScriptRval, needScriptGlobal,
chars, length, filename,
lineno, cx->findVersion(), linearStr,
staticLevel);
if (!compiled)
return false;
esg.setNewScript(compiled);
}
return ExecuteKernel(cx, esg.script(), *scopeobj, thisv, ExecuteType(evalType),
NULL /* evalInFrame */, &args.rval());
}
/*
* We once supported a second argument to eval to use as the scope chain
* when evaluating the code string. Warn when such uses are seen so that
* authors will know that support for eval(s, o) has been removed.
*/
static inline bool
WarnOnTooManyArgs(JSContext *cx, const CallArgs &args)
{
if (args.length() > 1) {
if (JSScript *script = cx->stack.currentScript()) {
if (!script->warnedAboutTwoArgumentEval) {
static const char TWO_ARGUMENT_WARNING[] =
"Support for eval(code, scopeObject) has been removed. "
"Use |with (scopeObject) eval(code);| instead.";
if (!JS_ReportWarning(cx, TWO_ARGUMENT_WARNING))
return false;
script->warnedAboutTwoArgumentEval = true;
}
} else {
/*
* In the case of an indirect call without a caller frame, avoid a
* potential warning-flood by doing nothing.
*/
}
}
return true;
}
namespace js {
/*
* ES5 15.1.2.1.
*
* NB: This method handles only indirect eval.
*/
JSBool
eval(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (!WarnOnTooManyArgs(cx, args))
return false;
Rooted<GlobalObject*> global(cx, &args.callee().global());
return EvalKernel(cx, args, INDIRECT_EVAL, NULL, global);
}
bool
DirectEval(JSContext *cx, const CallArgs &args)
{
/* Direct eval can assume it was called from an interpreted frame. */
StackFrame *caller = cx->fp();
JS_ASSERT(caller->isScriptFrame());
JS_ASSERT(IsBuiltinEvalForScope(caller->scopeChain(), args.calleev()));
JS_ASSERT(JSOp(*cx->regs().pc) == JSOP_EVAL);
AutoFunctionCallProbe callProbe(cx, args.callee().toFunction(), caller->script());
if (!WarnOnTooManyArgs(cx, args))
return false;
return EvalKernel(cx, args, DIRECT_EVAL, caller, caller->scopeChain());
}
bool
IsBuiltinEvalForScope(JSObject *scopeChain, const Value &v)
{
return scopeChain->global().getOriginalEval() == v;
}
bool
IsAnyBuiltinEval(JSFunction *fun)
{
return fun->maybeNative() == eval;
}
JSPrincipals *
PrincipalsForCompiledCode(const CallReceiver &call, JSContext *cx)
{
JS_ASSERT(IsAnyBuiltinEval(call.callee().toFunction()) ||
IsBuiltinFunctionConstructor(call.callee().toFunction()));
/*
* To compute the principals of the compiled eval/Function code, we simply
* use the callee's principals. To see why the caller's principals are
* ignored, consider first that, in the capability-model we assume, the
* high-privileged eval/Function should never have escaped to the
* low-privileged caller. (For the Mozilla embedding, this is brute-enforced
* by explicit filtering by wrappers.) Thus, the caller's privileges should
* subsume the callee's.
*
* In the converse situation, where the callee has lower privileges than the
* caller, we might initially guess that the caller would want to retain
* their higher privileges in the generated code. However, since the
* compiled code will be run with the callee's scope chain, this would make
* fp->script()->compartment() != fp->compartment().
*/
return call.callee().principals(cx);
}
} /* namespace js */
#if JS_HAS_OBJ_WATCHPOINT
static JSBool
@ -4670,7 +5123,7 @@ js_GetPropertyHelperInline(JSContext *cx, HandleObject obj, HandleObject receive
* XXX do not warn about missing __iterator__ as the function
* may be called from JS_GetMethodById. See bug 355145.
*/
if (JSID_IS_ATOM(id, cx->runtime->atomState.iteratorIntrinsicAtom))
if (JSID_IS_ATOM(id, cx->runtime->atomState.iteratorAtom))
return JS_TRUE;
/* Do not warn about tests like (obj[prop] == undefined). */

View File

@ -209,6 +209,7 @@ extern Class DateClass;
extern Class ErrorClass;
extern Class ElementIteratorClass;
extern Class GeneratorClass;
extern Class IteratorClass;
extern Class JSONClass;
extern Class MathClass;
extern Class NumberClass;
@ -239,7 +240,6 @@ class NestedScopeObject;
class NewObjectCache;
class NormalArgumentsObject;
class NumberObject;
class PropertyIteratorObject;
class ScopeObject;
class StaticBlockObject;
class StrictArgumentsObject;
@ -646,6 +646,9 @@ struct JSObject : public js::ObjectImpl
static const uint32_t ITER_CLASS_NFIXED_SLOTS = 1;
inline js::NativeIterator *getNativeIterator() const;
inline void setNativeIterator(js::NativeIterator *);
/*
* XML-related getters and setters.
*/
@ -898,9 +901,9 @@ struct JSObject : public js::ObjectImpl
inline bool isFunction() const;
inline bool isGenerator() const;
inline bool isGlobal() const;
inline bool isIterator() const;
inline bool isObject() const;
inline bool isPrimitive() const;
inline bool isPropertyIterator() const;
inline bool isProxy() const;
inline bool isRegExp() const;
inline bool isRegExpStatics() const;
@ -954,7 +957,6 @@ struct JSObject : public js::ObjectImpl
inline js::NestedScopeObject &asNestedScope();
inline js::NormalArgumentsObject &asNormalArguments();
inline js::NumberObject &asNumber();
inline js::PropertyIteratorObject &asPropertyIterator();
inline js::RegExpObject &asRegExp();
inline js::ScopeObject &asScope();
inline js::StrictArgumentsObject &asStrictArguments();
@ -1370,6 +1372,32 @@ SetProto(JSContext *cx, HandleObject obj, HandleObject proto, bool checkForCycle
extern JSString *
obj_toStringHelper(JSContext *cx, JSObject *obj);
extern JSBool
eval(JSContext *cx, unsigned argc, Value *vp);
/*
* Performs a direct eval for the given arguments, which must correspond to the
* currently-executing stack frame, which must be a script frame. On completion
* the result is returned in args.rval.
*/
extern bool
DirectEval(JSContext *cx, const CallArgs &args);
/*
* True iff |v| is the built-in eval function for the global object that
* corresponds to |scopeChain|.
*/
extern bool
IsBuiltinEvalForScope(JSObject *scopeChain, const js::Value &v);
/* True iff fun is a built-in eval function. */
extern bool
IsAnyBuiltinEval(JSFunction *fun);
/* 'call' should be for the eval/Function native invocation. */
extern JSPrincipals *
PrincipalsForCompiledCode(const CallReceiver &call, JSContext *cx);
extern JSObject *
NonNullObject(JSContext *cx, const Value &v);

View File

@ -561,6 +561,18 @@ JSObject::setDateUTCTime(const js::Value &time)
setFixedSlot(JSSLOT_DATE_UTC_TIME, time);
}
inline js::NativeIterator *
JSObject::getNativeIterator() const
{
return (js::NativeIterator *) getPrivate();
}
inline void
JSObject::setNativeIterator(js::NativeIterator *ni)
{
setPrivate(ni);
}
#if JS_HAS_XML_SUPPORT
inline JSLinearString *
@ -787,6 +799,7 @@ inline bool JSObject::isError() const { return hasClass(&js::ErrorClass); }
inline bool JSObject::isFunction() const { return hasClass(&js::FunctionClass); }
inline bool JSObject::isFunctionProxy() const { return hasClass(&js::FunctionProxyClass); }
inline bool JSObject::isGenerator() const { return hasClass(&js::GeneratorClass); }
inline bool JSObject::isIterator() const { return hasClass(&js::IteratorClass); }
inline bool JSObject::isNestedScope() const { return isBlock() || isWith(); }
inline bool JSObject::isNormalArguments() const { return hasClass(&js::NormalArgumentsObjectClass); }
inline bool JSObject::isNumber() const { return hasClass(&js::NumberClass); }

View File

@ -15,36 +15,7 @@ namespace js {
class Wrapper;
/*
* A proxy is a JSObject that implements generic behavior by providing custom
* implementations for each object trap. The implementation for each trap is
* provided by a C++ object stored on the proxy, known as its handler.
*
* A major use case for proxies is to forward each trap to another JSObject,
* known as its target. Not every proxy have the notion of a target, however.
*
* Proxy traps are grouped into fundamental and derived traps. Every proxy has
* to at least provide implementations for the fundamental traps, but the
* derived traps can be implemented in terms of the fundamental ones.
*
* To minimize code duplication, a set of abstract proxy handler classes is
* provided, from which other handlers may inherit. These abstract classes
* are organized in the following hierarchy:
*
* BaseProxyHandler
* |
* IndirectProxyHandler
* |
* DirectProxyHandler
*/
/*
* BaseProxyHandler is the most generic kind of proxy handler. It does not have
* the notion of a target. Consequently, it does not provide any default
* implementation for the fundamental traps. It does, however, implement the
* derived traps in terms of the fundamental ones. This allows consumers of this
* class to define any custom behavior they want.
*/
/* Base class for all C++ proxy handlers. */
class JS_FRIEND_API(BaseProxyHandler) {
void *mFamily;
public:
@ -74,29 +45,24 @@ class JS_FRIEND_API(BaseProxyHandler) {
}
/* ES5 Harmony fundamental proxy traps. */
virtual bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,
bool set, PropertyDescriptor *desc) = 0;
virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy,
jsid id, bool set,
virtual bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
PropertyDescriptor *desc) = 0;
virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
PropertyDescriptor *desc) = 0;
virtual bool defineProperty(JSContext *cx, JSObject *proxy, jsid id,
PropertyDescriptor *desc) = 0;
virtual bool getOwnPropertyNames(JSContext *cx, JSObject *proxy,
AutoIdVector &props) = 0;
virtual bool getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props) = 0;
virtual bool delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp) = 0;
virtual bool enumerate(JSContext *cx, JSObject *proxy,
AutoIdVector &props) = 0;
virtual bool enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props) = 0;
/* ES5 Harmony derived proxy traps. */
virtual bool has(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
virtual bool hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
virtual bool get(JSContext *cx, JSObject *proxy, JSObject *receiver,
jsid id, Value *vp);
virtual bool set(JSContext *cx, JSObject *proxy, JSObject *receiver,
jsid id, bool strict, Value *vp);
virtual bool get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp);
virtual bool set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, bool strict,
Value *vp);
virtual bool keys(JSContext *cx, JSObject *proxy, AutoIdVector &props);
virtual bool iterate(JSContext *cx, JSObject *proxy, unsigned flags,
Value *vp);
virtual bool iterate(JSContext *cx, JSObject *proxy, unsigned flags, Value *vp);
/* Spidermonkey extensions. */
virtual bool call(JSContext *cx, JSObject *proxy, unsigned argc, Value *vp);
@ -115,14 +81,6 @@ class JS_FRIEND_API(BaseProxyHandler) {
uint32_t index, Value *vp, bool *present);
};
/*
* IndirectProxyHandler assumes the existence of a target. Consequently, it
* provides default implementations for the fundamental traps that forward their
* behavior to the target. The derived traps, however, are inherited from
* BaseProxyHandler, and therefore still implemented in terms of the fundamental
* ones. This allows consumers of this class to define custom behavior without
* implementing the entire gamut of proxy traps.
*/
class JS_PUBLIC_API(IndirectProxyHandler) : public BaseProxyHandler {
public:
explicit IndirectProxyHandler(void *family);
@ -143,6 +101,8 @@ class JS_PUBLIC_API(IndirectProxyHandler) : public BaseProxyHandler {
virtual bool enumerate(JSContext *cx, JSObject *proxy,
AutoIdVector &props) MOZ_OVERRIDE;
/* Derived traps are implemented in terms of fundamental traps */
/* Spidermonkey extensions. */
virtual bool call(JSContext *cx, JSObject *proxy, unsigned argc,
Value *vp) MOZ_OVERRIDE;
@ -166,14 +126,6 @@ class JS_PUBLIC_API(IndirectProxyHandler) : public BaseProxyHandler {
Value *vp) MOZ_OVERRIDE;
};
/*
* DirectProxyHandler has the same assumptions about the target as its base,
* IndirectProxyHandler. Its fundamental traps are inherited from this class,
* and therefore forward their behavior to the target. The derived traps,
* however, are overrided so that, they too, forward their behavior to the
* target. This allows consumers of this class to forward to another object as
* transparently as possible.
*/
class JS_PUBLIC_API(DirectProxyHandler) : public IndirectProxyHandler {
public:
explicit DirectProxyHandler(void *family);

View File

@ -2975,7 +2975,6 @@ static JSFunctionSpec string_methods[] = {
JS_FN("sub", str_sub, 0,0),
#endif
JS_FN("iterator", JS_ArrayIterator, 0,0),
JS_FS_END
};

View File

@ -2882,7 +2882,6 @@ JSFunctionSpec ArrayBufferObject::jsfuncs[] = {
#define IMPL_TYPED_ARRAY_STATICS(_typedArray) \
JSFunctionSpec _typedArray::jsfuncs[] = { \
JS_FN("iterator", JS_ArrayIterator, 0, 0), \
JS_FN("subarray", _typedArray::fun_subarray, 2, JSFUN_GENERIC_NATIVE), \
JS_FN("set", _typedArray::fun_set, 2, JSFUN_GENERIC_NATIVE), \
JS_FN("move", _typedArray::fun_move, 3, JSFUN_GENERIC_NATIVE), \
@ -2948,6 +2947,7 @@ IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Float64, double)
JSCLASS_HAS_RESERVED_SLOTS(TypedArray::FIELD_MAX) | \
JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | \
JSCLASS_HAS_CACHED_PROTO(JSProto_##_typedArray) | \
JSCLASS_FOR_OF_ITERATION | \
Class::NON_NATIVE, \
JS_PropertyStub, /* addProperty */ \
JS_PropertyStub, /* delProperty */ \
@ -2966,7 +2966,7 @@ IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Float64, double)
NULL, /* equality */ \
NULL, /* outerObject */ \
NULL, /* innerObject */ \
NULL, /* iteratorObject */ \
JS_ElementIteratorStub, \
NULL, /* unused */ \
false, /* isWrappedNative */ \
}, \

View File

@ -338,6 +338,26 @@ ClearAllBitArrayElements(size_t *array, size_t length)
} /* namespace js */
#endif /* __cplusplus */
/*
* JS_ROTATE_LEFT32
*
* There is no rotate operation in the C Language so the construct (a << 4) |
* (a >> 28) is used instead. Most compilers convert this to a rotate
* instruction but some versions of MSVC don't without a little help. To get
* MSVC to generate a rotate instruction, we have to use the _rotl intrinsic
* and use a pragma to make _rotl inline.
*
* MSVC in VS2005 will do an inline rotate instruction on the above construct.
*/
#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64) || \
defined(_M_X64))
#include <stdlib.h>
#pragma intrinsic(_rotl)
#define JS_ROTATE_LEFT32(a, bits) _rotl(a, bits)
#else
#define JS_ROTATE_LEFT32(a, bits) (((a) << (bits)) | ((a) >> (32 - (bits))))
#endif
/* Static control-flow checks. */
#ifdef NS_STATIC_CHECKING
/* Trigger a control flow check to make sure that code flows through label */

View File

@ -218,7 +218,6 @@ typedef enum JSWhyMagic
JS_OVERWRITTEN_CALLEE, /* arguments.callee has been overwritten */
JS_FORWARD_TO_CALL_OBJECT, /* args object element stored in call object */
JS_BLOCK_NEEDS_CLONE, /* value of static block object slot */
JS_HASH_KEY_EMPTY, /* see class js::HashableValue */
JS_GENERIC_MAGIC /* for local use */
} JSWhyMagic;

View File

@ -23,7 +23,6 @@
#include "jsobjinlines.h"
#include "builtin/Iterator-inl.h"
#include "vm/RegExpObject-inl.h"
using namespace js;
@ -125,7 +124,7 @@ js::IsCrossCompartmentWrapper(const JSObject *wrapper)
!!(Wrapper::wrapperHandler(wrapper)->flags() & Wrapper::CROSS_COMPARTMENT);
}
IndirectWrapper::IndirectWrapper(unsigned flags) : Wrapper(flags),
AbstractWrapper::AbstractWrapper(unsigned flags) : Wrapper(flags),
IndirectProxyHandler(&sWrapperFamily)
{
}
@ -144,7 +143,7 @@ IndirectWrapper::IndirectWrapper(unsigned flags) : Wrapper(flags),
#define GET(action) CHECKED(action, GET)
bool
IndirectWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper,
AbstractWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper,
jsid id, bool set,
PropertyDescriptor *desc)
{
@ -154,7 +153,7 @@ IndirectWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper,
}
bool
IndirectWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper,
AbstractWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper,
jsid id, bool set,
PropertyDescriptor *desc)
{
@ -163,14 +162,14 @@ IndirectWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper,
}
bool
IndirectWrapper::defineProperty(JSContext *cx, JSObject *wrapper, jsid id,
AbstractWrapper::defineProperty(JSContext *cx, JSObject *wrapper, jsid id,
PropertyDescriptor *desc)
{
SET(IndirectProxyHandler::defineProperty(cx, wrapper, id, desc));
}
bool
IndirectWrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapper,
AbstractWrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapper,
AutoIdVector &props)
{
// if we refuse to perform this action, props remains empty
@ -179,14 +178,14 @@ IndirectWrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapper,
}
bool
IndirectWrapper::delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
AbstractWrapper::delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
{
*bp = true; // default result if we refuse to perform this action
SET(IndirectProxyHandler::delete_(cx, wrapper, id, bp));
}
bool
IndirectWrapper::enumerate(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
AbstractWrapper::enumerate(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
{
// if we refuse to perform this action, props remains empty
static jsid id = JSID_VOID;
@ -611,8 +610,8 @@ CanReify(Value *vp)
{
JSObject *obj;
return vp->isObject() &&
(obj = &vp->toObject())->isPropertyIterator() &&
(obj->asPropertyIterator().getNativeIterator()->flags & JSITER_ENUMERATE);
(obj = &vp->toObject())->getClass() == &IteratorClass &&
(obj->getNativeIterator()->flags & JSITER_ENUMERATE);
}
struct AutoCloseIterator
@ -631,7 +630,7 @@ struct AutoCloseIterator
static bool
Reify(JSContext *cx, JSCompartment *origin, Value *vp)
{
PropertyIteratorObject *iterObj = &vp->toObject().asPropertyIterator();
JSObject *iterObj = &vp->toObject();
NativeIterator *ni = iterObj->getNativeIterator();
AutoCloseIterator close(cx, iterObj);

View File

@ -17,27 +17,7 @@ namespace js {
class DummyFrameGuard;
/*
* A wrapper is essentially a proxy that restricts access to certain traps. The
* way in which a wrapper restricts access to its traps depends on the
* particular policy for that wrapper. To allow a wrapper's policy to be
* customized, the Wrapper base class contains two functions, enter/leave, which
* are called as a policy enforcement check before/after each trap is forwarded.
*
* To minimize code duplication, a set of abstract wrapper classes is
* provided, from which other wrappers may inherit. These abstract classes are
* organized in the following hierarchy:
*
* BaseProxyHandler Wrapper
* | | |
* IndirectProxyHandler | |
* | | | |
* | IndirectWrapper |
* | |
* DirectProxyHandler |
* | |
* DirectWrapper
*/
/* Base class for all C++ wrappers. */
class JS_FRIEND_API(Wrapper)
{
unsigned mFlags;
@ -124,16 +104,14 @@ class JS_FRIEND_API(Wrapper)
};
/*
* IndirectWrapper forwards its traps by forwarding them to
* IndirectProxyHandler. In effect, IndirectWrapper behaves the same as
* IndirectProxyHandler, except that it adds policy enforcement checks to each
* fundamental trap.
* AbstractWrappers forward their fundamental traps to the wrapped object, and
* implement their derived traps in terms of the fundamental traps.
*/
class JS_FRIEND_API(IndirectWrapper) : public Wrapper,
class JS_FRIEND_API(AbstractWrapper) : public Wrapper,
public IndirectProxyHandler
{
public:
explicit IndirectWrapper(unsigned flags);
explicit AbstractWrapper(unsigned flags);
virtual BaseProxyHandler* toBaseProxyHandler() {
return this;
@ -161,9 +139,8 @@ class JS_FRIEND_API(IndirectWrapper) : public Wrapper,
};
/*
* DirectWrapper forwards its traps by forwarding them to DirectProxyHandler.
* In effect, DirectWrapper behaves the same as DirectProxyHandler, except that
* it adds policy enforcement checks to each trap.
* DirectWrappers forward both their fundamental and derived traps to the
* wrapped object.
*/
class JS_FRIEND_API(DirectWrapper) : public Wrapper, public DirectProxyHandler
{

View File

@ -6090,8 +6090,7 @@ mjit::Compiler::iterNext(ptrdiff_t offset)
frame.unpinReg(reg);
/* Test clasp */
Jump notFast = masm.testObjClass(Assembler::NotEqual, reg, T1,
&PropertyIteratorObject::class_);
Jump notFast = masm.testObjClass(Assembler::NotEqual, reg, T1, &IteratorClass);
stubcc.linkExit(notFast, Uses(1));
/* Get private from iter obj. */
@ -6142,8 +6141,7 @@ mjit::Compiler::iterMore(jsbytecode *target)
RegisterID tempreg = frame.allocReg();
/* Test clasp */
Jump notFast = masm.testObjClass(Assembler::NotEqual, reg, tempreg,
&PropertyIteratorObject::class_);
Jump notFast = masm.testObjClass(Assembler::NotEqual, reg, tempreg, &IteratorClass);
stubcc.linkExitForBranch(notFast);
/* Get private from iter obj. */
@ -6182,8 +6180,7 @@ mjit::Compiler::iterEnd()
frame.unpinReg(reg);
/* Test clasp */
Jump notIterator = masm.testObjClass(Assembler::NotEqual, reg, T1,
&PropertyIteratorObject::class_);
Jump notIterator = masm.testObjClass(Assembler::NotEqual, reg, T1, &IteratorClass);
stubcc.linkExit(notIterator, Uses(1));
/* Get private from iter obj. */

View File

@ -5,7 +5,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jsanalyze.h"
#include "jscntxt.h"
#include "jsscope.h"
#include "jsobj.h"
@ -14,13 +13,12 @@
#include "jsnum.h"
#include "jsxml.h"
#include "jsbool.h"
#include "jstypes.h"
#include "assembler/assembler/MacroAssemblerCodeRef.h"
#include "assembler/assembler/CodeLocation.h"
#include "builtin/Eval.h"
#include "jstypes.h"
#include "methodjit/StubCalls.h"
#include "methodjit/MonoIC.h"
#include "jsanalyze.h"
#include "methodjit/BaseCompiler.h"
#include "methodjit/ICRepatcher.h"
#include "vm/Debugger.h"
@ -31,7 +29,6 @@
#include "jsobjinlines.h"
#include "jscntxtinlines.h"
#include "jsatominlines.h"
#include "StubCalls-inl.h"
#include "jsautooplen.h"

View File

@ -421,7 +421,8 @@ Class js::NormalArgumentsObjectClass = {
"Arguments",
JSCLASS_NEW_RESOLVE | JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_RESERVED_SLOTS(NormalArgumentsObject::RESERVED_SLOTS) |
JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
JSCLASS_FOR_OF_ITERATION,
JS_PropertyStub, /* addProperty */
args_delProperty,
JS_PropertyStub, /* getProperty */
@ -439,7 +440,7 @@ Class js::NormalArgumentsObjectClass = {
NULL, /* equality */
NULL, /* outerObject */
NULL, /* innerObject */
NULL, /* iteratorObject */
JS_ElementIteratorStub,
NULL, /* unused */
false, /* isWrappedNative */
}
@ -454,7 +455,8 @@ Class js::StrictArgumentsObjectClass = {
"Arguments",
JSCLASS_NEW_RESOLVE | JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_RESERVED_SLOTS(StrictArgumentsObject::RESERVED_SLOTS) |
JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
JSCLASS_FOR_OF_ITERATION,
JS_PropertyStub, /* addProperty */
args_delProperty,
JS_PropertyStub, /* getProperty */
@ -472,7 +474,7 @@ Class js::StrictArgumentsObjectClass = {
NULL, /* equality */
NULL, /* outerObject */
NULL, /* innerObject */
NULL, /* iteratorObject */
JS_ElementIteratorStub,
NULL, /* unused */
false, /* isWrappedNative */
}

View File

@ -14,14 +14,12 @@
#include "json.h"
#include "jsweakmap.h"
#include "builtin/Eval.h"
#include "builtin/MapObject.h"
#include "builtin/RegExp.h"
#include "frontend/BytecodeEmitter.h"
#include "vm/GlobalObject-inl.h"
#include "jsobjinlines.h"
#include "vm/RegExpObject-inl.h"
#include "vm/RegExpStatics-inl.h"
@ -206,7 +204,7 @@ GlobalObject::initFunctionAndObjectClasses(JSContext *cx)
/* ES5 15.1.2.1. */
RootedId id(cx, NameToId(cx->runtime->atomState.evalAtom));
JSObject *evalobj = js_DefineFunction(cx, self, id, IndirectEval, 1, JSFUN_STUB_GSOPS);
JSObject *evalobj = js_DefineFunction(cx, self, id, eval, 1, JSFUN_STUB_GSOPS);
if (!evalobj)
return NULL;
self->setOriginalEval(evalobj);

View File

@ -71,8 +71,7 @@ class GlobalObject : public JSObject
/* One-off properties stored after slots for built-ins. */
static const unsigned THROWTYPEERROR = STANDARD_CLASS_SLOTS;
static const unsigned ELEMENT_ITERATOR_PROTO = THROWTYPEERROR + 1;
static const unsigned GENERATOR_PROTO = ELEMENT_ITERATOR_PROTO + 1;
static const unsigned GENERATOR_PROTO = THROWTYPEERROR + 1;
static const unsigned REGEXP_STATICS = GENERATOR_PROTO + 1;
static const unsigned FUNCTION_NS = REGEXP_STATICS + 1;
static const unsigned RUNTIME_CODEGEN_ENABLED = FUNCTION_NS + 1;
@ -268,28 +267,14 @@ class GlobalObject : public JSObject
return &self->getPrototype(key).toObject();
}
JSObject *getIteratorPrototype() {
return &getPrototype(JSProto_Iterator).toObject();
}
private:
JSObject *getOrCreateIteratorSubclassPrototype(JSContext *cx, unsigned slot) {
Value v = getSlotRef(slot);
JSObject *getOrCreateGeneratorPrototype(JSContext *cx) {
Value v = getSlotRef(GENERATOR_PROTO);
if (v.isObject())
return &v.toObject();
Rooted<GlobalObject*> self(cx, this);
if (!initIteratorClasses(cx, self))
if (!js_InitIteratorClasses(cx, this))
return NULL;
return &self->getSlot(slot).toObject();
}
public:
JSObject *getOrCreateElementIteratorPrototype(JSContext *cx) {
return getOrCreateIteratorSubclassPrototype(cx, ELEMENT_ITERATOR_PROTO);
}
JSObject *getOrCreateGeneratorPrototype(JSContext *cx) {
return getOrCreateIteratorSubclassPrototype(cx, GENERATOR_PROTO);
return &self->getSlot(GENERATOR_PROTO).toObject();
}
inline RegExpStatics *getRegExpStatics() const;
@ -314,9 +299,7 @@ class GlobalObject : public JSObject
bool getFunctionNamespace(JSContext *cx, Value *vp);
// Implemented in jsiter.cpp.
static bool initIteratorClasses(JSContext *cx, Handle<GlobalObject*> global);
static bool initGeneratorClass(JSContext *cx, Handle<GlobalObject*> global);
static bool initStandardClasses(JSContext *cx, Handle<GlobalObject*> global);
typedef js::Vector<js::Debugger *, 0, js::SystemAllocPolicy> DebuggerVector;

View File

@ -37,8 +37,6 @@ static jsid s_prototype_id = JSID_VOID;
static jsid s_length_id = JSID_VOID;
static jsid s_iterator_id = JSID_VOID;
static jsid s_VOID_id = JSID_VOID;
bool
@ -61,7 +59,6 @@ DefineStaticJSVals(JSContext *cx)
return SET_JSID_TO_STRING(cx, prototype) &&
SET_JSID_TO_STRING(cx, length) &&
SET_JSID_TO_STRING(cx, iterator) &&
DefinePropertyStaticJSVals(cx);
}
@ -1166,6 +1163,13 @@ template<class LC>
bool
ListBase<LC>::iterate(JSContext *cx, JSObject *proxy, unsigned flags, Value *vp)
{
if (flags == JSITER_FOR_OF) {
JSObject *iterobj = JS_NewElementIterator(cx, proxy);
if (!iterobj)
return false;
vp->setObject(*iterobj);
return true;
}
return ProxyHandler::iterate(cx, proxy, flags, vp);
}

View File

@ -696,7 +696,6 @@ def writeStubFile(filename, config, interfaces):
if clazz.indexGetter:
#methodsList.append(" { s_%s_id, &item, 1 }" % clazz.indexGetter.name)
f.write(string.Template(indexGetterTemplate).substitute(clazz))
methodsList.append(" { s_iterator_id, JS_ArrayIterator, 0}")
if clazz.indexSetter:
f.write(string.Template(indexSetterTemplate).substitute(clazz))
if clazz.nameGetter:

View File

@ -458,7 +458,7 @@ static inline JSObject *
FindWrapper(JSObject *wrapper)
{
while (!js::IsWrapper(wrapper) ||
!(Wrapper::wrapperHandler(wrapper)->flags() &
!(AbstractWrapper::wrapperHandler(wrapper)->flags() &
WrapperFactory::IS_XRAY_WRAPPER_FLAG)) {
if (js::IsWrapper(wrapper) &&
js::GetProxyHandler(wrapper) == &sandboxProxyHandler) {

View File

@ -86,9 +86,9 @@ class XrayWrapper : public Base {
typedef XrayWrapper<js::CrossCompartmentWrapper, ProxyXrayTraits > XrayProxy;
typedef XrayWrapper<js::CrossCompartmentWrapper, DOMXrayTraits > XrayDOM;
class SandboxProxyHandler : public js::IndirectWrapper {
class SandboxProxyHandler : public js::AbstractWrapper {
public:
SandboxProxyHandler() : js::IndirectWrapper(0)
SandboxProxyHandler() : js::AbstractWrapper(0)
{
}