mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge backouts
This commit is contained in:
commit
5842de901d
@ -63,8 +63,6 @@ _TEST_FILES = \
|
||||
test_lookupGetter.html \
|
||||
test_InstanceOf.html \
|
||||
test_traceProtos.html \
|
||||
test_forOf.html \
|
||||
forOf_iframe.html \
|
||||
$(NULL)
|
||||
|
||||
|
||||
|
@ -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>
|
@ -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>
|
@ -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))
|
||||
|
@ -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 {
|
||||
|
@ -130,7 +130,6 @@ CPPSRCS = \
|
||||
TreeContext.cpp \
|
||||
TestingFunctions.cpp \
|
||||
LifoAlloc.cpp \
|
||||
Eval.cpp \
|
||||
MapObject.cpp \
|
||||
MemoryMetrics.cpp \
|
||||
RegExpObject.cpp \
|
||||
|
@ -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);
|
||||
}
|
@ -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__
|
@ -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_
|
@ -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;
|
||||
}
|
||||
|
@ -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
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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();
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
||||
/*
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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');
|
||||
|
@ -1,7 +1,5 @@
|
||||
// for-of can iterate strict arguments objects.
|
||||
|
||||
Object.prototype.iterator = Array.prototype.iterator;
|
||||
|
||||
var s;
|
||||
function test() {
|
||||
"use strict";
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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');
|
||||
|
@ -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);
|
@ -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({});
|
@ -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});
|
@ -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);
|
@ -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);
|
||||
}
|
@ -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");
|
@ -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);
|
@ -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);
|
||||
}
|
@ -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));
|
@ -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);
|
@ -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);
|
@ -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);
|
@ -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);
|
@ -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];
|
||||
|
@ -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)
|
||||
|
@ -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];
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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);
|
@ -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({});
|
@ -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');
|
@ -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);
|
@ -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);
|
@ -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);
|
@ -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.');
|
@ -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';
|
@ -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');
|
@ -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);
|
@ -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');
|
@ -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'}));
|
@ -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'));
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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. */
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
}
|
||||
|
108
js/src/jsiter.h
108
js/src/jsiter.h
@ -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];
|
||||
|
455
js/src/jsobj.cpp
455
js/src/jsobj.cpp
@ -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). */
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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); }
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -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 */ \
|
||||
}, \
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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. */
|
||||
|
@ -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"
|
||||
|
@ -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 */
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user